VirtualBox

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

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

VMM,Main,++: Retired the unfinished FTM component.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 534.6 KB
Line 
1/* $Id: MachineImpl.cpp 80074 2019-07-31 14:18:34Z 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
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mMDSClearOnSched = true;
201 mMDSClearOnVMEntry = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mDnDMode = DnDMode_Disabled;
217
218 mFirmwareType = FirmwareType_BIOS;
219 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
220 mPointingHIDType = PointingHIDType_PS2Mouse;
221 mChipsetType = ChipsetType_PIIX3;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param strOsType OS Type string (stored as is if aOsType is NULL).
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 const Utf8Str &strOsType,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 if (llGroups.size())
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352 }
353 else if (!strOsType.isEmpty())
354 {
355 /* Store OS type */
356 mUserData->s.strOsType = strOsType;
357
358 /* No guest OS type object. Pick some plausible defaults which the
359 * host can handle. There's no way to know or validate anything. */
360 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361 mHWData->mX2APIC = false;
362 }
363
364 /* Apply BIOS defaults. */
365 mBIOSSettings->i_applyDefaults(aOsType);
366
367 /* Apply record defaults. */
368 mRecordingSettings->i_applyDefaults();
369
370 /* Apply network adapters defaults */
371 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
372 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
373
374 /* Apply serial port defaults */
375 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
376 mSerialPorts[slot]->i_applyDefaults(aOsType);
377
378 /* Apply parallel port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
380 mParallelPorts[slot]->i_applyDefaults();
381
382 /* At this point the changing of the current state modification
383 * flag is allowed. */
384 i_allowStateModification();
385
386 /* commit all changes made during the initialization */
387 i_commit();
388 }
389
390 /* Confirm a successful initialization when it's the case */
391 if (SUCCEEDED(rc))
392 {
393 if (mData->mAccessible)
394 autoInitSpan.setSucceeded();
395 else
396 autoInitSpan.setLimited();
397 }
398
399 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
400 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
401 mData->mRegistered,
402 mData->mAccessible,
403 rc));
404
405 LogFlowThisFuncLeave();
406
407 return rc;
408}
409
410/**
411 * Initializes a new instance with data from machine XML (formerly Init_Registered).
412 * Gets called in two modes:
413 *
414 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
415 * UUID is specified and we mark the machine as "registered";
416 *
417 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
418 * and the machine remains unregistered until RegisterMachine() is called.
419 *
420 * @param aParent Associated parent object
421 * @param strConfigFile Local file system path to the VM settings file (can
422 * be relative to the VirtualBox config directory).
423 * @param aId UUID of the machine or NULL (see above).
424 *
425 * @return Success indicator. if not S_OK, the machine object is invalid
426 */
427HRESULT Machine::initFromSettings(VirtualBox *aParent,
428 const Utf8Str &strConfigFile,
429 const Guid *aId)
430{
431 LogFlowThisFuncEnter();
432 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
433
434 /* Enclose the state transition NotReady->InInit->Ready */
435 AutoInitSpan autoInitSpan(this);
436 AssertReturn(autoInitSpan.isOk(), E_FAIL);
437
438 HRESULT rc = initImpl(aParent, strConfigFile);
439 if (FAILED(rc)) return rc;
440
441 if (aId)
442 {
443 // loading a registered VM:
444 unconst(mData->mUuid) = *aId;
445 mData->mRegistered = TRUE;
446 // now load the settings from XML:
447 rc = i_registeredInit();
448 // this calls initDataAndChildObjects() and loadSettings()
449 }
450 else
451 {
452 // opening an unregistered VM (VirtualBox::OpenMachine()):
453 rc = initDataAndChildObjects();
454
455 if (SUCCEEDED(rc))
456 {
457 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
458 mData->mAccessible = TRUE;
459
460 try
461 {
462 // load and parse machine XML; this will throw on XML or logic errors
463 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
464
465 // reject VM UUID duplicates, they can happen if someone
466 // tries to register an already known VM config again
467 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
468 true /* fPermitInaccessible */,
469 false /* aDoSetError */,
470 NULL) != VBOX_E_OBJECT_NOT_FOUND)
471 {
472 throw setError(E_FAIL,
473 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
474 mData->m_strConfigFile.c_str());
475 }
476
477 // use UUID from machine config
478 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
479
480 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
481 NULL /* puuidRegistry */);
482 if (FAILED(rc)) throw rc;
483
484 /* At this point the changing of the current state modification
485 * flag is allowed. */
486 i_allowStateModification();
487
488 i_commit();
489 }
490 catch (HRESULT err)
491 {
492 /* we assume that error info is set by the thrower */
493 rc = err;
494 }
495 catch (...)
496 {
497 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
498 }
499 }
500 }
501
502 /* Confirm a successful initialization when it's the case */
503 if (SUCCEEDED(rc))
504 {
505 if (mData->mAccessible)
506 autoInitSpan.setSucceeded();
507 else
508 {
509 autoInitSpan.setLimited();
510
511 // uninit media from this machine's media registry, or else
512 // reloading the settings will fail
513 mParent->i_unregisterMachineMedia(i_getId());
514 }
515 }
516
517 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
518 "rc=%08X\n",
519 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
520 mData->mRegistered, mData->mAccessible, rc));
521
522 LogFlowThisFuncLeave();
523
524 return rc;
525}
526
527/**
528 * Initializes a new instance from a machine config that is already in memory
529 * (import OVF case). Since we are importing, the UUID in the machine
530 * config is ignored and we always generate a fresh one.
531 *
532 * @param aParent Associated parent object.
533 * @param strName Name for the new machine; this overrides what is specified in config.
534 * @param strSettingsFilename File name of .vbox file.
535 * @param config Machine configuration loaded and parsed from XML.
536 *
537 * @return Success indicator. if not S_OK, the machine object is invalid
538 */
539HRESULT Machine::init(VirtualBox *aParent,
540 const Utf8Str &strName,
541 const Utf8Str &strSettingsFilename,
542 const settings::MachineConfigFile &config)
543{
544 LogFlowThisFuncEnter();
545
546 /* Enclose the state transition NotReady->InInit->Ready */
547 AutoInitSpan autoInitSpan(this);
548 AssertReturn(autoInitSpan.isOk(), E_FAIL);
549
550 HRESULT rc = initImpl(aParent, strSettingsFilename);
551 if (FAILED(rc)) return rc;
552
553 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
554 if (FAILED(rc)) return rc;
555
556 rc = initDataAndChildObjects();
557
558 if (SUCCEEDED(rc))
559 {
560 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
561 mData->mAccessible = TRUE;
562
563 // create empty machine config for instance data
564 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
565
566 // generate fresh UUID, ignore machine config
567 unconst(mData->mUuid).create();
568
569 rc = i_loadMachineDataFromSettings(config,
570 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
571
572 // override VM name as well, it may be different
573 mUserData->s.strName = strName;
574
575 if (SUCCEEDED(rc))
576 {
577 /* At this point the changing of the current state modification
578 * flag is allowed. */
579 i_allowStateModification();
580
581 /* commit all changes made during the initialization */
582 i_commit();
583 }
584 }
585
586 /* Confirm a successful initialization when it's the case */
587 if (SUCCEEDED(rc))
588 {
589 if (mData->mAccessible)
590 autoInitSpan.setSucceeded();
591 else
592 {
593 /* Ignore all errors from unregistering, they would destroy
594- * the more interesting error information we already have,
595- * pinpointing the issue with the VM config. */
596 ErrorInfoKeeper eik;
597
598 autoInitSpan.setLimited();
599
600 // uninit media from this machine's media registry, or else
601 // reloading the settings will fail
602 mParent->i_unregisterMachineMedia(i_getId());
603 }
604 }
605
606 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
607 "rc=%08X\n",
608 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
609 mData->mRegistered, mData->mAccessible, rc));
610
611 LogFlowThisFuncLeave();
612
613 return rc;
614}
615
616/**
617 * Shared code between the various init() implementations.
618 * @param aParent The VirtualBox object.
619 * @param strConfigFile Settings file.
620 * @return
621 */
622HRESULT Machine::initImpl(VirtualBox *aParent,
623 const Utf8Str &strConfigFile)
624{
625 LogFlowThisFuncEnter();
626
627 AssertReturn(aParent, E_INVALIDARG);
628 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
629
630 HRESULT rc = S_OK;
631
632 /* share the parent weakly */
633 unconst(mParent) = aParent;
634
635 /* allocate the essential machine data structure (the rest will be
636 * allocated later by initDataAndChildObjects() */
637 mData.allocate();
638
639 /* memorize the config file name (as provided) */
640 mData->m_strConfigFile = strConfigFile;
641
642 /* get the full file name */
643 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
644 if (RT_FAILURE(vrc1))
645 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
646 tr("Invalid machine settings file name '%s' (%Rrc)"),
647 strConfigFile.c_str(),
648 vrc1);
649
650 LogFlowThisFuncLeave();
651
652 return rc;
653}
654
655/**
656 * Tries to create a machine settings file in the path stored in the machine
657 * instance data. Used when a new machine is created to fail gracefully if
658 * the settings file could not be written (e.g. because machine dir is read-only).
659 * @return
660 */
661HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
662{
663 HRESULT rc = S_OK;
664
665 // when we create a new machine, we must be able to create the settings file
666 RTFILE f = NIL_RTFILE;
667 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
668 if ( RT_SUCCESS(vrc)
669 || vrc == VERR_SHARING_VIOLATION
670 )
671 {
672 if (RT_SUCCESS(vrc))
673 RTFileClose(f);
674 if (!fForceOverwrite)
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Machine settings file '%s' already exists"),
677 mData->m_strConfigFileFull.c_str());
678 else
679 {
680 /* try to delete the config file, as otherwise the creation
681 * of a new settings file will fail. */
682 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
683 if (RT_FAILURE(vrc2))
684 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
685 tr("Could not delete the existing settings file '%s' (%Rrc)"),
686 mData->m_strConfigFileFull.c_str(), vrc2);
687 }
688 }
689 else if ( vrc != VERR_FILE_NOT_FOUND
690 && vrc != VERR_PATH_NOT_FOUND
691 )
692 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
693 tr("Invalid machine settings file name '%s' (%Rrc)"),
694 mData->m_strConfigFileFull.c_str(),
695 vrc);
696 return rc;
697}
698
699/**
700 * Initializes the registered machine by loading the settings file.
701 * This method is separated from #init() in order to make it possible to
702 * retry the operation after VirtualBox startup instead of refusing to
703 * startup the whole VirtualBox server in case if the settings file of some
704 * registered VM is invalid or inaccessible.
705 *
706 * @note Must be always called from this object's write lock
707 * (unless called from #init() that doesn't need any locking).
708 * @note Locks the mUSBController method for writing.
709 * @note Subclasses must not call this method.
710 */
711HRESULT Machine::i_registeredInit()
712{
713 AssertReturn(!i_isSessionMachine(), E_FAIL);
714 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
715 AssertReturn(mData->mUuid.isValid(), E_FAIL);
716 AssertReturn(!mData->mAccessible, E_FAIL);
717
718 HRESULT rc = initDataAndChildObjects();
719
720 if (SUCCEEDED(rc))
721 {
722 /* Temporarily reset the registered flag in order to let setters
723 * potentially called from loadSettings() succeed (isMutable() used in
724 * all setters will return FALSE for a Machine instance if mRegistered
725 * is TRUE). */
726 mData->mRegistered = FALSE;
727
728 try
729 {
730 // load and parse machine XML; this will throw on XML or logic errors
731 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
732
733 if (mData->mUuid != mData->pMachineConfigFile->uuid)
734 throw setError(E_FAIL,
735 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
736 mData->pMachineConfigFile->uuid.raw(),
737 mData->m_strConfigFileFull.c_str(),
738 mData->mUuid.toString().c_str(),
739 mParent->i_settingsFilePath().c_str());
740
741 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
742 NULL /* const Guid *puuidRegistry */);
743 if (FAILED(rc)) throw rc;
744 }
745 catch (HRESULT err)
746 {
747 /* we assume that error info is set by the thrower */
748 rc = err;
749 }
750 catch (...)
751 {
752 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
753 }
754
755 /* Restore the registered flag (even on failure) */
756 mData->mRegistered = TRUE;
757 }
758
759 if (SUCCEEDED(rc))
760 {
761 /* Set mAccessible to TRUE only if we successfully locked and loaded
762 * the settings file */
763 mData->mAccessible = TRUE;
764
765 /* commit all changes made during loading the settings file */
766 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
767 /// @todo r=klaus for some reason the settings loading logic backs up
768 // the settings, and therefore a commit is needed. Should probably be changed.
769 }
770 else
771 {
772 /* If the machine is registered, then, instead of returning a
773 * failure, we mark it as inaccessible and set the result to
774 * success to give it a try later */
775
776 /* fetch the current error info */
777 mData->mAccessError = com::ErrorInfo();
778 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
779
780 /* rollback all changes */
781 i_rollback(false /* aNotify */);
782
783 // uninit media from this machine's media registry, or else
784 // reloading the settings will fail
785 mParent->i_unregisterMachineMedia(i_getId());
786
787 /* uninitialize the common part to make sure all data is reset to
788 * default (null) values */
789 uninitDataAndChildObjects();
790
791 rc = S_OK;
792 }
793
794 return rc;
795}
796
797/**
798 * Uninitializes the instance.
799 * Called either from FinalRelease() or by the parent when it gets destroyed.
800 *
801 * @note The caller of this method must make sure that this object
802 * a) doesn't have active callers on the current thread and b) is not locked
803 * by the current thread; otherwise uninit() will hang either a) due to
804 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
805 * a dead-lock caused by this thread waiting for all callers on the other
806 * threads are done but preventing them from doing so by holding a lock.
807 */
808void Machine::uninit()
809{
810 LogFlowThisFuncEnter();
811
812 Assert(!isWriteLockOnCurrentThread());
813
814 Assert(!uRegistryNeedsSaving);
815 if (uRegistryNeedsSaving)
816 {
817 AutoCaller autoCaller(this);
818 if (SUCCEEDED(autoCaller.rc()))
819 {
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821 i_saveSettings(NULL, Machine::SaveS_Force);
822 }
823 }
824
825 /* Enclose the state transition Ready->InUninit->NotReady */
826 AutoUninitSpan autoUninitSpan(this);
827 if (autoUninitSpan.uninitDone())
828 return;
829
830 Assert(!i_isSnapshotMachine());
831 Assert(!i_isSessionMachine());
832 Assert(!!mData);
833
834 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
835 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
836
837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
838
839 if (!mData->mSession.mMachine.isNull())
840 {
841 /* Theoretically, this can only happen if the VirtualBox server has been
842 * terminated while there were clients running that owned open direct
843 * sessions. Since in this case we are definitely called by
844 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
845 * won't happen on the client watcher thread (because it has a
846 * VirtualBox caller for the duration of the
847 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
848 * cannot happen until the VirtualBox caller is released). This is
849 * important, because SessionMachine::uninit() cannot correctly operate
850 * after we return from this method (it expects the Machine instance is
851 * still valid). We'll call it ourselves below.
852 */
853 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
854 (SessionMachine*)mData->mSession.mMachine));
855
856 if (Global::IsOnlineOrTransient(mData->mMachineState))
857 {
858 Log1WarningThisFunc(("Setting state to Aborted!\n"));
859 /* set machine state using SessionMachine reimplementation */
860 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
861 }
862
863 /*
864 * Uninitialize SessionMachine using public uninit() to indicate
865 * an unexpected uninitialization.
866 */
867 mData->mSession.mMachine->uninit();
868 /* SessionMachine::uninit() must set mSession.mMachine to null */
869 Assert(mData->mSession.mMachine.isNull());
870 }
871
872 // uninit media from this machine's media registry, if they're still there
873 Guid uuidMachine(i_getId());
874
875 /* the lock is no more necessary (SessionMachine is uninitialized) */
876 alock.release();
877
878 /* XXX This will fail with
879 * "cannot be closed because it is still attached to 1 virtual machines"
880 * because at this point we did not call uninitDataAndChildObjects() yet
881 * and therefore also removeBackReference() for all these mediums was not called! */
882
883 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
884 mParent->i_unregisterMachineMedia(uuidMachine);
885
886 // has machine been modified?
887 if (mData->flModifications)
888 {
889 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
890 i_rollback(false /* aNotify */);
891 }
892
893 if (mData->mAccessible)
894 uninitDataAndChildObjects();
895
896 /* free the essential data structure last */
897 mData.free();
898
899 LogFlowThisFuncLeave();
900}
901
902// Wrapped IMachine properties
903/////////////////////////////////////////////////////////////////////////////
904HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
905{
906 /* mParent is constant during life time, no need to lock */
907 ComObjPtr<VirtualBox> pVirtualBox(mParent);
908 aParent = pVirtualBox;
909
910 return S_OK;
911}
912
913
914HRESULT Machine::getAccessible(BOOL *aAccessible)
915{
916 /* In some cases (medium registry related), it is necessary to be able to
917 * go through the list of all machines. Happens when an inaccessible VM
918 * has a sensible medium registry. */
919 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
921
922 HRESULT rc = S_OK;
923
924 if (!mData->mAccessible)
925 {
926 /* try to initialize the VM once more if not accessible */
927
928 AutoReinitSpan autoReinitSpan(this);
929 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
930
931#ifdef DEBUG
932 LogFlowThisFunc(("Dumping media backreferences\n"));
933 mParent->i_dumpAllBackRefs();
934#endif
935
936 if (mData->pMachineConfigFile)
937 {
938 // reset the XML file to force loadSettings() (called from i_registeredInit())
939 // to parse it again; the file might have changed
940 delete mData->pMachineConfigFile;
941 mData->pMachineConfigFile = NULL;
942 }
943
944 rc = i_registeredInit();
945
946 if (SUCCEEDED(rc) && mData->mAccessible)
947 {
948 autoReinitSpan.setSucceeded();
949
950 /* make sure interesting parties will notice the accessibility
951 * state change */
952 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
953 mParent->i_onMachineDataChange(mData->mUuid);
954 }
955 }
956
957 if (SUCCEEDED(rc))
958 *aAccessible = mData->mAccessible;
959
960 LogFlowThisFuncLeave();
961
962 return rc;
963}
964
965HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
966{
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 aAccessError = errorInfo;
987 }
988
989 return rc;
990}
991
992HRESULT Machine::getName(com::Utf8Str &aName)
993{
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 aName = mUserData->s.strName;
997
998 return S_OK;
999}
1000
1001HRESULT Machine::setName(const com::Utf8Str &aName)
1002{
1003 // prohibit setting a UUID only as the machine name, or else it can
1004 // never be found by findMachine()
1005 Guid test(aName);
1006
1007 if (test.isValid())
1008 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1009
1010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 HRESULT rc = i_checkStateDependency(MutableStateDep);
1013 if (FAILED(rc)) return rc;
1014
1015 i_setModified(IsModified_MachineData);
1016 mUserData.backup();
1017 mUserData->s.strName = aName;
1018
1019 return S_OK;
1020}
1021
1022HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1023{
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 aDescription = mUserData->s.strDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1032{
1033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 // this can be done in principle in any state as it doesn't affect the VM
1036 // significantly, but play safe by not messing around while complex
1037 // activities are going on
1038 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1039 if (FAILED(rc)) return rc;
1040
1041 i_setModified(IsModified_MachineData);
1042 mUserData.backup();
1043 mUserData->s.strDescription = aDescription;
1044
1045 return S_OK;
1046}
1047
1048HRESULT Machine::getId(com::Guid &aId)
1049{
1050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 aId = mData->mUuid;
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1058{
1059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1060 aGroups.resize(mUserData->s.llGroups.size());
1061 size_t i = 0;
1062 for (StringsList::const_iterator
1063 it = mUserData->s.llGroups.begin();
1064 it != mUserData->s.llGroups.end();
1065 ++it, ++i)
1066 aGroups[i] = (*it);
1067
1068 return S_OK;
1069}
1070
1071HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1072{
1073 StringsList llGroups;
1074 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1075 if (FAILED(rc))
1076 return rc;
1077
1078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 rc = i_checkStateDependency(MutableOrSavedStateDep);
1081 if (FAILED(rc)) return rc;
1082
1083 i_setModified(IsModified_MachineData);
1084 mUserData.backup();
1085 mUserData->s.llGroups = llGroups;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1091{
1092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1093
1094 aOSTypeId = mUserData->s.strOsType;
1095
1096 return S_OK;
1097}
1098
1099HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1100{
1101 /* look up the object by Id to check it is valid */
1102 ComObjPtr<GuestOSType> pGuestOSType;
1103 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1104
1105 /* when setting, always use the "etalon" value for consistency -- lookup
1106 * by ID is case-insensitive and the input value may have different case */
1107 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1108
1109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 HRESULT rc = i_checkStateDependency(MutableStateDep);
1112 if (FAILED(rc)) return rc;
1113
1114 i_setModified(IsModified_MachineData);
1115 mUserData.backup();
1116 mUserData->s.strOsType = osTypeId;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1122{
1123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 *aFirmwareType = mHWData->mFirmwareType;
1126
1127 return S_OK;
1128}
1129
1130HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1131{
1132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 HRESULT rc = i_checkStateDependency(MutableStateDep);
1135 if (FAILED(rc)) return rc;
1136
1137 i_setModified(IsModified_MachineData);
1138 mHWData.backup();
1139 mHWData->mFirmwareType = aFirmwareType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1145{
1146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1149
1150 return S_OK;
1151}
1152
1153HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1154{
1155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 HRESULT rc = i_checkStateDependency(MutableStateDep);
1158 if (FAILED(rc)) return rc;
1159
1160 i_setModified(IsModified_MachineData);
1161 mHWData.backup();
1162 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1168{
1169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 *aPointingHIDType = mHWData->mPointingHIDType;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1177{
1178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 HRESULT rc = i_checkStateDependency(MutableStateDep);
1181 if (FAILED(rc)) return rc;
1182
1183 i_setModified(IsModified_MachineData);
1184 mHWData.backup();
1185 mHWData->mPointingHIDType = aPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1191{
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aChipsetType = mHWData->mChipsetType;
1195
1196 return S_OK;
1197}
1198
1199HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1200{
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 HRESULT rc = i_checkStateDependency(MutableStateDep);
1204 if (FAILED(rc)) return rc;
1205
1206 if (aChipsetType != mHWData->mChipsetType)
1207 {
1208 i_setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mChipsetType = aChipsetType;
1211
1212 // Resize network adapter array, to be finalized on commit/rollback.
1213 // We must not throw away entries yet, otherwise settings are lost
1214 // without a way to roll back.
1215 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1216 size_t oldCount = mNetworkAdapters.size();
1217 if (newCount > oldCount)
1218 {
1219 mNetworkAdapters.resize(newCount);
1220 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1221 {
1222 unconst(mNetworkAdapters[slot]).createObject();
1223 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1224 }
1225 }
1226 }
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1232{
1233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 aParavirtDebug = mHWData->mParavirtDebug;
1236 return S_OK;
1237}
1238
1239HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1240{
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = i_checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 /** @todo Parse/validate options? */
1247 if (aParavirtDebug != mHWData->mParavirtDebug)
1248 {
1249 i_setModified(IsModified_MachineData);
1250 mHWData.backup();
1251 mHWData->mParavirtDebug = aParavirtDebug;
1252 }
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1258{
1259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 *aParavirtProvider = mHWData->mParavirtProvider;
1262
1263 return S_OK;
1264}
1265
1266HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1267{
1268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 HRESULT rc = i_checkStateDependency(MutableStateDep);
1271 if (FAILED(rc)) return rc;
1272
1273 if (aParavirtProvider != mHWData->mParavirtProvider)
1274 {
1275 i_setModified(IsModified_MachineData);
1276 mHWData.backup();
1277 mHWData->mParavirtProvider = aParavirtProvider;
1278 }
1279
1280 return S_OK;
1281}
1282
1283HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1284{
1285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1286
1287 *aParavirtProvider = mHWData->mParavirtProvider;
1288 switch (mHWData->mParavirtProvider)
1289 {
1290 case ParavirtProvider_None:
1291 case ParavirtProvider_HyperV:
1292 case ParavirtProvider_KVM:
1293 case ParavirtProvider_Minimal:
1294 break;
1295
1296 /* Resolve dynamic provider types to the effective types. */
1297 default:
1298 {
1299 ComObjPtr<GuestOSType> pGuestOSType;
1300 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1301 pGuestOSType);
1302 if (FAILED(hrc2) || pGuestOSType.isNull())
1303 {
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1309 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1310
1311 switch (mHWData->mParavirtProvider)
1312 {
1313 case ParavirtProvider_Legacy:
1314 {
1315 if (fOsXGuest)
1316 *aParavirtProvider = ParavirtProvider_Minimal;
1317 else
1318 *aParavirtProvider = ParavirtProvider_None;
1319 break;
1320 }
1321
1322 case ParavirtProvider_Default:
1323 {
1324 if (fOsXGuest)
1325 *aParavirtProvider = ParavirtProvider_Minimal;
1326 else if ( mUserData->s.strOsType == "Windows10"
1327 || mUserData->s.strOsType == "Windows10_64"
1328 || mUserData->s.strOsType == "Windows81"
1329 || mUserData->s.strOsType == "Windows81_64"
1330 || mUserData->s.strOsType == "Windows8"
1331 || mUserData->s.strOsType == "Windows8_64"
1332 || mUserData->s.strOsType == "Windows7"
1333 || mUserData->s.strOsType == "Windows7_64"
1334 || mUserData->s.strOsType == "WindowsVista"
1335 || mUserData->s.strOsType == "WindowsVista_64"
1336 || mUserData->s.strOsType == "Windows2012"
1337 || mUserData->s.strOsType == "Windows2012_64"
1338 || mUserData->s.strOsType == "Windows2008"
1339 || mUserData->s.strOsType == "Windows2008_64")
1340 {
1341 *aParavirtProvider = ParavirtProvider_HyperV;
1342 }
1343 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1344 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1345 || mUserData->s.strOsType == "Linux"
1346 || mUserData->s.strOsType == "Linux_64"
1347 || mUserData->s.strOsType == "ArchLinux"
1348 || mUserData->s.strOsType == "ArchLinux_64"
1349 || mUserData->s.strOsType == "Debian"
1350 || mUserData->s.strOsType == "Debian_64"
1351 || mUserData->s.strOsType == "Fedora"
1352 || mUserData->s.strOsType == "Fedora_64"
1353 || mUserData->s.strOsType == "Gentoo"
1354 || mUserData->s.strOsType == "Gentoo_64"
1355 || mUserData->s.strOsType == "Mandriva"
1356 || mUserData->s.strOsType == "Mandriva_64"
1357 || mUserData->s.strOsType == "OpenSUSE"
1358 || mUserData->s.strOsType == "OpenSUSE_64"
1359 || mUserData->s.strOsType == "Oracle"
1360 || mUserData->s.strOsType == "Oracle_64"
1361 || mUserData->s.strOsType == "RedHat"
1362 || mUserData->s.strOsType == "RedHat_64"
1363 || mUserData->s.strOsType == "Turbolinux"
1364 || mUserData->s.strOsType == "Turbolinux_64"
1365 || mUserData->s.strOsType == "Ubuntu"
1366 || mUserData->s.strOsType == "Ubuntu_64"
1367 || mUserData->s.strOsType == "Xandros"
1368 || mUserData->s.strOsType == "Xandros_64")
1369 {
1370 *aParavirtProvider = ParavirtProvider_KVM;
1371 }
1372 else
1373 *aParavirtProvider = ParavirtProvider_None;
1374 break;
1375 }
1376
1377 default: AssertFailedBreak(); /* Shut up MSC. */
1378 }
1379 break;
1380 }
1381 }
1382
1383 Assert( *aParavirtProvider == ParavirtProvider_None
1384 || *aParavirtProvider == ParavirtProvider_Minimal
1385 || *aParavirtProvider == ParavirtProvider_HyperV
1386 || *aParavirtProvider == ParavirtProvider_KVM);
1387 return S_OK;
1388}
1389
1390HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1391{
1392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 aHardwareVersion = mHWData->mHWVersion;
1395
1396 return S_OK;
1397}
1398
1399HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1400{
1401 /* check known version */
1402 Utf8Str hwVersion = aHardwareVersion;
1403 if ( hwVersion.compare("1") != 0
1404 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1405 return setError(E_INVALIDARG,
1406 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = i_checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 i_setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mHWVersion = aHardwareVersion;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1421{
1422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1423
1424 if (!mHWData->mHardwareUUID.isZero())
1425 aHardwareUUID = mHWData->mHardwareUUID;
1426 else
1427 aHardwareUUID = mData->mUuid;
1428
1429 return S_OK;
1430}
1431
1432HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1433{
1434 if (!aHardwareUUID.isValid())
1435 return E_INVALIDARG;
1436
1437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 HRESULT rc = i_checkStateDependency(MutableStateDep);
1440 if (FAILED(rc)) return rc;
1441
1442 i_setModified(IsModified_MachineData);
1443 mHWData.backup();
1444 if (aHardwareUUID == mData->mUuid)
1445 mHWData->mHardwareUUID.clear();
1446 else
1447 mHWData->mHardwareUUID = aHardwareUUID;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aMemorySize = mHWData->mMemorySize;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setMemorySize(ULONG aMemorySize)
1462{
1463 /* check RAM limits */
1464 if ( aMemorySize < MM_RAM_MIN_IN_MB
1465 || aMemorySize > MM_RAM_MAX_IN_MB
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1469 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 HRESULT rc = i_checkStateDependency(MutableStateDep);
1474 if (FAILED(rc)) return rc;
1475
1476 i_setModified(IsModified_MachineData);
1477 mHWData.backup();
1478 mHWData->mMemorySize = aMemorySize;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1484{
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aCPUCount = mHWData->mCPUCount;
1488
1489 return S_OK;
1490}
1491
1492HRESULT Machine::setCPUCount(ULONG aCPUCount)
1493{
1494 /* check CPU limits */
1495 if ( aCPUCount < SchemaDefs::MinCPUCount
1496 || aCPUCount > SchemaDefs::MaxCPUCount
1497 )
1498 return setError(E_INVALIDARG,
1499 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1500 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1505 if (mHWData->mCPUHotPlugEnabled)
1506 {
1507 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1508 {
1509 if (mHWData->mCPUAttached[idx])
1510 return setError(E_INVALIDARG,
1511 tr("There is still a CPU attached to socket %lu."
1512 "Detach the CPU before removing the socket"),
1513 aCPUCount, idx+1);
1514 }
1515 }
1516
1517 HRESULT rc = i_checkStateDependency(MutableStateDep);
1518 if (FAILED(rc)) return rc;
1519
1520 i_setModified(IsModified_MachineData);
1521 mHWData.backup();
1522 mHWData->mCPUCount = aCPUCount;
1523
1524 return S_OK;
1525}
1526
1527HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1528{
1529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1530
1531 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1532
1533 return S_OK;
1534}
1535
1536HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1537{
1538 HRESULT rc = S_OK;
1539
1540 /* check throttle limits */
1541 if ( aCPUExecutionCap < 1
1542 || aCPUExecutionCap > 100
1543 )
1544 return setError(E_INVALIDARG,
1545 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1546 aCPUExecutionCap, 1, 100);
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 alock.release();
1551 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1552 alock.acquire();
1553 if (FAILED(rc)) return rc;
1554
1555 i_setModified(IsModified_MachineData);
1556 mHWData.backup();
1557 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1558
1559 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1560 if (Global::IsOnline(mData->mMachineState))
1561 i_saveSettings(NULL);
1562
1563 return S_OK;
1564}
1565
1566HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1567{
1568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1571
1572 return S_OK;
1573}
1574
1575HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1576{
1577 HRESULT rc = S_OK;
1578
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 rc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(rc)) return rc;
1583
1584 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1585 {
1586 if (aCPUHotPlugEnabled)
1587 {
1588 i_setModified(IsModified_MachineData);
1589 mHWData.backup();
1590
1591 /* Add the amount of CPUs currently attached */
1592 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1593 mHWData->mCPUAttached[i] = true;
1594 }
1595 else
1596 {
1597 /*
1598 * We can disable hotplug only if the amount of maximum CPUs is equal
1599 * to the amount of attached CPUs
1600 */
1601 unsigned cCpusAttached = 0;
1602 unsigned iHighestId = 0;
1603
1604 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1605 {
1606 if (mHWData->mCPUAttached[i])
1607 {
1608 cCpusAttached++;
1609 iHighestId = i;
1610 }
1611 }
1612
1613 if ( (cCpusAttached != mHWData->mCPUCount)
1614 || (iHighestId >= mHWData->mCPUCount))
1615 return setError(E_INVALIDARG,
1616 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1617
1618 i_setModified(IsModified_MachineData);
1619 mHWData.backup();
1620 }
1621 }
1622
1623 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1624
1625 return rc;
1626}
1627
1628HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1629{
1630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1631
1632 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1633
1634 return S_OK;
1635}
1636
1637HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1638{
1639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1642 if (SUCCEEDED(hrc))
1643 {
1644 i_setModified(IsModified_MachineData);
1645 mHWData.backup();
1646 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1647 }
1648 return hrc;
1649}
1650
1651HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1652{
1653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1654 aCPUProfile = mHWData->mCpuProfile;
1655 return S_OK;
1656}
1657
1658HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1659{
1660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1661 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1662 if (SUCCEEDED(hrc))
1663 {
1664 i_setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 /* Empty equals 'host'. */
1667 if (aCPUProfile.isNotEmpty())
1668 mHWData->mCpuProfile = aCPUProfile;
1669 else
1670 mHWData->mCpuProfile = "host";
1671 }
1672 return hrc;
1673}
1674
1675HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1681
1682 return S_OK;
1683#else
1684 NOREF(aEmulatedUSBCardReaderEnabled);
1685 return E_NOTIMPL;
1686#endif
1687}
1688
1689HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1690{
1691#ifdef VBOX_WITH_USB_CARDREADER
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 i_setModified(IsModified_MachineData);
1698 mHWData.backup();
1699 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1700
1701 return S_OK;
1702#else
1703 NOREF(aEmulatedUSBCardReaderEnabled);
1704 return E_NOTIMPL;
1705#endif
1706}
1707
1708HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1709{
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *aHPETEnabled = mHWData->mHPETEnabled;
1713
1714 return S_OK;
1715}
1716
1717HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1718{
1719 HRESULT rc = S_OK;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 rc = i_checkStateDependency(MutableStateDep);
1724 if (FAILED(rc)) return rc;
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728
1729 mHWData->mHPETEnabled = aHPETEnabled;
1730
1731 return rc;
1732}
1733
1734HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1739
1740 return S_OK;
1741}
1742
1743HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1744{
1745 switch (aGraphicsControllerType)
1746 {
1747 case GraphicsControllerType_Null:
1748 case GraphicsControllerType_VBoxVGA:
1749#ifdef VBOX_WITH_VMSVGA
1750 case GraphicsControllerType_VMSVGA:
1751 case GraphicsControllerType_VBoxSVGA:
1752#endif
1753 break;
1754 default:
1755 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1756 }
1757
1758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 HRESULT rc = i_checkStateDependency(MutableStateDep);
1761 if (FAILED(rc)) return rc;
1762
1763 i_setModified(IsModified_MachineData);
1764 mHWData.backup();
1765 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1766
1767 return S_OK;
1768}
1769
1770HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1771{
1772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 *aVRAMSize = mHWData->mVRAMSize;
1775
1776 return S_OK;
1777}
1778
1779HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1780{
1781 /* check VRAM limits */
1782 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1783 return setError(E_INVALIDARG,
1784 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1785 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 HRESULT rc = i_checkStateDependency(MutableStateDep);
1790 if (FAILED(rc)) return rc;
1791
1792 i_setModified(IsModified_MachineData);
1793 mHWData.backup();
1794 mHWData->mVRAMSize = aVRAMSize;
1795
1796 return S_OK;
1797}
1798
1799/** @todo this method should not be public */
1800HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1805
1806 return S_OK;
1807}
1808
1809/**
1810 * Set the memory balloon size.
1811 *
1812 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1813 * we have to make sure that we never call IGuest from here.
1814 */
1815HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1816{
1817 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1818#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1819 /* check limits */
1820 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1821 return setError(E_INVALIDARG,
1822 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1823 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1824
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1830
1831 return S_OK;
1832#else
1833 NOREF(aMemoryBalloonSize);
1834 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1835#endif
1836}
1837
1838HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1839{
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1843 return S_OK;
1844}
1845
1846HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1847{
1848#ifdef VBOX_WITH_PAGE_SHARING
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1855 return S_OK;
1856#else
1857 NOREF(aPageFusionEnabled);
1858 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1859#endif
1860}
1861
1862HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1867
1868 return S_OK;
1869}
1870
1871HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1872{
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 HRESULT rc = i_checkStateDependency(MutableStateDep);
1876 if (FAILED(rc)) return rc;
1877
1878 /** @todo check validity! */
1879
1880 i_setModified(IsModified_MachineData);
1881 mHWData.backup();
1882 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1883
1884 return S_OK;
1885}
1886
1887
1888HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1889{
1890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1893
1894 return S_OK;
1895}
1896
1897HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1898{
1899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 HRESULT rc = i_checkStateDependency(MutableStateDep);
1902 if (FAILED(rc)) return rc;
1903
1904 /** @todo check validity! */
1905 i_setModified(IsModified_MachineData);
1906 mHWData.backup();
1907 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1908
1909 return S_OK;
1910}
1911
1912HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1913{
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915
1916 *aMonitorCount = mHWData->mMonitorCount;
1917
1918 return S_OK;
1919}
1920
1921HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1922{
1923 /* make sure monitor count is a sensible number */
1924 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1925 return setError(E_INVALIDARG,
1926 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1927 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1928
1929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1930
1931 HRESULT rc = i_checkStateDependency(MutableStateDep);
1932 if (FAILED(rc)) return rc;
1933
1934 i_setModified(IsModified_MachineData);
1935 mHWData.backup();
1936 mHWData->mMonitorCount = aMonitorCount;
1937
1938 return S_OK;
1939}
1940
1941HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1942{
1943 /* mBIOSSettings is constant during life time, no need to lock */
1944 aBIOSSettings = mBIOSSettings;
1945
1946 return S_OK;
1947}
1948
1949HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1950{
1951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1952
1953 aRecordingSettings = mRecordingSettings;
1954
1955 return S_OK;
1956}
1957
1958HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1959{
1960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 switch (aProperty)
1963 {
1964 case CPUPropertyType_PAE:
1965 *aValue = mHWData->mPAEEnabled;
1966 break;
1967
1968 case CPUPropertyType_LongMode:
1969 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1970 *aValue = TRUE;
1971 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1972 *aValue = FALSE;
1973#if HC_ARCH_BITS == 64
1974 else
1975 *aValue = TRUE;
1976#else
1977 else
1978 {
1979 *aValue = FALSE;
1980
1981 ComObjPtr<GuestOSType> pGuestOSType;
1982 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1983 pGuestOSType);
1984 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1985 {
1986 if (pGuestOSType->i_is64Bit())
1987 {
1988 ComObjPtr<Host> pHost = mParent->i_host();
1989 alock.release();
1990
1991 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1992 if (FAILED(hrc2))
1993 *aValue = FALSE;
1994 }
1995 }
1996 }
1997#endif
1998 break;
1999
2000 case CPUPropertyType_TripleFaultReset:
2001 *aValue = mHWData->mTripleFaultReset;
2002 break;
2003
2004 case CPUPropertyType_APIC:
2005 *aValue = mHWData->mAPIC;
2006 break;
2007
2008 case CPUPropertyType_X2APIC:
2009 *aValue = mHWData->mX2APIC;
2010 break;
2011
2012 case CPUPropertyType_IBPBOnVMExit:
2013 *aValue = mHWData->mIBPBOnVMExit;
2014 break;
2015
2016 case CPUPropertyType_IBPBOnVMEntry:
2017 *aValue = mHWData->mIBPBOnVMEntry;
2018 break;
2019
2020 case CPUPropertyType_SpecCtrl:
2021 *aValue = mHWData->mSpecCtrl;
2022 break;
2023
2024 case CPUPropertyType_SpecCtrlByHost:
2025 *aValue = mHWData->mSpecCtrlByHost;
2026 break;
2027
2028 case CPUPropertyType_HWVirt:
2029 *aValue = mHWData->mNestedHWVirt;
2030 break;
2031
2032 case CPUPropertyType_L1DFlushOnEMTScheduling:
2033 *aValue = mHWData->mL1DFlushOnSched;
2034 break;
2035
2036 case CPUPropertyType_L1DFlushOnVMEntry:
2037 *aValue = mHWData->mL1DFlushOnVMEntry;
2038 break;
2039
2040 case CPUPropertyType_MDSClearOnEMTScheduling:
2041 *aValue = mHWData->mMDSClearOnSched;
2042 break;
2043
2044 case CPUPropertyType_MDSClearOnVMEntry:
2045 *aValue = mHWData->mMDSClearOnVMEntry;
2046 break;
2047
2048 default:
2049 return E_INVALIDARG;
2050 }
2051 return S_OK;
2052}
2053
2054HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2055{
2056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 HRESULT rc = i_checkStateDependency(MutableStateDep);
2059 if (FAILED(rc)) return rc;
2060
2061 switch (aProperty)
2062 {
2063 case CPUPropertyType_PAE:
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mPAEEnabled = !!aValue;
2067 break;
2068
2069 case CPUPropertyType_LongMode:
2070 i_setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2073 break;
2074
2075 case CPUPropertyType_TripleFaultReset:
2076 i_setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mTripleFaultReset = !!aValue;
2079 break;
2080
2081 case CPUPropertyType_APIC:
2082 if (mHWData->mX2APIC)
2083 aValue = TRUE;
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mAPIC = !!aValue;
2087 break;
2088
2089 case CPUPropertyType_X2APIC:
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mX2APIC = !!aValue;
2093 if (aValue)
2094 mHWData->mAPIC = !!aValue;
2095 break;
2096
2097 case CPUPropertyType_IBPBOnVMExit:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mIBPBOnVMExit = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_IBPBOnVMEntry:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mIBPBOnVMEntry = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_SpecCtrl:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mSpecCtrl = !!aValue;
2113 break;
2114
2115 case CPUPropertyType_SpecCtrlByHost:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mSpecCtrlByHost = !!aValue;
2119 break;
2120
2121 case CPUPropertyType_HWVirt:
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mNestedHWVirt = !!aValue;
2125 break;
2126
2127 case CPUPropertyType_L1DFlushOnEMTScheduling:
2128 i_setModified(IsModified_MachineData);
2129 mHWData.backup();
2130 mHWData->mL1DFlushOnSched = !!aValue;
2131 break;
2132
2133 case CPUPropertyType_L1DFlushOnVMEntry:
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mL1DFlushOnVMEntry = !!aValue;
2137 break;
2138
2139 case CPUPropertyType_MDSClearOnEMTScheduling:
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mMDSClearOnSched = !!aValue;
2143 break;
2144
2145 case CPUPropertyType_MDSClearOnVMEntry:
2146 i_setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mMDSClearOnVMEntry = !!aValue;
2149 break;
2150
2151 default:
2152 return E_INVALIDARG;
2153 }
2154 return S_OK;
2155}
2156
2157HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2158 ULONG *aValEcx, ULONG *aValEdx)
2159{
2160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2161 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2162 {
2163 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2164 it != mHWData->mCpuIdLeafList.end();
2165 ++it)
2166 {
2167 if (aOrdinal == 0)
2168 {
2169 const settings::CpuIdLeaf &rLeaf= *it;
2170 *aIdx = rLeaf.idx;
2171 *aSubIdx = rLeaf.idxSub;
2172 *aValEax = rLeaf.uEax;
2173 *aValEbx = rLeaf.uEbx;
2174 *aValEcx = rLeaf.uEcx;
2175 *aValEdx = rLeaf.uEdx;
2176 return S_OK;
2177 }
2178 aOrdinal--;
2179 }
2180 }
2181 return E_INVALIDARG;
2182}
2183
2184HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2185{
2186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 /*
2189 * Search the list.
2190 */
2191 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2192 {
2193 const settings::CpuIdLeaf &rLeaf= *it;
2194 if ( rLeaf.idx == aIdx
2195 && ( aSubIdx == UINT32_MAX
2196 || rLeaf.idxSub == aSubIdx) )
2197 {
2198 *aValEax = rLeaf.uEax;
2199 *aValEbx = rLeaf.uEbx;
2200 *aValEcx = rLeaf.uEcx;
2201 *aValEdx = rLeaf.uEdx;
2202 return S_OK;
2203 }
2204 }
2205
2206 return E_INVALIDARG;
2207}
2208
2209
2210HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2211{
2212 /*
2213 * Validate input before taking locks and checking state.
2214 */
2215 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2216 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2217 if ( aIdx >= UINT32_C(0x20)
2218 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2219 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2220 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2221
2222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2223 HRESULT rc = i_checkStateDependency(MutableStateDep);
2224 if (FAILED(rc)) return rc;
2225
2226 /*
2227 * Impose a maximum number of leaves.
2228 */
2229 if (mHWData->mCpuIdLeafList.size() > 256)
2230 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2231
2232 /*
2233 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2234 */
2235 i_setModified(IsModified_MachineData);
2236 mHWData.backup();
2237
2238 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2239 {
2240 settings::CpuIdLeaf &rLeaf= *it;
2241 if ( rLeaf.idx == aIdx
2242 && ( aSubIdx == UINT32_MAX
2243 || rLeaf.idxSub == aSubIdx) )
2244 it = mHWData->mCpuIdLeafList.erase(it);
2245 else
2246 ++it;
2247 }
2248
2249 settings::CpuIdLeaf NewLeaf;
2250 NewLeaf.idx = aIdx;
2251 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2252 NewLeaf.uEax = aValEax;
2253 NewLeaf.uEbx = aValEbx;
2254 NewLeaf.uEcx = aValEcx;
2255 NewLeaf.uEdx = aValEdx;
2256 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2257 return S_OK;
2258}
2259
2260HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2261{
2262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2263
2264 HRESULT rc = i_checkStateDependency(MutableStateDep);
2265 if (FAILED(rc)) return rc;
2266
2267 /*
2268 * Do the removal.
2269 */
2270 bool fModified = mHWData.isBackedUp();
2271 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2272 {
2273 settings::CpuIdLeaf &rLeaf= *it;
2274 if ( rLeaf.idx == aIdx
2275 && ( aSubIdx == UINT32_MAX
2276 || rLeaf.idxSub == aSubIdx) )
2277 {
2278 if (!fModified)
2279 {
2280 fModified = true;
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 // Start from the beginning, since mHWData.backup() creates
2284 // a new list, causing iterator mixup. This makes sure that
2285 // the settings are not unnecessarily marked as modified,
2286 // at the price of extra list walking.
2287 it = mHWData->mCpuIdLeafList.begin();
2288 }
2289 else
2290 it = mHWData->mCpuIdLeafList.erase(it);
2291 }
2292 else
2293 ++it;
2294 }
2295
2296 return S_OK;
2297}
2298
2299HRESULT Machine::removeAllCPUIDLeaves()
2300{
2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 HRESULT rc = i_checkStateDependency(MutableStateDep);
2304 if (FAILED(rc)) return rc;
2305
2306 if (mHWData->mCpuIdLeafList.size() > 0)
2307 {
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310
2311 mHWData->mCpuIdLeafList.clear();
2312 }
2313
2314 return S_OK;
2315}
2316HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2317{
2318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 switch(aProperty)
2321 {
2322 case HWVirtExPropertyType_Enabled:
2323 *aValue = mHWData->mHWVirtExEnabled;
2324 break;
2325
2326 case HWVirtExPropertyType_VPID:
2327 *aValue = mHWData->mHWVirtExVPIDEnabled;
2328 break;
2329
2330 case HWVirtExPropertyType_NestedPaging:
2331 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2332 break;
2333
2334 case HWVirtExPropertyType_UnrestrictedExecution:
2335 *aValue = mHWData->mHWVirtExUXEnabled;
2336 break;
2337
2338 case HWVirtExPropertyType_LargePages:
2339 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2340#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2341 *aValue = FALSE;
2342#endif
2343 break;
2344
2345 case HWVirtExPropertyType_Force:
2346 *aValue = mHWData->mHWVirtExForceEnabled;
2347 break;
2348
2349 case HWVirtExPropertyType_UseNativeApi:
2350 *aValue = mHWData->mHWVirtExUseNativeApi;
2351 break;
2352
2353 default:
2354 return E_INVALIDARG;
2355 }
2356 return S_OK;
2357}
2358
2359HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2360{
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 HRESULT rc = i_checkStateDependency(MutableStateDep);
2364 if (FAILED(rc)) return rc;
2365
2366 switch (aProperty)
2367 {
2368 case HWVirtExPropertyType_Enabled:
2369 i_setModified(IsModified_MachineData);
2370 mHWData.backup();
2371 mHWData->mHWVirtExEnabled = !!aValue;
2372 break;
2373
2374 case HWVirtExPropertyType_VPID:
2375 i_setModified(IsModified_MachineData);
2376 mHWData.backup();
2377 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2378 break;
2379
2380 case HWVirtExPropertyType_NestedPaging:
2381 i_setModified(IsModified_MachineData);
2382 mHWData.backup();
2383 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2384 break;
2385
2386 case HWVirtExPropertyType_UnrestrictedExecution:
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mHWVirtExUXEnabled = !!aValue;
2390 break;
2391
2392 case HWVirtExPropertyType_LargePages:
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2396 break;
2397
2398 case HWVirtExPropertyType_Force:
2399 i_setModified(IsModified_MachineData);
2400 mHWData.backup();
2401 mHWData->mHWVirtExForceEnabled = !!aValue;
2402 break;
2403
2404 case HWVirtExPropertyType_UseNativeApi:
2405 i_setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mHWVirtExUseNativeApi = !!aValue;
2408 break;
2409
2410 default:
2411 return E_INVALIDARG;
2412 }
2413
2414 return S_OK;
2415}
2416
2417HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2418{
2419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2422
2423 return S_OK;
2424}
2425
2426HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2427{
2428 /** @todo (r=dmik):
2429 * 1. Allow to change the name of the snapshot folder containing snapshots
2430 * 2. Rename the folder on disk instead of just changing the property
2431 * value (to be smart and not to leave garbage). Note that it cannot be
2432 * done here because the change may be rolled back. Thus, the right
2433 * place is #saveSettings().
2434 */
2435
2436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 HRESULT rc = i_checkStateDependency(MutableStateDep);
2439 if (FAILED(rc)) return rc;
2440
2441 if (!mData->mCurrentSnapshot.isNull())
2442 return setError(E_FAIL,
2443 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2444
2445 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2446
2447 if (strSnapshotFolder.isEmpty())
2448 strSnapshotFolder = "Snapshots";
2449 int vrc = i_calculateFullPath(strSnapshotFolder,
2450 strSnapshotFolder);
2451 if (RT_FAILURE(vrc))
2452 return setErrorBoth(E_FAIL, vrc,
2453 tr("Invalid snapshot folder '%s' (%Rrc)"),
2454 strSnapshotFolder.c_str(), vrc);
2455
2456 i_setModified(IsModified_MachineData);
2457 mUserData.backup();
2458
2459 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2460
2461 return S_OK;
2462}
2463
2464HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aMediumAttachments.resize(mMediumAttachments->size());
2469 size_t i = 0;
2470 for (MediumAttachmentList::const_iterator
2471 it = mMediumAttachments->begin();
2472 it != mMediumAttachments->end();
2473 ++it, ++i)
2474 aMediumAttachments[i] = *it;
2475
2476 return S_OK;
2477}
2478
2479HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2480{
2481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2482
2483 Assert(!!mVRDEServer);
2484
2485 aVRDEServer = mVRDEServer;
2486
2487 return S_OK;
2488}
2489
2490HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2491{
2492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 aAudioAdapter = mAudioAdapter;
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2500{
2501#ifdef VBOX_WITH_VUSB
2502 clearError();
2503 MultiResult rc(S_OK);
2504
2505# ifdef VBOX_WITH_USB
2506 rc = mParent->i_host()->i_checkUSBProxyService();
2507 if (FAILED(rc)) return rc;
2508# endif
2509
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 aUSBControllers.resize(mUSBControllers->size());
2513 size_t i = 0;
2514 for (USBControllerList::const_iterator
2515 it = mUSBControllers->begin();
2516 it != mUSBControllers->end();
2517 ++it, ++i)
2518 aUSBControllers[i] = *it;
2519
2520 return S_OK;
2521#else
2522 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2523 * extended error info to indicate that USB is simply not available
2524 * (w/o treating it as a failure), for example, as in OSE */
2525 NOREF(aUSBControllers);
2526 ReturnComNotImplemented();
2527#endif /* VBOX_WITH_VUSB */
2528}
2529
2530HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2531{
2532#ifdef VBOX_WITH_VUSB
2533 clearError();
2534 MultiResult rc(S_OK);
2535
2536# ifdef VBOX_WITH_USB
2537 rc = mParent->i_host()->i_checkUSBProxyService();
2538 if (FAILED(rc)) return rc;
2539# endif
2540
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 aUSBDeviceFilters = mUSBDeviceFilters;
2544 return rc;
2545#else
2546 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2547 * extended error info to indicate that USB is simply not available
2548 * (w/o treating it as a failure), for example, as in OSE */
2549 NOREF(aUSBDeviceFilters);
2550 ReturnComNotImplemented();
2551#endif /* VBOX_WITH_VUSB */
2552}
2553
2554HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 aSettingsFilePath = mData->m_strConfigFileFull;
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2564{
2565 RT_NOREF(aSettingsFilePath);
2566 ReturnComNotImplemented();
2567}
2568
2569HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2574 if (FAILED(rc)) return rc;
2575
2576 if (!mData->pMachineConfigFile->fileExists())
2577 // this is a new machine, and no config file exists yet:
2578 *aSettingsModified = TRUE;
2579 else
2580 *aSettingsModified = (mData->flModifications != 0);
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 *aSessionState = mData->mSession.mState;
2590
2591 return S_OK;
2592}
2593
2594HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2595{
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 aSessionName = mData->mSession.mName;
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 *aSessionPID = mData->mSession.mPID;
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::getState(MachineState_T *aState)
2613{
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 *aState = mData->mMachineState;
2617 Assert(mData->mMachineState != MachineState_Null);
2618
2619 return S_OK;
2620}
2621
2622HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2623{
2624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 aStateFilePath = mSSData->strStateFilePath;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2641{
2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 i_getLogFolder(aLogFolder);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aCurrentSnapshot = mData->mCurrentSnapshot;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2659{
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2663 ? 0
2664 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 /* Note: for machines with no snapshots, we always return FALSE
2674 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2675 * reasons :) */
2676
2677 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2678 ? FALSE
2679 : mData->mCurrentStateModified;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aSharedFolders.resize(mHWData->mSharedFolders.size());
2689 size_t i = 0;
2690 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2691 it = mHWData->mSharedFolders.begin();
2692 it != mHWData->mSharedFolders.end();
2693 ++it, ++i)
2694 aSharedFolders[i] = *it;
2695
2696 return S_OK;
2697}
2698
2699HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2700{
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 *aClipboardMode = mHWData->mClipboardMode;
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2709{
2710 HRESULT rc = S_OK;
2711
2712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 alock.release();
2715 rc = i_onClipboardModeChange(aClipboardMode);
2716 alock.acquire();
2717 if (FAILED(rc)) return rc;
2718
2719 i_setModified(IsModified_MachineData);
2720 mHWData.backup();
2721 mHWData->mClipboardMode = aClipboardMode;
2722
2723 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2724 if (Global::IsOnline(mData->mMachineState))
2725 i_saveSettings(NULL);
2726
2727 return S_OK;
2728}
2729
2730HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 *aDnDMode = mHWData->mDnDMode;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2740{
2741 HRESULT rc = S_OK;
2742
2743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 alock.release();
2746 rc = i_onDnDModeChange(aDnDMode);
2747
2748 alock.acquire();
2749 if (FAILED(rc)) return rc;
2750
2751 i_setModified(IsModified_MachineData);
2752 mHWData.backup();
2753 mHWData->mDnDMode = aDnDMode;
2754
2755 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2756 if (Global::IsOnline(mData->mMachineState))
2757 i_saveSettings(NULL);
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aStorageControllers.resize(mStorageControllers->size());
2767 size_t i = 0;
2768 for (StorageControllerList::const_iterator
2769 it = mStorageControllers->begin();
2770 it != mStorageControllers->end();
2771 ++it, ++i)
2772 aStorageControllers[i] = *it;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aEnabled = mUserData->s.fTeleporterEnabled;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2787{
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 /* Only allow it to be set to true when PoweredOff or Aborted.
2791 (Clearing it is always permitted.) */
2792 if ( aTeleporterEnabled
2793 && mData->mRegistered
2794 && ( !i_isSessionMachine()
2795 || ( mData->mMachineState != MachineState_PoweredOff
2796 && mData->mMachineState != MachineState_Teleported
2797 && mData->mMachineState != MachineState_Aborted
2798 )
2799 )
2800 )
2801 return setError(VBOX_E_INVALID_VM_STATE,
2802 tr("The machine is not powered off (state is %s)"),
2803 Global::stringifyMachineState(mData->mMachineState));
2804
2805 i_setModified(IsModified_MachineData);
2806 mUserData.backup();
2807 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2808
2809 return S_OK;
2810}
2811
2812HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2813{
2814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2817
2818 return S_OK;
2819}
2820
2821HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2822{
2823 if (aTeleporterPort >= _64K)
2824 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2825
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2829 if (FAILED(rc)) return rc;
2830
2831 i_setModified(IsModified_MachineData);
2832 mUserData.backup();
2833 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2848{
2849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2852 if (FAILED(rc)) return rc;
2853
2854 i_setModified(IsModified_MachineData);
2855 mUserData.backup();
2856 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2870{
2871 /*
2872 * Hash the password first.
2873 */
2874 com::Utf8Str aT = aTeleporterPassword;
2875
2876 if (!aT.isEmpty())
2877 {
2878 if (VBoxIsPasswordHashed(&aT))
2879 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2880 VBoxHashPassword(&aT);
2881 }
2882
2883 /*
2884 * Do the update.
2885 */
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2888 if (SUCCEEDED(hrc))
2889 {
2890 i_setModified(IsModified_MachineData);
2891 mUserData.backup();
2892 mUserData->s.strTeleporterPassword = aT;
2893 }
2894
2895 return hrc;
2896}
2897
2898HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2903
2904 return S_OK;
2905}
2906
2907HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2908{
2909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 /* Only allow it to be set to true when PoweredOff or Aborted.
2912 (Clearing it is always permitted.) */
2913 if ( aRTCUseUTC
2914 && mData->mRegistered
2915 && ( !i_isSessionMachine()
2916 || ( mData->mMachineState != MachineState_PoweredOff
2917 && mData->mMachineState != MachineState_Teleported
2918 && mData->mMachineState != MachineState_Aborted
2919 )
2920 )
2921 )
2922 return setError(VBOX_E_INVALID_VM_STATE,
2923 tr("The machine is not powered off (state is %s)"),
2924 Global::stringifyMachineState(mData->mMachineState));
2925
2926 i_setModified(IsModified_MachineData);
2927 mUserData.backup();
2928 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2929
2930 return S_OK;
2931}
2932
2933HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2934{
2935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2938
2939 return S_OK;
2940}
2941
2942HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2943{
2944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 HRESULT rc = i_checkStateDependency(MutableStateDep);
2947 if (FAILED(rc)) return rc;
2948
2949 i_setModified(IsModified_MachineData);
2950 mHWData.backup();
2951 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2952
2953 return S_OK;
2954}
2955
2956HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2957{
2958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 *aIOCacheSize = mHWData->mIOCacheSize;
2961
2962 return S_OK;
2963}
2964
2965HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2966{
2967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 HRESULT rc = i_checkStateDependency(MutableStateDep);
2970 if (FAILED(rc)) return rc;
2971
2972 i_setModified(IsModified_MachineData);
2973 mHWData.backup();
2974 mHWData->mIOCacheSize = aIOCacheSize;
2975
2976 return S_OK;
2977}
2978
2979
2980/**
2981 * @note Locks objects!
2982 */
2983HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2984 LockType_T aLockType)
2985{
2986 /* check the session state */
2987 SessionState_T state;
2988 HRESULT rc = aSession->COMGETTER(State)(&state);
2989 if (FAILED(rc)) return rc;
2990
2991 if (state != SessionState_Unlocked)
2992 return setError(VBOX_E_INVALID_OBJECT_STATE,
2993 tr("The given session is busy"));
2994
2995 // get the client's IInternalSessionControl interface
2996 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2997 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2998 E_INVALIDARG);
2999
3000 // session name (only used in some code paths)
3001 Utf8Str strSessionName;
3002
3003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 if (!mData->mRegistered)
3006 return setError(E_UNEXPECTED,
3007 tr("The machine '%s' is not registered"),
3008 mUserData->s.strName.c_str());
3009
3010 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3011
3012 SessionState_T oldState = mData->mSession.mState;
3013 /* Hack: in case the session is closing and there is a progress object
3014 * which allows waiting for the session to be closed, take the opportunity
3015 * and do a limited wait (max. 1 second). This helps a lot when the system
3016 * is busy and thus session closing can take a little while. */
3017 if ( mData->mSession.mState == SessionState_Unlocking
3018 && mData->mSession.mProgress)
3019 {
3020 alock.release();
3021 mData->mSession.mProgress->WaitForCompletion(1000);
3022 alock.acquire();
3023 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3024 }
3025
3026 // try again now
3027 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3028 // (i.e. session machine exists)
3029 && (aLockType == LockType_Shared) // caller wants a shared link to the
3030 // existing session that holds the write lock:
3031 )
3032 {
3033 // OK, share the session... we are now dealing with three processes:
3034 // 1) VBoxSVC (where this code runs);
3035 // 2) process C: the caller's client process (who wants a shared session);
3036 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3037
3038 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3039 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3040 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3041 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3042 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3043
3044 /*
3045 * Release the lock before calling the client process. It's safe here
3046 * since the only thing to do after we get the lock again is to add
3047 * the remote control to the list (which doesn't directly influence
3048 * anything).
3049 */
3050 alock.release();
3051
3052 // get the console of the session holding the write lock (this is a remote call)
3053 ComPtr<IConsole> pConsoleW;
3054 if (mData->mSession.mLockType == LockType_VM)
3055 {
3056 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3057 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3058 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3059 if (FAILED(rc))
3060 // the failure may occur w/o any error info (from RPC), so provide one
3061 return setError(VBOX_E_VM_ERROR,
3062 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3063 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3064 }
3065
3066 // share the session machine and W's console with the caller's session
3067 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3068 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3069 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3070
3071 if (FAILED(rc))
3072 // the failure may occur w/o any error info (from RPC), so provide one
3073 return setError(VBOX_E_VM_ERROR,
3074 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3075 alock.acquire();
3076
3077 // need to revalidate the state after acquiring the lock again
3078 if (mData->mSession.mState != SessionState_Locked)
3079 {
3080 pSessionControl->Uninitialize();
3081 return setError(VBOX_E_INVALID_SESSION_STATE,
3082 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3083 mUserData->s.strName.c_str());
3084 }
3085
3086 // add the caller's session to the list
3087 mData->mSession.mRemoteControls.push_back(pSessionControl);
3088 }
3089 else if ( mData->mSession.mState == SessionState_Locked
3090 || mData->mSession.mState == SessionState_Unlocking
3091 )
3092 {
3093 // sharing not permitted, or machine still unlocking:
3094 return setError(VBOX_E_INVALID_OBJECT_STATE,
3095 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3096 mUserData->s.strName.c_str());
3097 }
3098 else
3099 {
3100 // machine is not locked: then write-lock the machine (create the session machine)
3101
3102 // must not be busy
3103 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3104
3105 // get the caller's session PID
3106 RTPROCESS pid = NIL_RTPROCESS;
3107 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3108 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3109 Assert(pid != NIL_RTPROCESS);
3110
3111 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3112
3113 if (fLaunchingVMProcess)
3114 {
3115 if (mData->mSession.mPID == NIL_RTPROCESS)
3116 {
3117 // two or more clients racing for a lock, the one which set the
3118 // session state to Spawning will win, the others will get an
3119 // error as we can't decide here if waiting a little would help
3120 // (only for shared locks this would avoid an error)
3121 return setError(VBOX_E_INVALID_OBJECT_STATE,
3122 tr("The machine '%s' already has a lock request pending"),
3123 mUserData->s.strName.c_str());
3124 }
3125
3126 // this machine is awaiting for a spawning session to be opened:
3127 // then the calling process must be the one that got started by
3128 // LaunchVMProcess()
3129
3130 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3131 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3132
3133#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3134 /* Hardened windows builds spawns three processes when a VM is
3135 launched, the 3rd one is the one that will end up here. */
3136 RTPROCESS ppid;
3137 int rc = RTProcQueryParent(pid, &ppid);
3138 if (RT_SUCCESS(rc))
3139 rc = RTProcQueryParent(ppid, &ppid);
3140 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3141 || rc == VERR_ACCESS_DENIED)
3142 {
3143 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3144 mData->mSession.mPID = pid;
3145 }
3146#endif
3147
3148 if (mData->mSession.mPID != pid)
3149 return setError(E_ACCESSDENIED,
3150 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3151 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3152 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3153 }
3154
3155 // create the mutable SessionMachine from the current machine
3156 ComObjPtr<SessionMachine> sessionMachine;
3157 sessionMachine.createObject();
3158 rc = sessionMachine->init(this);
3159 AssertComRC(rc);
3160
3161 /* NOTE: doing return from this function after this point but
3162 * before the end is forbidden since it may call SessionMachine::uninit()
3163 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3164 * lock while still holding the Machine lock in alock so that a deadlock
3165 * is possible due to the wrong lock order. */
3166
3167 if (SUCCEEDED(rc))
3168 {
3169 /*
3170 * Set the session state to Spawning to protect against subsequent
3171 * attempts to open a session and to unregister the machine after
3172 * we release the lock.
3173 */
3174 SessionState_T origState = mData->mSession.mState;
3175 mData->mSession.mState = SessionState_Spawning;
3176
3177#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3178 /* Get the client token ID to be passed to the client process */
3179 Utf8Str strTokenId;
3180 sessionMachine->i_getTokenId(strTokenId);
3181 Assert(!strTokenId.isEmpty());
3182#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3183 /* Get the client token to be passed to the client process */
3184 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3185 /* The token is now "owned" by pToken, fix refcount */
3186 if (!pToken.isNull())
3187 pToken->Release();
3188#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3189
3190 /*
3191 * Release the lock before calling the client process -- it will call
3192 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3193 * because the state is Spawning, so that LaunchVMProcess() and
3194 * LockMachine() calls will fail. This method, called before we
3195 * acquire the lock again, will fail because of the wrong PID.
3196 *
3197 * Note that mData->mSession.mRemoteControls accessed outside
3198 * the lock may not be modified when state is Spawning, so it's safe.
3199 */
3200 alock.release();
3201
3202 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3203#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3204 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3205#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3206 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3207 /* Now the token is owned by the client process. */
3208 pToken.setNull();
3209#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3210 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3211
3212 /* The failure may occur w/o any error info (from RPC), so provide one */
3213 if (FAILED(rc))
3214 setError(VBOX_E_VM_ERROR,
3215 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3216
3217 // get session name, either to remember or to compare against
3218 // the already known session name.
3219 {
3220 Bstr bstrSessionName;
3221 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3222 if (SUCCEEDED(rc2))
3223 strSessionName = bstrSessionName;
3224 }
3225
3226 if ( SUCCEEDED(rc)
3227 && fLaunchingVMProcess
3228 )
3229 {
3230 /* complete the remote session initialization */
3231
3232 /* get the console from the direct session */
3233 ComPtr<IConsole> console;
3234 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3235 ComAssertComRC(rc);
3236
3237 if (SUCCEEDED(rc) && !console)
3238 {
3239 ComAssert(!!console);
3240 rc = E_FAIL;
3241 }
3242
3243 /* assign machine & console to the remote session */
3244 if (SUCCEEDED(rc))
3245 {
3246 /*
3247 * after LaunchVMProcess(), the first and the only
3248 * entry in remoteControls is that remote session
3249 */
3250 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3251 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3252 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3253
3254 /* The failure may occur w/o any error info (from RPC), so provide one */
3255 if (FAILED(rc))
3256 setError(VBOX_E_VM_ERROR,
3257 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3258 }
3259
3260 if (FAILED(rc))
3261 pSessionControl->Uninitialize();
3262 }
3263
3264 /* acquire the lock again */
3265 alock.acquire();
3266
3267 /* Restore the session state */
3268 mData->mSession.mState = origState;
3269 }
3270
3271 // finalize spawning anyway (this is why we don't return on errors above)
3272 if (fLaunchingVMProcess)
3273 {
3274 Assert(mData->mSession.mName == strSessionName);
3275 /* Note that the progress object is finalized later */
3276 /** @todo Consider checking mData->mSession.mProgress for cancellation
3277 * around here. */
3278
3279 /* We don't reset mSession.mPID here because it is necessary for
3280 * SessionMachine::uninit() to reap the child process later. */
3281
3282 if (FAILED(rc))
3283 {
3284 /* Close the remote session, remove the remote control from the list
3285 * and reset session state to Closed (@note keep the code in sync
3286 * with the relevant part in checkForSpawnFailure()). */
3287
3288 Assert(mData->mSession.mRemoteControls.size() == 1);
3289 if (mData->mSession.mRemoteControls.size() == 1)
3290 {
3291 ErrorInfoKeeper eik;
3292 mData->mSession.mRemoteControls.front()->Uninitialize();
3293 }
3294
3295 mData->mSession.mRemoteControls.clear();
3296 mData->mSession.mState = SessionState_Unlocked;
3297 }
3298 }
3299 else
3300 {
3301 /* memorize PID of the directly opened session */
3302 if (SUCCEEDED(rc))
3303 mData->mSession.mPID = pid;
3304 }
3305
3306 if (SUCCEEDED(rc))
3307 {
3308 mData->mSession.mLockType = aLockType;
3309 /* memorize the direct session control and cache IUnknown for it */
3310 mData->mSession.mDirectControl = pSessionControl;
3311 mData->mSession.mState = SessionState_Locked;
3312 if (!fLaunchingVMProcess)
3313 mData->mSession.mName = strSessionName;
3314 /* associate the SessionMachine with this Machine */
3315 mData->mSession.mMachine = sessionMachine;
3316
3317 /* request an IUnknown pointer early from the remote party for later
3318 * identity checks (it will be internally cached within mDirectControl
3319 * at least on XPCOM) */
3320 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3321 NOREF(unk);
3322 }
3323
3324 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3325 * would break the lock order */
3326 alock.release();
3327
3328 /* uninitialize the created session machine on failure */
3329 if (FAILED(rc))
3330 sessionMachine->uninit();
3331 }
3332
3333 if (SUCCEEDED(rc))
3334 {
3335 /*
3336 * tell the client watcher thread to update the set of
3337 * machines that have open sessions
3338 */
3339 mParent->i_updateClientWatcher();
3340
3341 if (oldState != SessionState_Locked)
3342 /* fire an event */
3343 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3344 }
3345
3346 return rc;
3347}
3348
3349/**
3350 * @note Locks objects!
3351 */
3352HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3353 const com::Utf8Str &aName,
3354 const com::Utf8Str &aEnvironment,
3355 ComPtr<IProgress> &aProgress)
3356{
3357 Utf8Str strFrontend(aName);
3358 /* "emergencystop" doesn't need the session, so skip the checks/interface
3359 * retrieval. This code doesn't quite fit in here, but introducing a
3360 * special API method would be even more effort, and would require explicit
3361 * support by every API client. It's better to hide the feature a bit. */
3362 if (strFrontend != "emergencystop")
3363 CheckComArgNotNull(aSession);
3364
3365 HRESULT rc = S_OK;
3366 if (strFrontend.isEmpty())
3367 {
3368 Bstr bstrFrontend;
3369 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3370 if (FAILED(rc))
3371 return rc;
3372 strFrontend = bstrFrontend;
3373 if (strFrontend.isEmpty())
3374 {
3375 ComPtr<ISystemProperties> systemProperties;
3376 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3377 if (FAILED(rc))
3378 return rc;
3379 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3380 if (FAILED(rc))
3381 return rc;
3382 strFrontend = bstrFrontend;
3383 }
3384 /* paranoia - emergencystop is not a valid default */
3385 if (strFrontend == "emergencystop")
3386 strFrontend = Utf8Str::Empty;
3387 }
3388 /* default frontend: Qt GUI */
3389 if (strFrontend.isEmpty())
3390 strFrontend = "GUI/Qt";
3391
3392 if (strFrontend != "emergencystop")
3393 {
3394 /* check the session state */
3395 SessionState_T state;
3396 rc = aSession->COMGETTER(State)(&state);
3397 if (FAILED(rc))
3398 return rc;
3399
3400 if (state != SessionState_Unlocked)
3401 return setError(VBOX_E_INVALID_OBJECT_STATE,
3402 tr("The given session is busy"));
3403
3404 /* get the IInternalSessionControl interface */
3405 ComPtr<IInternalSessionControl> control(aSession);
3406 ComAssertMsgRet(!control.isNull(),
3407 ("No IInternalSessionControl interface"),
3408 E_INVALIDARG);
3409
3410 /* get the teleporter enable state for the progress object init. */
3411 BOOL fTeleporterEnabled;
3412 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3413 if (FAILED(rc))
3414 return rc;
3415
3416 /* create a progress object */
3417 ComObjPtr<ProgressProxy> progress;
3418 progress.createObject();
3419 rc = progress->init(mParent,
3420 static_cast<IMachine*>(this),
3421 Bstr(tr("Starting VM")).raw(),
3422 TRUE /* aCancelable */,
3423 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3424 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3425 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3426 2 /* uFirstOperationWeight */,
3427 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3428
3429 if (SUCCEEDED(rc))
3430 {
3431 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3432 if (SUCCEEDED(rc))
3433 {
3434 aProgress = progress;
3435
3436 /* signal the client watcher thread */
3437 mParent->i_updateClientWatcher();
3438
3439 /* fire an event */
3440 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3441 }
3442 }
3443 }
3444 else
3445 {
3446 /* no progress object - either instant success or failure */
3447 aProgress = NULL;
3448
3449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3450
3451 if (mData->mSession.mState != SessionState_Locked)
3452 return setError(VBOX_E_INVALID_OBJECT_STATE,
3453 tr("The machine '%s' is not locked by a session"),
3454 mUserData->s.strName.c_str());
3455
3456 /* must have a VM process associated - do not kill normal API clients
3457 * with an open session */
3458 if (!Global::IsOnline(mData->mMachineState))
3459 return setError(VBOX_E_INVALID_OBJECT_STATE,
3460 tr("The machine '%s' does not have a VM process"),
3461 mUserData->s.strName.c_str());
3462
3463 /* forcibly terminate the VM process */
3464 if (mData->mSession.mPID != NIL_RTPROCESS)
3465 RTProcTerminate(mData->mSession.mPID);
3466
3467 /* signal the client watcher thread, as most likely the client has
3468 * been terminated */
3469 mParent->i_updateClientWatcher();
3470 }
3471
3472 return rc;
3473}
3474
3475HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3476{
3477 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3478 return setError(E_INVALIDARG,
3479 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3480 aPosition, SchemaDefs::MaxBootPosition);
3481
3482 if (aDevice == DeviceType_USB)
3483 return setError(E_NOTIMPL,
3484 tr("Booting from USB device is currently not supported"));
3485
3486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3487
3488 HRESULT rc = i_checkStateDependency(MutableStateDep);
3489 if (FAILED(rc)) return rc;
3490
3491 i_setModified(IsModified_MachineData);
3492 mHWData.backup();
3493 mHWData->mBootOrder[aPosition - 1] = aDevice;
3494
3495 return S_OK;
3496}
3497
3498HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3499{
3500 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3501 return setError(E_INVALIDARG,
3502 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3503 aPosition, SchemaDefs::MaxBootPosition);
3504
3505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3506
3507 *aDevice = mHWData->mBootOrder[aPosition - 1];
3508
3509 return S_OK;
3510}
3511
3512HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3513 LONG aControllerPort,
3514 LONG aDevice,
3515 DeviceType_T aType,
3516 const ComPtr<IMedium> &aMedium)
3517{
3518 IMedium *aM = aMedium;
3519 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3520 aName.c_str(), aControllerPort, aDevice, aType, aM));
3521
3522 // request the host lock first, since might be calling Host methods for getting host drives;
3523 // next, protect the media tree all the while we're in here, as well as our member variables
3524 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3525 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3526
3527 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3528 if (FAILED(rc)) return rc;
3529
3530 /// @todo NEWMEDIA implicit machine registration
3531 if (!mData->mRegistered)
3532 return setError(VBOX_E_INVALID_OBJECT_STATE,
3533 tr("Cannot attach storage devices to an unregistered machine"));
3534
3535 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3536
3537 /* Check for an existing controller. */
3538 ComObjPtr<StorageController> ctl;
3539 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3540 if (FAILED(rc)) return rc;
3541
3542 StorageControllerType_T ctrlType;
3543 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3544 if (FAILED(rc))
3545 return setError(E_FAIL,
3546 tr("Could not get type of controller '%s'"),
3547 aName.c_str());
3548
3549 bool fSilent = false;
3550 Utf8Str strReconfig;
3551
3552 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3553 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3554 if ( mData->mMachineState == MachineState_Paused
3555 && strReconfig == "1")
3556 fSilent = true;
3557
3558 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3559 bool fHotplug = false;
3560 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3561 fHotplug = true;
3562
3563 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3564 return setError(VBOX_E_INVALID_VM_STATE,
3565 tr("Controller '%s' does not support hotplugging"),
3566 aName.c_str());
3567
3568 // check that the port and device are not out of range
3569 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3570 if (FAILED(rc)) return rc;
3571
3572 /* check if the device slot is already busy */
3573 MediumAttachment *pAttachTemp;
3574 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3575 aName,
3576 aControllerPort,
3577 aDevice)))
3578 {
3579 Medium *pMedium = pAttachTemp->i_getMedium();
3580 if (pMedium)
3581 {
3582 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3583 return setError(VBOX_E_OBJECT_IN_USE,
3584 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3585 pMedium->i_getLocationFull().c_str(),
3586 aControllerPort,
3587 aDevice,
3588 aName.c_str());
3589 }
3590 else
3591 return setError(VBOX_E_OBJECT_IN_USE,
3592 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3593 aControllerPort, aDevice, aName.c_str());
3594 }
3595
3596 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3597 if (aMedium && medium.isNull())
3598 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3599
3600 AutoCaller mediumCaller(medium);
3601 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3602
3603 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3604
3605 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3606 && !medium.isNull()
3607 )
3608 return setError(VBOX_E_OBJECT_IN_USE,
3609 tr("Medium '%s' is already attached to this virtual machine"),
3610 medium->i_getLocationFull().c_str());
3611
3612 if (!medium.isNull())
3613 {
3614 MediumType_T mtype = medium->i_getType();
3615 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3616 // For DVDs it's not written to the config file, so needs no global config
3617 // version bump. For floppies it's a new attribute "type", which is ignored
3618 // by older VirtualBox version, so needs no global config version bump either.
3619 // For hard disks this type is not accepted.
3620 if (mtype == MediumType_MultiAttach)
3621 {
3622 // This type is new with VirtualBox 4.0 and therefore requires settings
3623 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3624 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3625 // two reasons: The medium type is a property of the media registry tree, which
3626 // can reside in the global config file (for pre-4.0 media); we would therefore
3627 // possibly need to bump the global config version. We don't want to do that though
3628 // because that might make downgrading to pre-4.0 impossible.
3629 // As a result, we can only use these two new types if the medium is NOT in the
3630 // global registry:
3631 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3632 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3633 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3634 )
3635 return setError(VBOX_E_INVALID_OBJECT_STATE,
3636 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3637 "to machines that were created with VirtualBox 4.0 or later"),
3638 medium->i_getLocationFull().c_str());
3639 }
3640 }
3641
3642 bool fIndirect = false;
3643 if (!medium.isNull())
3644 fIndirect = medium->i_isReadOnly();
3645 bool associate = true;
3646
3647 do
3648 {
3649 if ( aType == DeviceType_HardDisk
3650 && mMediumAttachments.isBackedUp())
3651 {
3652 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3653
3654 /* check if the medium was attached to the VM before we started
3655 * changing attachments in which case the attachment just needs to
3656 * be restored */
3657 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3658 {
3659 AssertReturn(!fIndirect, E_FAIL);
3660
3661 /* see if it's the same bus/channel/device */
3662 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3663 {
3664 /* the simplest case: restore the whole attachment
3665 * and return, nothing else to do */
3666 mMediumAttachments->push_back(pAttachTemp);
3667
3668 /* Reattach the medium to the VM. */
3669 if (fHotplug || fSilent)
3670 {
3671 mediumLock.release();
3672 treeLock.release();
3673 alock.release();
3674
3675 MediumLockList *pMediumLockList(new MediumLockList());
3676
3677 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3678 medium /* pToLockWrite */,
3679 false /* fMediumLockWriteAll */,
3680 NULL,
3681 *pMediumLockList);
3682 alock.acquire();
3683 if (FAILED(rc))
3684 delete pMediumLockList;
3685 else
3686 {
3687 mData->mSession.mLockedMedia.Unlock();
3688 alock.release();
3689 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3690 mData->mSession.mLockedMedia.Lock();
3691 alock.acquire();
3692 }
3693 alock.release();
3694
3695 if (SUCCEEDED(rc))
3696 {
3697 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3698 /* Remove lock list in case of error. */
3699 if (FAILED(rc))
3700 {
3701 mData->mSession.mLockedMedia.Unlock();
3702 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3703 mData->mSession.mLockedMedia.Lock();
3704 }
3705 }
3706 }
3707
3708 return S_OK;
3709 }
3710
3711 /* bus/channel/device differ; we need a new attachment object,
3712 * but don't try to associate it again */
3713 associate = false;
3714 break;
3715 }
3716 }
3717
3718 /* go further only if the attachment is to be indirect */
3719 if (!fIndirect)
3720 break;
3721
3722 /* perform the so called smart attachment logic for indirect
3723 * attachments. Note that smart attachment is only applicable to base
3724 * hard disks. */
3725
3726 if (medium->i_getParent().isNull())
3727 {
3728 /* first, investigate the backup copy of the current hard disk
3729 * attachments to make it possible to re-attach existing diffs to
3730 * another device slot w/o losing their contents */
3731 if (mMediumAttachments.isBackedUp())
3732 {
3733 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3734
3735 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3736 uint32_t foundLevel = 0;
3737
3738 for (MediumAttachmentList::const_iterator
3739 it = oldAtts.begin();
3740 it != oldAtts.end();
3741 ++it)
3742 {
3743 uint32_t level = 0;
3744 MediumAttachment *pAttach = *it;
3745 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3746 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3747 if (pMedium.isNull())
3748 continue;
3749
3750 if (pMedium->i_getBase(&level) == medium)
3751 {
3752 /* skip the hard disk if its currently attached (we
3753 * cannot attach the same hard disk twice) */
3754 if (i_findAttachment(*mMediumAttachments.data(),
3755 pMedium))
3756 continue;
3757
3758 /* matched device, channel and bus (i.e. attached to the
3759 * same place) will win and immediately stop the search;
3760 * otherwise the attachment that has the youngest
3761 * descendant of medium will be used
3762 */
3763 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3764 {
3765 /* the simplest case: restore the whole attachment
3766 * and return, nothing else to do */
3767 mMediumAttachments->push_back(*it);
3768
3769 /* Reattach the medium to the VM. */
3770 if (fHotplug || fSilent)
3771 {
3772 mediumLock.release();
3773 treeLock.release();
3774 alock.release();
3775
3776 MediumLockList *pMediumLockList(new MediumLockList());
3777
3778 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3779 medium /* pToLockWrite */,
3780 false /* fMediumLockWriteAll */,
3781 NULL,
3782 *pMediumLockList);
3783 alock.acquire();
3784 if (FAILED(rc))
3785 delete pMediumLockList;
3786 else
3787 {
3788 mData->mSession.mLockedMedia.Unlock();
3789 alock.release();
3790 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3791 mData->mSession.mLockedMedia.Lock();
3792 alock.acquire();
3793 }
3794 alock.release();
3795
3796 if (SUCCEEDED(rc))
3797 {
3798 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3799 /* Remove lock list in case of error. */
3800 if (FAILED(rc))
3801 {
3802 mData->mSession.mLockedMedia.Unlock();
3803 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3804 mData->mSession.mLockedMedia.Lock();
3805 }
3806 }
3807 }
3808
3809 return S_OK;
3810 }
3811 else if ( foundIt == oldAtts.end()
3812 || level > foundLevel /* prefer younger */
3813 )
3814 {
3815 foundIt = it;
3816 foundLevel = level;
3817 }
3818 }
3819 }
3820
3821 if (foundIt != oldAtts.end())
3822 {
3823 /* use the previously attached hard disk */
3824 medium = (*foundIt)->i_getMedium();
3825 mediumCaller.attach(medium);
3826 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3827 mediumLock.attach(medium);
3828 /* not implicit, doesn't require association with this VM */
3829 fIndirect = false;
3830 associate = false;
3831 /* go right to the MediumAttachment creation */
3832 break;
3833 }
3834 }
3835
3836 /* must give up the medium lock and medium tree lock as below we
3837 * go over snapshots, which needs a lock with higher lock order. */
3838 mediumLock.release();
3839 treeLock.release();
3840
3841 /* then, search through snapshots for the best diff in the given
3842 * hard disk's chain to base the new diff on */
3843
3844 ComObjPtr<Medium> base;
3845 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3846 while (snap)
3847 {
3848 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3849
3850 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3851
3852 MediumAttachment *pAttachFound = NULL;
3853 uint32_t foundLevel = 0;
3854
3855 for (MediumAttachmentList::const_iterator
3856 it = snapAtts.begin();
3857 it != snapAtts.end();
3858 ++it)
3859 {
3860 MediumAttachment *pAttach = *it;
3861 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3862 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3863 if (pMedium.isNull())
3864 continue;
3865
3866 uint32_t level = 0;
3867 if (pMedium->i_getBase(&level) == medium)
3868 {
3869 /* matched device, channel and bus (i.e. attached to the
3870 * same place) will win and immediately stop the search;
3871 * otherwise the attachment that has the youngest
3872 * descendant of medium will be used
3873 */
3874 if ( pAttach->i_getDevice() == aDevice
3875 && pAttach->i_getPort() == aControllerPort
3876 && pAttach->i_getControllerName() == aName
3877 )
3878 {
3879 pAttachFound = pAttach;
3880 break;
3881 }
3882 else if ( !pAttachFound
3883 || level > foundLevel /* prefer younger */
3884 )
3885 {
3886 pAttachFound = pAttach;
3887 foundLevel = level;
3888 }
3889 }
3890 }
3891
3892 if (pAttachFound)
3893 {
3894 base = pAttachFound->i_getMedium();
3895 break;
3896 }
3897
3898 snap = snap->i_getParent();
3899 }
3900
3901 /* re-lock medium tree and the medium, as we need it below */
3902 treeLock.acquire();
3903 mediumLock.acquire();
3904
3905 /* found a suitable diff, use it as a base */
3906 if (!base.isNull())
3907 {
3908 medium = base;
3909 mediumCaller.attach(medium);
3910 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3911 mediumLock.attach(medium);
3912 }
3913 }
3914
3915 Utf8Str strFullSnapshotFolder;
3916 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3917
3918 ComObjPtr<Medium> diff;
3919 diff.createObject();
3920 // store this diff in the same registry as the parent
3921 Guid uuidRegistryParent;
3922 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3923 {
3924 // parent image has no registry: this can happen if we're attaching a new immutable
3925 // image that has not yet been attached (medium then points to the base and we're
3926 // creating the diff image for the immutable, and the parent is not yet registered);
3927 // put the parent in the machine registry then
3928 mediumLock.release();
3929 treeLock.release();
3930 alock.release();
3931 i_addMediumToRegistry(medium);
3932 alock.acquire();
3933 treeLock.acquire();
3934 mediumLock.acquire();
3935 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3936 }
3937 rc = diff->init(mParent,
3938 medium->i_getPreferredDiffFormat(),
3939 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3940 uuidRegistryParent,
3941 DeviceType_HardDisk);
3942 if (FAILED(rc)) return rc;
3943
3944 /* Apply the normal locking logic to the entire chain. */
3945 MediumLockList *pMediumLockList(new MediumLockList());
3946 mediumLock.release();
3947 treeLock.release();
3948 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3949 diff /* pToLockWrite */,
3950 false /* fMediumLockWriteAll */,
3951 medium,
3952 *pMediumLockList);
3953 treeLock.acquire();
3954 mediumLock.acquire();
3955 if (SUCCEEDED(rc))
3956 {
3957 mediumLock.release();
3958 treeLock.release();
3959 rc = pMediumLockList->Lock();
3960 treeLock.acquire();
3961 mediumLock.acquire();
3962 if (FAILED(rc))
3963 setError(rc,
3964 tr("Could not lock medium when creating diff '%s'"),
3965 diff->i_getLocationFull().c_str());
3966 else
3967 {
3968 /* will release the lock before the potentially lengthy
3969 * operation, so protect with the special state */
3970 MachineState_T oldState = mData->mMachineState;
3971 i_setMachineState(MachineState_SettingUp);
3972
3973 mediumLock.release();
3974 treeLock.release();
3975 alock.release();
3976
3977 rc = medium->i_createDiffStorage(diff,
3978 medium->i_getPreferredDiffVariant(),
3979 pMediumLockList,
3980 NULL /* aProgress */,
3981 true /* aWait */,
3982 false /* aNotify */);
3983
3984 alock.acquire();
3985 treeLock.acquire();
3986 mediumLock.acquire();
3987
3988 i_setMachineState(oldState);
3989 }
3990 }
3991
3992 /* Unlock the media and free the associated memory. */
3993 delete pMediumLockList;
3994
3995 if (FAILED(rc)) return rc;
3996
3997 /* use the created diff for the actual attachment */
3998 medium = diff;
3999 mediumCaller.attach(medium);
4000 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4001 mediumLock.attach(medium);
4002 }
4003 while (0);
4004
4005 ComObjPtr<MediumAttachment> attachment;
4006 attachment.createObject();
4007 rc = attachment->init(this,
4008 medium,
4009 aName,
4010 aControllerPort,
4011 aDevice,
4012 aType,
4013 fIndirect,
4014 false /* fPassthrough */,
4015 false /* fTempEject */,
4016 false /* fNonRotational */,
4017 false /* fDiscard */,
4018 fHotplug /* fHotPluggable */,
4019 Utf8Str::Empty);
4020 if (FAILED(rc)) return rc;
4021
4022 if (associate && !medium.isNull())
4023 {
4024 // as the last step, associate the medium to the VM
4025 rc = medium->i_addBackReference(mData->mUuid);
4026 // here we can fail because of Deleting, or being in process of creating a Diff
4027 if (FAILED(rc)) return rc;
4028
4029 mediumLock.release();
4030 treeLock.release();
4031 alock.release();
4032 i_addMediumToRegistry(medium);
4033 alock.acquire();
4034 treeLock.acquire();
4035 mediumLock.acquire();
4036 }
4037
4038 /* success: finally remember the attachment */
4039 i_setModified(IsModified_Storage);
4040 mMediumAttachments.backup();
4041 mMediumAttachments->push_back(attachment);
4042
4043 mediumLock.release();
4044 treeLock.release();
4045 alock.release();
4046
4047 if (fHotplug || fSilent)
4048 {
4049 if (!medium.isNull())
4050 {
4051 MediumLockList *pMediumLockList(new MediumLockList());
4052
4053 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4054 medium /* pToLockWrite */,
4055 false /* fMediumLockWriteAll */,
4056 NULL,
4057 *pMediumLockList);
4058 alock.acquire();
4059 if (FAILED(rc))
4060 delete pMediumLockList;
4061 else
4062 {
4063 mData->mSession.mLockedMedia.Unlock();
4064 alock.release();
4065 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4066 mData->mSession.mLockedMedia.Lock();
4067 alock.acquire();
4068 }
4069 alock.release();
4070 }
4071
4072 if (SUCCEEDED(rc))
4073 {
4074 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4075 /* Remove lock list in case of error. */
4076 if (FAILED(rc))
4077 {
4078 mData->mSession.mLockedMedia.Unlock();
4079 mData->mSession.mLockedMedia.Remove(attachment);
4080 mData->mSession.mLockedMedia.Lock();
4081 }
4082 }
4083 }
4084
4085 /* Save modified registries, but skip this machine as it's the caller's
4086 * job to save its settings like all other settings changes. */
4087 mParent->i_unmarkRegistryModified(i_getId());
4088 mParent->i_saveModifiedRegistries();
4089
4090 if (SUCCEEDED(rc))
4091 {
4092 if (fIndirect && medium != aM)
4093 mParent->i_onMediumConfigChanged(medium);
4094 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4095 }
4096
4097 return rc;
4098}
4099
4100HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4101 LONG aDevice)
4102{
4103 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4104 aName.c_str(), aControllerPort, aDevice));
4105
4106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4107
4108 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4109 if (FAILED(rc)) return rc;
4110
4111 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4112
4113 /* Check for an existing controller. */
4114 ComObjPtr<StorageController> ctl;
4115 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4116 if (FAILED(rc)) return rc;
4117
4118 StorageControllerType_T ctrlType;
4119 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4120 if (FAILED(rc))
4121 return setError(E_FAIL,
4122 tr("Could not get type of controller '%s'"),
4123 aName.c_str());
4124
4125 bool fSilent = false;
4126 Utf8Str strReconfig;
4127
4128 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4129 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4130 if ( mData->mMachineState == MachineState_Paused
4131 && strReconfig == "1")
4132 fSilent = true;
4133
4134 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4135 bool fHotplug = false;
4136 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4137 fHotplug = true;
4138
4139 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4140 return setError(VBOX_E_INVALID_VM_STATE,
4141 tr("Controller '%s' does not support hotplugging"),
4142 aName.c_str());
4143
4144 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4145 aName,
4146 aControllerPort,
4147 aDevice);
4148 if (!pAttach)
4149 return setError(VBOX_E_OBJECT_NOT_FOUND,
4150 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4151 aDevice, aControllerPort, aName.c_str());
4152
4153 if (fHotplug && !pAttach->i_getHotPluggable())
4154 return setError(VBOX_E_NOT_SUPPORTED,
4155 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4156 aDevice, aControllerPort, aName.c_str());
4157
4158 /*
4159 * The VM has to detach the device before we delete any implicit diffs.
4160 * If this fails we can roll back without loosing data.
4161 */
4162 if (fHotplug || fSilent)
4163 {
4164 alock.release();
4165 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4166 alock.acquire();
4167 }
4168 if (FAILED(rc)) return rc;
4169
4170 /* If we are here everything went well and we can delete the implicit now. */
4171 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4172
4173 alock.release();
4174
4175 /* Save modified registries, but skip this machine as it's the caller's
4176 * job to save its settings like all other settings changes. */
4177 mParent->i_unmarkRegistryModified(i_getId());
4178 mParent->i_saveModifiedRegistries();
4179
4180 if (SUCCEEDED(rc))
4181 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4182
4183 return rc;
4184}
4185
4186HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4187 LONG aDevice, BOOL aPassthrough)
4188{
4189 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4190 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4191
4192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4193
4194 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4195 if (FAILED(rc)) return rc;
4196
4197 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4198
4199 /* Check for an existing controller. */
4200 ComObjPtr<StorageController> ctl;
4201 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4202 if (FAILED(rc)) return rc;
4203
4204 StorageControllerType_T ctrlType;
4205 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4206 if (FAILED(rc))
4207 return setError(E_FAIL,
4208 tr("Could not get type of controller '%s'"),
4209 aName.c_str());
4210
4211 bool fSilent = false;
4212 Utf8Str strReconfig;
4213
4214 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4215 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4216 if ( mData->mMachineState == MachineState_Paused
4217 && strReconfig == "1")
4218 fSilent = true;
4219
4220 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4221 bool fHotplug = false;
4222 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4223 fHotplug = true;
4224
4225 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4226 return setError(VBOX_E_INVALID_VM_STATE,
4227 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4228 aName.c_str());
4229
4230 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4231 aName,
4232 aControllerPort,
4233 aDevice);
4234 if (!pAttach)
4235 return setError(VBOX_E_OBJECT_NOT_FOUND,
4236 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4237 aDevice, aControllerPort, aName.c_str());
4238
4239
4240 i_setModified(IsModified_Storage);
4241 mMediumAttachments.backup();
4242
4243 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4244
4245 if (pAttach->i_getType() != DeviceType_DVD)
4246 return setError(E_INVALIDARG,
4247 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4248 aDevice, aControllerPort, aName.c_str());
4249
4250 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4251
4252 pAttach->i_updatePassthrough(!!aPassthrough);
4253
4254 attLock.release();
4255 alock.release();
4256 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4257 if (SUCCEEDED(rc) && fValueChanged)
4258 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4259
4260 return rc;
4261}
4262
4263HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4264 LONG aDevice, BOOL aTemporaryEject)
4265{
4266
4267 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4268 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4269
4270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4271
4272 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4273 if (FAILED(rc)) return rc;
4274
4275 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4276 aName,
4277 aControllerPort,
4278 aDevice);
4279 if (!pAttach)
4280 return setError(VBOX_E_OBJECT_NOT_FOUND,
4281 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4282 aDevice, aControllerPort, aName.c_str());
4283
4284
4285 i_setModified(IsModified_Storage);
4286 mMediumAttachments.backup();
4287
4288 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4289
4290 if (pAttach->i_getType() != DeviceType_DVD)
4291 return setError(E_INVALIDARG,
4292 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4293 aDevice, aControllerPort, aName.c_str());
4294 pAttach->i_updateTempEject(!!aTemporaryEject);
4295
4296 return S_OK;
4297}
4298
4299HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4300 LONG aDevice, BOOL aNonRotational)
4301{
4302
4303 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4304 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4305
4306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4307
4308 HRESULT rc = i_checkStateDependency(MutableStateDep);
4309 if (FAILED(rc)) return rc;
4310
4311 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4312
4313 if (Global::IsOnlineOrTransient(mData->mMachineState))
4314 return setError(VBOX_E_INVALID_VM_STATE,
4315 tr("Invalid machine state: %s"),
4316 Global::stringifyMachineState(mData->mMachineState));
4317
4318 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4319 aName,
4320 aControllerPort,
4321 aDevice);
4322 if (!pAttach)
4323 return setError(VBOX_E_OBJECT_NOT_FOUND,
4324 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4325 aDevice, aControllerPort, aName.c_str());
4326
4327
4328 i_setModified(IsModified_Storage);
4329 mMediumAttachments.backup();
4330
4331 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4332
4333 if (pAttach->i_getType() != DeviceType_HardDisk)
4334 return setError(E_INVALIDARG,
4335 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"),
4336 aDevice, aControllerPort, aName.c_str());
4337 pAttach->i_updateNonRotational(!!aNonRotational);
4338
4339 return S_OK;
4340}
4341
4342HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4343 LONG aDevice, BOOL aDiscard)
4344{
4345
4346 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4347 aName.c_str(), aControllerPort, aDevice, aDiscard));
4348
4349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4350
4351 HRESULT rc = i_checkStateDependency(MutableStateDep);
4352 if (FAILED(rc)) return rc;
4353
4354 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4355
4356 if (Global::IsOnlineOrTransient(mData->mMachineState))
4357 return setError(VBOX_E_INVALID_VM_STATE,
4358 tr("Invalid machine state: %s"),
4359 Global::stringifyMachineState(mData->mMachineState));
4360
4361 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4362 aName,
4363 aControllerPort,
4364 aDevice);
4365 if (!pAttach)
4366 return setError(VBOX_E_OBJECT_NOT_FOUND,
4367 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4368 aDevice, aControllerPort, aName.c_str());
4369
4370
4371 i_setModified(IsModified_Storage);
4372 mMediumAttachments.backup();
4373
4374 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4375
4376 if (pAttach->i_getType() != DeviceType_HardDisk)
4377 return setError(E_INVALIDARG,
4378 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"),
4379 aDevice, aControllerPort, aName.c_str());
4380 pAttach->i_updateDiscard(!!aDiscard);
4381
4382 return S_OK;
4383}
4384
4385HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4386 LONG aDevice, BOOL aHotPluggable)
4387{
4388 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4389 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4390
4391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4392
4393 HRESULT rc = i_checkStateDependency(MutableStateDep);
4394 if (FAILED(rc)) return rc;
4395
4396 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4397
4398 if (Global::IsOnlineOrTransient(mData->mMachineState))
4399 return setError(VBOX_E_INVALID_VM_STATE,
4400 tr("Invalid machine state: %s"),
4401 Global::stringifyMachineState(mData->mMachineState));
4402
4403 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4404 aName,
4405 aControllerPort,
4406 aDevice);
4407 if (!pAttach)
4408 return setError(VBOX_E_OBJECT_NOT_FOUND,
4409 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4410 aDevice, aControllerPort, aName.c_str());
4411
4412 /* Check for an existing controller. */
4413 ComObjPtr<StorageController> ctl;
4414 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4415 if (FAILED(rc)) return rc;
4416
4417 StorageControllerType_T ctrlType;
4418 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4419 if (FAILED(rc))
4420 return setError(E_FAIL,
4421 tr("Could not get type of controller '%s'"),
4422 aName.c_str());
4423
4424 if (!i_isControllerHotplugCapable(ctrlType))
4425 return setError(VBOX_E_NOT_SUPPORTED,
4426 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4427 aName.c_str());
4428
4429 i_setModified(IsModified_Storage);
4430 mMediumAttachments.backup();
4431
4432 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4433
4434 if (pAttach->i_getType() == DeviceType_Floppy)
4435 return setError(E_INVALIDARG,
4436 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"),
4437 aDevice, aControllerPort, aName.c_str());
4438 pAttach->i_updateHotPluggable(!!aHotPluggable);
4439
4440 return S_OK;
4441}
4442
4443HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4444 LONG aDevice)
4445{
4446 int rc = S_OK;
4447 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4448 aName.c_str(), aControllerPort, aDevice));
4449
4450 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4451
4452 return rc;
4453}
4454
4455HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4456 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4457{
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4459 aName.c_str(), aControllerPort, aDevice));
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 if (Global::IsOnlineOrTransient(mData->mMachineState))
4467 return setError(VBOX_E_INVALID_VM_STATE,
4468 tr("Invalid machine state: %s"),
4469 Global::stringifyMachineState(mData->mMachineState));
4470
4471 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4472 aName,
4473 aControllerPort,
4474 aDevice);
4475 if (!pAttach)
4476 return setError(VBOX_E_OBJECT_NOT_FOUND,
4477 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4478 aDevice, aControllerPort, aName.c_str());
4479
4480
4481 i_setModified(IsModified_Storage);
4482 mMediumAttachments.backup();
4483
4484 IBandwidthGroup *iB = aBandwidthGroup;
4485 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4486 if (aBandwidthGroup && group.isNull())
4487 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4488
4489 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4490
4491 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4492 if (strBandwidthGroupOld.isNotEmpty())
4493 {
4494 /* Get the bandwidth group object and release it - this must not fail. */
4495 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4496 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4497 Assert(SUCCEEDED(rc));
4498
4499 pBandwidthGroupOld->i_release();
4500 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4501 }
4502
4503 if (!group.isNull())
4504 {
4505 group->i_reference();
4506 pAttach->i_updateBandwidthGroup(group->i_getName());
4507 }
4508
4509 return S_OK;
4510}
4511
4512HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4513 LONG aControllerPort,
4514 LONG aDevice,
4515 DeviceType_T aType)
4516{
4517 HRESULT rc = S_OK;
4518
4519 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4520 aName.c_str(), aControllerPort, aDevice, aType));
4521
4522 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4523
4524 return rc;
4525}
4526
4527
4528HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4529 LONG aControllerPort,
4530 LONG aDevice,
4531 BOOL aForce)
4532{
4533 int rc = S_OK;
4534 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4535 aName.c_str(), aControllerPort, aForce));
4536
4537 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4538
4539 return rc;
4540}
4541
4542HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4543 LONG aControllerPort,
4544 LONG aDevice,
4545 const ComPtr<IMedium> &aMedium,
4546 BOOL aForce)
4547{
4548 int rc = S_OK;
4549 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4550 aName.c_str(), aControllerPort, aDevice, aForce));
4551
4552 // request the host lock first, since might be calling Host methods for getting host drives;
4553 // next, protect the media tree all the while we're in here, as well as our member variables
4554 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4555 this->lockHandle(),
4556 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4557
4558 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4559 aName,
4560 aControllerPort,
4561 aDevice);
4562 if (pAttach.isNull())
4563 return setError(VBOX_E_OBJECT_NOT_FOUND,
4564 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4565 aDevice, aControllerPort, aName.c_str());
4566
4567 /* Remember previously mounted medium. The medium before taking the
4568 * backup is not necessarily the same thing. */
4569 ComObjPtr<Medium> oldmedium;
4570 oldmedium = pAttach->i_getMedium();
4571
4572 IMedium *iM = aMedium;
4573 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4574 if (aMedium && pMedium.isNull())
4575 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4576
4577 AutoCaller mediumCaller(pMedium);
4578 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4579
4580 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4581 if (pMedium)
4582 {
4583 DeviceType_T mediumType = pAttach->i_getType();
4584 switch (mediumType)
4585 {
4586 case DeviceType_DVD:
4587 case DeviceType_Floppy:
4588 break;
4589
4590 default:
4591 return setError(VBOX_E_INVALID_OBJECT_STATE,
4592 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4593 aControllerPort,
4594 aDevice,
4595 aName.c_str());
4596 }
4597 }
4598
4599 i_setModified(IsModified_Storage);
4600 mMediumAttachments.backup();
4601
4602 {
4603 // The backup operation makes the pAttach reference point to the
4604 // old settings. Re-get the correct reference.
4605 pAttach = i_findAttachment(*mMediumAttachments.data(),
4606 aName,
4607 aControllerPort,
4608 aDevice);
4609 if (!oldmedium.isNull())
4610 oldmedium->i_removeBackReference(mData->mUuid);
4611 if (!pMedium.isNull())
4612 {
4613 pMedium->i_addBackReference(mData->mUuid);
4614
4615 mediumLock.release();
4616 multiLock.release();
4617 i_addMediumToRegistry(pMedium);
4618 multiLock.acquire();
4619 mediumLock.acquire();
4620 }
4621
4622 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4623 pAttach->i_updateMedium(pMedium);
4624 }
4625
4626 i_setModified(IsModified_Storage);
4627
4628 mediumLock.release();
4629 multiLock.release();
4630 rc = i_onMediumChange(pAttach, aForce);
4631 multiLock.acquire();
4632 mediumLock.acquire();
4633
4634 /* On error roll back this change only. */
4635 if (FAILED(rc))
4636 {
4637 if (!pMedium.isNull())
4638 pMedium->i_removeBackReference(mData->mUuid);
4639 pAttach = i_findAttachment(*mMediumAttachments.data(),
4640 aName,
4641 aControllerPort,
4642 aDevice);
4643 /* If the attachment is gone in the meantime, bail out. */
4644 if (pAttach.isNull())
4645 return rc;
4646 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4647 if (!oldmedium.isNull())
4648 oldmedium->i_addBackReference(mData->mUuid);
4649 pAttach->i_updateMedium(oldmedium);
4650 }
4651
4652 mediumLock.release();
4653 multiLock.release();
4654
4655 /* Save modified registries, but skip this machine as it's the caller's
4656 * job to save its settings like all other settings changes. */
4657 mParent->i_unmarkRegistryModified(i_getId());
4658 mParent->i_saveModifiedRegistries();
4659
4660 return rc;
4661}
4662HRESULT Machine::getMedium(const com::Utf8Str &aName,
4663 LONG aControllerPort,
4664 LONG aDevice,
4665 ComPtr<IMedium> &aMedium)
4666{
4667 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4668 aName.c_str(), aControllerPort, aDevice));
4669
4670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4671
4672 aMedium = NULL;
4673
4674 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4675 aName,
4676 aControllerPort,
4677 aDevice);
4678 if (pAttach.isNull())
4679 return setError(VBOX_E_OBJECT_NOT_FOUND,
4680 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4681 aDevice, aControllerPort, aName.c_str());
4682
4683 aMedium = pAttach->i_getMedium();
4684
4685 return S_OK;
4686}
4687
4688HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4689{
4690
4691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4694
4695 return S_OK;
4696}
4697
4698HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4699{
4700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4701
4702 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4703
4704 return S_OK;
4705}
4706
4707HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4708{
4709 /* Do not assert if slot is out of range, just return the advertised
4710 status. testdriver/vbox.py triggers this in logVmInfo. */
4711 if (aSlot >= mNetworkAdapters.size())
4712 return setError(E_INVALIDARG,
4713 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4714 aSlot, mNetworkAdapters.size());
4715
4716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4717
4718 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4719
4720 return S_OK;
4721}
4722
4723HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4724{
4725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4726
4727 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4728 size_t i = 0;
4729 for (settings::StringsMap::const_iterator
4730 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4731 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4732 ++it, ++i)
4733 aKeys[i] = it->first;
4734
4735 return S_OK;
4736}
4737
4738 /**
4739 * @note Locks this object for reading.
4740 */
4741HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4742 com::Utf8Str &aValue)
4743{
4744 /* start with nothing found */
4745 aValue = "";
4746
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4748
4749 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4750 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4751 // found:
4752 aValue = it->second; // source is a Utf8Str
4753
4754 /* return the result to caller (may be empty) */
4755 return S_OK;
4756}
4757
4758 /**
4759 * @note Locks mParent for writing + this object for writing.
4760 */
4761HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4762{
4763 /* Because control characters in aKey have caused problems in the settings
4764 * they are rejected unless the key should be deleted. */
4765 if (!aValue.isEmpty())
4766 {
4767 for (size_t i = 0; i < aKey.length(); ++i)
4768 {
4769 char ch = aKey[i];
4770 if (RTLocCIsCntrl(ch))
4771 return E_INVALIDARG;
4772 }
4773 }
4774
4775 Utf8Str strOldValue; // empty
4776
4777 // locking note: we only hold the read lock briefly to look up the old value,
4778 // then release it and call the onExtraCanChange callbacks. There is a small
4779 // chance of a race insofar as the callback might be called twice if two callers
4780 // change the same key at the same time, but that's a much better solution
4781 // than the deadlock we had here before. The actual changing of the extradata
4782 // is then performed under the write lock and race-free.
4783
4784 // look up the old value first; if nothing has changed then we need not do anything
4785 {
4786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4787
4788 // For snapshots don't even think about allowing changes, extradata
4789 // is global for a machine, so there is nothing snapshot specific.
4790 if (i_isSnapshotMachine())
4791 return setError(VBOX_E_INVALID_VM_STATE,
4792 tr("Cannot set extradata for a snapshot"));
4793
4794 // check if the right IMachine instance is used
4795 if (mData->mRegistered && !i_isSessionMachine())
4796 return setError(VBOX_E_INVALID_VM_STATE,
4797 tr("Cannot set extradata for an immutable machine"));
4798
4799 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4800 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4801 strOldValue = it->second;
4802 }
4803
4804 bool fChanged;
4805 if ((fChanged = (strOldValue != aValue)))
4806 {
4807 // ask for permission from all listeners outside the locks;
4808 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4809 // lock to copy the list of callbacks to invoke
4810 Bstr error;
4811 Bstr bstrValue(aValue);
4812
4813 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4814 {
4815 const char *sep = error.isEmpty() ? "" : ": ";
4816 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4817 return setError(E_ACCESSDENIED,
4818 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4819 aKey.c_str(),
4820 aValue.c_str(),
4821 sep,
4822 error.raw());
4823 }
4824
4825 // data is changing and change not vetoed: then write it out under the lock
4826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4827
4828 if (aValue.isEmpty())
4829 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4830 else
4831 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4832 // creates a new key if needed
4833
4834 bool fNeedsGlobalSaveSettings = false;
4835 // This saving of settings is tricky: there is no "old state" for the
4836 // extradata items at all (unlike all other settings), so the old/new
4837 // settings comparison would give a wrong result!
4838 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4839
4840 if (fNeedsGlobalSaveSettings)
4841 {
4842 // save the global settings; for that we should hold only the VirtualBox lock
4843 alock.release();
4844 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4845 mParent->i_saveSettings();
4846 }
4847 }
4848
4849 // fire notification outside the lock
4850 if (fChanged)
4851 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4852
4853 return S_OK;
4854}
4855
4856HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4857{
4858 aProgress = NULL;
4859 NOREF(aSettingsFilePath);
4860 ReturnComNotImplemented();
4861}
4862
4863HRESULT Machine::saveSettings()
4864{
4865 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4866
4867 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4868 if (FAILED(rc)) return rc;
4869
4870 /* the settings file path may never be null */
4871 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4872
4873 /* save all VM data excluding snapshots */
4874 bool fNeedsGlobalSaveSettings = false;
4875 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4876 mlock.release();
4877
4878 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4879 {
4880 // save the global settings; for that we should hold only the VirtualBox lock
4881 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4882 rc = mParent->i_saveSettings();
4883 }
4884
4885 return rc;
4886}
4887
4888
4889HRESULT Machine::discardSettings()
4890{
4891 /*
4892 * We need to take the machine list lock here as well as the machine one
4893 * or we'll get into trouble should any media stuff require rolling back.
4894 *
4895 * Details:
4896 *
4897 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4898 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4899 * 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]
4900 * 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
4901 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4902 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4903 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4904 * 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
4905 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4906 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4907 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4908 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4909 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4910 * 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]
4911 * 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] (*)
4912 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4913 * 0:005> k
4914 * # Child-SP RetAddr Call Site
4915 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4916 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4917 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4918 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4919 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4920 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4921 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4922 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4923 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4924 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4925 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4926 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4927 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4928 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4929 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4930 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4931 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4932 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4933 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4934 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4935 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4936 *
4937 */
4938 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4940
4941 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4942 if (FAILED(rc)) return rc;
4943
4944 /*
4945 * during this rollback, the session will be notified if data has
4946 * been actually changed
4947 */
4948 i_rollback(true /* aNotify */);
4949
4950 return S_OK;
4951}
4952
4953/** @note Locks objects! */
4954HRESULT Machine::unregister(AutoCaller &autoCaller,
4955 CleanupMode_T aCleanupMode,
4956 std::vector<ComPtr<IMedium> > &aMedia)
4957{
4958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4959
4960 Guid id(i_getId());
4961
4962 if (mData->mSession.mState != SessionState_Unlocked)
4963 return setError(VBOX_E_INVALID_OBJECT_STATE,
4964 tr("Cannot unregister the machine '%s' while it is locked"),
4965 mUserData->s.strName.c_str());
4966
4967 // wait for state dependents to drop to zero
4968 i_ensureNoStateDependencies();
4969
4970 if (!mData->mAccessible)
4971 {
4972 // inaccessible maschines can only be unregistered; uninitialize ourselves
4973 // here because currently there may be no unregistered that are inaccessible
4974 // (this state combination is not supported). Note releasing the caller and
4975 // leaving the lock before calling uninit()
4976 alock.release();
4977 autoCaller.release();
4978
4979 uninit();
4980
4981 mParent->i_unregisterMachine(this, id);
4982 // calls VirtualBox::i_saveSettings()
4983
4984 return S_OK;
4985 }
4986
4987 HRESULT rc = S_OK;
4988
4989 /// @todo r=klaus this is stupid... why is the saved state always deleted?
4990 // discard saved state
4991 if (mData->mMachineState == MachineState_Saved)
4992 {
4993 // add the saved state file to the list of files the caller should delete
4994 Assert(!mSSData->strStateFilePath.isEmpty());
4995 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4996
4997 mSSData->strStateFilePath.setNull();
4998
4999 // unconditionally set the machine state to powered off, we now
5000 // know no session has locked the machine
5001 mData->mMachineState = MachineState_PoweredOff;
5002 }
5003
5004 size_t cSnapshots = 0;
5005 if (mData->mFirstSnapshot)
5006 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5007 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5008 // fail now before we start detaching media
5009 return setError(VBOX_E_INVALID_OBJECT_STATE,
5010 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5011 mUserData->s.strName.c_str(), cSnapshots);
5012
5013 // This list collects the medium objects from all medium attachments
5014 // which we will detach from the machine and its snapshots, in a specific
5015 // order which allows for closing all media without getting "media in use"
5016 // errors, simply by going through the list from the front to the back:
5017 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5018 // and must be closed before the parent media from the snapshots, or closing the parents
5019 // will fail because they still have children);
5020 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5021 // the root ("first") snapshot of the machine.
5022 MediaList llMedia;
5023
5024 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5025 && mMediumAttachments->size()
5026 )
5027 {
5028 // we have media attachments: detach them all and add the Medium objects to our list
5029 if (aCleanupMode != CleanupMode_UnregisterOnly)
5030 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5031 else
5032 return setError(VBOX_E_INVALID_OBJECT_STATE,
5033 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5034 mUserData->s.strName.c_str(), mMediumAttachments->size());
5035 }
5036
5037 if (cSnapshots)
5038 {
5039 // add the media from the medium attachments of the snapshots to llMedia
5040 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5041 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5042 // into the children first
5043
5044 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5045 MachineState_T oldState = mData->mMachineState;
5046 mData->mMachineState = MachineState_DeletingSnapshot;
5047
5048 // make a copy of the first snapshot so the refcount does not drop to 0
5049 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5050 // because of the AutoCaller voodoo)
5051 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5052
5053 // GO!
5054 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5055
5056 mData->mMachineState = oldState;
5057 }
5058
5059 if (FAILED(rc))
5060 {
5061 i_rollbackMedia();
5062 return rc;
5063 }
5064
5065 // commit all the media changes made above
5066 i_commitMedia();
5067
5068 mData->mRegistered = false;
5069
5070 // machine lock no longer needed
5071 alock.release();
5072
5073 // return media to caller
5074 aMedia.resize(llMedia.size());
5075 size_t i = 0;
5076 for (MediaList::const_iterator
5077 it = llMedia.begin();
5078 it != llMedia.end();
5079 ++it, ++i)
5080 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5081
5082 mParent->i_unregisterMachine(this, id);
5083 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5084
5085 return S_OK;
5086}
5087
5088/**
5089 * Task record for deleting a machine config.
5090 */
5091class Machine::DeleteConfigTask
5092 : public Machine::Task
5093{
5094public:
5095 DeleteConfigTask(Machine *m,
5096 Progress *p,
5097 const Utf8Str &t,
5098 const RTCList<ComPtr<IMedium> > &llMediums,
5099 const StringsList &llFilesToDelete)
5100 : Task(m, p, t),
5101 m_llMediums(llMediums),
5102 m_llFilesToDelete(llFilesToDelete)
5103 {}
5104
5105private:
5106 void handler()
5107 {
5108 try
5109 {
5110 m_pMachine->i_deleteConfigHandler(*this);
5111 }
5112 catch (...)
5113 {
5114 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5115 }
5116 }
5117
5118 RTCList<ComPtr<IMedium> > m_llMediums;
5119 StringsList m_llFilesToDelete;
5120
5121 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5122};
5123
5124/**
5125 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5126 * SessionMachine::taskHandler().
5127 *
5128 * @note Locks this object for writing.
5129 *
5130 * @param task
5131 * @return
5132 */
5133void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5134{
5135 LogFlowThisFuncEnter();
5136
5137 AutoCaller autoCaller(this);
5138 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5139 if (FAILED(autoCaller.rc()))
5140 {
5141 /* we might have been uninitialized because the session was accidentally
5142 * closed by the client, so don't assert */
5143 HRESULT rc = setError(E_FAIL,
5144 tr("The session has been accidentally closed"));
5145 task.m_pProgress->i_notifyComplete(rc);
5146 LogFlowThisFuncLeave();
5147 return;
5148 }
5149
5150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5151
5152 HRESULT rc = S_OK;
5153
5154 try
5155 {
5156 ULONG uLogHistoryCount = 3;
5157 ComPtr<ISystemProperties> systemProperties;
5158 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5159 if (FAILED(rc)) throw rc;
5160
5161 if (!systemProperties.isNull())
5162 {
5163 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5164 if (FAILED(rc)) throw rc;
5165 }
5166
5167 MachineState_T oldState = mData->mMachineState;
5168 i_setMachineState(MachineState_SettingUp);
5169 alock.release();
5170 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5171 {
5172 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5173 {
5174 AutoCaller mac(pMedium);
5175 if (FAILED(mac.rc())) throw mac.rc();
5176 Utf8Str strLocation = pMedium->i_getLocationFull();
5177 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5178 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5179 if (FAILED(rc)) throw rc;
5180 }
5181 if (pMedium->i_isMediumFormatFile())
5182 {
5183 ComPtr<IProgress> pProgress2;
5184 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5185 if (FAILED(rc)) throw rc;
5186 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5187 if (FAILED(rc)) throw rc;
5188 }
5189
5190 /* Close the medium, deliberately without checking the return
5191 * code, and without leaving any trace in the error info, as
5192 * a failure here is a very minor issue, which shouldn't happen
5193 * as above we even managed to delete the medium. */
5194 {
5195 ErrorInfoKeeper eik;
5196 pMedium->Close();
5197 }
5198 }
5199 i_setMachineState(oldState);
5200 alock.acquire();
5201
5202 // delete the files pushed on the task list by Machine::Delete()
5203 // (this includes saved states of the machine and snapshots and
5204 // medium storage files from the IMedium list passed in, and the
5205 // machine XML file)
5206 for (StringsList::const_iterator
5207 it = task.m_llFilesToDelete.begin();
5208 it != task.m_llFilesToDelete.end();
5209 ++it)
5210 {
5211 const Utf8Str &strFile = *it;
5212 LogFunc(("Deleting file %s\n", strFile.c_str()));
5213 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5214 if (FAILED(rc)) throw rc;
5215
5216 int vrc = RTFileDelete(strFile.c_str());
5217 if (RT_FAILURE(vrc))
5218 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5219 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5220 }
5221
5222 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5223 if (FAILED(rc)) throw rc;
5224
5225 /* delete the settings only when the file actually exists */
5226 if (mData->pMachineConfigFile->fileExists())
5227 {
5228 /* Delete any backup or uncommitted XML files. Ignore failures.
5229 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5230 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5231 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5232 RTFileDelete(otherXml.c_str());
5233 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5234 RTFileDelete(otherXml.c_str());
5235
5236 /* delete the Logs folder, nothing important should be left
5237 * there (we don't check for errors because the user might have
5238 * some private files there that we don't want to delete) */
5239 Utf8Str logFolder;
5240 getLogFolder(logFolder);
5241 Assert(logFolder.length());
5242 if (RTDirExists(logFolder.c_str()))
5243 {
5244 /* Delete all VBox.log[.N] files from the Logs folder
5245 * (this must be in sync with the rotation logic in
5246 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5247 * files that may have been created by the GUI. */
5248 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5249 logFolder.c_str(), RTPATH_DELIMITER);
5250 RTFileDelete(log.c_str());
5251 log = Utf8StrFmt("%s%cVBox.png",
5252 logFolder.c_str(), RTPATH_DELIMITER);
5253 RTFileDelete(log.c_str());
5254 for (int i = uLogHistoryCount; i > 0; i--)
5255 {
5256 log = Utf8StrFmt("%s%cVBox.log.%d",
5257 logFolder.c_str(), RTPATH_DELIMITER, i);
5258 RTFileDelete(log.c_str());
5259 log = Utf8StrFmt("%s%cVBox.png.%d",
5260 logFolder.c_str(), RTPATH_DELIMITER, i);
5261 RTFileDelete(log.c_str());
5262 }
5263#if defined(RT_OS_WINDOWS)
5264 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5265 RTFileDelete(log.c_str());
5266 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5267 RTFileDelete(log.c_str());
5268#endif
5269
5270 RTDirRemove(logFolder.c_str());
5271 }
5272
5273 /* delete the Snapshots folder, nothing important should be left
5274 * there (we don't check for errors because the user might have
5275 * some private files there that we don't want to delete) */
5276 Utf8Str strFullSnapshotFolder;
5277 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5278 Assert(!strFullSnapshotFolder.isEmpty());
5279 if (RTDirExists(strFullSnapshotFolder.c_str()))
5280 RTDirRemove(strFullSnapshotFolder.c_str());
5281
5282 // delete the directory that contains the settings file, but only
5283 // if it matches the VM name
5284 Utf8Str settingsDir;
5285 if (i_isInOwnDir(&settingsDir))
5286 RTDirRemove(settingsDir.c_str());
5287 }
5288
5289 alock.release();
5290
5291 mParent->i_saveModifiedRegistries();
5292 }
5293 catch (HRESULT aRC) { rc = aRC; }
5294
5295 task.m_pProgress->i_notifyComplete(rc);
5296
5297 LogFlowThisFuncLeave();
5298}
5299
5300HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5301{
5302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5303
5304 HRESULT rc = i_checkStateDependency(MutableStateDep);
5305 if (FAILED(rc)) return rc;
5306
5307 if (mData->mRegistered)
5308 return setError(VBOX_E_INVALID_VM_STATE,
5309 tr("Cannot delete settings of a registered machine"));
5310
5311 // collect files to delete
5312 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5313 if (mData->pMachineConfigFile->fileExists())
5314 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5315
5316 RTCList<ComPtr<IMedium> > llMediums;
5317 for (size_t i = 0; i < aMedia.size(); ++i)
5318 {
5319 IMedium *pIMedium(aMedia[i]);
5320 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5321 if (pMedium.isNull())
5322 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5323 SafeArray<BSTR> ids;
5324 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5325 if (FAILED(rc)) return rc;
5326 /* At this point the medium should not have any back references
5327 * anymore. If it has it is attached to another VM and *must* not
5328 * deleted. */
5329 if (ids.size() < 1)
5330 llMediums.append(pMedium);
5331 }
5332
5333 ComObjPtr<Progress> pProgress;
5334 pProgress.createObject();
5335 rc = pProgress->init(i_getVirtualBox(),
5336 static_cast<IMachine*>(this) /* aInitiator */,
5337 tr("Deleting files"),
5338 true /* fCancellable */,
5339 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5340 tr("Collecting file inventory"));
5341 if (FAILED(rc))
5342 return rc;
5343
5344 /* create and start the task on a separate thread (note that it will not
5345 * start working until we release alock) */
5346 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5347 rc = pTask->createThread();
5348 pTask = NULL;
5349 if (FAILED(rc))
5350 return rc;
5351
5352 pProgress.queryInterfaceTo(aProgress.asOutParam());
5353
5354 LogFlowFuncLeave();
5355
5356 return S_OK;
5357}
5358
5359HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5360{
5361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5362
5363 ComObjPtr<Snapshot> pSnapshot;
5364 HRESULT rc;
5365
5366 if (aNameOrId.isEmpty())
5367 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5368 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5369 else
5370 {
5371 Guid uuid(aNameOrId);
5372 if (uuid.isValid())
5373 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5374 else
5375 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5376 }
5377 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5378
5379 return rc;
5380}
5381
5382HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5383 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5384{
5385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5386
5387 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5388 if (FAILED(rc)) return rc;
5389
5390 ComObjPtr<SharedFolder> sharedFolder;
5391 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5392 if (SUCCEEDED(rc))
5393 return setError(VBOX_E_OBJECT_IN_USE,
5394 tr("Shared folder named '%s' already exists"),
5395 aName.c_str());
5396
5397 sharedFolder.createObject();
5398 rc = sharedFolder->init(i_getMachine(),
5399 aName,
5400 aHostPath,
5401 !!aWritable,
5402 !!aAutomount,
5403 aAutoMountPoint,
5404 true /* fFailOnError */);
5405 if (FAILED(rc)) return rc;
5406
5407 i_setModified(IsModified_SharedFolders);
5408 mHWData.backup();
5409 mHWData->mSharedFolders.push_back(sharedFolder);
5410
5411 /* inform the direct session if any */
5412 alock.release();
5413 i_onSharedFolderChange();
5414
5415 return S_OK;
5416}
5417
5418HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5419{
5420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5421
5422 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5423 if (FAILED(rc)) return rc;
5424
5425 ComObjPtr<SharedFolder> sharedFolder;
5426 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5427 if (FAILED(rc)) return rc;
5428
5429 i_setModified(IsModified_SharedFolders);
5430 mHWData.backup();
5431 mHWData->mSharedFolders.remove(sharedFolder);
5432
5433 /* inform the direct session if any */
5434 alock.release();
5435 i_onSharedFolderChange();
5436
5437 return S_OK;
5438}
5439
5440HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5441{
5442 /* start with No */
5443 *aCanShow = FALSE;
5444
5445 ComPtr<IInternalSessionControl> directControl;
5446 {
5447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5448
5449 if (mData->mSession.mState != SessionState_Locked)
5450 return setError(VBOX_E_INVALID_VM_STATE,
5451 tr("Machine is not locked for session (session state: %s)"),
5452 Global::stringifySessionState(mData->mSession.mState));
5453
5454 if (mData->mSession.mLockType == LockType_VM)
5455 directControl = mData->mSession.mDirectControl;
5456 }
5457
5458 /* ignore calls made after #OnSessionEnd() is called */
5459 if (!directControl)
5460 return S_OK;
5461
5462 LONG64 dummy;
5463 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5464}
5465
5466HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5467{
5468 ComPtr<IInternalSessionControl> directControl;
5469 {
5470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5471
5472 if (mData->mSession.mState != SessionState_Locked)
5473 return setError(E_FAIL,
5474 tr("Machine is not locked for session (session state: %s)"),
5475 Global::stringifySessionState(mData->mSession.mState));
5476
5477 if (mData->mSession.mLockType == LockType_VM)
5478 directControl = mData->mSession.mDirectControl;
5479 }
5480
5481 /* ignore calls made after #OnSessionEnd() is called */
5482 if (!directControl)
5483 return S_OK;
5484
5485 BOOL dummy;
5486 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5487}
5488
5489#ifdef VBOX_WITH_GUEST_PROPS
5490/**
5491 * Look up a guest property in VBoxSVC's internal structures.
5492 */
5493HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5494 com::Utf8Str &aValue,
5495 LONG64 *aTimestamp,
5496 com::Utf8Str &aFlags) const
5497{
5498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5499
5500 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5501 if (it != mHWData->mGuestProperties.end())
5502 {
5503 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5504 aValue = it->second.strValue;
5505 *aTimestamp = it->second.mTimestamp;
5506 GuestPropWriteFlags(it->second.mFlags, szFlags);
5507 aFlags = Utf8Str(szFlags);
5508 }
5509
5510 return S_OK;
5511}
5512
5513/**
5514 * Query the VM that a guest property belongs to for the property.
5515 * @returns E_ACCESSDENIED if the VM process is not available or not
5516 * currently handling queries and the lookup should then be done in
5517 * VBoxSVC.
5518 */
5519HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5520 com::Utf8Str &aValue,
5521 LONG64 *aTimestamp,
5522 com::Utf8Str &aFlags) const
5523{
5524 HRESULT rc = S_OK;
5525 Bstr bstrValue;
5526 Bstr bstrFlags;
5527
5528 ComPtr<IInternalSessionControl> directControl;
5529 {
5530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5531 if (mData->mSession.mLockType == LockType_VM)
5532 directControl = mData->mSession.mDirectControl;
5533 }
5534
5535 /* ignore calls made after #OnSessionEnd() is called */
5536 if (!directControl)
5537 rc = E_ACCESSDENIED;
5538 else
5539 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5540 0 /* accessMode */,
5541 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5542
5543 aValue = bstrValue;
5544 aFlags = bstrFlags;
5545
5546 return rc;
5547}
5548#endif // VBOX_WITH_GUEST_PROPS
5549
5550HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5551 com::Utf8Str &aValue,
5552 LONG64 *aTimestamp,
5553 com::Utf8Str &aFlags)
5554{
5555#ifndef VBOX_WITH_GUEST_PROPS
5556 ReturnComNotImplemented();
5557#else // VBOX_WITH_GUEST_PROPS
5558
5559 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5560
5561 if (rc == E_ACCESSDENIED)
5562 /* The VM is not running or the service is not (yet) accessible */
5563 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5564 return rc;
5565#endif // VBOX_WITH_GUEST_PROPS
5566}
5567
5568HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5569{
5570 LONG64 dummyTimestamp;
5571 com::Utf8Str dummyFlags;
5572 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5573 return rc;
5574
5575}
5576HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5577{
5578 com::Utf8Str dummyFlags;
5579 com::Utf8Str dummyValue;
5580 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5581 return rc;
5582}
5583
5584#ifdef VBOX_WITH_GUEST_PROPS
5585/**
5586 * Set a guest property in VBoxSVC's internal structures.
5587 */
5588HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5589 const com::Utf8Str &aFlags, bool fDelete)
5590{
5591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5592 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5593 if (FAILED(rc)) return rc;
5594
5595 try
5596 {
5597 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5598 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5599 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5600
5601 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5602 if (it == mHWData->mGuestProperties.end())
5603 {
5604 if (!fDelete)
5605 {
5606 i_setModified(IsModified_MachineData);
5607 mHWData.backupEx();
5608
5609 RTTIMESPEC time;
5610 HWData::GuestProperty prop;
5611 prop.strValue = Bstr(aValue).raw();
5612 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5613 prop.mFlags = fFlags;
5614 mHWData->mGuestProperties[aName] = prop;
5615 }
5616 }
5617 else
5618 {
5619 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5620 {
5621 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5622 }
5623 else
5624 {
5625 i_setModified(IsModified_MachineData);
5626 mHWData.backupEx();
5627
5628 /* The backupEx() operation invalidates our iterator,
5629 * so get a new one. */
5630 it = mHWData->mGuestProperties.find(aName);
5631 Assert(it != mHWData->mGuestProperties.end());
5632
5633 if (!fDelete)
5634 {
5635 RTTIMESPEC time;
5636 it->second.strValue = aValue;
5637 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5638 it->second.mFlags = fFlags;
5639 }
5640 else
5641 mHWData->mGuestProperties.erase(it);
5642 }
5643 }
5644
5645 if (SUCCEEDED(rc))
5646 {
5647 alock.release();
5648
5649 mParent->i_onGuestPropertyChange(mData->mUuid,
5650 Bstr(aName).raw(),
5651 Bstr(aValue).raw(),
5652 Bstr(aFlags).raw());
5653 }
5654 }
5655 catch (std::bad_alloc &)
5656 {
5657 rc = E_OUTOFMEMORY;
5658 }
5659
5660 return rc;
5661}
5662
5663/**
5664 * Set a property on the VM that that property belongs to.
5665 * @returns E_ACCESSDENIED if the VM process is not available or not
5666 * currently handling queries and the setting should then be done in
5667 * VBoxSVC.
5668 */
5669HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5670 const com::Utf8Str &aFlags, bool fDelete)
5671{
5672 HRESULT rc;
5673
5674 try
5675 {
5676 ComPtr<IInternalSessionControl> directControl;
5677 {
5678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5679 if (mData->mSession.mLockType == LockType_VM)
5680 directControl = mData->mSession.mDirectControl;
5681 }
5682
5683 Bstr dummy1; /* will not be changed (setter) */
5684 Bstr dummy2; /* will not be changed (setter) */
5685 LONG64 dummy64;
5686 if (!directControl)
5687 rc = E_ACCESSDENIED;
5688 else
5689 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5690 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5691 fDelete ? 2 : 1 /* accessMode */,
5692 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5693 }
5694 catch (std::bad_alloc &)
5695 {
5696 rc = E_OUTOFMEMORY;
5697 }
5698
5699 return rc;
5700}
5701#endif // VBOX_WITH_GUEST_PROPS
5702
5703HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5704 const com::Utf8Str &aFlags)
5705{
5706#ifndef VBOX_WITH_GUEST_PROPS
5707 ReturnComNotImplemented();
5708#else // VBOX_WITH_GUEST_PROPS
5709 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5710 if (rc == E_ACCESSDENIED)
5711 /* The VM is not running or the service is not (yet) accessible */
5712 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5713 return rc;
5714#endif // VBOX_WITH_GUEST_PROPS
5715}
5716
5717HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5718{
5719 return setGuestProperty(aProperty, aValue, "");
5720}
5721
5722HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5723{
5724#ifndef VBOX_WITH_GUEST_PROPS
5725 ReturnComNotImplemented();
5726#else // VBOX_WITH_GUEST_PROPS
5727 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5728 if (rc == E_ACCESSDENIED)
5729 /* The VM is not running or the service is not (yet) accessible */
5730 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5731 return rc;
5732#endif // VBOX_WITH_GUEST_PROPS
5733}
5734
5735#ifdef VBOX_WITH_GUEST_PROPS
5736/**
5737 * Enumerate the guest properties in VBoxSVC's internal structures.
5738 */
5739HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5740 std::vector<com::Utf8Str> &aNames,
5741 std::vector<com::Utf8Str> &aValues,
5742 std::vector<LONG64> &aTimestamps,
5743 std::vector<com::Utf8Str> &aFlags)
5744{
5745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5746 Utf8Str strPatterns(aPatterns);
5747
5748 /*
5749 * Look for matching patterns and build up a list.
5750 */
5751 HWData::GuestPropertyMap propMap;
5752 for (HWData::GuestPropertyMap::const_iterator
5753 it = mHWData->mGuestProperties.begin();
5754 it != mHWData->mGuestProperties.end();
5755 ++it)
5756 {
5757 if ( strPatterns.isEmpty()
5758 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5759 RTSTR_MAX,
5760 it->first.c_str(),
5761 RTSTR_MAX,
5762 NULL)
5763 )
5764 propMap.insert(*it);
5765 }
5766
5767 alock.release();
5768
5769 /*
5770 * And build up the arrays for returning the property information.
5771 */
5772 size_t cEntries = propMap.size();
5773
5774 aNames.resize(cEntries);
5775 aValues.resize(cEntries);
5776 aTimestamps.resize(cEntries);
5777 aFlags.resize(cEntries);
5778
5779 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5780 size_t i = 0;
5781 for (HWData::GuestPropertyMap::const_iterator
5782 it = propMap.begin();
5783 it != propMap.end();
5784 ++it, ++i)
5785 {
5786 aNames[i] = it->first;
5787 aValues[i] = it->second.strValue;
5788 aTimestamps[i] = it->second.mTimestamp;
5789 GuestPropWriteFlags(it->second.mFlags, szFlags);
5790 aFlags[i] = Utf8Str(szFlags);
5791 }
5792
5793 return S_OK;
5794}
5795
5796/**
5797 * Enumerate the properties managed by a VM.
5798 * @returns E_ACCESSDENIED if the VM process is not available or not
5799 * currently handling queries and the setting should then be done in
5800 * VBoxSVC.
5801 */
5802HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5803 std::vector<com::Utf8Str> &aNames,
5804 std::vector<com::Utf8Str> &aValues,
5805 std::vector<LONG64> &aTimestamps,
5806 std::vector<com::Utf8Str> &aFlags)
5807{
5808 HRESULT rc;
5809 ComPtr<IInternalSessionControl> directControl;
5810 {
5811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5812 if (mData->mSession.mLockType == LockType_VM)
5813 directControl = mData->mSession.mDirectControl;
5814 }
5815
5816 com::SafeArray<BSTR> bNames;
5817 com::SafeArray<BSTR> bValues;
5818 com::SafeArray<LONG64> bTimestamps;
5819 com::SafeArray<BSTR> bFlags;
5820
5821 if (!directControl)
5822 rc = E_ACCESSDENIED;
5823 else
5824 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5825 ComSafeArrayAsOutParam(bNames),
5826 ComSafeArrayAsOutParam(bValues),
5827 ComSafeArrayAsOutParam(bTimestamps),
5828 ComSafeArrayAsOutParam(bFlags));
5829 size_t i;
5830 aNames.resize(bNames.size());
5831 for (i = 0; i < bNames.size(); ++i)
5832 aNames[i] = Utf8Str(bNames[i]);
5833 aValues.resize(bValues.size());
5834 for (i = 0; i < bValues.size(); ++i)
5835 aValues[i] = Utf8Str(bValues[i]);
5836 aTimestamps.resize(bTimestamps.size());
5837 for (i = 0; i < bTimestamps.size(); ++i)
5838 aTimestamps[i] = bTimestamps[i];
5839 aFlags.resize(bFlags.size());
5840 for (i = 0; i < bFlags.size(); ++i)
5841 aFlags[i] = Utf8Str(bFlags[i]);
5842
5843 return rc;
5844}
5845#endif // VBOX_WITH_GUEST_PROPS
5846HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5847 std::vector<com::Utf8Str> &aNames,
5848 std::vector<com::Utf8Str> &aValues,
5849 std::vector<LONG64> &aTimestamps,
5850 std::vector<com::Utf8Str> &aFlags)
5851{
5852#ifndef VBOX_WITH_GUEST_PROPS
5853 ReturnComNotImplemented();
5854#else // VBOX_WITH_GUEST_PROPS
5855
5856 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5857
5858 if (rc == E_ACCESSDENIED)
5859 /* The VM is not running or the service is not (yet) accessible */
5860 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5861 return rc;
5862#endif // VBOX_WITH_GUEST_PROPS
5863}
5864
5865HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5866 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5867{
5868 MediumAttachmentList atts;
5869
5870 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5871 if (FAILED(rc)) return rc;
5872
5873 aMediumAttachments.resize(atts.size());
5874 size_t i = 0;
5875 for (MediumAttachmentList::const_iterator
5876 it = atts.begin();
5877 it != atts.end();
5878 ++it, ++i)
5879 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5880
5881 return S_OK;
5882}
5883
5884HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5885 LONG aControllerPort,
5886 LONG aDevice,
5887 ComPtr<IMediumAttachment> &aAttachment)
5888{
5889 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5890 aName.c_str(), aControllerPort, aDevice));
5891
5892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5893
5894 aAttachment = NULL;
5895
5896 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5897 aName,
5898 aControllerPort,
5899 aDevice);
5900 if (pAttach.isNull())
5901 return setError(VBOX_E_OBJECT_NOT_FOUND,
5902 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5903 aDevice, aControllerPort, aName.c_str());
5904
5905 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5906
5907 return S_OK;
5908}
5909
5910
5911HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5912 StorageBus_T aConnectionType,
5913 ComPtr<IStorageController> &aController)
5914{
5915 if ( (aConnectionType <= StorageBus_Null)
5916 || (aConnectionType > StorageBus_VirtioSCSI))
5917 return setError(E_INVALIDARG,
5918 tr("Invalid connection type: %d"),
5919 aConnectionType);
5920
5921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5922
5923 HRESULT rc = i_checkStateDependency(MutableStateDep);
5924 if (FAILED(rc)) return rc;
5925
5926 /* try to find one with the name first. */
5927 ComObjPtr<StorageController> ctrl;
5928
5929 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5930 if (SUCCEEDED(rc))
5931 return setError(VBOX_E_OBJECT_IN_USE,
5932 tr("Storage controller named '%s' already exists"),
5933 aName.c_str());
5934
5935 ctrl.createObject();
5936
5937 /* get a new instance number for the storage controller */
5938 ULONG ulInstance = 0;
5939 bool fBootable = true;
5940 for (StorageControllerList::const_iterator
5941 it = mStorageControllers->begin();
5942 it != mStorageControllers->end();
5943 ++it)
5944 {
5945 if ((*it)->i_getStorageBus() == aConnectionType)
5946 {
5947 ULONG ulCurInst = (*it)->i_getInstance();
5948
5949 if (ulCurInst >= ulInstance)
5950 ulInstance = ulCurInst + 1;
5951
5952 /* Only one controller of each type can be marked as bootable. */
5953 if ((*it)->i_getBootable())
5954 fBootable = false;
5955 }
5956 }
5957
5958 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5959 if (FAILED(rc)) return rc;
5960
5961 i_setModified(IsModified_Storage);
5962 mStorageControllers.backup();
5963 mStorageControllers->push_back(ctrl);
5964
5965 ctrl.queryInterfaceTo(aController.asOutParam());
5966
5967 /* inform the direct session if any */
5968 alock.release();
5969 i_onStorageControllerChange(i_getId(), aName);
5970
5971 return S_OK;
5972}
5973
5974HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5975 ComPtr<IStorageController> &aStorageController)
5976{
5977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5978
5979 ComObjPtr<StorageController> ctrl;
5980
5981 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5982 if (SUCCEEDED(rc))
5983 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5984
5985 return rc;
5986}
5987
5988HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5989 ULONG aInstance,
5990 ComPtr<IStorageController> &aStorageController)
5991{
5992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5993
5994 for (StorageControllerList::const_iterator
5995 it = mStorageControllers->begin();
5996 it != mStorageControllers->end();
5997 ++it)
5998 {
5999 if ( (*it)->i_getStorageBus() == aConnectionType
6000 && (*it)->i_getInstance() == aInstance)
6001 {
6002 (*it).queryInterfaceTo(aStorageController.asOutParam());
6003 return S_OK;
6004 }
6005 }
6006
6007 return setError(VBOX_E_OBJECT_NOT_FOUND,
6008 tr("Could not find a storage controller with instance number '%lu'"),
6009 aInstance);
6010}
6011
6012HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6013{
6014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6015
6016 HRESULT rc = i_checkStateDependency(MutableStateDep);
6017 if (FAILED(rc)) return rc;
6018
6019 ComObjPtr<StorageController> ctrl;
6020
6021 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6022 if (SUCCEEDED(rc))
6023 {
6024 /* Ensure that only one controller of each type is marked as bootable. */
6025 if (aBootable == TRUE)
6026 {
6027 for (StorageControllerList::const_iterator
6028 it = mStorageControllers->begin();
6029 it != mStorageControllers->end();
6030 ++it)
6031 {
6032 ComObjPtr<StorageController> aCtrl = (*it);
6033
6034 if ( (aCtrl->i_getName() != aName)
6035 && aCtrl->i_getBootable() == TRUE
6036 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6037 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6038 {
6039 aCtrl->i_setBootable(FALSE);
6040 break;
6041 }
6042 }
6043 }
6044
6045 if (SUCCEEDED(rc))
6046 {
6047 ctrl->i_setBootable(aBootable);
6048 i_setModified(IsModified_Storage);
6049 }
6050 }
6051
6052 if (SUCCEEDED(rc))
6053 {
6054 /* inform the direct session if any */
6055 alock.release();
6056 i_onStorageControllerChange(i_getId(), aName);
6057 }
6058
6059 return rc;
6060}
6061
6062HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6063{
6064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6065
6066 HRESULT rc = i_checkStateDependency(MutableStateDep);
6067 if (FAILED(rc)) return rc;
6068
6069 ComObjPtr<StorageController> ctrl;
6070 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6071 if (FAILED(rc)) return rc;
6072
6073 MediumAttachmentList llDetachedAttachments;
6074 {
6075 /* find all attached devices to the appropriate storage controller and detach them all */
6076 // make a temporary list because detachDevice invalidates iterators into
6077 // mMediumAttachments
6078 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6079
6080 for (MediumAttachmentList::const_iterator
6081 it = llAttachments2.begin();
6082 it != llAttachments2.end();
6083 ++it)
6084 {
6085 MediumAttachment *pAttachTemp = *it;
6086
6087 AutoCaller localAutoCaller(pAttachTemp);
6088 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6089
6090 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6091
6092 if (pAttachTemp->i_getControllerName() == aName)
6093 {
6094 llDetachedAttachments.push_back(pAttachTemp);
6095 rc = i_detachDevice(pAttachTemp, alock, NULL);
6096 if (FAILED(rc)) return rc;
6097 }
6098 }
6099 }
6100
6101 /* send event about detached devices before removing parent controller */
6102 for (MediumAttachmentList::const_iterator
6103 it = llDetachedAttachments.begin();
6104 it != llDetachedAttachments.end();
6105 ++it)
6106 {
6107 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6108 }
6109
6110 /* We can remove it now. */
6111 i_setModified(IsModified_Storage);
6112 mStorageControllers.backup();
6113
6114 ctrl->i_unshare();
6115
6116 mStorageControllers->remove(ctrl);
6117
6118 /* inform the direct session if any */
6119 alock.release();
6120 i_onStorageControllerChange(i_getId(), aName);
6121
6122 return S_OK;
6123}
6124
6125HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6126 ComPtr<IUSBController> &aController)
6127{
6128 if ( (aType <= USBControllerType_Null)
6129 || (aType >= USBControllerType_Last))
6130 return setError(E_INVALIDARG,
6131 tr("Invalid USB controller type: %d"),
6132 aType);
6133
6134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6135
6136 HRESULT rc = i_checkStateDependency(MutableStateDep);
6137 if (FAILED(rc)) return rc;
6138
6139 /* try to find one with the same type first. */
6140 ComObjPtr<USBController> ctrl;
6141
6142 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6143 if (SUCCEEDED(rc))
6144 return setError(VBOX_E_OBJECT_IN_USE,
6145 tr("USB controller named '%s' already exists"),
6146 aName.c_str());
6147
6148 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6149 ULONG maxInstances;
6150 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6151 if (FAILED(rc))
6152 return rc;
6153
6154 ULONG cInstances = i_getUSBControllerCountByType(aType);
6155 if (cInstances >= maxInstances)
6156 return setError(E_INVALIDARG,
6157 tr("Too many USB controllers of this type"));
6158
6159 ctrl.createObject();
6160
6161 rc = ctrl->init(this, aName, aType);
6162 if (FAILED(rc)) return rc;
6163
6164 i_setModified(IsModified_USB);
6165 mUSBControllers.backup();
6166 mUSBControllers->push_back(ctrl);
6167
6168 ctrl.queryInterfaceTo(aController.asOutParam());
6169
6170 /* inform the direct session if any */
6171 alock.release();
6172 i_onUSBControllerChange();
6173
6174 return S_OK;
6175}
6176
6177HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6178{
6179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6180
6181 ComObjPtr<USBController> ctrl;
6182
6183 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6184 if (SUCCEEDED(rc))
6185 ctrl.queryInterfaceTo(aController.asOutParam());
6186
6187 return rc;
6188}
6189
6190HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6191 ULONG *aControllers)
6192{
6193 if ( (aType <= USBControllerType_Null)
6194 || (aType >= USBControllerType_Last))
6195 return setError(E_INVALIDARG,
6196 tr("Invalid USB controller type: %d"),
6197 aType);
6198
6199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 ComObjPtr<USBController> ctrl;
6202
6203 *aControllers = i_getUSBControllerCountByType(aType);
6204
6205 return S_OK;
6206}
6207
6208HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6209{
6210
6211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6212
6213 HRESULT rc = i_checkStateDependency(MutableStateDep);
6214 if (FAILED(rc)) return rc;
6215
6216 ComObjPtr<USBController> ctrl;
6217 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6218 if (FAILED(rc)) return rc;
6219
6220 i_setModified(IsModified_USB);
6221 mUSBControllers.backup();
6222
6223 ctrl->i_unshare();
6224
6225 mUSBControllers->remove(ctrl);
6226
6227 /* inform the direct session if any */
6228 alock.release();
6229 i_onUSBControllerChange();
6230
6231 return S_OK;
6232}
6233
6234HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6235 ULONG *aOriginX,
6236 ULONG *aOriginY,
6237 ULONG *aWidth,
6238 ULONG *aHeight,
6239 BOOL *aEnabled)
6240{
6241 uint32_t u32OriginX= 0;
6242 uint32_t u32OriginY= 0;
6243 uint32_t u32Width = 0;
6244 uint32_t u32Height = 0;
6245 uint16_t u16Flags = 0;
6246
6247 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6248 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6249 if (RT_FAILURE(vrc))
6250 {
6251#ifdef RT_OS_WINDOWS
6252 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6253 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6254 * So just assign fEnable to TRUE again.
6255 * The right fix would be to change GUI API wrappers to make sure that parameters
6256 * are changed only if API succeeds.
6257 */
6258 *aEnabled = TRUE;
6259#endif
6260 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6261 tr("Saved guest size is not available (%Rrc)"),
6262 vrc);
6263 }
6264
6265 *aOriginX = u32OriginX;
6266 *aOriginY = u32OriginY;
6267 *aWidth = u32Width;
6268 *aHeight = u32Height;
6269 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6270
6271 return S_OK;
6272}
6273
6274HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6275 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6276{
6277 if (aScreenId != 0)
6278 return E_NOTIMPL;
6279
6280 if ( aBitmapFormat != BitmapFormat_BGR0
6281 && aBitmapFormat != BitmapFormat_BGRA
6282 && aBitmapFormat != BitmapFormat_RGBA
6283 && aBitmapFormat != BitmapFormat_PNG)
6284 return setError(E_NOTIMPL,
6285 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6286
6287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6288
6289 uint8_t *pu8Data = NULL;
6290 uint32_t cbData = 0;
6291 uint32_t u32Width = 0;
6292 uint32_t u32Height = 0;
6293
6294 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6295
6296 if (RT_FAILURE(vrc))
6297 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6298 tr("Saved thumbnail data is not available (%Rrc)"),
6299 vrc);
6300
6301 HRESULT hr = S_OK;
6302
6303 *aWidth = u32Width;
6304 *aHeight = u32Height;
6305
6306 if (cbData > 0)
6307 {
6308 /* Convert pixels to the format expected by the API caller. */
6309 if (aBitmapFormat == BitmapFormat_BGR0)
6310 {
6311 /* [0] B, [1] G, [2] R, [3] 0. */
6312 aData.resize(cbData);
6313 memcpy(&aData.front(), pu8Data, cbData);
6314 }
6315 else if (aBitmapFormat == BitmapFormat_BGRA)
6316 {
6317 /* [0] B, [1] G, [2] R, [3] A. */
6318 aData.resize(cbData);
6319 for (uint32_t i = 0; i < cbData; i += 4)
6320 {
6321 aData[i] = pu8Data[i];
6322 aData[i + 1] = pu8Data[i + 1];
6323 aData[i + 2] = pu8Data[i + 2];
6324 aData[i + 3] = 0xff;
6325 }
6326 }
6327 else if (aBitmapFormat == BitmapFormat_RGBA)
6328 {
6329 /* [0] R, [1] G, [2] B, [3] A. */
6330 aData.resize(cbData);
6331 for (uint32_t i = 0; i < cbData; i += 4)
6332 {
6333 aData[i] = pu8Data[i + 2];
6334 aData[i + 1] = pu8Data[i + 1];
6335 aData[i + 2] = pu8Data[i];
6336 aData[i + 3] = 0xff;
6337 }
6338 }
6339 else if (aBitmapFormat == BitmapFormat_PNG)
6340 {
6341 uint8_t *pu8PNG = NULL;
6342 uint32_t cbPNG = 0;
6343 uint32_t cxPNG = 0;
6344 uint32_t cyPNG = 0;
6345
6346 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6347
6348 if (RT_SUCCESS(vrc))
6349 {
6350 aData.resize(cbPNG);
6351 if (cbPNG)
6352 memcpy(&aData.front(), pu8PNG, cbPNG);
6353 }
6354 else
6355 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6356 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6357 vrc);
6358
6359 RTMemFree(pu8PNG);
6360 }
6361 }
6362
6363 freeSavedDisplayScreenshot(pu8Data);
6364
6365 return hr;
6366}
6367
6368HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6369 ULONG *aWidth,
6370 ULONG *aHeight,
6371 std::vector<BitmapFormat_T> &aBitmapFormats)
6372{
6373 if (aScreenId != 0)
6374 return E_NOTIMPL;
6375
6376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6377
6378 uint8_t *pu8Data = NULL;
6379 uint32_t cbData = 0;
6380 uint32_t u32Width = 0;
6381 uint32_t u32Height = 0;
6382
6383 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6384
6385 if (RT_FAILURE(vrc))
6386 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6387 tr("Saved screenshot data is not available (%Rrc)"),
6388 vrc);
6389
6390 *aWidth = u32Width;
6391 *aHeight = u32Height;
6392 aBitmapFormats.resize(1);
6393 aBitmapFormats[0] = BitmapFormat_PNG;
6394
6395 freeSavedDisplayScreenshot(pu8Data);
6396
6397 return S_OK;
6398}
6399
6400HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6401 BitmapFormat_T aBitmapFormat,
6402 ULONG *aWidth,
6403 ULONG *aHeight,
6404 std::vector<BYTE> &aData)
6405{
6406 if (aScreenId != 0)
6407 return E_NOTIMPL;
6408
6409 if (aBitmapFormat != BitmapFormat_PNG)
6410 return E_NOTIMPL;
6411
6412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6413
6414 uint8_t *pu8Data = NULL;
6415 uint32_t cbData = 0;
6416 uint32_t u32Width = 0;
6417 uint32_t u32Height = 0;
6418
6419 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6420
6421 if (RT_FAILURE(vrc))
6422 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6423 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6424 vrc);
6425
6426 *aWidth = u32Width;
6427 *aHeight = u32Height;
6428
6429 aData.resize(cbData);
6430 if (cbData)
6431 memcpy(&aData.front(), pu8Data, cbData);
6432
6433 freeSavedDisplayScreenshot(pu8Data);
6434
6435 return S_OK;
6436}
6437
6438HRESULT Machine::hotPlugCPU(ULONG aCpu)
6439{
6440 HRESULT rc = S_OK;
6441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6442
6443 if (!mHWData->mCPUHotPlugEnabled)
6444 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6445
6446 if (aCpu >= mHWData->mCPUCount)
6447 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6448
6449 if (mHWData->mCPUAttached[aCpu])
6450 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6451
6452 alock.release();
6453 rc = i_onCPUChange(aCpu, false);
6454 alock.acquire();
6455 if (FAILED(rc)) return rc;
6456
6457 i_setModified(IsModified_MachineData);
6458 mHWData.backup();
6459 mHWData->mCPUAttached[aCpu] = true;
6460
6461 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6462 if (Global::IsOnline(mData->mMachineState))
6463 i_saveSettings(NULL);
6464
6465 return S_OK;
6466}
6467
6468HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6469{
6470 HRESULT rc = S_OK;
6471
6472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 if (!mHWData->mCPUHotPlugEnabled)
6475 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6476
6477 if (aCpu >= SchemaDefs::MaxCPUCount)
6478 return setError(E_INVALIDARG,
6479 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6480 SchemaDefs::MaxCPUCount);
6481
6482 if (!mHWData->mCPUAttached[aCpu])
6483 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6484
6485 /* CPU 0 can't be detached */
6486 if (aCpu == 0)
6487 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6488
6489 alock.release();
6490 rc = i_onCPUChange(aCpu, true);
6491 alock.acquire();
6492 if (FAILED(rc)) return rc;
6493
6494 i_setModified(IsModified_MachineData);
6495 mHWData.backup();
6496 mHWData->mCPUAttached[aCpu] = false;
6497
6498 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6499 if (Global::IsOnline(mData->mMachineState))
6500 i_saveSettings(NULL);
6501
6502 return S_OK;
6503}
6504
6505HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6506{
6507 *aAttached = false;
6508
6509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 /* If hotplug is enabled the CPU is always enabled. */
6512 if (!mHWData->mCPUHotPlugEnabled)
6513 {
6514 if (aCpu < mHWData->mCPUCount)
6515 *aAttached = true;
6516 }
6517 else
6518 {
6519 if (aCpu < SchemaDefs::MaxCPUCount)
6520 *aAttached = mHWData->mCPUAttached[aCpu];
6521 }
6522
6523 return S_OK;
6524}
6525
6526HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6527{
6528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6529
6530 Utf8Str log = i_getLogFilename(aIdx);
6531 if (!RTFileExists(log.c_str()))
6532 log.setNull();
6533 aFilename = log;
6534
6535 return S_OK;
6536}
6537
6538HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6539{
6540 if (aSize < 0)
6541 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6542
6543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6544
6545 HRESULT rc = S_OK;
6546 Utf8Str log = i_getLogFilename(aIdx);
6547
6548 /* do not unnecessarily hold the lock while doing something which does
6549 * not need the lock and potentially takes a long time. */
6550 alock.release();
6551
6552 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6553 * keeps the SOAP reply size under 1M for the webservice (we're using
6554 * base64 encoded strings for binary data for years now, avoiding the
6555 * expansion of each byte array element to approx. 25 bytes of XML. */
6556 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6557 aData.resize(cbData);
6558
6559 RTFILE LogFile;
6560 int vrc = RTFileOpen(&LogFile, log.c_str(),
6561 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6562 if (RT_SUCCESS(vrc))
6563 {
6564 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6565 if (RT_SUCCESS(vrc))
6566 aData.resize(cbData);
6567 else
6568 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6569 tr("Could not read log file '%s' (%Rrc)"),
6570 log.c_str(), vrc);
6571 RTFileClose(LogFile);
6572 }
6573 else
6574 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6575 tr("Could not open log file '%s' (%Rrc)"),
6576 log.c_str(), vrc);
6577
6578 if (FAILED(rc))
6579 aData.resize(0);
6580
6581 return rc;
6582}
6583
6584
6585/**
6586 * Currently this method doesn't attach device to the running VM,
6587 * just makes sure it's plugged on next VM start.
6588 */
6589HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6590{
6591 // lock scope
6592 {
6593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 HRESULT rc = i_checkStateDependency(MutableStateDep);
6596 if (FAILED(rc)) return rc;
6597
6598 ChipsetType_T aChipset = ChipsetType_PIIX3;
6599 COMGETTER(ChipsetType)(&aChipset);
6600
6601 if (aChipset != ChipsetType_ICH9)
6602 {
6603 return setError(E_INVALIDARG,
6604 tr("Host PCI attachment only supported with ICH9 chipset"));
6605 }
6606
6607 // check if device with this host PCI address already attached
6608 for (HWData::PCIDeviceAssignmentList::const_iterator
6609 it = mHWData->mPCIDeviceAssignments.begin();
6610 it != mHWData->mPCIDeviceAssignments.end();
6611 ++it)
6612 {
6613 LONG iHostAddress = -1;
6614 ComPtr<PCIDeviceAttachment> pAttach;
6615 pAttach = *it;
6616 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6617 if (iHostAddress == aHostAddress)
6618 return setError(E_INVALIDARG,
6619 tr("Device with host PCI address already attached to this VM"));
6620 }
6621
6622 ComObjPtr<PCIDeviceAttachment> pda;
6623 char name[32];
6624
6625 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6626 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6627 pda.createObject();
6628 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6629 i_setModified(IsModified_MachineData);
6630 mHWData.backup();
6631 mHWData->mPCIDeviceAssignments.push_back(pda);
6632 }
6633
6634 return S_OK;
6635}
6636
6637/**
6638 * Currently this method doesn't detach device from the running VM,
6639 * just makes sure it's not plugged on next VM start.
6640 */
6641HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6642{
6643 ComObjPtr<PCIDeviceAttachment> pAttach;
6644 bool fRemoved = false;
6645 HRESULT rc;
6646
6647 // lock scope
6648 {
6649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 rc = i_checkStateDependency(MutableStateDep);
6652 if (FAILED(rc)) return rc;
6653
6654 for (HWData::PCIDeviceAssignmentList::const_iterator
6655 it = mHWData->mPCIDeviceAssignments.begin();
6656 it != mHWData->mPCIDeviceAssignments.end();
6657 ++it)
6658 {
6659 LONG iHostAddress = -1;
6660 pAttach = *it;
6661 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6662 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6663 {
6664 i_setModified(IsModified_MachineData);
6665 mHWData.backup();
6666 mHWData->mPCIDeviceAssignments.remove(pAttach);
6667 fRemoved = true;
6668 break;
6669 }
6670 }
6671 }
6672
6673
6674 /* Fire event outside of the lock */
6675 if (fRemoved)
6676 {
6677 Assert(!pAttach.isNull());
6678 ComPtr<IEventSource> es;
6679 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6680 Assert(SUCCEEDED(rc));
6681 Bstr mid;
6682 rc = this->COMGETTER(Id)(mid.asOutParam());
6683 Assert(SUCCEEDED(rc));
6684 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6685 }
6686
6687 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6688 tr("No host PCI device %08x attached"),
6689 aHostAddress
6690 );
6691}
6692
6693HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6694{
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6698 size_t i = 0;
6699 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6700 it = mHWData->mPCIDeviceAssignments.begin();
6701 it != mHWData->mPCIDeviceAssignments.end();
6702 ++it, ++i)
6703 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6704
6705 return S_OK;
6706}
6707
6708HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6709{
6710 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6711
6712 return S_OK;
6713}
6714
6715HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6716{
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718
6719 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6720
6721 return S_OK;
6722}
6723
6724HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6725{
6726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6727 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6728 if (SUCCEEDED(hrc))
6729 {
6730 hrc = mHWData.backupEx();
6731 if (SUCCEEDED(hrc))
6732 {
6733 i_setModified(IsModified_MachineData);
6734 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6735 }
6736 }
6737 return hrc;
6738}
6739
6740HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6741{
6742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6743 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6744 return S_OK;
6745}
6746
6747HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6748{
6749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6750 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6751 if (SUCCEEDED(hrc))
6752 {
6753 hrc = mHWData.backupEx();
6754 if (SUCCEEDED(hrc))
6755 {
6756 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6757 if (SUCCEEDED(hrc))
6758 i_setModified(IsModified_MachineData);
6759 }
6760 }
6761 return hrc;
6762}
6763
6764HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6765{
6766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6767
6768 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6774{
6775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6776 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6777 if (SUCCEEDED(hrc))
6778 {
6779 hrc = mHWData.backupEx();
6780 if (SUCCEEDED(hrc))
6781 {
6782 i_setModified(IsModified_MachineData);
6783 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6784 }
6785 }
6786 return hrc;
6787}
6788
6789HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6790{
6791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6794
6795 return S_OK;
6796}
6797
6798HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6799{
6800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6801
6802 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6803 if ( SUCCEEDED(hrc)
6804 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6805 {
6806 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6807 int vrc;
6808
6809 if (aAutostartEnabled)
6810 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6811 else
6812 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6813
6814 if (RT_SUCCESS(vrc))
6815 {
6816 hrc = mHWData.backupEx();
6817 if (SUCCEEDED(hrc))
6818 {
6819 i_setModified(IsModified_MachineData);
6820 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6821 }
6822 }
6823 else if (vrc == VERR_NOT_SUPPORTED)
6824 hrc = setError(VBOX_E_NOT_SUPPORTED,
6825 tr("The VM autostart feature is not supported on this platform"));
6826 else if (vrc == VERR_PATH_NOT_FOUND)
6827 hrc = setError(E_FAIL,
6828 tr("The path to the autostart database is not set"));
6829 else
6830 hrc = setError(E_UNEXPECTED,
6831 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6832 aAutostartEnabled ? "Adding" : "Removing",
6833 mUserData->s.strName.c_str(), vrc);
6834 }
6835 return hrc;
6836}
6837
6838HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6839{
6840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6843
6844 return S_OK;
6845}
6846
6847HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6848{
6849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6850 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6851 if (SUCCEEDED(hrc))
6852 {
6853 hrc = mHWData.backupEx();
6854 if (SUCCEEDED(hrc))
6855 {
6856 i_setModified(IsModified_MachineData);
6857 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6858 }
6859 }
6860 return hrc;
6861}
6862
6863HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6864{
6865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6868
6869 return S_OK;
6870}
6871
6872HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6873{
6874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6875 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6876 if ( SUCCEEDED(hrc)
6877 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6878 {
6879 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6880 int vrc;
6881
6882 if (aAutostopType != AutostopType_Disabled)
6883 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6884 else
6885 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6886
6887 if (RT_SUCCESS(vrc))
6888 {
6889 hrc = mHWData.backupEx();
6890 if (SUCCEEDED(hrc))
6891 {
6892 i_setModified(IsModified_MachineData);
6893 mHWData->mAutostart.enmAutostopType = aAutostopType;
6894 }
6895 }
6896 else if (vrc == VERR_NOT_SUPPORTED)
6897 hrc = setError(VBOX_E_NOT_SUPPORTED,
6898 tr("The VM autostop feature is not supported on this platform"));
6899 else if (vrc == VERR_PATH_NOT_FOUND)
6900 hrc = setError(E_FAIL,
6901 tr("The path to the autostart database is not set"));
6902 else
6903 hrc = setError(E_UNEXPECTED,
6904 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6905 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6906 mUserData->s.strName.c_str(), vrc);
6907 }
6908 return hrc;
6909}
6910
6911HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6912{
6913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6914
6915 aDefaultFrontend = mHWData->mDefaultFrontend;
6916
6917 return S_OK;
6918}
6919
6920HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6921{
6922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6923 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6924 if (SUCCEEDED(hrc))
6925 {
6926 hrc = mHWData.backupEx();
6927 if (SUCCEEDED(hrc))
6928 {
6929 i_setModified(IsModified_MachineData);
6930 mHWData->mDefaultFrontend = aDefaultFrontend;
6931 }
6932 }
6933 return hrc;
6934}
6935
6936HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6937{
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 size_t cbIcon = mUserData->s.ovIcon.size();
6940 aIcon.resize(cbIcon);
6941 if (cbIcon)
6942 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6943 return S_OK;
6944}
6945
6946HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6947{
6948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6949 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6950 if (SUCCEEDED(hrc))
6951 {
6952 i_setModified(IsModified_MachineData);
6953 mUserData.backup();
6954 size_t cbIcon = aIcon.size();
6955 mUserData->s.ovIcon.resize(cbIcon);
6956 if (cbIcon)
6957 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6958 }
6959 return hrc;
6960}
6961
6962HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6963{
6964#ifdef VBOX_WITH_USB
6965 *aUSBProxyAvailable = true;
6966#else
6967 *aUSBProxyAvailable = false;
6968#endif
6969 return S_OK;
6970}
6971
6972HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6973{
6974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6975
6976 *aVMProcessPriority = mUserData->s.enmVMPriority;
6977
6978 return S_OK;
6979}
6980
6981HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6982{
6983 RT_NOREF(aVMProcessPriority);
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6986 if (SUCCEEDED(hrc))
6987 {
6988 hrc = mUserData.backupEx();
6989 if (SUCCEEDED(hrc))
6990 {
6991 i_setModified(IsModified_MachineData);
6992 mUserData->s.enmVMPriority = aVMProcessPriority;
6993 }
6994 }
6995 alock.release();
6996 if (SUCCEEDED(hrc))
6997 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6998 return hrc;
6999}
7000
7001HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7002 ComPtr<IProgress> &aProgress)
7003{
7004 ComObjPtr<Progress> pP;
7005 Progress *ppP = pP;
7006 IProgress *iP = static_cast<IProgress *>(ppP);
7007 IProgress **pProgress = &iP;
7008
7009 IMachine *pTarget = aTarget;
7010
7011 /* Convert the options. */
7012 RTCList<CloneOptions_T> optList;
7013 if (aOptions.size())
7014 for (size_t i = 0; i < aOptions.size(); ++i)
7015 optList.append(aOptions[i]);
7016
7017 if (optList.contains(CloneOptions_Link))
7018 {
7019 if (!i_isSnapshotMachine())
7020 return setError(E_INVALIDARG,
7021 tr("Linked clone can only be created from a snapshot"));
7022 if (aMode != CloneMode_MachineState)
7023 return setError(E_INVALIDARG,
7024 tr("Linked clone can only be created for a single machine state"));
7025 }
7026 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7027
7028 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7029
7030 HRESULT rc = pWorker->start(pProgress);
7031
7032 pP = static_cast<Progress *>(*pProgress);
7033 pP.queryInterfaceTo(aProgress.asOutParam());
7034
7035 return rc;
7036
7037}
7038
7039HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7040 const com::Utf8Str &aType,
7041 ComPtr<IProgress> &aProgress)
7042{
7043 LogFlowThisFuncEnter();
7044
7045 ComObjPtr<Progress> ptrProgress;
7046 HRESULT hrc = ptrProgress.createObject();
7047 if (SUCCEEDED(hrc))
7048 {
7049 /* Initialize our worker task */
7050 MachineMoveVM *pTask = NULL;
7051 try
7052 {
7053 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7054 }
7055 catch (std::bad_alloc &)
7056 {
7057 return E_OUTOFMEMORY;
7058 }
7059
7060 hrc = pTask->init();//no exceptions are thrown
7061
7062 if (SUCCEEDED(hrc))
7063 {
7064 hrc = pTask->createThread();
7065 pTask = NULL; /* Consumed by createThread(). */
7066 if (SUCCEEDED(hrc))
7067 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7068 else
7069 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7070 }
7071 else
7072 delete pTask;
7073 }
7074
7075 LogFlowThisFuncLeave();
7076 return hrc;
7077
7078}
7079
7080HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7081{
7082 NOREF(aProgress);
7083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 // This check should always fail.
7086 HRESULT rc = i_checkStateDependency(MutableStateDep);
7087 if (FAILED(rc)) return rc;
7088
7089 AssertFailedReturn(E_NOTIMPL);
7090}
7091
7092HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7093{
7094 NOREF(aSavedStateFile);
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 // This check should always fail.
7098 HRESULT rc = i_checkStateDependency(MutableStateDep);
7099 if (FAILED(rc)) return rc;
7100
7101 AssertFailedReturn(E_NOTIMPL);
7102}
7103
7104HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7105{
7106 NOREF(aFRemoveFile);
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108
7109 // This check should always fail.
7110 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7111 if (FAILED(rc)) return rc;
7112
7113 AssertFailedReturn(E_NOTIMPL);
7114}
7115
7116// public methods for internal purposes
7117/////////////////////////////////////////////////////////////////////////////
7118
7119/**
7120 * Adds the given IsModified_* flag to the dirty flags of the machine.
7121 * This must be called either during i_loadSettings or under the machine write lock.
7122 * @param fl Flag
7123 * @param fAllowStateModification If state modifications are allowed.
7124 */
7125void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7126{
7127 mData->flModifications |= fl;
7128 if (fAllowStateModification && i_isStateModificationAllowed())
7129 mData->mCurrentStateModified = true;
7130}
7131
7132/**
7133 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7134 * care of the write locking.
7135 *
7136 * @param fModification The flag to add.
7137 * @param fAllowStateModification If state modifications are allowed.
7138 */
7139void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7140{
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142 i_setModified(fModification, fAllowStateModification);
7143}
7144
7145/**
7146 * Saves the registry entry of this machine to the given configuration node.
7147 *
7148 * @param data Machine registry data.
7149 *
7150 * @note locks this object for reading.
7151 */
7152HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7153{
7154 AutoLimitedCaller autoCaller(this);
7155 AssertComRCReturnRC(autoCaller.rc());
7156
7157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7158
7159 data.uuid = mData->mUuid;
7160 data.strSettingsFile = mData->m_strConfigFile;
7161
7162 return S_OK;
7163}
7164
7165/**
7166 * Calculates the absolute path of the given path taking the directory of the
7167 * machine settings file as the current directory.
7168 *
7169 * @param strPath Path to calculate the absolute path for.
7170 * @param aResult Where to put the result (used only on success, can be the
7171 * same Utf8Str instance as passed in @a aPath).
7172 * @return IPRT result.
7173 *
7174 * @note Locks this object for reading.
7175 */
7176int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7177{
7178 AutoCaller autoCaller(this);
7179 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7180
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7184
7185 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7186
7187 strSettingsDir.stripFilename();
7188 char szFolder[RTPATH_MAX];
7189 size_t cbFolder = sizeof(szFolder);
7190 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7191 if (RT_SUCCESS(vrc))
7192 aResult = szFolder;
7193
7194 return vrc;
7195}
7196
7197/**
7198 * Copies strSource to strTarget, making it relative to the machine folder
7199 * if it is a subdirectory thereof, or simply copying it otherwise.
7200 *
7201 * @param strSource Path to evaluate and copy.
7202 * @param strTarget Buffer to receive target path.
7203 *
7204 * @note Locks this object for reading.
7205 */
7206void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7207 Utf8Str &strTarget)
7208{
7209 AutoCaller autoCaller(this);
7210 AssertComRCReturn(autoCaller.rc(), (void)0);
7211
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7215 // use strTarget as a temporary buffer to hold the machine settings dir
7216 strTarget = mData->m_strConfigFileFull;
7217 strTarget.stripFilename();
7218 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7219 {
7220 // is relative: then append what's left
7221 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7222 // for empty paths (only possible for subdirs) use "." to avoid
7223 // triggering default settings for not present config attributes.
7224 if (strTarget.isEmpty())
7225 strTarget = ".";
7226 }
7227 else
7228 // is not relative: then overwrite
7229 strTarget = strSource;
7230}
7231
7232/**
7233 * Returns the full path to the machine's log folder in the
7234 * \a aLogFolder argument.
7235 */
7236void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7237{
7238 AutoCaller autoCaller(this);
7239 AssertComRCReturnVoid(autoCaller.rc());
7240
7241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7242
7243 char szTmp[RTPATH_MAX];
7244 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7245 if (RT_SUCCESS(vrc))
7246 {
7247 if (szTmp[0] && !mUserData.isNull())
7248 {
7249 char szTmp2[RTPATH_MAX];
7250 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7251 if (RT_SUCCESS(vrc))
7252 aLogFolder = Utf8StrFmt("%s%c%s",
7253 szTmp2,
7254 RTPATH_DELIMITER,
7255 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7256 }
7257 else
7258 vrc = VERR_PATH_IS_RELATIVE;
7259 }
7260
7261 if (RT_FAILURE(vrc))
7262 {
7263 // fallback if VBOX_USER_LOGHOME is not set or invalid
7264 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7265 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7266 aLogFolder.append(RTPATH_DELIMITER);
7267 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7268 }
7269}
7270
7271/**
7272 * Returns the full path to the machine's log file for an given index.
7273 */
7274Utf8Str Machine::i_getLogFilename(ULONG idx)
7275{
7276 Utf8Str logFolder;
7277 getLogFolder(logFolder);
7278 Assert(logFolder.length());
7279
7280 Utf8Str log;
7281 if (idx == 0)
7282 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7283#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7284 else if (idx == 1)
7285 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7286 else
7287 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7288#else
7289 else
7290 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7291#endif
7292 return log;
7293}
7294
7295/**
7296 * Returns the full path to the machine's hardened log file.
7297 */
7298Utf8Str Machine::i_getHardeningLogFilename(void)
7299{
7300 Utf8Str strFilename;
7301 getLogFolder(strFilename);
7302 Assert(strFilename.length());
7303 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7304 return strFilename;
7305}
7306
7307
7308/**
7309 * Composes a unique saved state filename based on the current system time. The filename is
7310 * granular to the second so this will work so long as no more than one snapshot is taken on
7311 * a machine per second.
7312 *
7313 * Before version 4.1, we used this formula for saved state files:
7314 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7315 * which no longer works because saved state files can now be shared between the saved state of the
7316 * "saved" machine and an online snapshot, and the following would cause problems:
7317 * 1) save machine
7318 * 2) create online snapshot from that machine state --> reusing saved state file
7319 * 3) save machine again --> filename would be reused, breaking the online snapshot
7320 *
7321 * So instead we now use a timestamp.
7322 *
7323 * @param strStateFilePath
7324 */
7325
7326void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7327{
7328 AutoCaller autoCaller(this);
7329 AssertComRCReturnVoid(autoCaller.rc());
7330
7331 {
7332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7333 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7334 }
7335
7336 RTTIMESPEC ts;
7337 RTTimeNow(&ts);
7338 RTTIME time;
7339 RTTimeExplode(&time, &ts);
7340
7341 strStateFilePath += RTPATH_DELIMITER;
7342 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7343 time.i32Year, time.u8Month, time.u8MonthDay,
7344 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7345}
7346
7347/**
7348 * Returns whether at least one USB controller is present for the VM.
7349 */
7350bool Machine::i_isUSBControllerPresent()
7351{
7352 AutoCaller autoCaller(this);
7353 AssertComRCReturn(autoCaller.rc(), false);
7354
7355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7356
7357 return (mUSBControllers->size() > 0);
7358}
7359
7360/**
7361 * @note Locks this object for writing, calls the client process
7362 * (inside the lock).
7363 */
7364HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7365 const Utf8Str &strFrontend,
7366 const Utf8Str &strEnvironment,
7367 ProgressProxy *aProgress)
7368{
7369 LogFlowThisFuncEnter();
7370
7371 AssertReturn(aControl, E_FAIL);
7372 AssertReturn(aProgress, E_FAIL);
7373 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7374
7375 AutoCaller autoCaller(this);
7376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7377
7378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7379
7380 if (!mData->mRegistered)
7381 return setError(E_UNEXPECTED,
7382 tr("The machine '%s' is not registered"),
7383 mUserData->s.strName.c_str());
7384
7385 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7386
7387 /* The process started when launching a VM with separate UI/VM processes is always
7388 * the UI process, i.e. needs special handling as it won't claim the session. */
7389 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7390
7391 if (fSeparate)
7392 {
7393 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7394 return setError(VBOX_E_INVALID_OBJECT_STATE,
7395 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7396 mUserData->s.strName.c_str());
7397 }
7398 else
7399 {
7400 if ( mData->mSession.mState == SessionState_Locked
7401 || mData->mSession.mState == SessionState_Spawning
7402 || mData->mSession.mState == SessionState_Unlocking)
7403 return setError(VBOX_E_INVALID_OBJECT_STATE,
7404 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7405 mUserData->s.strName.c_str());
7406
7407 /* may not be busy */
7408 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7409 }
7410
7411 /* get the path to the executable */
7412 char szPath[RTPATH_MAX];
7413 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7414 size_t cchBufLeft = strlen(szPath);
7415 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7416 szPath[cchBufLeft] = 0;
7417 char *pszNamePart = szPath + cchBufLeft;
7418 cchBufLeft = sizeof(szPath) - cchBufLeft;
7419
7420 int vrc = VINF_SUCCESS;
7421 RTPROCESS pid = NIL_RTPROCESS;
7422
7423 RTENV env = RTENV_DEFAULT;
7424
7425 if (!strEnvironment.isEmpty())
7426 {
7427 char *newEnvStr = NULL;
7428
7429 do
7430 {
7431 /* clone the current environment */
7432 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7433 AssertRCBreakStmt(vrc2, vrc = vrc2);
7434
7435 newEnvStr = RTStrDup(strEnvironment.c_str());
7436 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7437
7438 /* put new variables to the environment
7439 * (ignore empty variable names here since RTEnv API
7440 * intentionally doesn't do that) */
7441 char *var = newEnvStr;
7442 for (char *p = newEnvStr; *p; ++p)
7443 {
7444 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7445 {
7446 *p = '\0';
7447 if (*var)
7448 {
7449 char *val = strchr(var, '=');
7450 if (val)
7451 {
7452 *val++ = '\0';
7453 vrc2 = RTEnvSetEx(env, var, val);
7454 }
7455 else
7456 vrc2 = RTEnvUnsetEx(env, var);
7457 if (RT_FAILURE(vrc2))
7458 break;
7459 }
7460 var = p + 1;
7461 }
7462 }
7463 if (RT_SUCCESS(vrc2) && *var)
7464 vrc2 = RTEnvPutEx(env, var);
7465
7466 AssertRCBreakStmt(vrc2, vrc = vrc2);
7467 }
7468 while (0);
7469
7470 if (newEnvStr != NULL)
7471 RTStrFree(newEnvStr);
7472 }
7473
7474 /* Hardening logging */
7475#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7476 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7477 {
7478 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7479 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7480 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7481 {
7482 Utf8Str strStartupLogDir = strHardeningLogFile;
7483 strStartupLogDir.stripFilename();
7484 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7485 file without stripping the file. */
7486 }
7487 strSupHardeningLogArg.append(strHardeningLogFile);
7488
7489 /* Remove legacy log filename to avoid confusion. */
7490 Utf8Str strOldStartupLogFile;
7491 getLogFolder(strOldStartupLogFile);
7492 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7493 RTFileDelete(strOldStartupLogFile.c_str());
7494 }
7495 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7496#else
7497 const char *pszSupHardeningLogArg = NULL;
7498#endif
7499
7500 Utf8Str strCanonicalName;
7501
7502#ifdef VBOX_WITH_QTGUI
7503 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7504 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7505 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7506 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7507 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7508 {
7509 strCanonicalName = "GUI/Qt";
7510# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7511 /* Modify the base path so that we don't need to use ".." below. */
7512 RTPathStripTrailingSlash(szPath);
7513 RTPathStripFilename(szPath);
7514 cchBufLeft = strlen(szPath);
7515 pszNamePart = szPath + cchBufLeft;
7516 cchBufLeft = sizeof(szPath) - cchBufLeft;
7517
7518# define OSX_APP_NAME "VirtualBoxVM"
7519# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7520
7521 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7522 if ( strAppOverride.contains(".")
7523 || strAppOverride.contains("/")
7524 || strAppOverride.contains("\\")
7525 || strAppOverride.contains(":"))
7526 strAppOverride.setNull();
7527 Utf8Str strAppPath;
7528 if (!strAppOverride.isEmpty())
7529 {
7530 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7531 Utf8Str strFullPath(szPath);
7532 strFullPath.append(strAppPath);
7533 /* there is a race, but people using this deserve the failure */
7534 if (!RTFileExists(strFullPath.c_str()))
7535 strAppOverride.setNull();
7536 }
7537 if (strAppOverride.isEmpty())
7538 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7539 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7540 strcpy(pszNamePart, strAppPath.c_str());
7541# else
7542 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7543 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7544 strcpy(pszNamePart, s_szVirtualBox_exe);
7545# endif
7546
7547 Utf8Str idStr = mData->mUuid.toString();
7548 const char *apszArgs[] =
7549 {
7550 szPath,
7551 "--comment", mUserData->s.strName.c_str(),
7552 "--startvm", idStr.c_str(),
7553 "--no-startvm-errormsgbox",
7554 NULL, /* For "--separate". */
7555 NULL, /* For "--sup-startup-log". */
7556 NULL
7557 };
7558 unsigned iArg = 6;
7559 if (fSeparate)
7560 apszArgs[iArg++] = "--separate";
7561 apszArgs[iArg++] = pszSupHardeningLogArg;
7562
7563 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7564 }
7565#else /* !VBOX_WITH_QTGUI */
7566 if (0)
7567 ;
7568#endif /* VBOX_WITH_QTGUI */
7569
7570 else
7571
7572#ifdef VBOX_WITH_VBOXSDL
7573 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7574 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7575 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7576 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7577 {
7578 strCanonicalName = "GUI/SDL";
7579 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7580 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7581 strcpy(pszNamePart, s_szVBoxSDL_exe);
7582
7583 Utf8Str idStr = mData->mUuid.toString();
7584 const char *apszArgs[] =
7585 {
7586 szPath,
7587 "--comment", mUserData->s.strName.c_str(),
7588 "--startvm", idStr.c_str(),
7589 NULL, /* For "--separate". */
7590 NULL, /* For "--sup-startup-log". */
7591 NULL
7592 };
7593 unsigned iArg = 5;
7594 if (fSeparate)
7595 apszArgs[iArg++] = "--separate";
7596 apszArgs[iArg++] = pszSupHardeningLogArg;
7597
7598 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7599 }
7600#else /* !VBOX_WITH_VBOXSDL */
7601 if (0)
7602 ;
7603#endif /* !VBOX_WITH_VBOXSDL */
7604
7605 else
7606
7607#ifdef VBOX_WITH_HEADLESS
7608 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7609 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7610 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7611 )
7612 {
7613 strCanonicalName = "headless";
7614 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7615 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7616 * and a VM works even if the server has not been installed.
7617 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7618 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7619 * differently in 4.0 and 3.x.
7620 */
7621 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7622 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7623 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7624
7625 Utf8Str idStr = mData->mUuid.toString();
7626 const char *apszArgs[] =
7627 {
7628 szPath,
7629 "--comment", mUserData->s.strName.c_str(),
7630 "--startvm", idStr.c_str(),
7631 "--vrde", "config",
7632 NULL, /* For "--capture". */
7633 NULL, /* For "--sup-startup-log". */
7634 NULL
7635 };
7636 unsigned iArg = 7;
7637 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7638 apszArgs[iArg++] = "--capture";
7639 apszArgs[iArg++] = pszSupHardeningLogArg;
7640
7641# ifdef RT_OS_WINDOWS
7642 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7643# else
7644 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7645# endif
7646 }
7647#else /* !VBOX_WITH_HEADLESS */
7648 if (0)
7649 ;
7650#endif /* !VBOX_WITH_HEADLESS */
7651 else
7652 {
7653 RTEnvDestroy(env);
7654 return setError(E_INVALIDARG,
7655 tr("Invalid frontend name: '%s'"),
7656 strFrontend.c_str());
7657 }
7658
7659 RTEnvDestroy(env);
7660
7661 if (RT_FAILURE(vrc))
7662 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7663 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7664 mUserData->s.strName.c_str(), vrc);
7665
7666 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7667
7668 if (!fSeparate)
7669 {
7670 /*
7671 * Note that we don't release the lock here before calling the client,
7672 * because it doesn't need to call us back if called with a NULL argument.
7673 * Releasing the lock here is dangerous because we didn't prepare the
7674 * launch data yet, but the client we've just started may happen to be
7675 * too fast and call LockMachine() that will fail (because of PID, etc.),
7676 * so that the Machine will never get out of the Spawning session state.
7677 */
7678
7679 /* inform the session that it will be a remote one */
7680 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7681#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7682 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7683#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7684 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7685#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7686 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7687
7688 if (FAILED(rc))
7689 {
7690 /* restore the session state */
7691 mData->mSession.mState = SessionState_Unlocked;
7692 alock.release();
7693 mParent->i_addProcessToReap(pid);
7694 /* The failure may occur w/o any error info (from RPC), so provide one */
7695 return setError(VBOX_E_VM_ERROR,
7696 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7697 }
7698
7699 /* attach launch data to the machine */
7700 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7701 mData->mSession.mRemoteControls.push_back(aControl);
7702 mData->mSession.mProgress = aProgress;
7703 mData->mSession.mPID = pid;
7704 mData->mSession.mState = SessionState_Spawning;
7705 Assert(strCanonicalName.isNotEmpty());
7706 mData->mSession.mName = strCanonicalName;
7707 }
7708 else
7709 {
7710 /* For separate UI process we declare the launch as completed instantly, as the
7711 * actual headless VM start may or may not come. No point in remembering anything
7712 * yet, as what matters for us is when the headless VM gets started. */
7713 aProgress->i_notifyComplete(S_OK);
7714 }
7715
7716 alock.release();
7717 mParent->i_addProcessToReap(pid);
7718
7719 LogFlowThisFuncLeave();
7720 return S_OK;
7721}
7722
7723/**
7724 * Returns @c true if the given session machine instance has an open direct
7725 * session (and optionally also for direct sessions which are closing) and
7726 * returns the session control machine instance if so.
7727 *
7728 * Note that when the method returns @c false, the arguments remain unchanged.
7729 *
7730 * @param aMachine Session machine object.
7731 * @param aControl Direct session control object (optional).
7732 * @param aRequireVM If true then only allow VM sessions.
7733 * @param aAllowClosing If true then additionally a session which is currently
7734 * being closed will also be allowed.
7735 *
7736 * @note locks this object for reading.
7737 */
7738bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7739 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7740 bool aRequireVM /*= false*/,
7741 bool aAllowClosing /*= false*/)
7742{
7743 AutoLimitedCaller autoCaller(this);
7744 AssertComRCReturn(autoCaller.rc(), false);
7745
7746 /* just return false for inaccessible machines */
7747 if (getObjectState().getState() != ObjectState::Ready)
7748 return false;
7749
7750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7751
7752 if ( ( mData->mSession.mState == SessionState_Locked
7753 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7754 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7755 )
7756 {
7757 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7758
7759 aMachine = mData->mSession.mMachine;
7760
7761 if (aControl != NULL)
7762 *aControl = mData->mSession.mDirectControl;
7763
7764 return true;
7765 }
7766
7767 return false;
7768}
7769
7770/**
7771 * Returns @c true if the given machine has an spawning direct session.
7772 *
7773 * @note locks this object for reading.
7774 */
7775bool Machine::i_isSessionSpawning()
7776{
7777 AutoLimitedCaller autoCaller(this);
7778 AssertComRCReturn(autoCaller.rc(), false);
7779
7780 /* just return false for inaccessible machines */
7781 if (getObjectState().getState() != ObjectState::Ready)
7782 return false;
7783
7784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7785
7786 if (mData->mSession.mState == SessionState_Spawning)
7787 return true;
7788
7789 return false;
7790}
7791
7792/**
7793 * Called from the client watcher thread to check for unexpected client process
7794 * death during Session_Spawning state (e.g. before it successfully opened a
7795 * direct session).
7796 *
7797 * On Win32 and on OS/2, this method is called only when we've got the
7798 * direct client's process termination notification, so it always returns @c
7799 * true.
7800 *
7801 * On other platforms, this method returns @c true if the client process is
7802 * terminated and @c false if it's still alive.
7803 *
7804 * @note Locks this object for writing.
7805 */
7806bool Machine::i_checkForSpawnFailure()
7807{
7808 AutoCaller autoCaller(this);
7809 if (!autoCaller.isOk())
7810 {
7811 /* nothing to do */
7812 LogFlowThisFunc(("Already uninitialized!\n"));
7813 return true;
7814 }
7815
7816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7817
7818 if (mData->mSession.mState != SessionState_Spawning)
7819 {
7820 /* nothing to do */
7821 LogFlowThisFunc(("Not spawning any more!\n"));
7822 return true;
7823 }
7824
7825 HRESULT rc = S_OK;
7826
7827 /* PID not yet initialized, skip check. */
7828 if (mData->mSession.mPID == NIL_RTPROCESS)
7829 return false;
7830
7831 RTPROCSTATUS status;
7832 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7833
7834 if (vrc != VERR_PROCESS_RUNNING)
7835 {
7836 Utf8Str strExtraInfo;
7837
7838#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7839 /* If the startup logfile exists and is of non-zero length, tell the
7840 user to look there for more details to encourage them to attach it
7841 when reporting startup issues. */
7842 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7843 uint64_t cbStartupLogFile = 0;
7844 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7845 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7846 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7847#endif
7848
7849 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7850 rc = setError(E_FAIL,
7851 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7852 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7853 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7854 rc = setError(E_FAIL,
7855 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7856 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7857 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7858 rc = setError(E_FAIL,
7859 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7860 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7861 else
7862 rc = setErrorBoth(E_FAIL, vrc,
7863 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7864 i_getName().c_str(), vrc, strExtraInfo.c_str());
7865 }
7866
7867 if (FAILED(rc))
7868 {
7869 /* Close the remote session, remove the remote control from the list
7870 * and reset session state to Closed (@note keep the code in sync with
7871 * the relevant part in LockMachine()). */
7872
7873 Assert(mData->mSession.mRemoteControls.size() == 1);
7874 if (mData->mSession.mRemoteControls.size() == 1)
7875 {
7876 ErrorInfoKeeper eik;
7877 mData->mSession.mRemoteControls.front()->Uninitialize();
7878 }
7879
7880 mData->mSession.mRemoteControls.clear();
7881 mData->mSession.mState = SessionState_Unlocked;
7882
7883 /* finalize the progress after setting the state */
7884 if (!mData->mSession.mProgress.isNull())
7885 {
7886 mData->mSession.mProgress->notifyComplete(rc);
7887 mData->mSession.mProgress.setNull();
7888 }
7889
7890 mData->mSession.mPID = NIL_RTPROCESS;
7891
7892 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7893 return true;
7894 }
7895
7896 return false;
7897}
7898
7899/**
7900 * Checks whether the machine can be registered. If so, commits and saves
7901 * all settings.
7902 *
7903 * @note Must be called from mParent's write lock. Locks this object and
7904 * children for writing.
7905 */
7906HRESULT Machine::i_prepareRegister()
7907{
7908 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7909
7910 AutoLimitedCaller autoCaller(this);
7911 AssertComRCReturnRC(autoCaller.rc());
7912
7913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7914
7915 /* wait for state dependents to drop to zero */
7916 i_ensureNoStateDependencies();
7917
7918 if (!mData->mAccessible)
7919 return setError(VBOX_E_INVALID_OBJECT_STATE,
7920 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7921 mUserData->s.strName.c_str(),
7922 mData->mUuid.toString().c_str());
7923
7924 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7925
7926 if (mData->mRegistered)
7927 return setError(VBOX_E_INVALID_OBJECT_STATE,
7928 tr("The machine '%s' with UUID {%s} is already registered"),
7929 mUserData->s.strName.c_str(),
7930 mData->mUuid.toString().c_str());
7931
7932 HRESULT rc = S_OK;
7933
7934 // Ensure the settings are saved. If we are going to be registered and
7935 // no config file exists yet, create it by calling i_saveSettings() too.
7936 if ( (mData->flModifications)
7937 || (!mData->pMachineConfigFile->fileExists())
7938 )
7939 {
7940 rc = i_saveSettings(NULL);
7941 // no need to check whether VirtualBox.xml needs saving too since
7942 // we can't have a machine XML file rename pending
7943 if (FAILED(rc)) return rc;
7944 }
7945
7946 /* more config checking goes here */
7947
7948 if (SUCCEEDED(rc))
7949 {
7950 /* we may have had implicit modifications we want to fix on success */
7951 i_commit();
7952
7953 mData->mRegistered = true;
7954 }
7955 else
7956 {
7957 /* we may have had implicit modifications we want to cancel on failure*/
7958 i_rollback(false /* aNotify */);
7959 }
7960
7961 return rc;
7962}
7963
7964/**
7965 * Increases the number of objects dependent on the machine state or on the
7966 * registered state. Guarantees that these two states will not change at least
7967 * until #i_releaseStateDependency() is called.
7968 *
7969 * Depending on the @a aDepType value, additional state checks may be made.
7970 * These checks will set extended error info on failure. See
7971 * #i_checkStateDependency() for more info.
7972 *
7973 * If this method returns a failure, the dependency is not added and the caller
7974 * is not allowed to rely on any particular machine state or registration state
7975 * value and may return the failed result code to the upper level.
7976 *
7977 * @param aDepType Dependency type to add.
7978 * @param aState Current machine state (NULL if not interested).
7979 * @param aRegistered Current registered state (NULL if not interested).
7980 *
7981 * @note Locks this object for writing.
7982 */
7983HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7984 MachineState_T *aState /* = NULL */,
7985 BOOL *aRegistered /* = NULL */)
7986{
7987 AutoCaller autoCaller(this);
7988 AssertComRCReturnRC(autoCaller.rc());
7989
7990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7991
7992 HRESULT rc = i_checkStateDependency(aDepType);
7993 if (FAILED(rc)) return rc;
7994
7995 {
7996 if (mData->mMachineStateChangePending != 0)
7997 {
7998 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7999 * drop to zero so don't add more. It may make sense to wait a bit
8000 * and retry before reporting an error (since the pending state
8001 * transition should be really quick) but let's just assert for
8002 * now to see if it ever happens on practice. */
8003
8004 AssertFailed();
8005
8006 return setError(E_ACCESSDENIED,
8007 tr("Machine state change is in progress. Please retry the operation later."));
8008 }
8009
8010 ++mData->mMachineStateDeps;
8011 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8012 }
8013
8014 if (aState)
8015 *aState = mData->mMachineState;
8016 if (aRegistered)
8017 *aRegistered = mData->mRegistered;
8018
8019 return S_OK;
8020}
8021
8022/**
8023 * Decreases the number of objects dependent on the machine state.
8024 * Must always complete the #i_addStateDependency() call after the state
8025 * dependency is no more necessary.
8026 */
8027void Machine::i_releaseStateDependency()
8028{
8029 AutoCaller autoCaller(this);
8030 AssertComRCReturnVoid(autoCaller.rc());
8031
8032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8033
8034 /* releaseStateDependency() w/o addStateDependency()? */
8035 AssertReturnVoid(mData->mMachineStateDeps != 0);
8036 -- mData->mMachineStateDeps;
8037
8038 if (mData->mMachineStateDeps == 0)
8039 {
8040 /* inform i_ensureNoStateDependencies() that there are no more deps */
8041 if (mData->mMachineStateChangePending != 0)
8042 {
8043 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8044 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8045 }
8046 }
8047}
8048
8049Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8050{
8051 /* start with nothing found */
8052 Utf8Str strResult("");
8053
8054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8055
8056 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8057 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8058 // found:
8059 strResult = it->second; // source is a Utf8Str
8060
8061 return strResult;
8062}
8063
8064// protected methods
8065/////////////////////////////////////////////////////////////////////////////
8066
8067/**
8068 * Performs machine state checks based on the @a aDepType value. If a check
8069 * fails, this method will set extended error info, otherwise it will return
8070 * S_OK. It is supposed, that on failure, the caller will immediately return
8071 * the return value of this method to the upper level.
8072 *
8073 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8074 *
8075 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8076 * current state of this machine object allows to change settings of the
8077 * machine (i.e. the machine is not registered, or registered but not running
8078 * and not saved). It is useful to call this method from Machine setters
8079 * before performing any change.
8080 *
8081 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8082 * as for MutableStateDep except that if the machine is saved, S_OK is also
8083 * returned. This is useful in setters which allow changing machine
8084 * properties when it is in the saved state.
8085 *
8086 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8087 * if the current state of this machine object allows to change runtime
8088 * changeable settings of the machine (i.e. the machine is not registered, or
8089 * registered but either running or not running and not saved). It is useful
8090 * to call this method from Machine setters before performing any changes to
8091 * runtime changeable settings.
8092 *
8093 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8094 * the same as for MutableOrRunningStateDep except that if the machine is
8095 * saved, S_OK is also returned. This is useful in setters which allow
8096 * changing runtime and saved state changeable machine properties.
8097 *
8098 * @param aDepType Dependency type to check.
8099 *
8100 * @note Non Machine based classes should use #i_addStateDependency() and
8101 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8102 * template.
8103 *
8104 * @note This method must be called from under this object's read or write
8105 * lock.
8106 */
8107HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8108{
8109 switch (aDepType)
8110 {
8111 case AnyStateDep:
8112 {
8113 break;
8114 }
8115 case MutableStateDep:
8116 {
8117 if ( mData->mRegistered
8118 && ( !i_isSessionMachine()
8119 || ( mData->mMachineState != MachineState_Aborted
8120 && mData->mMachineState != MachineState_Teleported
8121 && mData->mMachineState != MachineState_PoweredOff
8122 )
8123 )
8124 )
8125 return setError(VBOX_E_INVALID_VM_STATE,
8126 tr("The machine is not mutable (state is %s)"),
8127 Global::stringifyMachineState(mData->mMachineState));
8128 break;
8129 }
8130 case MutableOrSavedStateDep:
8131 {
8132 if ( mData->mRegistered
8133 && ( !i_isSessionMachine()
8134 || ( mData->mMachineState != MachineState_Aborted
8135 && mData->mMachineState != MachineState_Teleported
8136 && mData->mMachineState != MachineState_Saved
8137 && mData->mMachineState != MachineState_PoweredOff
8138 )
8139 )
8140 )
8141 return setError(VBOX_E_INVALID_VM_STATE,
8142 tr("The machine is not mutable or saved (state is %s)"),
8143 Global::stringifyMachineState(mData->mMachineState));
8144 break;
8145 }
8146 case MutableOrRunningStateDep:
8147 {
8148 if ( mData->mRegistered
8149 && ( !i_isSessionMachine()
8150 || ( mData->mMachineState != MachineState_Aborted
8151 && mData->mMachineState != MachineState_Teleported
8152 && mData->mMachineState != MachineState_PoweredOff
8153 && !Global::IsOnline(mData->mMachineState)
8154 )
8155 )
8156 )
8157 return setError(VBOX_E_INVALID_VM_STATE,
8158 tr("The machine is not mutable or running (state is %s)"),
8159 Global::stringifyMachineState(mData->mMachineState));
8160 break;
8161 }
8162 case MutableOrSavedOrRunningStateDep:
8163 {
8164 if ( mData->mRegistered
8165 && ( !i_isSessionMachine()
8166 || ( mData->mMachineState != MachineState_Aborted
8167 && mData->mMachineState != MachineState_Teleported
8168 && mData->mMachineState != MachineState_Saved
8169 && mData->mMachineState != MachineState_PoweredOff
8170 && !Global::IsOnline(mData->mMachineState)
8171 )
8172 )
8173 )
8174 return setError(VBOX_E_INVALID_VM_STATE,
8175 tr("The machine is not mutable, saved or running (state is %s)"),
8176 Global::stringifyMachineState(mData->mMachineState));
8177 break;
8178 }
8179 }
8180
8181 return S_OK;
8182}
8183
8184/**
8185 * Helper to initialize all associated child objects and allocate data
8186 * structures.
8187 *
8188 * This method must be called as a part of the object's initialization procedure
8189 * (usually done in the #init() method).
8190 *
8191 * @note Must be called only from #init() or from #i_registeredInit().
8192 */
8193HRESULT Machine::initDataAndChildObjects()
8194{
8195 AutoCaller autoCaller(this);
8196 AssertComRCReturnRC(autoCaller.rc());
8197 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8198 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8199
8200 AssertReturn(!mData->mAccessible, E_FAIL);
8201
8202 /* allocate data structures */
8203 mSSData.allocate();
8204 mUserData.allocate();
8205 mHWData.allocate();
8206 mMediumAttachments.allocate();
8207 mStorageControllers.allocate();
8208 mUSBControllers.allocate();
8209
8210 /* initialize mOSTypeId */
8211 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8212
8213/** @todo r=bird: init() methods never fails, right? Why don't we make them
8214 * return void then! */
8215
8216 /* create associated BIOS settings object */
8217 unconst(mBIOSSettings).createObject();
8218 mBIOSSettings->init(this);
8219
8220 /* create associated record settings object */
8221 unconst(mRecordingSettings).createObject();
8222 mRecordingSettings->init(this);
8223
8224 /* create an associated VRDE object (default is disabled) */
8225 unconst(mVRDEServer).createObject();
8226 mVRDEServer->init(this);
8227
8228 /* create associated serial port objects */
8229 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8230 {
8231 unconst(mSerialPorts[slot]).createObject();
8232 mSerialPorts[slot]->init(this, slot);
8233 }
8234
8235 /* create associated parallel port objects */
8236 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8237 {
8238 unconst(mParallelPorts[slot]).createObject();
8239 mParallelPorts[slot]->init(this, slot);
8240 }
8241
8242 /* create the audio adapter object (always present, default is disabled) */
8243 unconst(mAudioAdapter).createObject();
8244 mAudioAdapter->init(this);
8245
8246 /* create the USB device filters object (always present) */
8247 unconst(mUSBDeviceFilters).createObject();
8248 mUSBDeviceFilters->init(this);
8249
8250 /* create associated network adapter objects */
8251 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8252 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8253 {
8254 unconst(mNetworkAdapters[slot]).createObject();
8255 mNetworkAdapters[slot]->init(this, slot);
8256 }
8257
8258 /* create the bandwidth control */
8259 unconst(mBandwidthControl).createObject();
8260 mBandwidthControl->init(this);
8261
8262 return S_OK;
8263}
8264
8265/**
8266 * Helper to uninitialize all associated child objects and to free all data
8267 * structures.
8268 *
8269 * This method must be called as a part of the object's uninitialization
8270 * procedure (usually done in the #uninit() method).
8271 *
8272 * @note Must be called only from #uninit() or from #i_registeredInit().
8273 */
8274void Machine::uninitDataAndChildObjects()
8275{
8276 AutoCaller autoCaller(this);
8277 AssertComRCReturnVoid(autoCaller.rc());
8278 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8279 || getObjectState().getState() == ObjectState::Limited);
8280
8281 /* tell all our other child objects we've been uninitialized */
8282 if (mBandwidthControl)
8283 {
8284 mBandwidthControl->uninit();
8285 unconst(mBandwidthControl).setNull();
8286 }
8287
8288 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8289 {
8290 if (mNetworkAdapters[slot])
8291 {
8292 mNetworkAdapters[slot]->uninit();
8293 unconst(mNetworkAdapters[slot]).setNull();
8294 }
8295 }
8296
8297 if (mUSBDeviceFilters)
8298 {
8299 mUSBDeviceFilters->uninit();
8300 unconst(mUSBDeviceFilters).setNull();
8301 }
8302
8303 if (mAudioAdapter)
8304 {
8305 mAudioAdapter->uninit();
8306 unconst(mAudioAdapter).setNull();
8307 }
8308
8309 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8310 {
8311 if (mParallelPorts[slot])
8312 {
8313 mParallelPorts[slot]->uninit();
8314 unconst(mParallelPorts[slot]).setNull();
8315 }
8316 }
8317
8318 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8319 {
8320 if (mSerialPorts[slot])
8321 {
8322 mSerialPorts[slot]->uninit();
8323 unconst(mSerialPorts[slot]).setNull();
8324 }
8325 }
8326
8327 if (mVRDEServer)
8328 {
8329 mVRDEServer->uninit();
8330 unconst(mVRDEServer).setNull();
8331 }
8332
8333 if (mBIOSSettings)
8334 {
8335 mBIOSSettings->uninit();
8336 unconst(mBIOSSettings).setNull();
8337 }
8338
8339 if (mRecordingSettings)
8340 {
8341 mRecordingSettings->uninit();
8342 unconst(mRecordingSettings).setNull();
8343 }
8344
8345 /* Deassociate media (only when a real Machine or a SnapshotMachine
8346 * instance is uninitialized; SessionMachine instances refer to real
8347 * Machine media). This is necessary for a clean re-initialization of
8348 * the VM after successfully re-checking the accessibility state. Note
8349 * that in case of normal Machine or SnapshotMachine uninitialization (as
8350 * a result of unregistering or deleting the snapshot), outdated media
8351 * attachments will already be uninitialized and deleted, so this
8352 * code will not affect them. */
8353 if ( !mMediumAttachments.isNull()
8354 && !i_isSessionMachine()
8355 )
8356 {
8357 for (MediumAttachmentList::const_iterator
8358 it = mMediumAttachments->begin();
8359 it != mMediumAttachments->end();
8360 ++it)
8361 {
8362 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8363 if (pMedium.isNull())
8364 continue;
8365 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8366 AssertComRC(rc);
8367 }
8368 }
8369
8370 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8371 {
8372 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8373 if (mData->mFirstSnapshot)
8374 {
8375 // snapshots tree is protected by machine write lock; strictly
8376 // this isn't necessary here since we're deleting the entire
8377 // machine, but otherwise we assert in Snapshot::uninit()
8378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8379 mData->mFirstSnapshot->uninit();
8380 mData->mFirstSnapshot.setNull();
8381 }
8382
8383 mData->mCurrentSnapshot.setNull();
8384 }
8385
8386 /* free data structures (the essential mData structure is not freed here
8387 * since it may be still in use) */
8388 mMediumAttachments.free();
8389 mStorageControllers.free();
8390 mUSBControllers.free();
8391 mHWData.free();
8392 mUserData.free();
8393 mSSData.free();
8394}
8395
8396/**
8397 * Returns a pointer to the Machine object for this machine that acts like a
8398 * parent for complex machine data objects such as shared folders, etc.
8399 *
8400 * For primary Machine objects and for SnapshotMachine objects, returns this
8401 * object's pointer itself. For SessionMachine objects, returns the peer
8402 * (primary) machine pointer.
8403 */
8404Machine *Machine::i_getMachine()
8405{
8406 if (i_isSessionMachine())
8407 return (Machine*)mPeer;
8408 return this;
8409}
8410
8411/**
8412 * Makes sure that there are no machine state dependents. If necessary, waits
8413 * for the number of dependents to drop to zero.
8414 *
8415 * Make sure this method is called from under this object's write lock to
8416 * guarantee that no new dependents may be added when this method returns
8417 * control to the caller.
8418 *
8419 * @note Locks this object for writing. The lock will be released while waiting
8420 * (if necessary).
8421 *
8422 * @warning To be used only in methods that change the machine state!
8423 */
8424void Machine::i_ensureNoStateDependencies()
8425{
8426 AssertReturnVoid(isWriteLockOnCurrentThread());
8427
8428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8429
8430 /* Wait for all state dependents if necessary */
8431 if (mData->mMachineStateDeps != 0)
8432 {
8433 /* lazy semaphore creation */
8434 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8435 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8436
8437 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8438 mData->mMachineStateDeps));
8439
8440 ++mData->mMachineStateChangePending;
8441
8442 /* reset the semaphore before waiting, the last dependent will signal
8443 * it */
8444 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8445
8446 alock.release();
8447
8448 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8449
8450 alock.acquire();
8451
8452 -- mData->mMachineStateChangePending;
8453 }
8454}
8455
8456/**
8457 * Changes the machine state and informs callbacks.
8458 *
8459 * This method is not intended to fail so it either returns S_OK or asserts (and
8460 * returns a failure).
8461 *
8462 * @note Locks this object for writing.
8463 */
8464HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8465{
8466 LogFlowThisFuncEnter();
8467 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8468 Assert(aMachineState != MachineState_Null);
8469
8470 AutoCaller autoCaller(this);
8471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8472
8473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8474
8475 /* wait for state dependents to drop to zero */
8476 i_ensureNoStateDependencies();
8477
8478 MachineState_T const enmOldState = mData->mMachineState;
8479 if (enmOldState != aMachineState)
8480 {
8481 mData->mMachineState = aMachineState;
8482 RTTimeNow(&mData->mLastStateChange);
8483
8484#ifdef VBOX_WITH_DTRACE_R3_MAIN
8485 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8486#endif
8487 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8488 }
8489
8490 LogFlowThisFuncLeave();
8491 return S_OK;
8492}
8493
8494/**
8495 * Searches for a shared folder with the given logical name
8496 * in the collection of shared folders.
8497 *
8498 * @param aName logical name of the shared folder
8499 * @param aSharedFolder where to return the found object
8500 * @param aSetError whether to set the error info if the folder is
8501 * not found
8502 * @return
8503 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8504 *
8505 * @note
8506 * must be called from under the object's lock!
8507 */
8508HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8509 ComObjPtr<SharedFolder> &aSharedFolder,
8510 bool aSetError /* = false */)
8511{
8512 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8513 for (HWData::SharedFolderList::const_iterator
8514 it = mHWData->mSharedFolders.begin();
8515 it != mHWData->mSharedFolders.end();
8516 ++it)
8517 {
8518 SharedFolder *pSF = *it;
8519 AutoCaller autoCaller(pSF);
8520 if (pSF->i_getName() == aName)
8521 {
8522 aSharedFolder = pSF;
8523 rc = S_OK;
8524 break;
8525 }
8526 }
8527
8528 if (aSetError && FAILED(rc))
8529 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8530
8531 return rc;
8532}
8533
8534/**
8535 * Initializes all machine instance data from the given settings structures
8536 * from XML. The exception is the machine UUID which needs special handling
8537 * depending on the caller's use case, so the caller needs to set that herself.
8538 *
8539 * This gets called in several contexts during machine initialization:
8540 *
8541 * -- When machine XML exists on disk already and needs to be loaded into memory,
8542 * for example, from #i_registeredInit() to load all registered machines on
8543 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8544 * attached to the machine should be part of some media registry already.
8545 *
8546 * -- During OVF import, when a machine config has been constructed from an
8547 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8548 * ensure that the media listed as attachments in the config (which have
8549 * been imported from the OVF) receive the correct registry ID.
8550 *
8551 * -- During VM cloning.
8552 *
8553 * @param config Machine settings from XML.
8554 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8555 * for each attached medium in the config.
8556 * @return
8557 */
8558HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8559 const Guid *puuidRegistry)
8560{
8561 // copy name, description, OS type, teleporter, UTC etc.
8562 mUserData->s = config.machineUserData;
8563
8564 // look up the object by Id to check it is valid
8565 ComObjPtr<GuestOSType> pGuestOSType;
8566 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8567 if (!pGuestOSType.isNull())
8568 mUserData->s.strOsType = pGuestOSType->i_id();
8569
8570 // stateFile (optional)
8571 if (config.strStateFile.isEmpty())
8572 mSSData->strStateFilePath.setNull();
8573 else
8574 {
8575 Utf8Str stateFilePathFull(config.strStateFile);
8576 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8577 if (RT_FAILURE(vrc))
8578 return setErrorBoth(E_FAIL, vrc,
8579 tr("Invalid saved state file path '%s' (%Rrc)"),
8580 config.strStateFile.c_str(),
8581 vrc);
8582 mSSData->strStateFilePath = stateFilePathFull;
8583 }
8584
8585 // snapshot folder needs special processing so set it again
8586 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8587 if (FAILED(rc)) return rc;
8588
8589 /* Copy the extra data items (config may or may not be the same as
8590 * mData->pMachineConfigFile) if necessary. When loading the XML files
8591 * from disk they are the same, but not for OVF import. */
8592 if (mData->pMachineConfigFile != &config)
8593 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8594
8595 /* currentStateModified (optional, default is true) */
8596 mData->mCurrentStateModified = config.fCurrentStateModified;
8597
8598 mData->mLastStateChange = config.timeLastStateChange;
8599
8600 /*
8601 * note: all mUserData members must be assigned prior this point because
8602 * we need to commit changes in order to let mUserData be shared by all
8603 * snapshot machine instances.
8604 */
8605 mUserData.commitCopy();
8606
8607 // machine registry, if present (must be loaded before snapshots)
8608 if (config.canHaveOwnMediaRegistry())
8609 {
8610 // determine machine folder
8611 Utf8Str strMachineFolder = i_getSettingsFileFull();
8612 strMachineFolder.stripFilename();
8613 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8614 config.mediaRegistry,
8615 strMachineFolder);
8616 if (FAILED(rc)) return rc;
8617 }
8618
8619 /* Snapshot node (optional) */
8620 size_t cRootSnapshots;
8621 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8622 {
8623 // there must be only one root snapshot
8624 Assert(cRootSnapshots == 1);
8625
8626 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8627
8628 rc = i_loadSnapshot(snap,
8629 config.uuidCurrentSnapshot,
8630 NULL); // no parent == first snapshot
8631 if (FAILED(rc)) return rc;
8632 }
8633
8634 // hardware data
8635 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8636 if (FAILED(rc)) return rc;
8637
8638 /*
8639 * NOTE: the assignment below must be the last thing to do,
8640 * otherwise it will be not possible to change the settings
8641 * somewhere in the code above because all setters will be
8642 * blocked by i_checkStateDependency(MutableStateDep).
8643 */
8644
8645 /* set the machine state to Aborted or Saved when appropriate */
8646 if (config.fAborted)
8647 {
8648 mSSData->strStateFilePath.setNull();
8649
8650 /* no need to use i_setMachineState() during init() */
8651 mData->mMachineState = MachineState_Aborted;
8652 }
8653 else if (!mSSData->strStateFilePath.isEmpty())
8654 {
8655 /* no need to use i_setMachineState() during init() */
8656 mData->mMachineState = MachineState_Saved;
8657 }
8658
8659 // after loading settings, we are no longer different from the XML on disk
8660 mData->flModifications = 0;
8661
8662 return S_OK;
8663}
8664
8665/**
8666 * Recursively loads all snapshots starting from the given.
8667 *
8668 * @param data snapshot settings.
8669 * @param aCurSnapshotId Current snapshot ID from the settings file.
8670 * @param aParentSnapshot Parent snapshot.
8671 */
8672HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8673 const Guid &aCurSnapshotId,
8674 Snapshot *aParentSnapshot)
8675{
8676 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8677 AssertReturn(!i_isSessionMachine(), E_FAIL);
8678
8679 HRESULT rc = S_OK;
8680
8681 Utf8Str strStateFile;
8682 if (!data.strStateFile.isEmpty())
8683 {
8684 /* optional */
8685 strStateFile = data.strStateFile;
8686 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8687 if (RT_FAILURE(vrc))
8688 return setErrorBoth(E_FAIL, vrc,
8689 tr("Invalid saved state file path '%s' (%Rrc)"),
8690 strStateFile.c_str(),
8691 vrc);
8692 }
8693
8694 /* create a snapshot machine object */
8695 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8696 pSnapshotMachine.createObject();
8697 rc = pSnapshotMachine->initFromSettings(this,
8698 data.hardware,
8699 &data.debugging,
8700 &data.autostart,
8701 data.uuid.ref(),
8702 strStateFile);
8703 if (FAILED(rc)) return rc;
8704
8705 /* create a snapshot object */
8706 ComObjPtr<Snapshot> pSnapshot;
8707 pSnapshot.createObject();
8708 /* initialize the snapshot */
8709 rc = pSnapshot->init(mParent, // VirtualBox object
8710 data.uuid,
8711 data.strName,
8712 data.strDescription,
8713 data.timestamp,
8714 pSnapshotMachine,
8715 aParentSnapshot);
8716 if (FAILED(rc)) return rc;
8717
8718 /* memorize the first snapshot if necessary */
8719 if (!mData->mFirstSnapshot)
8720 mData->mFirstSnapshot = pSnapshot;
8721
8722 /* memorize the current snapshot when appropriate */
8723 if ( !mData->mCurrentSnapshot
8724 && pSnapshot->i_getId() == aCurSnapshotId
8725 )
8726 mData->mCurrentSnapshot = pSnapshot;
8727
8728 // now create the children
8729 for (settings::SnapshotsList::const_iterator
8730 it = data.llChildSnapshots.begin();
8731 it != data.llChildSnapshots.end();
8732 ++it)
8733 {
8734 const settings::Snapshot &childData = *it;
8735 // recurse
8736 rc = i_loadSnapshot(childData,
8737 aCurSnapshotId,
8738 pSnapshot); // parent = the one we created above
8739 if (FAILED(rc)) return rc;
8740 }
8741
8742 return rc;
8743}
8744
8745/**
8746 * Loads settings into mHWData.
8747 *
8748 * @param puuidRegistry Registry ID.
8749 * @param puuidSnapshot Snapshot ID
8750 * @param data Reference to the hardware settings.
8751 * @param pDbg Pointer to the debugging settings.
8752 * @param pAutostart Pointer to the autostart settings.
8753 */
8754HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8755 const Guid *puuidSnapshot,
8756 const settings::Hardware &data,
8757 const settings::Debugging *pDbg,
8758 const settings::Autostart *pAutostart)
8759{
8760 AssertReturn(!i_isSessionMachine(), E_FAIL);
8761
8762 HRESULT rc = S_OK;
8763
8764 try
8765 {
8766 ComObjPtr<GuestOSType> pGuestOSType;
8767 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8768
8769 /* The hardware version attribute (optional). */
8770 mHWData->mHWVersion = data.strVersion;
8771 mHWData->mHardwareUUID = data.uuid;
8772
8773 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8774 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8775 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8776 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8777 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8778 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8779 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8780 mHWData->mPAEEnabled = data.fPAE;
8781 mHWData->mLongMode = data.enmLongMode;
8782 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8783 mHWData->mAPIC = data.fAPIC;
8784 mHWData->mX2APIC = data.fX2APIC;
8785 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8786 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8787 mHWData->mSpecCtrl = data.fSpecCtrl;
8788 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8789 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8790 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8791 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8792 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8793 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8794 mHWData->mCPUCount = data.cCPUs;
8795 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8796 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8797 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8798 mHWData->mCpuProfile = data.strCpuProfile;
8799
8800 // cpu
8801 if (mHWData->mCPUHotPlugEnabled)
8802 {
8803 for (settings::CpuList::const_iterator
8804 it = data.llCpus.begin();
8805 it != data.llCpus.end();
8806 ++it)
8807 {
8808 const settings::Cpu &cpu = *it;
8809
8810 mHWData->mCPUAttached[cpu.ulId] = true;
8811 }
8812 }
8813
8814 // cpuid leafs
8815 for (settings::CpuIdLeafsList::const_iterator
8816 it = data.llCpuIdLeafs.begin();
8817 it != data.llCpuIdLeafs.end();
8818 ++it)
8819 {
8820 const settings::CpuIdLeaf &rLeaf= *it;
8821 if ( rLeaf.idx < UINT32_C(0x20)
8822 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8823 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8824 mHWData->mCpuIdLeafList.push_back(rLeaf);
8825 /* else: just ignore */
8826 }
8827
8828 mHWData->mMemorySize = data.ulMemorySizeMB;
8829 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8830
8831 // boot order
8832 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8833 {
8834 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8835 if (it == data.mapBootOrder.end())
8836 mHWData->mBootOrder[i] = DeviceType_Null;
8837 else
8838 mHWData->mBootOrder[i] = it->second;
8839 }
8840
8841 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8842 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8843 mHWData->mMonitorCount = data.cMonitors;
8844 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8845 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8846 mHWData->mFirmwareType = data.firmwareType;
8847 mHWData->mPointingHIDType = data.pointingHIDType;
8848 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8849 mHWData->mChipsetType = data.chipsetType;
8850 mHWData->mParavirtProvider = data.paravirtProvider;
8851 mHWData->mParavirtDebug = data.strParavirtDebug;
8852 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8853 mHWData->mHPETEnabled = data.fHPETEnabled;
8854
8855 /* VRDEServer */
8856 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8857 if (FAILED(rc)) return rc;
8858
8859 /* BIOS */
8860 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8861 if (FAILED(rc)) return rc;
8862
8863 /* Recording settings */
8864 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8865 if (FAILED(rc)) return rc;
8866
8867 // Bandwidth control (must come before network adapters)
8868 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8869 if (FAILED(rc)) return rc;
8870
8871 /* USB controllers */
8872 for (settings::USBControllerList::const_iterator
8873 it = data.usbSettings.llUSBControllers.begin();
8874 it != data.usbSettings.llUSBControllers.end();
8875 ++it)
8876 {
8877 const settings::USBController &settingsCtrl = *it;
8878 ComObjPtr<USBController> newCtrl;
8879
8880 newCtrl.createObject();
8881 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8882 mUSBControllers->push_back(newCtrl);
8883 }
8884
8885 /* USB device filters */
8886 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8887 if (FAILED(rc)) return rc;
8888
8889 // network adapters (establish array size first and apply defaults, to
8890 // ensure reading the same settings as we saved, since the list skips
8891 // adapters having defaults)
8892 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8893 size_t oldCount = mNetworkAdapters.size();
8894 if (newCount > oldCount)
8895 {
8896 mNetworkAdapters.resize(newCount);
8897 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8898 {
8899 unconst(mNetworkAdapters[slot]).createObject();
8900 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8901 }
8902 }
8903 else if (newCount < oldCount)
8904 mNetworkAdapters.resize(newCount);
8905 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8906 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8907 for (settings::NetworkAdaptersList::const_iterator
8908 it = data.llNetworkAdapters.begin();
8909 it != data.llNetworkAdapters.end();
8910 ++it)
8911 {
8912 const settings::NetworkAdapter &nic = *it;
8913
8914 /* slot uniqueness is guaranteed by XML Schema */
8915 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8916 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8917 if (FAILED(rc)) return rc;
8918 }
8919
8920 // serial ports (establish defaults first, to ensure reading the same
8921 // settings as we saved, since the list skips ports having defaults)
8922 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8923 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8924 for (settings::SerialPortsList::const_iterator
8925 it = data.llSerialPorts.begin();
8926 it != data.llSerialPorts.end();
8927 ++it)
8928 {
8929 const settings::SerialPort &s = *it;
8930
8931 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8932 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8933 if (FAILED(rc)) return rc;
8934 }
8935
8936 // parallel ports (establish defaults first, to ensure reading the same
8937 // settings as we saved, since the list skips ports having defaults)
8938 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8939 mParallelPorts[i]->i_applyDefaults();
8940 for (settings::ParallelPortsList::const_iterator
8941 it = data.llParallelPorts.begin();
8942 it != data.llParallelPorts.end();
8943 ++it)
8944 {
8945 const settings::ParallelPort &p = *it;
8946
8947 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8948 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8949 if (FAILED(rc)) return rc;
8950 }
8951
8952 /* AudioAdapter */
8953 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8954 if (FAILED(rc)) return rc;
8955
8956 /* storage controllers */
8957 rc = i_loadStorageControllers(data.storage,
8958 puuidRegistry,
8959 puuidSnapshot);
8960 if (FAILED(rc)) return rc;
8961
8962 /* Shared folders */
8963 for (settings::SharedFoldersList::const_iterator
8964 it = data.llSharedFolders.begin();
8965 it != data.llSharedFolders.end();
8966 ++it)
8967 {
8968 const settings::SharedFolder &sf = *it;
8969
8970 ComObjPtr<SharedFolder> sharedFolder;
8971 /* Check for double entries. Not allowed! */
8972 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8973 if (SUCCEEDED(rc))
8974 return setError(VBOX_E_OBJECT_IN_USE,
8975 tr("Shared folder named '%s' already exists"),
8976 sf.strName.c_str());
8977
8978 /* Create the new shared folder. Don't break on error. This will be
8979 * reported when the machine starts. */
8980 sharedFolder.createObject();
8981 rc = sharedFolder->init(i_getMachine(),
8982 sf.strName,
8983 sf.strHostPath,
8984 RT_BOOL(sf.fWritable),
8985 RT_BOOL(sf.fAutoMount),
8986 sf.strAutoMountPoint,
8987 false /* fFailOnError */);
8988 if (FAILED(rc)) return rc;
8989 mHWData->mSharedFolders.push_back(sharedFolder);
8990 }
8991
8992 // Clipboard
8993 mHWData->mClipboardMode = data.clipboardMode;
8994
8995 // drag'n'drop
8996 mHWData->mDnDMode = data.dndMode;
8997
8998 // guest settings
8999 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9000
9001 // IO settings
9002 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9003 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9004
9005 // Host PCI devices
9006 for (settings::HostPCIDeviceAttachmentList::const_iterator
9007 it = data.pciAttachments.begin();
9008 it != data.pciAttachments.end();
9009 ++it)
9010 {
9011 const settings::HostPCIDeviceAttachment &hpda = *it;
9012 ComObjPtr<PCIDeviceAttachment> pda;
9013
9014 pda.createObject();
9015 pda->i_loadSettings(this, hpda);
9016 mHWData->mPCIDeviceAssignments.push_back(pda);
9017 }
9018
9019 /*
9020 * (The following isn't really real hardware, but it lives in HWData
9021 * for reasons of convenience.)
9022 */
9023
9024#ifdef VBOX_WITH_GUEST_PROPS
9025 /* Guest properties (optional) */
9026
9027 /* Only load transient guest properties for configs which have saved
9028 * state, because there shouldn't be any for powered off VMs. The same
9029 * logic applies for snapshots, as offline snapshots shouldn't have
9030 * any such properties. They confuse the code in various places.
9031 * Note: can't rely on the machine state, as it isn't set yet. */
9032 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9033 /* apologies for the hacky unconst() usage, but this needs hacking
9034 * actually inconsistent settings into consistency, otherwise there
9035 * will be some corner cases where the inconsistency survives
9036 * surprisingly long without getting fixed, especially for snapshots
9037 * as there are no config changes. */
9038 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9039 for (settings::GuestPropertiesList::iterator
9040 it = llGuestProperties.begin();
9041 it != llGuestProperties.end();
9042 /*nothing*/)
9043 {
9044 const settings::GuestProperty &prop = *it;
9045 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9046 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9047 if ( fSkipTransientGuestProperties
9048 && ( fFlags & GUEST_PROP_F_TRANSIENT
9049 || fFlags & GUEST_PROP_F_TRANSRESET))
9050 {
9051 it = llGuestProperties.erase(it);
9052 continue;
9053 }
9054 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9055 mHWData->mGuestProperties[prop.strName] = property;
9056 ++it;
9057 }
9058#endif /* VBOX_WITH_GUEST_PROPS defined */
9059
9060 rc = i_loadDebugging(pDbg);
9061 if (FAILED(rc))
9062 return rc;
9063
9064 mHWData->mAutostart = *pAutostart;
9065
9066 /* default frontend */
9067 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9068 }
9069 catch (std::bad_alloc &)
9070 {
9071 return E_OUTOFMEMORY;
9072 }
9073
9074 AssertComRC(rc);
9075 return rc;
9076}
9077
9078/**
9079 * Called from i_loadHardware() to load the debugging settings of the
9080 * machine.
9081 *
9082 * @param pDbg Pointer to the settings.
9083 */
9084HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9085{
9086 mHWData->mDebugging = *pDbg;
9087 /* no more processing currently required, this will probably change. */
9088 return S_OK;
9089}
9090
9091/**
9092 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9093 *
9094 * @param data storage settings.
9095 * @param puuidRegistry media registry ID to set media to or NULL;
9096 * see Machine::i_loadMachineDataFromSettings()
9097 * @param puuidSnapshot snapshot ID
9098 * @return
9099 */
9100HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9101 const Guid *puuidRegistry,
9102 const Guid *puuidSnapshot)
9103{
9104 AssertReturn(!i_isSessionMachine(), E_FAIL);
9105
9106 HRESULT rc = S_OK;
9107
9108 for (settings::StorageControllersList::const_iterator
9109 it = data.llStorageControllers.begin();
9110 it != data.llStorageControllers.end();
9111 ++it)
9112 {
9113 const settings::StorageController &ctlData = *it;
9114
9115 ComObjPtr<StorageController> pCtl;
9116 /* Try to find one with the name first. */
9117 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9118 if (SUCCEEDED(rc))
9119 return setError(VBOX_E_OBJECT_IN_USE,
9120 tr("Storage controller named '%s' already exists"),
9121 ctlData.strName.c_str());
9122
9123 pCtl.createObject();
9124 rc = pCtl->init(this,
9125 ctlData.strName,
9126 ctlData.storageBus,
9127 ctlData.ulInstance,
9128 ctlData.fBootable);
9129 if (FAILED(rc)) return rc;
9130
9131 mStorageControllers->push_back(pCtl);
9132
9133 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9134 if (FAILED(rc)) return rc;
9135
9136 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9137 if (FAILED(rc)) return rc;
9138
9139 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9140 if (FAILED(rc)) return rc;
9141
9142 /* Load the attached devices now. */
9143 rc = i_loadStorageDevices(pCtl,
9144 ctlData,
9145 puuidRegistry,
9146 puuidSnapshot);
9147 if (FAILED(rc)) return rc;
9148 }
9149
9150 return S_OK;
9151}
9152
9153/**
9154 * Called from i_loadStorageControllers for a controller's devices.
9155 *
9156 * @param aStorageController
9157 * @param data
9158 * @param puuidRegistry media registry ID to set media to or NULL; see
9159 * Machine::i_loadMachineDataFromSettings()
9160 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9161 * @return
9162 */
9163HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9164 const settings::StorageController &data,
9165 const Guid *puuidRegistry,
9166 const Guid *puuidSnapshot)
9167{
9168 HRESULT rc = S_OK;
9169
9170 /* paranoia: detect duplicate attachments */
9171 for (settings::AttachedDevicesList::const_iterator
9172 it = data.llAttachedDevices.begin();
9173 it != data.llAttachedDevices.end();
9174 ++it)
9175 {
9176 const settings::AttachedDevice &ad = *it;
9177
9178 for (settings::AttachedDevicesList::const_iterator it2 = it;
9179 it2 != data.llAttachedDevices.end();
9180 ++it2)
9181 {
9182 if (it == it2)
9183 continue;
9184
9185 const settings::AttachedDevice &ad2 = *it2;
9186
9187 if ( ad.lPort == ad2.lPort
9188 && ad.lDevice == ad2.lDevice)
9189 {
9190 return setError(E_FAIL,
9191 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9192 aStorageController->i_getName().c_str(),
9193 ad.lPort,
9194 ad.lDevice,
9195 mUserData->s.strName.c_str());
9196 }
9197 }
9198 }
9199
9200 for (settings::AttachedDevicesList::const_iterator
9201 it = data.llAttachedDevices.begin();
9202 it != data.llAttachedDevices.end();
9203 ++it)
9204 {
9205 const settings::AttachedDevice &dev = *it;
9206 ComObjPtr<Medium> medium;
9207
9208 switch (dev.deviceType)
9209 {
9210 case DeviceType_Floppy:
9211 case DeviceType_DVD:
9212 if (dev.strHostDriveSrc.isNotEmpty())
9213 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9214 false /* fRefresh */, medium);
9215 else
9216 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9217 dev.uuid,
9218 false /* fRefresh */,
9219 false /* aSetError */,
9220 medium);
9221 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9222 // This is not an error. The host drive or UUID might have vanished, so just go
9223 // ahead without this removeable medium attachment
9224 rc = S_OK;
9225 break;
9226
9227 case DeviceType_HardDisk:
9228 {
9229 /* find a hard disk by UUID */
9230 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9231 if (FAILED(rc))
9232 {
9233 if (i_isSnapshotMachine())
9234 {
9235 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9236 // so the user knows that the bad disk is in a snapshot somewhere
9237 com::ErrorInfo info;
9238 return setError(E_FAIL,
9239 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9240 puuidSnapshot->raw(),
9241 info.getText().raw());
9242 }
9243 else
9244 return rc;
9245 }
9246
9247 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9248
9249 if (medium->i_getType() == MediumType_Immutable)
9250 {
9251 if (i_isSnapshotMachine())
9252 return setError(E_FAIL,
9253 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9254 "of the virtual machine '%s' ('%s')"),
9255 medium->i_getLocationFull().c_str(),
9256 dev.uuid.raw(),
9257 puuidSnapshot->raw(),
9258 mUserData->s.strName.c_str(),
9259 mData->m_strConfigFileFull.c_str());
9260
9261 return setError(E_FAIL,
9262 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9263 medium->i_getLocationFull().c_str(),
9264 dev.uuid.raw(),
9265 mUserData->s.strName.c_str(),
9266 mData->m_strConfigFileFull.c_str());
9267 }
9268
9269 if (medium->i_getType() == MediumType_MultiAttach)
9270 {
9271 if (i_isSnapshotMachine())
9272 return setError(E_FAIL,
9273 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9274 "of the virtual machine '%s' ('%s')"),
9275 medium->i_getLocationFull().c_str(),
9276 dev.uuid.raw(),
9277 puuidSnapshot->raw(),
9278 mUserData->s.strName.c_str(),
9279 mData->m_strConfigFileFull.c_str());
9280
9281 return setError(E_FAIL,
9282 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9283 medium->i_getLocationFull().c_str(),
9284 dev.uuid.raw(),
9285 mUserData->s.strName.c_str(),
9286 mData->m_strConfigFileFull.c_str());
9287 }
9288
9289 if ( !i_isSnapshotMachine()
9290 && medium->i_getChildren().size() != 0
9291 )
9292 return setError(E_FAIL,
9293 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9294 "because it has %d differencing child hard disks"),
9295 medium->i_getLocationFull().c_str(),
9296 dev.uuid.raw(),
9297 mUserData->s.strName.c_str(),
9298 mData->m_strConfigFileFull.c_str(),
9299 medium->i_getChildren().size());
9300
9301 if (i_findAttachment(*mMediumAttachments.data(),
9302 medium))
9303 return setError(E_FAIL,
9304 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9305 medium->i_getLocationFull().c_str(),
9306 dev.uuid.raw(),
9307 mUserData->s.strName.c_str(),
9308 mData->m_strConfigFileFull.c_str());
9309
9310 break;
9311 }
9312
9313 default:
9314 return setError(E_FAIL,
9315 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9316 medium->i_getLocationFull().c_str(),
9317 mUserData->s.strName.c_str(),
9318 mData->m_strConfigFileFull.c_str());
9319 }
9320
9321 if (FAILED(rc))
9322 break;
9323
9324 /* Bandwidth groups are loaded at this point. */
9325 ComObjPtr<BandwidthGroup> pBwGroup;
9326
9327 if (!dev.strBwGroup.isEmpty())
9328 {
9329 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9330 if (FAILED(rc))
9331 return setError(E_FAIL,
9332 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9333 medium->i_getLocationFull().c_str(),
9334 dev.strBwGroup.c_str(),
9335 mUserData->s.strName.c_str(),
9336 mData->m_strConfigFileFull.c_str());
9337 pBwGroup->i_reference();
9338 }
9339
9340 const Utf8Str controllerName = aStorageController->i_getName();
9341 ComObjPtr<MediumAttachment> pAttachment;
9342 pAttachment.createObject();
9343 rc = pAttachment->init(this,
9344 medium,
9345 controllerName,
9346 dev.lPort,
9347 dev.lDevice,
9348 dev.deviceType,
9349 false,
9350 dev.fPassThrough,
9351 dev.fTempEject,
9352 dev.fNonRotational,
9353 dev.fDiscard,
9354 dev.fHotPluggable,
9355 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9356 if (FAILED(rc)) break;
9357
9358 /* associate the medium with this machine and snapshot */
9359 if (!medium.isNull())
9360 {
9361 AutoCaller medCaller(medium);
9362 if (FAILED(medCaller.rc())) return medCaller.rc();
9363 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9364
9365 if (i_isSnapshotMachine())
9366 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9367 else
9368 rc = medium->i_addBackReference(mData->mUuid);
9369 /* If the medium->addBackReference fails it sets an appropriate
9370 * error message, so no need to do any guesswork here. */
9371
9372 if (puuidRegistry)
9373 // caller wants registry ID to be set on all attached media (OVF import case)
9374 medium->i_addRegistry(*puuidRegistry);
9375 }
9376
9377 if (FAILED(rc))
9378 break;
9379
9380 /* back up mMediumAttachments to let registeredInit() properly rollback
9381 * on failure (= limited accessibility) */
9382 i_setModified(IsModified_Storage);
9383 mMediumAttachments.backup();
9384 mMediumAttachments->push_back(pAttachment);
9385 }
9386
9387 return rc;
9388}
9389
9390/**
9391 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9392 *
9393 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9394 * @param aSnapshot where to return the found snapshot
9395 * @param aSetError true to set extended error info on failure
9396 */
9397HRESULT Machine::i_findSnapshotById(const Guid &aId,
9398 ComObjPtr<Snapshot> &aSnapshot,
9399 bool aSetError /* = false */)
9400{
9401 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9402
9403 if (!mData->mFirstSnapshot)
9404 {
9405 if (aSetError)
9406 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9407 return E_FAIL;
9408 }
9409
9410 if (aId.isZero())
9411 aSnapshot = mData->mFirstSnapshot;
9412 else
9413 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9414
9415 if (!aSnapshot)
9416 {
9417 if (aSetError)
9418 return setError(E_FAIL,
9419 tr("Could not find a snapshot with UUID {%s}"),
9420 aId.toString().c_str());
9421 return E_FAIL;
9422 }
9423
9424 return S_OK;
9425}
9426
9427/**
9428 * Returns the snapshot with the given name or fails of no such snapshot.
9429 *
9430 * @param strName snapshot name to find
9431 * @param aSnapshot where to return the found snapshot
9432 * @param aSetError true to set extended error info on failure
9433 */
9434HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9435 ComObjPtr<Snapshot> &aSnapshot,
9436 bool aSetError /* = false */)
9437{
9438 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9439
9440 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9441
9442 if (!mData->mFirstSnapshot)
9443 {
9444 if (aSetError)
9445 return setError(VBOX_E_OBJECT_NOT_FOUND,
9446 tr("This machine does not have any snapshots"));
9447 return VBOX_E_OBJECT_NOT_FOUND;
9448 }
9449
9450 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9451
9452 if (!aSnapshot)
9453 {
9454 if (aSetError)
9455 return setError(VBOX_E_OBJECT_NOT_FOUND,
9456 tr("Could not find a snapshot named '%s'"), strName.c_str());
9457 return VBOX_E_OBJECT_NOT_FOUND;
9458 }
9459
9460 return S_OK;
9461}
9462
9463/**
9464 * Returns a storage controller object with the given name.
9465 *
9466 * @param aName storage controller name to find
9467 * @param aStorageController where to return the found storage controller
9468 * @param aSetError true to set extended error info on failure
9469 */
9470HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9471 ComObjPtr<StorageController> &aStorageController,
9472 bool aSetError /* = false */)
9473{
9474 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9475
9476 for (StorageControllerList::const_iterator
9477 it = mStorageControllers->begin();
9478 it != mStorageControllers->end();
9479 ++it)
9480 {
9481 if ((*it)->i_getName() == aName)
9482 {
9483 aStorageController = (*it);
9484 return S_OK;
9485 }
9486 }
9487
9488 if (aSetError)
9489 return setError(VBOX_E_OBJECT_NOT_FOUND,
9490 tr("Could not find a storage controller named '%s'"),
9491 aName.c_str());
9492 return VBOX_E_OBJECT_NOT_FOUND;
9493}
9494
9495/**
9496 * Returns a USB controller object with the given name.
9497 *
9498 * @param aName USB controller name to find
9499 * @param aUSBController where to return the found USB controller
9500 * @param aSetError true to set extended error info on failure
9501 */
9502HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9503 ComObjPtr<USBController> &aUSBController,
9504 bool aSetError /* = false */)
9505{
9506 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9507
9508 for (USBControllerList::const_iterator
9509 it = mUSBControllers->begin();
9510 it != mUSBControllers->end();
9511 ++it)
9512 {
9513 if ((*it)->i_getName() == aName)
9514 {
9515 aUSBController = (*it);
9516 return S_OK;
9517 }
9518 }
9519
9520 if (aSetError)
9521 return setError(VBOX_E_OBJECT_NOT_FOUND,
9522 tr("Could not find a storage controller named '%s'"),
9523 aName.c_str());
9524 return VBOX_E_OBJECT_NOT_FOUND;
9525}
9526
9527/**
9528 * Returns the number of USB controller instance of the given type.
9529 *
9530 * @param enmType USB controller type.
9531 */
9532ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9533{
9534 ULONG cCtrls = 0;
9535
9536 for (USBControllerList::const_iterator
9537 it = mUSBControllers->begin();
9538 it != mUSBControllers->end();
9539 ++it)
9540 {
9541 if ((*it)->i_getControllerType() == enmType)
9542 cCtrls++;
9543 }
9544
9545 return cCtrls;
9546}
9547
9548HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9549 MediumAttachmentList &atts)
9550{
9551 AutoCaller autoCaller(this);
9552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9553
9554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9555
9556 for (MediumAttachmentList::const_iterator
9557 it = mMediumAttachments->begin();
9558 it != mMediumAttachments->end();
9559 ++it)
9560 {
9561 const ComObjPtr<MediumAttachment> &pAtt = *it;
9562 // should never happen, but deal with NULL pointers in the list.
9563 AssertContinue(!pAtt.isNull());
9564
9565 // getControllerName() needs caller+read lock
9566 AutoCaller autoAttCaller(pAtt);
9567 if (FAILED(autoAttCaller.rc()))
9568 {
9569 atts.clear();
9570 return autoAttCaller.rc();
9571 }
9572 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9573
9574 if (pAtt->i_getControllerName() == aName)
9575 atts.push_back(pAtt);
9576 }
9577
9578 return S_OK;
9579}
9580
9581
9582/**
9583 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9584 * file if the machine name was changed and about creating a new settings file
9585 * if this is a new machine.
9586 *
9587 * @note Must be never called directly but only from #saveSettings().
9588 */
9589HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9590{
9591 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9592
9593 HRESULT rc = S_OK;
9594
9595 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9596
9597 /// @todo need to handle primary group change, too
9598
9599 /* attempt to rename the settings file if machine name is changed */
9600 if ( mUserData->s.fNameSync
9601 && mUserData.isBackedUp()
9602 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9603 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9604 )
9605 {
9606 bool dirRenamed = false;
9607 bool fileRenamed = false;
9608
9609 Utf8Str configFile, newConfigFile;
9610 Utf8Str configFilePrev, newConfigFilePrev;
9611 Utf8Str configDir, newConfigDir;
9612
9613 do
9614 {
9615 int vrc = VINF_SUCCESS;
9616
9617 Utf8Str name = mUserData.backedUpData()->s.strName;
9618 Utf8Str newName = mUserData->s.strName;
9619 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9620 if (group == "/")
9621 group.setNull();
9622 Utf8Str newGroup = mUserData->s.llGroups.front();
9623 if (newGroup == "/")
9624 newGroup.setNull();
9625
9626 configFile = mData->m_strConfigFileFull;
9627
9628 /* first, rename the directory if it matches the group and machine name */
9629 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9630 group.c_str(), RTPATH_DELIMITER, name.c_str());
9631 /** @todo hack, make somehow use of ComposeMachineFilename */
9632 if (mUserData->s.fDirectoryIncludesUUID)
9633 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9634 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9635 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9636 /** @todo hack, make somehow use of ComposeMachineFilename */
9637 if (mUserData->s.fDirectoryIncludesUUID)
9638 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9639 configDir = configFile;
9640 configDir.stripFilename();
9641 newConfigDir = configDir;
9642 if ( configDir.length() >= groupPlusName.length()
9643 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9644 groupPlusName.c_str()))
9645 {
9646 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9647 Utf8Str newConfigBaseDir(newConfigDir);
9648 newConfigDir.append(newGroupPlusName);
9649 /* consistency: use \ if appropriate on the platform */
9650 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9651 /* new dir and old dir cannot be equal here because of 'if'
9652 * above and because name != newName */
9653 Assert(configDir != newConfigDir);
9654 if (!fSettingsFileIsNew)
9655 {
9656 /* perform real rename only if the machine is not new */
9657 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9658 if ( vrc == VERR_FILE_NOT_FOUND
9659 || vrc == VERR_PATH_NOT_FOUND)
9660 {
9661 /* create the parent directory, then retry renaming */
9662 Utf8Str parent(newConfigDir);
9663 parent.stripFilename();
9664 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9665 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9666 }
9667 if (RT_FAILURE(vrc))
9668 {
9669 rc = setErrorBoth(E_FAIL, vrc,
9670 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9671 configDir.c_str(),
9672 newConfigDir.c_str(),
9673 vrc);
9674 break;
9675 }
9676 /* delete subdirectories which are no longer needed */
9677 Utf8Str dir(configDir);
9678 dir.stripFilename();
9679 while (dir != newConfigBaseDir && dir != ".")
9680 {
9681 vrc = RTDirRemove(dir.c_str());
9682 if (RT_FAILURE(vrc))
9683 break;
9684 dir.stripFilename();
9685 }
9686 dirRenamed = true;
9687 }
9688 }
9689
9690 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9691 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9692
9693 /* then try to rename the settings file itself */
9694 if (newConfigFile != configFile)
9695 {
9696 /* get the path to old settings file in renamed directory */
9697 configFile = Utf8StrFmt("%s%c%s",
9698 newConfigDir.c_str(),
9699 RTPATH_DELIMITER,
9700 RTPathFilename(configFile.c_str()));
9701 if (!fSettingsFileIsNew)
9702 {
9703 /* perform real rename only if the machine is not new */
9704 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9705 if (RT_FAILURE(vrc))
9706 {
9707 rc = setErrorBoth(E_FAIL, vrc,
9708 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9709 configFile.c_str(),
9710 newConfigFile.c_str(),
9711 vrc);
9712 break;
9713 }
9714 fileRenamed = true;
9715 configFilePrev = configFile;
9716 configFilePrev += "-prev";
9717 newConfigFilePrev = newConfigFile;
9718 newConfigFilePrev += "-prev";
9719 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9720 }
9721 }
9722
9723 // update m_strConfigFileFull amd mConfigFile
9724 mData->m_strConfigFileFull = newConfigFile;
9725 // compute the relative path too
9726 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9727
9728 // store the old and new so that VirtualBox::i_saveSettings() can update
9729 // the media registry
9730 if ( mData->mRegistered
9731 && (configDir != newConfigDir || configFile != newConfigFile))
9732 {
9733 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9734
9735 if (pfNeedsGlobalSaveSettings)
9736 *pfNeedsGlobalSaveSettings = true;
9737 }
9738
9739 // in the saved state file path, replace the old directory with the new directory
9740 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9741 {
9742 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9743 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9744 }
9745
9746 // and do the same thing for the saved state file paths of all the online snapshots
9747 if (mData->mFirstSnapshot)
9748 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9749 newConfigDir.c_str());
9750 }
9751 while (0);
9752
9753 if (FAILED(rc))
9754 {
9755 /* silently try to rename everything back */
9756 if (fileRenamed)
9757 {
9758 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9759 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9760 }
9761 if (dirRenamed)
9762 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9763 }
9764
9765 if (FAILED(rc)) return rc;
9766 }
9767
9768 if (fSettingsFileIsNew)
9769 {
9770 /* create a virgin config file */
9771 int vrc = VINF_SUCCESS;
9772
9773 /* ensure the settings directory exists */
9774 Utf8Str path(mData->m_strConfigFileFull);
9775 path.stripFilename();
9776 if (!RTDirExists(path.c_str()))
9777 {
9778 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9779 if (RT_FAILURE(vrc))
9780 {
9781 return setErrorBoth(E_FAIL, vrc,
9782 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9783 path.c_str(),
9784 vrc);
9785 }
9786 }
9787
9788 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9789 path = Utf8Str(mData->m_strConfigFileFull);
9790 RTFILE f = NIL_RTFILE;
9791 vrc = RTFileOpen(&f, path.c_str(),
9792 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9793 if (RT_FAILURE(vrc))
9794 return setErrorBoth(E_FAIL, vrc,
9795 tr("Could not create the settings file '%s' (%Rrc)"),
9796 path.c_str(),
9797 vrc);
9798 RTFileClose(f);
9799 }
9800
9801 return rc;
9802}
9803
9804/**
9805 * Saves and commits machine data, user data and hardware data.
9806 *
9807 * Note that on failure, the data remains uncommitted.
9808 *
9809 * @a aFlags may combine the following flags:
9810 *
9811 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9812 * Used when saving settings after an operation that makes them 100%
9813 * correspond to the settings from the current snapshot.
9814 * - SaveS_Force: settings will be saved without doing a deep compare of the
9815 * settings structures. This is used when this is called because snapshots
9816 * have changed to avoid the overhead of the deep compare.
9817 *
9818 * @note Must be called from under this object's write lock. Locks children for
9819 * writing.
9820 *
9821 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9822 * initialized to false and that will be set to true by this function if
9823 * the caller must invoke VirtualBox::i_saveSettings() because the global
9824 * settings have changed. This will happen if a machine rename has been
9825 * saved and the global machine and media registries will therefore need
9826 * updating.
9827 * @param aFlags Flags.
9828 */
9829HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9830 int aFlags /*= 0*/)
9831{
9832 LogFlowThisFuncEnter();
9833
9834 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9835
9836 /* make sure child objects are unable to modify the settings while we are
9837 * saving them */
9838 i_ensureNoStateDependencies();
9839
9840 AssertReturn(!i_isSnapshotMachine(),
9841 E_FAIL);
9842
9843 if (!mData->mAccessible)
9844 return setError(VBOX_E_INVALID_VM_STATE,
9845 tr("The machine is not accessible, so cannot save settings"));
9846
9847 HRESULT rc = S_OK;
9848 bool fNeedsWrite = false;
9849
9850 /* First, prepare to save settings. It will care about renaming the
9851 * settings directory and file if the machine name was changed and about
9852 * creating a new settings file if this is a new machine. */
9853 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9854 if (FAILED(rc)) return rc;
9855
9856 // keep a pointer to the current settings structures
9857 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9858 settings::MachineConfigFile *pNewConfig = NULL;
9859
9860 try
9861 {
9862 // make a fresh one to have everyone write stuff into
9863 pNewConfig = new settings::MachineConfigFile(NULL);
9864 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9865
9866 // now go and copy all the settings data from COM to the settings structures
9867 // (this calls i_saveSettings() on all the COM objects in the machine)
9868 i_copyMachineDataToSettings(*pNewConfig);
9869
9870 if (aFlags & SaveS_ResetCurStateModified)
9871 {
9872 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9873 mData->mCurrentStateModified = FALSE;
9874 fNeedsWrite = true; // always, no need to compare
9875 }
9876 else if (aFlags & SaveS_Force)
9877 {
9878 fNeedsWrite = true; // always, no need to compare
9879 }
9880 else
9881 {
9882 if (!mData->mCurrentStateModified)
9883 {
9884 // do a deep compare of the settings that we just saved with the settings
9885 // previously stored in the config file; this invokes MachineConfigFile::operator==
9886 // which does a deep compare of all the settings, which is expensive but less expensive
9887 // than writing out XML in vain
9888 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9889
9890 // could still be modified if any settings changed
9891 mData->mCurrentStateModified = fAnySettingsChanged;
9892
9893 fNeedsWrite = fAnySettingsChanged;
9894 }
9895 else
9896 fNeedsWrite = true;
9897 }
9898
9899 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9900
9901 if (fNeedsWrite)
9902 // now spit it all out!
9903 pNewConfig->write(mData->m_strConfigFileFull);
9904
9905 mData->pMachineConfigFile = pNewConfig;
9906 delete pOldConfig;
9907 i_commit();
9908
9909 // after saving settings, we are no longer different from the XML on disk
9910 mData->flModifications = 0;
9911 }
9912 catch (HRESULT err)
9913 {
9914 // we assume that error info is set by the thrower
9915 rc = err;
9916
9917 // restore old config
9918 delete pNewConfig;
9919 mData->pMachineConfigFile = pOldConfig;
9920 }
9921 catch (...)
9922 {
9923 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9924 }
9925
9926 if (fNeedsWrite)
9927 {
9928 /* Fire the data change event, even on failure (since we've already
9929 * committed all data). This is done only for SessionMachines because
9930 * mutable Machine instances are always not registered (i.e. private
9931 * to the client process that creates them) and thus don't need to
9932 * inform callbacks. */
9933 if (i_isSessionMachine())
9934 mParent->i_onMachineDataChange(mData->mUuid);
9935 }
9936
9937 LogFlowThisFunc(("rc=%08X\n", rc));
9938 LogFlowThisFuncLeave();
9939 return rc;
9940}
9941
9942/**
9943 * Implementation for saving the machine settings into the given
9944 * settings::MachineConfigFile instance. This copies machine extradata
9945 * from the previous machine config file in the instance data, if any.
9946 *
9947 * This gets called from two locations:
9948 *
9949 * -- Machine::i_saveSettings(), during the regular XML writing;
9950 *
9951 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9952 * exported to OVF and we write the VirtualBox proprietary XML
9953 * into a <vbox:Machine> tag.
9954 *
9955 * This routine fills all the fields in there, including snapshots, *except*
9956 * for the following:
9957 *
9958 * -- fCurrentStateModified. There is some special logic associated with that.
9959 *
9960 * The caller can then call MachineConfigFile::write() or do something else
9961 * with it.
9962 *
9963 * Caller must hold the machine lock!
9964 *
9965 * This throws XML errors and HRESULT, so the caller must have a catch block!
9966 */
9967void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9968{
9969 // deep copy extradata, being extra careful with self assignment (the STL
9970 // map assignment on Mac OS X clang based Xcode isn't checking)
9971 if (&config != mData->pMachineConfigFile)
9972 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9973
9974 config.uuid = mData->mUuid;
9975
9976 // copy name, description, OS type, teleport, UTC etc.
9977 config.machineUserData = mUserData->s;
9978
9979 if ( mData->mMachineState == MachineState_Saved
9980 || mData->mMachineState == MachineState_Restoring
9981 // when doing certain snapshot operations we may or may not have
9982 // a saved state in the current state, so keep everything as is
9983 || ( ( mData->mMachineState == MachineState_Snapshotting
9984 || mData->mMachineState == MachineState_DeletingSnapshot
9985 || mData->mMachineState == MachineState_RestoringSnapshot)
9986 && (!mSSData->strStateFilePath.isEmpty())
9987 )
9988 )
9989 {
9990 Assert(!mSSData->strStateFilePath.isEmpty());
9991 /* try to make the file name relative to the settings file dir */
9992 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9993 }
9994 else
9995 {
9996 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9997 config.strStateFile.setNull();
9998 }
9999
10000 if (mData->mCurrentSnapshot)
10001 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10002 else
10003 config.uuidCurrentSnapshot.clear();
10004
10005 config.timeLastStateChange = mData->mLastStateChange;
10006 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10007 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10008
10009 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10010 if (FAILED(rc)) throw rc;
10011
10012 // save machine's media registry if this is VirtualBox 4.0 or later
10013 if (config.canHaveOwnMediaRegistry())
10014 {
10015 // determine machine folder
10016 Utf8Str strMachineFolder = i_getSettingsFileFull();
10017 strMachineFolder.stripFilename();
10018 mParent->i_saveMediaRegistry(config.mediaRegistry,
10019 i_getId(), // only media with registry ID == machine UUID
10020 strMachineFolder);
10021 // this throws HRESULT
10022 }
10023
10024 // save snapshots
10025 rc = i_saveAllSnapshots(config);
10026 if (FAILED(rc)) throw rc;
10027}
10028
10029/**
10030 * Saves all snapshots of the machine into the given machine config file. Called
10031 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10032 * @param config
10033 * @return
10034 */
10035HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10036{
10037 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10038
10039 HRESULT rc = S_OK;
10040
10041 try
10042 {
10043 config.llFirstSnapshot.clear();
10044
10045 if (mData->mFirstSnapshot)
10046 {
10047 // the settings use a list for "the first snapshot"
10048 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10049
10050 // get reference to the snapshot on the list and work on that
10051 // element straight in the list to avoid excessive copying later
10052 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10053 if (FAILED(rc)) throw rc;
10054 }
10055
10056// if (mType == IsSessionMachine)
10057// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10058
10059 }
10060 catch (HRESULT err)
10061 {
10062 /* we assume that error info is set by the thrower */
10063 rc = err;
10064 }
10065 catch (...)
10066 {
10067 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10068 }
10069
10070 return rc;
10071}
10072
10073/**
10074 * Saves the VM hardware configuration. It is assumed that the
10075 * given node is empty.
10076 *
10077 * @param data Reference to the settings object for the hardware config.
10078 * @param pDbg Pointer to the settings object for the debugging config
10079 * which happens to live in mHWData.
10080 * @param pAutostart Pointer to the settings object for the autostart config
10081 * which happens to live in mHWData.
10082 */
10083HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10084 settings::Autostart *pAutostart)
10085{
10086 HRESULT rc = S_OK;
10087
10088 try
10089 {
10090 /* The hardware version attribute (optional).
10091 Automatically upgrade from 1 to current default hardware version
10092 when there is no saved state. (ugly!) */
10093 if ( mHWData->mHWVersion == "1"
10094 && mSSData->strStateFilePath.isEmpty()
10095 )
10096 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10097
10098 data.strVersion = mHWData->mHWVersion;
10099 data.uuid = mHWData->mHardwareUUID;
10100
10101 // CPU
10102 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10103 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10104 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10105 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10106 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10107 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10108 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10109 data.fPAE = !!mHWData->mPAEEnabled;
10110 data.enmLongMode = mHWData->mLongMode;
10111 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10112 data.fAPIC = !!mHWData->mAPIC;
10113 data.fX2APIC = !!mHWData->mX2APIC;
10114 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10115 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10116 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10117 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10118 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10119 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10120 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10121 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10122 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10123 data.cCPUs = mHWData->mCPUCount;
10124 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10125 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10126 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10127 data.strCpuProfile = mHWData->mCpuProfile;
10128
10129 data.llCpus.clear();
10130 if (data.fCpuHotPlug)
10131 {
10132 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10133 {
10134 if (mHWData->mCPUAttached[idx])
10135 {
10136 settings::Cpu cpu;
10137 cpu.ulId = idx;
10138 data.llCpus.push_back(cpu);
10139 }
10140 }
10141 }
10142
10143 /* Standard and Extended CPUID leafs. */
10144 data.llCpuIdLeafs.clear();
10145 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10146
10147 // memory
10148 data.ulMemorySizeMB = mHWData->mMemorySize;
10149 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10150
10151 // firmware
10152 data.firmwareType = mHWData->mFirmwareType;
10153
10154 // HID
10155 data.pointingHIDType = mHWData->mPointingHIDType;
10156 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10157
10158 // chipset
10159 data.chipsetType = mHWData->mChipsetType;
10160
10161 // paravirt
10162 data.paravirtProvider = mHWData->mParavirtProvider;
10163 data.strParavirtDebug = mHWData->mParavirtDebug;
10164
10165 // emulated USB card reader
10166 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10167
10168 // HPET
10169 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10170
10171 // boot order
10172 data.mapBootOrder.clear();
10173 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10174 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10175
10176 // display
10177 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10178 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10179 data.cMonitors = mHWData->mMonitorCount;
10180 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10181 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10182
10183 /* VRDEServer settings (optional) */
10184 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10185 if (FAILED(rc)) throw rc;
10186
10187 /* BIOS settings (required) */
10188 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10189 if (FAILED(rc)) throw rc;
10190
10191 /* Recording settings (required) */
10192 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10193 if (FAILED(rc)) throw rc;
10194
10195 /* USB Controller (required) */
10196 data.usbSettings.llUSBControllers.clear();
10197 for (USBControllerList::const_iterator
10198 it = mUSBControllers->begin();
10199 it != mUSBControllers->end();
10200 ++it)
10201 {
10202 ComObjPtr<USBController> ctrl = *it;
10203 settings::USBController settingsCtrl;
10204
10205 settingsCtrl.strName = ctrl->i_getName();
10206 settingsCtrl.enmType = ctrl->i_getControllerType();
10207
10208 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10209 }
10210
10211 /* USB device filters (required) */
10212 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10213 if (FAILED(rc)) throw rc;
10214
10215 /* Network adapters (required) */
10216 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10217 data.llNetworkAdapters.clear();
10218 /* Write out only the nominal number of network adapters for this
10219 * chipset type. Since Machine::commit() hasn't been called there
10220 * may be extra NIC settings in the vector. */
10221 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10222 {
10223 settings::NetworkAdapter nic;
10224 nic.ulSlot = (uint32_t)slot;
10225 /* paranoia check... must not be NULL, but must not crash either. */
10226 if (mNetworkAdapters[slot])
10227 {
10228 if (mNetworkAdapters[slot]->i_hasDefaults())
10229 continue;
10230
10231 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10232 if (FAILED(rc)) throw rc;
10233
10234 data.llNetworkAdapters.push_back(nic);
10235 }
10236 }
10237
10238 /* Serial ports */
10239 data.llSerialPorts.clear();
10240 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10241 {
10242 if (mSerialPorts[slot]->i_hasDefaults())
10243 continue;
10244
10245 settings::SerialPort s;
10246 s.ulSlot = slot;
10247 rc = mSerialPorts[slot]->i_saveSettings(s);
10248 if (FAILED(rc)) return rc;
10249
10250 data.llSerialPorts.push_back(s);
10251 }
10252
10253 /* Parallel ports */
10254 data.llParallelPorts.clear();
10255 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10256 {
10257 if (mParallelPorts[slot]->i_hasDefaults())
10258 continue;
10259
10260 settings::ParallelPort p;
10261 p.ulSlot = slot;
10262 rc = mParallelPorts[slot]->i_saveSettings(p);
10263 if (FAILED(rc)) return rc;
10264
10265 data.llParallelPorts.push_back(p);
10266 }
10267
10268 /* Audio adapter */
10269 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10270 if (FAILED(rc)) return rc;
10271
10272 rc = i_saveStorageControllers(data.storage);
10273 if (FAILED(rc)) return rc;
10274
10275 /* Shared folders */
10276 data.llSharedFolders.clear();
10277 for (HWData::SharedFolderList::const_iterator
10278 it = mHWData->mSharedFolders.begin();
10279 it != mHWData->mSharedFolders.end();
10280 ++it)
10281 {
10282 SharedFolder *pSF = *it;
10283 AutoCaller sfCaller(pSF);
10284 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10285 settings::SharedFolder sf;
10286 sf.strName = pSF->i_getName();
10287 sf.strHostPath = pSF->i_getHostPath();
10288 sf.fWritable = !!pSF->i_isWritable();
10289 sf.fAutoMount = !!pSF->i_isAutoMounted();
10290 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10291
10292 data.llSharedFolders.push_back(sf);
10293 }
10294
10295 // clipboard
10296 data.clipboardMode = mHWData->mClipboardMode;
10297
10298 // drag'n'drop
10299 data.dndMode = mHWData->mDnDMode;
10300
10301 /* Guest */
10302 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10303
10304 // IO settings
10305 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10306 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10307
10308 /* BandwidthControl (required) */
10309 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10310 if (FAILED(rc)) throw rc;
10311
10312 /* Host PCI devices */
10313 data.pciAttachments.clear();
10314 for (HWData::PCIDeviceAssignmentList::const_iterator
10315 it = mHWData->mPCIDeviceAssignments.begin();
10316 it != mHWData->mPCIDeviceAssignments.end();
10317 ++it)
10318 {
10319 ComObjPtr<PCIDeviceAttachment> pda = *it;
10320 settings::HostPCIDeviceAttachment hpda;
10321
10322 rc = pda->i_saveSettings(hpda);
10323 if (FAILED(rc)) throw rc;
10324
10325 data.pciAttachments.push_back(hpda);
10326 }
10327
10328 // guest properties
10329 data.llGuestProperties.clear();
10330#ifdef VBOX_WITH_GUEST_PROPS
10331 for (HWData::GuestPropertyMap::const_iterator
10332 it = mHWData->mGuestProperties.begin();
10333 it != mHWData->mGuestProperties.end();
10334 ++it)
10335 {
10336 HWData::GuestProperty property = it->second;
10337
10338 /* Remove transient guest properties at shutdown unless we
10339 * are saving state. Note that restoring snapshot intentionally
10340 * keeps them, they will be removed if appropriate once the final
10341 * machine state is set (as crashes etc. need to work). */
10342 if ( ( mData->mMachineState == MachineState_PoweredOff
10343 || mData->mMachineState == MachineState_Aborted
10344 || mData->mMachineState == MachineState_Teleported)
10345 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10346 continue;
10347 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10348 prop.strName = it->first;
10349 prop.strValue = property.strValue;
10350 prop.timestamp = property.mTimestamp;
10351 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10352 GuestPropWriteFlags(property.mFlags, szFlags);
10353 prop.strFlags = szFlags;
10354
10355 data.llGuestProperties.push_back(prop);
10356 }
10357
10358 /* I presume this doesn't require a backup(). */
10359 mData->mGuestPropertiesModified = FALSE;
10360#endif /* VBOX_WITH_GUEST_PROPS defined */
10361
10362 *pDbg = mHWData->mDebugging;
10363 *pAutostart = mHWData->mAutostart;
10364
10365 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10366 }
10367 catch (std::bad_alloc &)
10368 {
10369 return E_OUTOFMEMORY;
10370 }
10371
10372 AssertComRC(rc);
10373 return rc;
10374}
10375
10376/**
10377 * Saves the storage controller configuration.
10378 *
10379 * @param data storage settings.
10380 */
10381HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10382{
10383 data.llStorageControllers.clear();
10384
10385 for (StorageControllerList::const_iterator
10386 it = mStorageControllers->begin();
10387 it != mStorageControllers->end();
10388 ++it)
10389 {
10390 HRESULT rc;
10391 ComObjPtr<StorageController> pCtl = *it;
10392
10393 settings::StorageController ctl;
10394 ctl.strName = pCtl->i_getName();
10395 ctl.controllerType = pCtl->i_getControllerType();
10396 ctl.storageBus = pCtl->i_getStorageBus();
10397 ctl.ulInstance = pCtl->i_getInstance();
10398 ctl.fBootable = pCtl->i_getBootable();
10399
10400 /* Save the port count. */
10401 ULONG portCount;
10402 rc = pCtl->COMGETTER(PortCount)(&portCount);
10403 ComAssertComRCRet(rc, rc);
10404 ctl.ulPortCount = portCount;
10405
10406 /* Save fUseHostIOCache */
10407 BOOL fUseHostIOCache;
10408 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10409 ComAssertComRCRet(rc, rc);
10410 ctl.fUseHostIOCache = !!fUseHostIOCache;
10411
10412 /* save the devices now. */
10413 rc = i_saveStorageDevices(pCtl, ctl);
10414 ComAssertComRCRet(rc, rc);
10415
10416 data.llStorageControllers.push_back(ctl);
10417 }
10418
10419 return S_OK;
10420}
10421
10422/**
10423 * Saves the hard disk configuration.
10424 */
10425HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10426 settings::StorageController &data)
10427{
10428 MediumAttachmentList atts;
10429
10430 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10431 if (FAILED(rc)) return rc;
10432
10433 data.llAttachedDevices.clear();
10434 for (MediumAttachmentList::const_iterator
10435 it = atts.begin();
10436 it != atts.end();
10437 ++it)
10438 {
10439 settings::AttachedDevice dev;
10440 IMediumAttachment *iA = *it;
10441 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10442 Medium *pMedium = pAttach->i_getMedium();
10443
10444 dev.deviceType = pAttach->i_getType();
10445 dev.lPort = pAttach->i_getPort();
10446 dev.lDevice = pAttach->i_getDevice();
10447 dev.fPassThrough = pAttach->i_getPassthrough();
10448 dev.fHotPluggable = pAttach->i_getHotPluggable();
10449 if (pMedium)
10450 {
10451 if (pMedium->i_isHostDrive())
10452 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10453 else
10454 dev.uuid = pMedium->i_getId();
10455 dev.fTempEject = pAttach->i_getTempEject();
10456 dev.fNonRotational = pAttach->i_getNonRotational();
10457 dev.fDiscard = pAttach->i_getDiscard();
10458 }
10459
10460 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10461
10462 data.llAttachedDevices.push_back(dev);
10463 }
10464
10465 return S_OK;
10466}
10467
10468/**
10469 * Saves machine state settings as defined by aFlags
10470 * (SaveSTS_* values).
10471 *
10472 * @param aFlags Combination of SaveSTS_* flags.
10473 *
10474 * @note Locks objects for writing.
10475 */
10476HRESULT Machine::i_saveStateSettings(int aFlags)
10477{
10478 if (aFlags == 0)
10479 return S_OK;
10480
10481 AutoCaller autoCaller(this);
10482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10483
10484 /* This object's write lock is also necessary to serialize file access
10485 * (prevent concurrent reads and writes) */
10486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10487
10488 HRESULT rc = S_OK;
10489
10490 Assert(mData->pMachineConfigFile);
10491
10492 try
10493 {
10494 if (aFlags & SaveSTS_CurStateModified)
10495 mData->pMachineConfigFile->fCurrentStateModified = true;
10496
10497 if (aFlags & SaveSTS_StateFilePath)
10498 {
10499 if (!mSSData->strStateFilePath.isEmpty())
10500 /* try to make the file name relative to the settings file dir */
10501 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10502 else
10503 mData->pMachineConfigFile->strStateFile.setNull();
10504 }
10505
10506 if (aFlags & SaveSTS_StateTimeStamp)
10507 {
10508 Assert( mData->mMachineState != MachineState_Aborted
10509 || mSSData->strStateFilePath.isEmpty());
10510
10511 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10512
10513 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10514/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10515 }
10516
10517 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10518 }
10519 catch (...)
10520 {
10521 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10522 }
10523
10524 return rc;
10525}
10526
10527/**
10528 * Ensures that the given medium is added to a media registry. If this machine
10529 * was created with 4.0 or later, then the machine registry is used. Otherwise
10530 * the global VirtualBox media registry is used.
10531 *
10532 * Caller must NOT hold machine lock, media tree or any medium locks!
10533 *
10534 * @param pMedium
10535 */
10536void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10537{
10538 /* Paranoia checks: do not hold machine or media tree locks. */
10539 AssertReturnVoid(!isWriteLockOnCurrentThread());
10540 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10541
10542 ComObjPtr<Medium> pBase;
10543 {
10544 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10545 pBase = pMedium->i_getBase();
10546 }
10547
10548 /* Paranoia checks: do not hold medium locks. */
10549 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10550 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10551
10552 // decide which medium registry to use now that the medium is attached:
10553 Guid uuid;
10554 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10555 if (fCanHaveOwnMediaRegistry)
10556 // machine XML is VirtualBox 4.0 or higher:
10557 uuid = i_getId(); // machine UUID
10558 else
10559 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10560
10561 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10562 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10563 if (pMedium->i_addRegistry(uuid))
10564 mParent->i_markRegistryModified(uuid);
10565
10566 /* For more complex hard disk structures it can happen that the base
10567 * medium isn't yet associated with any medium registry. Do that now. */
10568 if (pMedium != pBase)
10569 {
10570 /* Tree lock needed by Medium::addRegistry when recursing. */
10571 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10572 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10573 {
10574 treeLock.release();
10575 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10576 treeLock.acquire();
10577 }
10578 if (pBase->i_addRegistryRecursive(uuid))
10579 {
10580 treeLock.release();
10581 mParent->i_markRegistryModified(uuid);
10582 }
10583 }
10584}
10585
10586/**
10587 * Creates differencing hard disks for all normal hard disks attached to this
10588 * machine and a new set of attachments to refer to created disks.
10589 *
10590 * Used when taking a snapshot or when deleting the current state. Gets called
10591 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10592 *
10593 * This method assumes that mMediumAttachments contains the original hard disk
10594 * attachments it needs to create diffs for. On success, these attachments will
10595 * be replaced with the created diffs.
10596 *
10597 * Attachments with non-normal hard disks are left as is.
10598 *
10599 * If @a aOnline is @c false then the original hard disks that require implicit
10600 * diffs will be locked for reading. Otherwise it is assumed that they are
10601 * already locked for writing (when the VM was started). Note that in the latter
10602 * case it is responsibility of the caller to lock the newly created diffs for
10603 * writing if this method succeeds.
10604 *
10605 * @param aProgress Progress object to run (must contain at least as
10606 * many operations left as the number of hard disks
10607 * attached).
10608 * @param aWeight Weight of this operation.
10609 * @param aOnline Whether the VM was online prior to this operation.
10610 *
10611 * @note The progress object is not marked as completed, neither on success nor
10612 * on failure. This is a responsibility of the caller.
10613 *
10614 * @note Locks this object and the media tree for writing.
10615 */
10616HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10617 ULONG aWeight,
10618 bool aOnline)
10619{
10620 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10621
10622 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10623 AssertReturn(!!pProgressControl, E_INVALIDARG);
10624
10625 AutoCaller autoCaller(this);
10626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10627
10628 AutoMultiWriteLock2 alock(this->lockHandle(),
10629 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10630
10631 /* must be in a protective state because we release the lock below */
10632 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10633 || mData->mMachineState == MachineState_OnlineSnapshotting
10634 || mData->mMachineState == MachineState_LiveSnapshotting
10635 || mData->mMachineState == MachineState_RestoringSnapshot
10636 || mData->mMachineState == MachineState_DeletingSnapshot
10637 , E_FAIL);
10638
10639 HRESULT rc = S_OK;
10640
10641 // use appropriate locked media map (online or offline)
10642 MediumLockListMap lockedMediaOffline;
10643 MediumLockListMap *lockedMediaMap;
10644 if (aOnline)
10645 lockedMediaMap = &mData->mSession.mLockedMedia;
10646 else
10647 lockedMediaMap = &lockedMediaOffline;
10648
10649 try
10650 {
10651 if (!aOnline)
10652 {
10653 /* lock all attached hard disks early to detect "in use"
10654 * situations before creating actual diffs */
10655 for (MediumAttachmentList::const_iterator
10656 it = mMediumAttachments->begin();
10657 it != mMediumAttachments->end();
10658 ++it)
10659 {
10660 MediumAttachment *pAtt = *it;
10661 if (pAtt->i_getType() == DeviceType_HardDisk)
10662 {
10663 Medium *pMedium = pAtt->i_getMedium();
10664 Assert(pMedium);
10665
10666 MediumLockList *pMediumLockList(new MediumLockList());
10667 alock.release();
10668 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10669 NULL /* pToLockWrite */,
10670 false /* fMediumLockWriteAll */,
10671 NULL,
10672 *pMediumLockList);
10673 alock.acquire();
10674 if (FAILED(rc))
10675 {
10676 delete pMediumLockList;
10677 throw rc;
10678 }
10679 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10680 if (FAILED(rc))
10681 {
10682 throw setError(rc,
10683 tr("Collecting locking information for all attached media failed"));
10684 }
10685 }
10686 }
10687
10688 /* Now lock all media. If this fails, nothing is locked. */
10689 alock.release();
10690 rc = lockedMediaMap->Lock();
10691 alock.acquire();
10692 if (FAILED(rc))
10693 {
10694 throw setError(rc,
10695 tr("Locking of attached media failed"));
10696 }
10697 }
10698
10699 /* remember the current list (note that we don't use backup() since
10700 * mMediumAttachments may be already backed up) */
10701 MediumAttachmentList atts = *mMediumAttachments.data();
10702
10703 /* start from scratch */
10704 mMediumAttachments->clear();
10705
10706 /* go through remembered attachments and create diffs for normal hard
10707 * disks and attach them */
10708 for (MediumAttachmentList::const_iterator
10709 it = atts.begin();
10710 it != atts.end();
10711 ++it)
10712 {
10713 MediumAttachment *pAtt = *it;
10714
10715 DeviceType_T devType = pAtt->i_getType();
10716 Medium *pMedium = pAtt->i_getMedium();
10717
10718 if ( devType != DeviceType_HardDisk
10719 || pMedium == NULL
10720 || pMedium->i_getType() != MediumType_Normal)
10721 {
10722 /* copy the attachment as is */
10723
10724 /** @todo the progress object created in SessionMachine::TakeSnaphot
10725 * only expects operations for hard disks. Later other
10726 * device types need to show up in the progress as well. */
10727 if (devType == DeviceType_HardDisk)
10728 {
10729 if (pMedium == NULL)
10730 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10731 aWeight); // weight
10732 else
10733 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10734 pMedium->i_getBase()->i_getName().c_str()).raw(),
10735 aWeight); // weight
10736 }
10737
10738 mMediumAttachments->push_back(pAtt);
10739 continue;
10740 }
10741
10742 /* need a diff */
10743 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10744 pMedium->i_getBase()->i_getName().c_str()).raw(),
10745 aWeight); // weight
10746
10747 Utf8Str strFullSnapshotFolder;
10748 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10749
10750 ComObjPtr<Medium> diff;
10751 diff.createObject();
10752 // store the diff in the same registry as the parent
10753 // (this cannot fail here because we can't create implicit diffs for
10754 // unregistered images)
10755 Guid uuidRegistryParent;
10756 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10757 Assert(fInRegistry); NOREF(fInRegistry);
10758 rc = diff->init(mParent,
10759 pMedium->i_getPreferredDiffFormat(),
10760 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10761 uuidRegistryParent,
10762 DeviceType_HardDisk);
10763 if (FAILED(rc)) throw rc;
10764
10765 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10766 * the push_back? Looks like we're going to release medium with the
10767 * wrong kind of lock (general issue with if we fail anywhere at all)
10768 * and an orphaned VDI in the snapshots folder. */
10769
10770 /* update the appropriate lock list */
10771 MediumLockList *pMediumLockList;
10772 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10773 AssertComRCThrowRC(rc);
10774 if (aOnline)
10775 {
10776 alock.release();
10777 /* The currently attached medium will be read-only, change
10778 * the lock type to read. */
10779 rc = pMediumLockList->Update(pMedium, false);
10780 alock.acquire();
10781 AssertComRCThrowRC(rc);
10782 }
10783
10784 /* release the locks before the potentially lengthy operation */
10785 alock.release();
10786 rc = pMedium->i_createDiffStorage(diff,
10787 pMedium->i_getPreferredDiffVariant(),
10788 pMediumLockList,
10789 NULL /* aProgress */,
10790 true /* aWait */,
10791 false /* aNotify */);
10792 alock.acquire();
10793 if (FAILED(rc)) throw rc;
10794
10795 /* actual lock list update is done in Machine::i_commitMedia */
10796
10797 rc = diff->i_addBackReference(mData->mUuid);
10798 AssertComRCThrowRC(rc);
10799
10800 /* add a new attachment */
10801 ComObjPtr<MediumAttachment> attachment;
10802 attachment.createObject();
10803 rc = attachment->init(this,
10804 diff,
10805 pAtt->i_getControllerName(),
10806 pAtt->i_getPort(),
10807 pAtt->i_getDevice(),
10808 DeviceType_HardDisk,
10809 true /* aImplicit */,
10810 false /* aPassthrough */,
10811 false /* aTempEject */,
10812 pAtt->i_getNonRotational(),
10813 pAtt->i_getDiscard(),
10814 pAtt->i_getHotPluggable(),
10815 pAtt->i_getBandwidthGroup());
10816 if (FAILED(rc)) throw rc;
10817
10818 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10819 AssertComRCThrowRC(rc);
10820 mMediumAttachments->push_back(attachment);
10821 }
10822 }
10823 catch (HRESULT aRC) { rc = aRC; }
10824
10825 /* unlock all hard disks we locked when there is no VM */
10826 if (!aOnline)
10827 {
10828 ErrorInfoKeeper eik;
10829
10830 HRESULT rc1 = lockedMediaMap->Clear();
10831 AssertComRC(rc1);
10832 }
10833
10834 return rc;
10835}
10836
10837/**
10838 * Deletes implicit differencing hard disks created either by
10839 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10840 * mMediumAttachments.
10841 *
10842 * Note that to delete hard disks created by #attachDevice() this method is
10843 * called from #i_rollbackMedia() when the changes are rolled back.
10844 *
10845 * @note Locks this object and the media tree for writing.
10846 */
10847HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10848{
10849 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10850
10851 AutoCaller autoCaller(this);
10852 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10853
10854 AutoMultiWriteLock2 alock(this->lockHandle(),
10855 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10856
10857 /* We absolutely must have backed up state. */
10858 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10859
10860 /* Check if there are any implicitly created diff images. */
10861 bool fImplicitDiffs = false;
10862 for (MediumAttachmentList::const_iterator
10863 it = mMediumAttachments->begin();
10864 it != mMediumAttachments->end();
10865 ++it)
10866 {
10867 const ComObjPtr<MediumAttachment> &pAtt = *it;
10868 if (pAtt->i_isImplicit())
10869 {
10870 fImplicitDiffs = true;
10871 break;
10872 }
10873 }
10874 /* If there is nothing to do, leave early. This saves lots of image locking
10875 * effort. It also avoids a MachineStateChanged event without real reason.
10876 * This is important e.g. when loading a VM config, because there should be
10877 * no events. Otherwise API clients can become thoroughly confused for
10878 * inaccessible VMs (the code for loading VM configs uses this method for
10879 * cleanup if the config makes no sense), as they take such events as an
10880 * indication that the VM is alive, and they would force the VM config to
10881 * be reread, leading to an endless loop. */
10882 if (!fImplicitDiffs)
10883 return S_OK;
10884
10885 HRESULT rc = S_OK;
10886 MachineState_T oldState = mData->mMachineState;
10887
10888 /* will release the lock before the potentially lengthy operation,
10889 * so protect with the special state (unless already protected) */
10890 if ( oldState != MachineState_Snapshotting
10891 && oldState != MachineState_OnlineSnapshotting
10892 && oldState != MachineState_LiveSnapshotting
10893 && oldState != MachineState_RestoringSnapshot
10894 && oldState != MachineState_DeletingSnapshot
10895 && oldState != MachineState_DeletingSnapshotOnline
10896 && oldState != MachineState_DeletingSnapshotPaused
10897 )
10898 i_setMachineState(MachineState_SettingUp);
10899
10900 // use appropriate locked media map (online or offline)
10901 MediumLockListMap lockedMediaOffline;
10902 MediumLockListMap *lockedMediaMap;
10903 if (aOnline)
10904 lockedMediaMap = &mData->mSession.mLockedMedia;
10905 else
10906 lockedMediaMap = &lockedMediaOffline;
10907
10908 try
10909 {
10910 if (!aOnline)
10911 {
10912 /* lock all attached hard disks early to detect "in use"
10913 * situations before deleting actual diffs */
10914 for (MediumAttachmentList::const_iterator
10915 it = mMediumAttachments->begin();
10916 it != mMediumAttachments->end();
10917 ++it)
10918 {
10919 MediumAttachment *pAtt = *it;
10920 if (pAtt->i_getType() == DeviceType_HardDisk)
10921 {
10922 Medium *pMedium = pAtt->i_getMedium();
10923 Assert(pMedium);
10924
10925 MediumLockList *pMediumLockList(new MediumLockList());
10926 alock.release();
10927 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10928 NULL /* pToLockWrite */,
10929 false /* fMediumLockWriteAll */,
10930 NULL,
10931 *pMediumLockList);
10932 alock.acquire();
10933
10934 if (FAILED(rc))
10935 {
10936 delete pMediumLockList;
10937 throw rc;
10938 }
10939
10940 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10941 if (FAILED(rc))
10942 throw rc;
10943 }
10944 }
10945
10946 if (FAILED(rc))
10947 throw rc;
10948 } // end of offline
10949
10950 /* Lock lists are now up to date and include implicitly created media */
10951
10952 /* Go through remembered attachments and delete all implicitly created
10953 * diffs and fix up the attachment information */
10954 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10955 MediumAttachmentList implicitAtts;
10956 for (MediumAttachmentList::const_iterator
10957 it = mMediumAttachments->begin();
10958 it != mMediumAttachments->end();
10959 ++it)
10960 {
10961 ComObjPtr<MediumAttachment> pAtt = *it;
10962 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10963 if (pMedium.isNull())
10964 continue;
10965
10966 // Implicit attachments go on the list for deletion and back references are removed.
10967 if (pAtt->i_isImplicit())
10968 {
10969 /* Deassociate and mark for deletion */
10970 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10971 rc = pMedium->i_removeBackReference(mData->mUuid);
10972 if (FAILED(rc))
10973 throw rc;
10974 implicitAtts.push_back(pAtt);
10975 continue;
10976 }
10977
10978 /* Was this medium attached before? */
10979 if (!i_findAttachment(oldAtts, pMedium))
10980 {
10981 /* no: de-associate */
10982 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10983 rc = pMedium->i_removeBackReference(mData->mUuid);
10984 if (FAILED(rc))
10985 throw rc;
10986 continue;
10987 }
10988 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10989 }
10990
10991 /* If there are implicit attachments to delete, throw away the lock
10992 * map contents (which will unlock all media) since the medium
10993 * attachments will be rolled back. Below we need to completely
10994 * recreate the lock map anyway since it is infinitely complex to
10995 * do this incrementally (would need reconstructing each attachment
10996 * change, which would be extremely hairy). */
10997 if (implicitAtts.size() != 0)
10998 {
10999 ErrorInfoKeeper eik;
11000
11001 HRESULT rc1 = lockedMediaMap->Clear();
11002 AssertComRC(rc1);
11003 }
11004
11005 /* rollback hard disk changes */
11006 mMediumAttachments.rollback();
11007
11008 MultiResult mrc(S_OK);
11009
11010 // Delete unused implicit diffs.
11011 if (implicitAtts.size() != 0)
11012 {
11013 alock.release();
11014
11015 for (MediumAttachmentList::const_iterator
11016 it = implicitAtts.begin();
11017 it != implicitAtts.end();
11018 ++it)
11019 {
11020 // Remove medium associated with this attachment.
11021 ComObjPtr<MediumAttachment> pAtt = *it;
11022 Assert(pAtt);
11023 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11024 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11025 Assert(pMedium);
11026
11027 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11028 // continue on delete failure, just collect error messages
11029 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11030 pMedium->i_getLocationFull().c_str() ));
11031 mrc = rc;
11032 }
11033 // Clear the list of deleted implicit attachments now, while not
11034 // holding the lock, as it will ultimately trigger Medium::uninit()
11035 // calls which assume that the media tree lock isn't held.
11036 implicitAtts.clear();
11037
11038 alock.acquire();
11039
11040 /* if there is a VM recreate media lock map as mentioned above,
11041 * otherwise it is a waste of time and we leave things unlocked */
11042 if (aOnline)
11043 {
11044 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11045 /* must never be NULL, but better safe than sorry */
11046 if (!pMachine.isNull())
11047 {
11048 alock.release();
11049 rc = mData->mSession.mMachine->i_lockMedia();
11050 alock.acquire();
11051 if (FAILED(rc))
11052 throw rc;
11053 }
11054 }
11055 }
11056 }
11057 catch (HRESULT aRC) {rc = aRC;}
11058
11059 if (mData->mMachineState == MachineState_SettingUp)
11060 i_setMachineState(oldState);
11061
11062 /* unlock all hard disks we locked when there is no VM */
11063 if (!aOnline)
11064 {
11065 ErrorInfoKeeper eik;
11066
11067 HRESULT rc1 = lockedMediaMap->Clear();
11068 AssertComRC(rc1);
11069 }
11070
11071 return rc;
11072}
11073
11074
11075/**
11076 * Looks through the given list of media attachments for one with the given parameters
11077 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11078 * can be searched as well if needed.
11079 *
11080 * @param ll
11081 * @param aControllerName
11082 * @param aControllerPort
11083 * @param aDevice
11084 * @return
11085 */
11086MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11087 const Utf8Str &aControllerName,
11088 LONG aControllerPort,
11089 LONG aDevice)
11090{
11091 for (MediumAttachmentList::const_iterator
11092 it = ll.begin();
11093 it != ll.end();
11094 ++it)
11095 {
11096 MediumAttachment *pAttach = *it;
11097 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11098 return pAttach;
11099 }
11100
11101 return NULL;
11102}
11103
11104/**
11105 * Looks through the given list of media attachments for one with the given parameters
11106 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11107 * can be searched as well if needed.
11108 *
11109 * @param ll
11110 * @param pMedium
11111 * @return
11112 */
11113MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11114 ComObjPtr<Medium> pMedium)
11115{
11116 for (MediumAttachmentList::const_iterator
11117 it = ll.begin();
11118 it != ll.end();
11119 ++it)
11120 {
11121 MediumAttachment *pAttach = *it;
11122 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11123 if (pMediumThis == pMedium)
11124 return pAttach;
11125 }
11126
11127 return NULL;
11128}
11129
11130/**
11131 * Looks through the given list of media attachments for one with the given parameters
11132 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11133 * can be searched as well if needed.
11134 *
11135 * @param ll
11136 * @param id
11137 * @return
11138 */
11139MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11140 Guid &id)
11141{
11142 for (MediumAttachmentList::const_iterator
11143 it = ll.begin();
11144 it != ll.end();
11145 ++it)
11146 {
11147 MediumAttachment *pAttach = *it;
11148 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11149 if (pMediumThis->i_getId() == id)
11150 return pAttach;
11151 }
11152
11153 return NULL;
11154}
11155
11156/**
11157 * Main implementation for Machine::DetachDevice. This also gets called
11158 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11159 *
11160 * @param pAttach Medium attachment to detach.
11161 * @param writeLock Machine write lock which the caller must have locked once.
11162 * This may be released temporarily in here.
11163 * @param pSnapshot If NULL, then the detachment is for the current machine.
11164 * Otherwise this is for a SnapshotMachine, and this must be
11165 * its snapshot.
11166 * @return
11167 */
11168HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11169 AutoWriteLock &writeLock,
11170 Snapshot *pSnapshot)
11171{
11172 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11173 DeviceType_T mediumType = pAttach->i_getType();
11174
11175 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11176
11177 if (pAttach->i_isImplicit())
11178 {
11179 /* attempt to implicitly delete the implicitly created diff */
11180
11181 /// @todo move the implicit flag from MediumAttachment to Medium
11182 /// and forbid any hard disk operation when it is implicit. Or maybe
11183 /// a special media state for it to make it even more simple.
11184
11185 Assert(mMediumAttachments.isBackedUp());
11186
11187 /* will release the lock before the potentially lengthy operation, so
11188 * protect with the special state */
11189 MachineState_T oldState = mData->mMachineState;
11190 i_setMachineState(MachineState_SettingUp);
11191
11192 writeLock.release();
11193
11194 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11195 true /*aWait*/,
11196 false /*aNotify*/);
11197
11198 writeLock.acquire();
11199
11200 i_setMachineState(oldState);
11201
11202 if (FAILED(rc)) return rc;
11203 }
11204
11205 i_setModified(IsModified_Storage);
11206 mMediumAttachments.backup();
11207 mMediumAttachments->remove(pAttach);
11208
11209 if (!oldmedium.isNull())
11210 {
11211 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11212 if (pSnapshot)
11213 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11214 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11215 else if (mediumType != DeviceType_HardDisk)
11216 oldmedium->i_removeBackReference(mData->mUuid);
11217 }
11218
11219 return S_OK;
11220}
11221
11222/**
11223 * Goes thru all media of the given list and
11224 *
11225 * 1) calls i_detachDevice() on each of them for this machine and
11226 * 2) adds all Medium objects found in the process to the given list,
11227 * depending on cleanupMode.
11228 *
11229 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11230 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11231 * media to the list.
11232 *
11233 * This gets called from Machine::Unregister, both for the actual Machine and
11234 * the SnapshotMachine objects that might be found in the snapshots.
11235 *
11236 * Requires caller and locking. The machine lock must be passed in because it
11237 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11238 *
11239 * @param writeLock Machine lock from top-level caller; this gets passed to
11240 * i_detachDevice.
11241 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11242 * object if called for a SnapshotMachine.
11243 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11244 * added to llMedia; if Full, then all media get added;
11245 * otherwise no media get added.
11246 * @param llMedia Caller's list to receive Medium objects which got detached so
11247 * caller can close() them, depending on cleanupMode.
11248 * @return
11249 */
11250HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11251 Snapshot *pSnapshot,
11252 CleanupMode_T cleanupMode,
11253 MediaList &llMedia)
11254{
11255 Assert(isWriteLockOnCurrentThread());
11256
11257 HRESULT rc;
11258
11259 // make a temporary list because i_detachDevice invalidates iterators into
11260 // mMediumAttachments
11261 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11262
11263 for (MediumAttachmentList::iterator
11264 it = llAttachments2.begin();
11265 it != llAttachments2.end();
11266 ++it)
11267 {
11268 ComObjPtr<MediumAttachment> &pAttach = *it;
11269 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11270
11271 if (!pMedium.isNull())
11272 {
11273 AutoCaller mac(pMedium);
11274 if (FAILED(mac.rc())) return mac.rc();
11275 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11276 DeviceType_T devType = pMedium->i_getDeviceType();
11277 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11278 && devType == DeviceType_HardDisk)
11279 || (cleanupMode == CleanupMode_Full)
11280 )
11281 {
11282 llMedia.push_back(pMedium);
11283 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11284 /* Not allowed to keep this lock as below we need the parent
11285 * medium lock, and the lock order is parent to child. */
11286 lock.release();
11287 /*
11288 * Search for medias which are not attached to any machine, but
11289 * in the chain to an attached disk. Mediums are only consided
11290 * if they are:
11291 * - have only one child
11292 * - no references to any machines
11293 * - are of normal medium type
11294 */
11295 while (!pParent.isNull())
11296 {
11297 AutoCaller mac1(pParent);
11298 if (FAILED(mac1.rc())) return mac1.rc();
11299 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11300 if (pParent->i_getChildren().size() == 1)
11301 {
11302 if ( pParent->i_getMachineBackRefCount() == 0
11303 && pParent->i_getType() == MediumType_Normal
11304 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11305 llMedia.push_back(pParent);
11306 }
11307 else
11308 break;
11309 pParent = pParent->i_getParent();
11310 }
11311 }
11312 }
11313
11314 // real machine: then we need to use the proper method
11315 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11316
11317 if (FAILED(rc))
11318 return rc;
11319 }
11320
11321 return S_OK;
11322}
11323
11324/**
11325 * Perform deferred hard disk detachments.
11326 *
11327 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11328 * changed (not backed up).
11329 *
11330 * If @a aOnline is @c true then this method will also unlock the old hard
11331 * disks for which the new implicit diffs were created and will lock these new
11332 * diffs for writing.
11333 *
11334 * @param aOnline Whether the VM was online prior to this operation.
11335 *
11336 * @note Locks this object for writing!
11337 */
11338void Machine::i_commitMedia(bool aOnline /*= false*/)
11339{
11340 AutoCaller autoCaller(this);
11341 AssertComRCReturnVoid(autoCaller.rc());
11342
11343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11344
11345 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11346
11347 HRESULT rc = S_OK;
11348
11349 /* no attach/detach operations -- nothing to do */
11350 if (!mMediumAttachments.isBackedUp())
11351 return;
11352
11353 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11354 bool fMediaNeedsLocking = false;
11355
11356 /* enumerate new attachments */
11357 for (MediumAttachmentList::const_iterator
11358 it = mMediumAttachments->begin();
11359 it != mMediumAttachments->end();
11360 ++it)
11361 {
11362 MediumAttachment *pAttach = *it;
11363
11364 pAttach->i_commit();
11365
11366 Medium *pMedium = pAttach->i_getMedium();
11367 bool fImplicit = pAttach->i_isImplicit();
11368
11369 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11370 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11371 fImplicit));
11372
11373 /** @todo convert all this Machine-based voodoo to MediumAttachment
11374 * based commit logic. */
11375 if (fImplicit)
11376 {
11377 /* convert implicit attachment to normal */
11378 pAttach->i_setImplicit(false);
11379
11380 if ( aOnline
11381 && pMedium
11382 && pAttach->i_getType() == DeviceType_HardDisk
11383 )
11384 {
11385 /* update the appropriate lock list */
11386 MediumLockList *pMediumLockList;
11387 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11388 AssertComRC(rc);
11389 if (pMediumLockList)
11390 {
11391 /* unlock if there's a need to change the locking */
11392 if (!fMediaNeedsLocking)
11393 {
11394 rc = mData->mSession.mLockedMedia.Unlock();
11395 AssertComRC(rc);
11396 fMediaNeedsLocking = true;
11397 }
11398 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11399 AssertComRC(rc);
11400 rc = pMediumLockList->Append(pMedium, true);
11401 AssertComRC(rc);
11402 }
11403 }
11404
11405 continue;
11406 }
11407
11408 if (pMedium)
11409 {
11410 /* was this medium attached before? */
11411 for (MediumAttachmentList::iterator
11412 oldIt = oldAtts.begin();
11413 oldIt != oldAtts.end();
11414 ++oldIt)
11415 {
11416 MediumAttachment *pOldAttach = *oldIt;
11417 if (pOldAttach->i_getMedium() == pMedium)
11418 {
11419 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11420
11421 /* yes: remove from old to avoid de-association */
11422 oldAtts.erase(oldIt);
11423 break;
11424 }
11425 }
11426 }
11427 }
11428
11429 /* enumerate remaining old attachments and de-associate from the
11430 * current machine state */
11431 for (MediumAttachmentList::const_iterator
11432 it = oldAtts.begin();
11433 it != oldAtts.end();
11434 ++it)
11435 {
11436 MediumAttachment *pAttach = *it;
11437 Medium *pMedium = pAttach->i_getMedium();
11438
11439 /* Detach only hard disks, since DVD/floppy media is detached
11440 * instantly in MountMedium. */
11441 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11442 {
11443 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11444
11445 /* now de-associate from the current machine state */
11446 rc = pMedium->i_removeBackReference(mData->mUuid);
11447 AssertComRC(rc);
11448
11449 if (aOnline)
11450 {
11451 /* unlock since medium is not used anymore */
11452 MediumLockList *pMediumLockList;
11453 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11454 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11455 {
11456 /* this happens for online snapshots, there the attachment
11457 * is changing, but only to a diff image created under
11458 * the old one, so there is no separate lock list */
11459 Assert(!pMediumLockList);
11460 }
11461 else
11462 {
11463 AssertComRC(rc);
11464 if (pMediumLockList)
11465 {
11466 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11467 AssertComRC(rc);
11468 }
11469 }
11470 }
11471 }
11472 }
11473
11474 /* take media locks again so that the locking state is consistent */
11475 if (fMediaNeedsLocking)
11476 {
11477 Assert(aOnline);
11478 rc = mData->mSession.mLockedMedia.Lock();
11479 AssertComRC(rc);
11480 }
11481
11482 /* commit the hard disk changes */
11483 mMediumAttachments.commit();
11484
11485 if (i_isSessionMachine())
11486 {
11487 /*
11488 * Update the parent machine to point to the new owner.
11489 * This is necessary because the stored parent will point to the
11490 * session machine otherwise and cause crashes or errors later
11491 * when the session machine gets invalid.
11492 */
11493 /** @todo Change the MediumAttachment class to behave like any other
11494 * class in this regard by creating peer MediumAttachment
11495 * objects for session machines and share the data with the peer
11496 * machine.
11497 */
11498 for (MediumAttachmentList::const_iterator
11499 it = mMediumAttachments->begin();
11500 it != mMediumAttachments->end();
11501 ++it)
11502 (*it)->i_updateParentMachine(mPeer);
11503
11504 /* attach new data to the primary machine and reshare it */
11505 mPeer->mMediumAttachments.attach(mMediumAttachments);
11506 }
11507
11508 return;
11509}
11510
11511/**
11512 * Perform deferred deletion of implicitly created diffs.
11513 *
11514 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11515 * changed (not backed up).
11516 *
11517 * @note Locks this object for writing!
11518 */
11519void Machine::i_rollbackMedia()
11520{
11521 AutoCaller autoCaller(this);
11522 AssertComRCReturnVoid(autoCaller.rc());
11523
11524 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11525 LogFlowThisFunc(("Entering rollbackMedia\n"));
11526
11527 HRESULT rc = S_OK;
11528
11529 /* no attach/detach operations -- nothing to do */
11530 if (!mMediumAttachments.isBackedUp())
11531 return;
11532
11533 /* enumerate new attachments */
11534 for (MediumAttachmentList::const_iterator
11535 it = mMediumAttachments->begin();
11536 it != mMediumAttachments->end();
11537 ++it)
11538 {
11539 MediumAttachment *pAttach = *it;
11540 /* Fix up the backrefs for DVD/floppy media. */
11541 if (pAttach->i_getType() != DeviceType_HardDisk)
11542 {
11543 Medium *pMedium = pAttach->i_getMedium();
11544 if (pMedium)
11545 {
11546 rc = pMedium->i_removeBackReference(mData->mUuid);
11547 AssertComRC(rc);
11548 }
11549 }
11550
11551 (*it)->i_rollback();
11552
11553 pAttach = *it;
11554 /* Fix up the backrefs for DVD/floppy media. */
11555 if (pAttach->i_getType() != DeviceType_HardDisk)
11556 {
11557 Medium *pMedium = pAttach->i_getMedium();
11558 if (pMedium)
11559 {
11560 rc = pMedium->i_addBackReference(mData->mUuid);
11561 AssertComRC(rc);
11562 }
11563 }
11564 }
11565
11566 /** @todo convert all this Machine-based voodoo to MediumAttachment
11567 * based rollback logic. */
11568 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11569
11570 return;
11571}
11572
11573/**
11574 * Returns true if the settings file is located in the directory named exactly
11575 * as the machine; this means, among other things, that the machine directory
11576 * should be auto-renamed.
11577 *
11578 * @param aSettingsDir if not NULL, the full machine settings file directory
11579 * name will be assigned there.
11580 *
11581 * @note Doesn't lock anything.
11582 * @note Not thread safe (must be called from this object's lock).
11583 */
11584bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11585{
11586 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11587 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11588 if (aSettingsDir)
11589 *aSettingsDir = strMachineDirName;
11590 strMachineDirName.stripPath(); // vmname
11591 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11592 strConfigFileOnly.stripPath() // vmname.vbox
11593 .stripSuffix(); // vmname
11594 /** @todo hack, make somehow use of ComposeMachineFilename */
11595 if (mUserData->s.fDirectoryIncludesUUID)
11596 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11597
11598 AssertReturn(!strMachineDirName.isEmpty(), false);
11599 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11600
11601 return strMachineDirName == strConfigFileOnly;
11602}
11603
11604/**
11605 * Discards all changes to machine settings.
11606 *
11607 * @param aNotify Whether to notify the direct session about changes or not.
11608 *
11609 * @note Locks objects for writing!
11610 */
11611void Machine::i_rollback(bool aNotify)
11612{
11613 AutoCaller autoCaller(this);
11614 AssertComRCReturn(autoCaller.rc(), (void)0);
11615
11616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11617
11618 if (!mStorageControllers.isNull())
11619 {
11620 if (mStorageControllers.isBackedUp())
11621 {
11622 /* unitialize all new devices (absent in the backed up list). */
11623 StorageControllerList *backedList = mStorageControllers.backedUpData();
11624 for (StorageControllerList::const_iterator
11625 it = mStorageControllers->begin();
11626 it != mStorageControllers->end();
11627 ++it)
11628 {
11629 if ( std::find(backedList->begin(), backedList->end(), *it)
11630 == backedList->end()
11631 )
11632 {
11633 (*it)->uninit();
11634 }
11635 }
11636
11637 /* restore the list */
11638 mStorageControllers.rollback();
11639 }
11640
11641 /* rollback any changes to devices after restoring the list */
11642 if (mData->flModifications & IsModified_Storage)
11643 {
11644 for (StorageControllerList::const_iterator
11645 it = mStorageControllers->begin();
11646 it != mStorageControllers->end();
11647 ++it)
11648 {
11649 (*it)->i_rollback();
11650 }
11651 }
11652 }
11653
11654 if (!mUSBControllers.isNull())
11655 {
11656 if (mUSBControllers.isBackedUp())
11657 {
11658 /* unitialize all new devices (absent in the backed up list). */
11659 USBControllerList *backedList = mUSBControllers.backedUpData();
11660 for (USBControllerList::const_iterator
11661 it = mUSBControllers->begin();
11662 it != mUSBControllers->end();
11663 ++it)
11664 {
11665 if ( std::find(backedList->begin(), backedList->end(), *it)
11666 == backedList->end()
11667 )
11668 {
11669 (*it)->uninit();
11670 }
11671 }
11672
11673 /* restore the list */
11674 mUSBControllers.rollback();
11675 }
11676
11677 /* rollback any changes to devices after restoring the list */
11678 if (mData->flModifications & IsModified_USB)
11679 {
11680 for (USBControllerList::const_iterator
11681 it = mUSBControllers->begin();
11682 it != mUSBControllers->end();
11683 ++it)
11684 {
11685 (*it)->i_rollback();
11686 }
11687 }
11688 }
11689
11690 mUserData.rollback();
11691
11692 mHWData.rollback();
11693
11694 if (mData->flModifications & IsModified_Storage)
11695 i_rollbackMedia();
11696
11697 if (mBIOSSettings)
11698 mBIOSSettings->i_rollback();
11699
11700 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11701 mRecordingSettings->i_rollback();
11702
11703 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11704 mVRDEServer->i_rollback();
11705
11706 if (mAudioAdapter)
11707 mAudioAdapter->i_rollback();
11708
11709 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11710 mUSBDeviceFilters->i_rollback();
11711
11712 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11713 mBandwidthControl->i_rollback();
11714
11715 if (!mHWData.isNull())
11716 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11717 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11718 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11719 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11720
11721 if (mData->flModifications & IsModified_NetworkAdapters)
11722 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11723 if ( mNetworkAdapters[slot]
11724 && mNetworkAdapters[slot]->i_isModified())
11725 {
11726 mNetworkAdapters[slot]->i_rollback();
11727 networkAdapters[slot] = mNetworkAdapters[slot];
11728 }
11729
11730 if (mData->flModifications & IsModified_SerialPorts)
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11732 if ( mSerialPorts[slot]
11733 && mSerialPorts[slot]->i_isModified())
11734 {
11735 mSerialPorts[slot]->i_rollback();
11736 serialPorts[slot] = mSerialPorts[slot];
11737 }
11738
11739 if (mData->flModifications & IsModified_ParallelPorts)
11740 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11741 if ( mParallelPorts[slot]
11742 && mParallelPorts[slot]->i_isModified())
11743 {
11744 mParallelPorts[slot]->i_rollback();
11745 parallelPorts[slot] = mParallelPorts[slot];
11746 }
11747
11748 if (aNotify)
11749 {
11750 /* inform the direct session about changes */
11751
11752 ComObjPtr<Machine> that = this;
11753 uint32_t flModifications = mData->flModifications;
11754 alock.release();
11755
11756 if (flModifications & IsModified_SharedFolders)
11757 that->i_onSharedFolderChange();
11758
11759 if (flModifications & IsModified_VRDEServer)
11760 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11761 if (flModifications & IsModified_USB)
11762 that->i_onUSBControllerChange();
11763
11764 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11765 if (networkAdapters[slot])
11766 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11767 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11768 if (serialPorts[slot])
11769 that->i_onSerialPortChange(serialPorts[slot]);
11770 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11771 if (parallelPorts[slot])
11772 that->i_onParallelPortChange(parallelPorts[slot]);
11773
11774 if (flModifications & IsModified_Storage)
11775 {
11776 for (StorageControllerList::const_iterator
11777 it = mStorageControllers->begin();
11778 it != mStorageControllers->end();
11779 ++it)
11780 {
11781 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11782 }
11783 }
11784
11785
11786#if 0
11787 if (flModifications & IsModified_BandwidthControl)
11788 that->onBandwidthControlChange();
11789#endif
11790 }
11791}
11792
11793/**
11794 * Commits all the changes to machine settings.
11795 *
11796 * Note that this operation is supposed to never fail.
11797 *
11798 * @note Locks this object and children for writing.
11799 */
11800void Machine::i_commit()
11801{
11802 AutoCaller autoCaller(this);
11803 AssertComRCReturnVoid(autoCaller.rc());
11804
11805 AutoCaller peerCaller(mPeer);
11806 AssertComRCReturnVoid(peerCaller.rc());
11807
11808 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11809
11810 /*
11811 * use safe commit to ensure Snapshot machines (that share mUserData)
11812 * will still refer to a valid memory location
11813 */
11814 mUserData.commitCopy();
11815
11816 mHWData.commit();
11817
11818 if (mMediumAttachments.isBackedUp())
11819 i_commitMedia(Global::IsOnline(mData->mMachineState));
11820
11821 mBIOSSettings->i_commit();
11822 mRecordingSettings->i_commit();
11823 mVRDEServer->i_commit();
11824 mAudioAdapter->i_commit();
11825 mUSBDeviceFilters->i_commit();
11826 mBandwidthControl->i_commit();
11827
11828 /* Since mNetworkAdapters is a list which might have been changed (resized)
11829 * without using the Backupable<> template we need to handle the copying
11830 * of the list entries manually, including the creation of peers for the
11831 * new objects. */
11832 bool commitNetworkAdapters = false;
11833 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11834 if (mPeer)
11835 {
11836 /* commit everything, even the ones which will go away */
11837 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11838 mNetworkAdapters[slot]->i_commit();
11839 /* copy over the new entries, creating a peer and uninit the original */
11840 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11841 for (size_t slot = 0; slot < newSize; slot++)
11842 {
11843 /* look if this adapter has a peer device */
11844 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11845 if (!peer)
11846 {
11847 /* no peer means the adapter is a newly created one;
11848 * create a peer owning data this data share it with */
11849 peer.createObject();
11850 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11851 }
11852 mPeer->mNetworkAdapters[slot] = peer;
11853 }
11854 /* uninit any no longer needed network adapters */
11855 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11856 mNetworkAdapters[slot]->uninit();
11857 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11858 {
11859 if (mPeer->mNetworkAdapters[slot])
11860 mPeer->mNetworkAdapters[slot]->uninit();
11861 }
11862 /* Keep the original network adapter count until this point, so that
11863 * discarding a chipset type change will not lose settings. */
11864 mNetworkAdapters.resize(newSize);
11865 mPeer->mNetworkAdapters.resize(newSize);
11866 }
11867 else
11868 {
11869 /* we have no peer (our parent is the newly created machine);
11870 * just commit changes to the network adapters */
11871 commitNetworkAdapters = true;
11872 }
11873 if (commitNetworkAdapters)
11874 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11875 mNetworkAdapters[slot]->i_commit();
11876
11877 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11878 mSerialPorts[slot]->i_commit();
11879 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11880 mParallelPorts[slot]->i_commit();
11881
11882 bool commitStorageControllers = false;
11883
11884 if (mStorageControllers.isBackedUp())
11885 {
11886 mStorageControllers.commit();
11887
11888 if (mPeer)
11889 {
11890 /* Commit all changes to new controllers (this will reshare data with
11891 * peers for those who have peers) */
11892 StorageControllerList *newList = new StorageControllerList();
11893 for (StorageControllerList::const_iterator
11894 it = mStorageControllers->begin();
11895 it != mStorageControllers->end();
11896 ++it)
11897 {
11898 (*it)->i_commit();
11899
11900 /* look if this controller has a peer device */
11901 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11902 if (!peer)
11903 {
11904 /* no peer means the device is a newly created one;
11905 * create a peer owning data this device share it with */
11906 peer.createObject();
11907 peer->init(mPeer, *it, true /* aReshare */);
11908 }
11909 else
11910 {
11911 /* remove peer from the old list */
11912 mPeer->mStorageControllers->remove(peer);
11913 }
11914 /* and add it to the new list */
11915 newList->push_back(peer);
11916 }
11917
11918 /* uninit old peer's controllers that are left */
11919 for (StorageControllerList::const_iterator
11920 it = mPeer->mStorageControllers->begin();
11921 it != mPeer->mStorageControllers->end();
11922 ++it)
11923 {
11924 (*it)->uninit();
11925 }
11926
11927 /* attach new list of controllers to our peer */
11928 mPeer->mStorageControllers.attach(newList);
11929 }
11930 else
11931 {
11932 /* we have no peer (our parent is the newly created machine);
11933 * just commit changes to devices */
11934 commitStorageControllers = true;
11935 }
11936 }
11937 else
11938 {
11939 /* the list of controllers itself is not changed,
11940 * just commit changes to controllers themselves */
11941 commitStorageControllers = true;
11942 }
11943
11944 if (commitStorageControllers)
11945 {
11946 for (StorageControllerList::const_iterator
11947 it = mStorageControllers->begin();
11948 it != mStorageControllers->end();
11949 ++it)
11950 {
11951 (*it)->i_commit();
11952 }
11953 }
11954
11955 bool commitUSBControllers = false;
11956
11957 if (mUSBControllers.isBackedUp())
11958 {
11959 mUSBControllers.commit();
11960
11961 if (mPeer)
11962 {
11963 /* Commit all changes to new controllers (this will reshare data with
11964 * peers for those who have peers) */
11965 USBControllerList *newList = new USBControllerList();
11966 for (USBControllerList::const_iterator
11967 it = mUSBControllers->begin();
11968 it != mUSBControllers->end();
11969 ++it)
11970 {
11971 (*it)->i_commit();
11972
11973 /* look if this controller has a peer device */
11974 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11975 if (!peer)
11976 {
11977 /* no peer means the device is a newly created one;
11978 * create a peer owning data this device share it with */
11979 peer.createObject();
11980 peer->init(mPeer, *it, true /* aReshare */);
11981 }
11982 else
11983 {
11984 /* remove peer from the old list */
11985 mPeer->mUSBControllers->remove(peer);
11986 }
11987 /* and add it to the new list */
11988 newList->push_back(peer);
11989 }
11990
11991 /* uninit old peer's controllers that are left */
11992 for (USBControllerList::const_iterator
11993 it = mPeer->mUSBControllers->begin();
11994 it != mPeer->mUSBControllers->end();
11995 ++it)
11996 {
11997 (*it)->uninit();
11998 }
11999
12000 /* attach new list of controllers to our peer */
12001 mPeer->mUSBControllers.attach(newList);
12002 }
12003 else
12004 {
12005 /* we have no peer (our parent is the newly created machine);
12006 * just commit changes to devices */
12007 commitUSBControllers = true;
12008 }
12009 }
12010 else
12011 {
12012 /* the list of controllers itself is not changed,
12013 * just commit changes to controllers themselves */
12014 commitUSBControllers = true;
12015 }
12016
12017 if (commitUSBControllers)
12018 {
12019 for (USBControllerList::const_iterator
12020 it = mUSBControllers->begin();
12021 it != mUSBControllers->end();
12022 ++it)
12023 {
12024 (*it)->i_commit();
12025 }
12026 }
12027
12028 if (i_isSessionMachine())
12029 {
12030 /* attach new data to the primary machine and reshare it */
12031 mPeer->mUserData.attach(mUserData);
12032 mPeer->mHWData.attach(mHWData);
12033 /* mmMediumAttachments is reshared by fixupMedia */
12034 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12035 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12036 }
12037}
12038
12039/**
12040 * Copies all the hardware data from the given machine.
12041 *
12042 * Currently, only called when the VM is being restored from a snapshot. In
12043 * particular, this implies that the VM is not running during this method's
12044 * call.
12045 *
12046 * @note This method must be called from under this object's lock.
12047 *
12048 * @note This method doesn't call #i_commit(), so all data remains backed up and
12049 * unsaved.
12050 */
12051void Machine::i_copyFrom(Machine *aThat)
12052{
12053 AssertReturnVoid(!i_isSnapshotMachine());
12054 AssertReturnVoid(aThat->i_isSnapshotMachine());
12055
12056 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12057
12058 mHWData.assignCopy(aThat->mHWData);
12059
12060 // create copies of all shared folders (mHWData after attaching a copy
12061 // contains just references to original objects)
12062 for (HWData::SharedFolderList::iterator
12063 it = mHWData->mSharedFolders.begin();
12064 it != mHWData->mSharedFolders.end();
12065 ++it)
12066 {
12067 ComObjPtr<SharedFolder> folder;
12068 folder.createObject();
12069 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12070 AssertComRC(rc);
12071 *it = folder;
12072 }
12073
12074 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12075 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12076 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12077 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12078 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12079 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12080
12081 /* create private copies of all controllers */
12082 mStorageControllers.backup();
12083 mStorageControllers->clear();
12084 for (StorageControllerList::const_iterator
12085 it = aThat->mStorageControllers->begin();
12086 it != aThat->mStorageControllers->end();
12087 ++it)
12088 {
12089 ComObjPtr<StorageController> ctrl;
12090 ctrl.createObject();
12091 ctrl->initCopy(this, *it);
12092 mStorageControllers->push_back(ctrl);
12093 }
12094
12095 /* create private copies of all USB controllers */
12096 mUSBControllers.backup();
12097 mUSBControllers->clear();
12098 for (USBControllerList::const_iterator
12099 it = aThat->mUSBControllers->begin();
12100 it != aThat->mUSBControllers->end();
12101 ++it)
12102 {
12103 ComObjPtr<USBController> ctrl;
12104 ctrl.createObject();
12105 ctrl->initCopy(this, *it);
12106 mUSBControllers->push_back(ctrl);
12107 }
12108
12109 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12110 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12111 {
12112 if (mNetworkAdapters[slot].isNotNull())
12113 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12114 else
12115 {
12116 unconst(mNetworkAdapters[slot]).createObject();
12117 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12118 }
12119 }
12120 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12121 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12122 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12123 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12124}
12125
12126/**
12127 * Returns whether the given storage controller is hotplug capable.
12128 *
12129 * @returns true if the controller supports hotplugging
12130 * false otherwise.
12131 * @param enmCtrlType The controller type to check for.
12132 */
12133bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12134{
12135 ComPtr<ISystemProperties> systemProperties;
12136 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12137 if (FAILED(rc))
12138 return false;
12139
12140 BOOL aHotplugCapable = FALSE;
12141 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12142
12143 return RT_BOOL(aHotplugCapable);
12144}
12145
12146#ifdef VBOX_WITH_RESOURCE_USAGE_API
12147
12148void Machine::i_getDiskList(MediaList &list)
12149{
12150 for (MediumAttachmentList::const_iterator
12151 it = mMediumAttachments->begin();
12152 it != mMediumAttachments->end();
12153 ++it)
12154 {
12155 MediumAttachment *pAttach = *it;
12156 /* just in case */
12157 AssertContinue(pAttach);
12158
12159 AutoCaller localAutoCallerA(pAttach);
12160 if (FAILED(localAutoCallerA.rc())) continue;
12161
12162 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12163
12164 if (pAttach->i_getType() == DeviceType_HardDisk)
12165 list.push_back(pAttach->i_getMedium());
12166 }
12167}
12168
12169void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12170{
12171 AssertReturnVoid(isWriteLockOnCurrentThread());
12172 AssertPtrReturnVoid(aCollector);
12173
12174 pm::CollectorHAL *hal = aCollector->getHAL();
12175 /* Create sub metrics */
12176 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12177 "Percentage of processor time spent in user mode by the VM process.");
12178 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12179 "Percentage of processor time spent in kernel mode by the VM process.");
12180 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12181 "Size of resident portion of VM process in memory.");
12182 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12183 "Actual size of all VM disks combined.");
12184 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12185 "Network receive rate.");
12186 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12187 "Network transmit rate.");
12188 /* Create and register base metrics */
12189 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12190 cpuLoadUser, cpuLoadKernel);
12191 aCollector->registerBaseMetric(cpuLoad);
12192 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12193 ramUsageUsed);
12194 aCollector->registerBaseMetric(ramUsage);
12195 MediaList disks;
12196 i_getDiskList(disks);
12197 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12198 diskUsageUsed);
12199 aCollector->registerBaseMetric(diskUsage);
12200
12201 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12202 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12203 new pm::AggregateAvg()));
12204 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12205 new pm::AggregateMin()));
12206 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12207 new pm::AggregateMax()));
12208 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12209 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12210 new pm::AggregateAvg()));
12211 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12212 new pm::AggregateMin()));
12213 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12214 new pm::AggregateMax()));
12215
12216 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12217 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12218 new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12220 new pm::AggregateMin()));
12221 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12222 new pm::AggregateMax()));
12223
12224 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12225 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12226 new pm::AggregateAvg()));
12227 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12228 new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12230 new pm::AggregateMax()));
12231
12232
12233 /* Guest metrics collector */
12234 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12235 aCollector->registerGuest(mCollectorGuest);
12236 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12237
12238 /* Create sub metrics */
12239 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12240 "Percentage of processor time spent in user mode as seen by the guest.");
12241 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12242 "Percentage of processor time spent in kernel mode as seen by the guest.");
12243 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12244 "Percentage of processor time spent idling as seen by the guest.");
12245
12246 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12247 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12248 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12249 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12250 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12251 pm::SubMetric *guestMemCache = new pm::SubMetric(
12252 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12253
12254 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12255 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12256
12257 /* Create and register base metrics */
12258 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12259 machineNetRx, machineNetTx);
12260 aCollector->registerBaseMetric(machineNetRate);
12261
12262 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12263 guestLoadUser, guestLoadKernel, guestLoadIdle);
12264 aCollector->registerBaseMetric(guestCpuLoad);
12265
12266 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12267 guestMemTotal, guestMemFree,
12268 guestMemBalloon, guestMemShared,
12269 guestMemCache, guestPagedTotal);
12270 aCollector->registerBaseMetric(guestCpuMem);
12271
12272 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12273 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12275 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12276
12277 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12278 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12283 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12293 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12294 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12295 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12296
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12306
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12311
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12316
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12321
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12326}
12327
12328void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12329{
12330 AssertReturnVoid(isWriteLockOnCurrentThread());
12331
12332 if (aCollector)
12333 {
12334 aCollector->unregisterMetricsFor(aMachine);
12335 aCollector->unregisterBaseMetricsFor(aMachine);
12336 }
12337}
12338
12339#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12340
12341
12342////////////////////////////////////////////////////////////////////////////////
12343
12344DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12345
12346HRESULT SessionMachine::FinalConstruct()
12347{
12348 LogFlowThisFunc(("\n"));
12349
12350 mClientToken = NULL;
12351
12352 return BaseFinalConstruct();
12353}
12354
12355void SessionMachine::FinalRelease()
12356{
12357 LogFlowThisFunc(("\n"));
12358
12359 Assert(!mClientToken);
12360 /* paranoia, should not hang around any more */
12361 if (mClientToken)
12362 {
12363 delete mClientToken;
12364 mClientToken = NULL;
12365 }
12366
12367 uninit(Uninit::Unexpected);
12368
12369 BaseFinalRelease();
12370}
12371
12372/**
12373 * @note Must be called only by Machine::LockMachine() from its own write lock.
12374 */
12375HRESULT SessionMachine::init(Machine *aMachine)
12376{
12377 LogFlowThisFuncEnter();
12378 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12379
12380 AssertReturn(aMachine, E_INVALIDARG);
12381
12382 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12383
12384 /* Enclose the state transition NotReady->InInit->Ready */
12385 AutoInitSpan autoInitSpan(this);
12386 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12387
12388 HRESULT rc = S_OK;
12389
12390 RT_ZERO(mAuthLibCtx);
12391
12392 /* create the machine client token */
12393 try
12394 {
12395 mClientToken = new ClientToken(aMachine, this);
12396 if (!mClientToken->isReady())
12397 {
12398 delete mClientToken;
12399 mClientToken = NULL;
12400 rc = E_FAIL;
12401 }
12402 }
12403 catch (std::bad_alloc &)
12404 {
12405 rc = E_OUTOFMEMORY;
12406 }
12407 if (FAILED(rc))
12408 return rc;
12409
12410 /* memorize the peer Machine */
12411 unconst(mPeer) = aMachine;
12412 /* share the parent pointer */
12413 unconst(mParent) = aMachine->mParent;
12414
12415 /* take the pointers to data to share */
12416 mData.share(aMachine->mData);
12417 mSSData.share(aMachine->mSSData);
12418
12419 mUserData.share(aMachine->mUserData);
12420 mHWData.share(aMachine->mHWData);
12421 mMediumAttachments.share(aMachine->mMediumAttachments);
12422
12423 mStorageControllers.allocate();
12424 for (StorageControllerList::const_iterator
12425 it = aMachine->mStorageControllers->begin();
12426 it != aMachine->mStorageControllers->end();
12427 ++it)
12428 {
12429 ComObjPtr<StorageController> ctl;
12430 ctl.createObject();
12431 ctl->init(this, *it);
12432 mStorageControllers->push_back(ctl);
12433 }
12434
12435 mUSBControllers.allocate();
12436 for (USBControllerList::const_iterator
12437 it = aMachine->mUSBControllers->begin();
12438 it != aMachine->mUSBControllers->end();
12439 ++it)
12440 {
12441 ComObjPtr<USBController> ctl;
12442 ctl.createObject();
12443 ctl->init(this, *it);
12444 mUSBControllers->push_back(ctl);
12445 }
12446
12447 unconst(mBIOSSettings).createObject();
12448 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12449 unconst(mRecordingSettings).createObject();
12450 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12451 /* create another VRDEServer object that will be mutable */
12452 unconst(mVRDEServer).createObject();
12453 mVRDEServer->init(this, aMachine->mVRDEServer);
12454 /* create another audio adapter object that will be mutable */
12455 unconst(mAudioAdapter).createObject();
12456 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12457 /* create a list of serial ports that will be mutable */
12458 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12459 {
12460 unconst(mSerialPorts[slot]).createObject();
12461 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12462 }
12463 /* create a list of parallel ports that will be mutable */
12464 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12465 {
12466 unconst(mParallelPorts[slot]).createObject();
12467 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12468 }
12469
12470 /* create another USB device filters object that will be mutable */
12471 unconst(mUSBDeviceFilters).createObject();
12472 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12473
12474 /* create a list of network adapters that will be mutable */
12475 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12476 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12477 {
12478 unconst(mNetworkAdapters[slot]).createObject();
12479 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12480 }
12481
12482 /* create another bandwidth control object that will be mutable */
12483 unconst(mBandwidthControl).createObject();
12484 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12485
12486 /* default is to delete saved state on Saved -> PoweredOff transition */
12487 mRemoveSavedState = true;
12488
12489 /* Confirm a successful initialization when it's the case */
12490 autoInitSpan.setSucceeded();
12491
12492 miNATNetworksStarted = 0;
12493
12494 LogFlowThisFuncLeave();
12495 return rc;
12496}
12497
12498/**
12499 * Uninitializes this session object. If the reason is other than
12500 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12501 * or the client watcher code.
12502 *
12503 * @param aReason uninitialization reason
12504 *
12505 * @note Locks mParent + this object for writing.
12506 */
12507void SessionMachine::uninit(Uninit::Reason aReason)
12508{
12509 LogFlowThisFuncEnter();
12510 LogFlowThisFunc(("reason=%d\n", aReason));
12511
12512 /*
12513 * Strongly reference ourselves to prevent this object deletion after
12514 * mData->mSession.mMachine.setNull() below (which can release the last
12515 * reference and call the destructor). Important: this must be done before
12516 * accessing any members (and before AutoUninitSpan that does it as well).
12517 * This self reference will be released as the very last step on return.
12518 */
12519 ComObjPtr<SessionMachine> selfRef;
12520 if (aReason != Uninit::Unexpected)
12521 selfRef = this;
12522
12523 /* Enclose the state transition Ready->InUninit->NotReady */
12524 AutoUninitSpan autoUninitSpan(this);
12525 if (autoUninitSpan.uninitDone())
12526 {
12527 LogFlowThisFunc(("Already uninitialized\n"));
12528 LogFlowThisFuncLeave();
12529 return;
12530 }
12531
12532 if (autoUninitSpan.initFailed())
12533 {
12534 /* We've been called by init() because it's failed. It's not really
12535 * necessary (nor it's safe) to perform the regular uninit sequence
12536 * below, the following is enough.
12537 */
12538 LogFlowThisFunc(("Initialization failed.\n"));
12539 /* destroy the machine client token */
12540 if (mClientToken)
12541 {
12542 delete mClientToken;
12543 mClientToken = NULL;
12544 }
12545 uninitDataAndChildObjects();
12546 mData.free();
12547 unconst(mParent) = NULL;
12548 unconst(mPeer) = NULL;
12549 LogFlowThisFuncLeave();
12550 return;
12551 }
12552
12553 MachineState_T lastState;
12554 {
12555 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12556 lastState = mData->mMachineState;
12557 }
12558 NOREF(lastState);
12559
12560#ifdef VBOX_WITH_USB
12561 // release all captured USB devices, but do this before requesting the locks below
12562 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12563 {
12564 /* Console::captureUSBDevices() is called in the VM process only after
12565 * setting the machine state to Starting or Restoring.
12566 * Console::detachAllUSBDevices() will be called upon successful
12567 * termination. So, we need to release USB devices only if there was
12568 * an abnormal termination of a running VM.
12569 *
12570 * This is identical to SessionMachine::DetachAllUSBDevices except
12571 * for the aAbnormal argument. */
12572 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12573 AssertComRC(rc);
12574 NOREF(rc);
12575
12576 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12577 if (service)
12578 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12579 }
12580#endif /* VBOX_WITH_USB */
12581
12582 // we need to lock this object in uninit() because the lock is shared
12583 // with mPeer (as well as data we modify below). mParent lock is needed
12584 // by several calls to it.
12585 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12586
12587#ifdef VBOX_WITH_RESOURCE_USAGE_API
12588 /*
12589 * It is safe to call Machine::i_unregisterMetrics() here because
12590 * PerformanceCollector::samplerCallback no longer accesses guest methods
12591 * holding the lock.
12592 */
12593 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12594 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12595 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12596 if (mCollectorGuest)
12597 {
12598 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12599 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12600 mCollectorGuest = NULL;
12601 }
12602#endif
12603
12604 if (aReason == Uninit::Abnormal)
12605 {
12606 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12607
12608 /* reset the state to Aborted */
12609 if (mData->mMachineState != MachineState_Aborted)
12610 i_setMachineState(MachineState_Aborted);
12611 }
12612
12613 // any machine settings modified?
12614 if (mData->flModifications)
12615 {
12616 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12617 i_rollback(false /* aNotify */);
12618 }
12619
12620 mData->mSession.mPID = NIL_RTPROCESS;
12621
12622 if (aReason == Uninit::Unexpected)
12623 {
12624 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12625 * client watcher thread to update the set of machines that have open
12626 * sessions. */
12627 mParent->i_updateClientWatcher();
12628 }
12629
12630 /* uninitialize all remote controls */
12631 if (mData->mSession.mRemoteControls.size())
12632 {
12633 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12634 mData->mSession.mRemoteControls.size()));
12635
12636 /* Always restart a the beginning, since the iterator is invalidated
12637 * by using erase(). */
12638 for (Data::Session::RemoteControlList::iterator
12639 it = mData->mSession.mRemoteControls.begin();
12640 it != mData->mSession.mRemoteControls.end();
12641 it = mData->mSession.mRemoteControls.begin())
12642 {
12643 ComPtr<IInternalSessionControl> pControl = *it;
12644 mData->mSession.mRemoteControls.erase(it);
12645 multilock.release();
12646 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12647 HRESULT rc = pControl->Uninitialize();
12648 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12649 if (FAILED(rc))
12650 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12651 multilock.acquire();
12652 }
12653 mData->mSession.mRemoteControls.clear();
12654 }
12655
12656 /* Remove all references to the NAT network service. The service will stop
12657 * if all references (also from other VMs) are removed. */
12658 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12659 {
12660 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12661 {
12662 BOOL enabled;
12663 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12664 if ( FAILED(hrc)
12665 || !enabled)
12666 continue;
12667
12668 NetworkAttachmentType_T type;
12669 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12670 if ( SUCCEEDED(hrc)
12671 && type == NetworkAttachmentType_NATNetwork)
12672 {
12673 Bstr name;
12674 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12675 if (SUCCEEDED(hrc))
12676 {
12677 multilock.release();
12678 Utf8Str strName(name);
12679 LogRel(("VM '%s' stops using NAT network '%s'\n",
12680 mUserData->s.strName.c_str(), strName.c_str()));
12681 mParent->i_natNetworkRefDec(strName);
12682 multilock.acquire();
12683 }
12684 }
12685 }
12686 }
12687
12688 /*
12689 * An expected uninitialization can come only from #i_checkForDeath().
12690 * Otherwise it means that something's gone really wrong (for example,
12691 * the Session implementation has released the VirtualBox reference
12692 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12693 * etc). However, it's also possible, that the client releases the IPC
12694 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12695 * but the VirtualBox release event comes first to the server process.
12696 * This case is practically possible, so we should not assert on an
12697 * unexpected uninit, just log a warning.
12698 */
12699
12700 if (aReason == Uninit::Unexpected)
12701 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12702
12703 if (aReason != Uninit::Normal)
12704 {
12705 mData->mSession.mDirectControl.setNull();
12706 }
12707 else
12708 {
12709 /* this must be null here (see #OnSessionEnd()) */
12710 Assert(mData->mSession.mDirectControl.isNull());
12711 Assert(mData->mSession.mState == SessionState_Unlocking);
12712 Assert(!mData->mSession.mProgress.isNull());
12713 }
12714 if (mData->mSession.mProgress)
12715 {
12716 if (aReason == Uninit::Normal)
12717 mData->mSession.mProgress->i_notifyComplete(S_OK);
12718 else
12719 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12720 COM_IIDOF(ISession),
12721 getComponentName(),
12722 tr("The VM session was aborted"));
12723 mData->mSession.mProgress.setNull();
12724 }
12725
12726 if (mConsoleTaskData.mProgress)
12727 {
12728 Assert(aReason == Uninit::Abnormal);
12729 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12730 COM_IIDOF(ISession),
12731 getComponentName(),
12732 tr("The VM session was aborted"));
12733 mConsoleTaskData.mProgress.setNull();
12734 }
12735
12736 /* remove the association between the peer machine and this session machine */
12737 Assert( (SessionMachine*)mData->mSession.mMachine == this
12738 || aReason == Uninit::Unexpected);
12739
12740 /* reset the rest of session data */
12741 mData->mSession.mLockType = LockType_Null;
12742 mData->mSession.mMachine.setNull();
12743 mData->mSession.mState = SessionState_Unlocked;
12744 mData->mSession.mName.setNull();
12745
12746 /* destroy the machine client token before leaving the exclusive lock */
12747 if (mClientToken)
12748 {
12749 delete mClientToken;
12750 mClientToken = NULL;
12751 }
12752
12753 /* fire an event */
12754 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12755
12756 uninitDataAndChildObjects();
12757
12758 /* free the essential data structure last */
12759 mData.free();
12760
12761 /* release the exclusive lock before setting the below two to NULL */
12762 multilock.release();
12763
12764 unconst(mParent) = NULL;
12765 unconst(mPeer) = NULL;
12766
12767 AuthLibUnload(&mAuthLibCtx);
12768
12769 LogFlowThisFuncLeave();
12770}
12771
12772// util::Lockable interface
12773////////////////////////////////////////////////////////////////////////////////
12774
12775/**
12776 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12777 * with the primary Machine instance (mPeer).
12778 */
12779RWLockHandle *SessionMachine::lockHandle() const
12780{
12781 AssertReturn(mPeer != NULL, NULL);
12782 return mPeer->lockHandle();
12783}
12784
12785// IInternalMachineControl methods
12786////////////////////////////////////////////////////////////////////////////////
12787
12788/**
12789 * Passes collected guest statistics to performance collector object
12790 */
12791HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12792 ULONG aCpuKernel, ULONG aCpuIdle,
12793 ULONG aMemTotal, ULONG aMemFree,
12794 ULONG aMemBalloon, ULONG aMemShared,
12795 ULONG aMemCache, ULONG aPageTotal,
12796 ULONG aAllocVMM, ULONG aFreeVMM,
12797 ULONG aBalloonedVMM, ULONG aSharedVMM,
12798 ULONG aVmNetRx, ULONG aVmNetTx)
12799{
12800#ifdef VBOX_WITH_RESOURCE_USAGE_API
12801 if (mCollectorGuest)
12802 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12803 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12804 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12805 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12806
12807 return S_OK;
12808#else
12809 NOREF(aValidStats);
12810 NOREF(aCpuUser);
12811 NOREF(aCpuKernel);
12812 NOREF(aCpuIdle);
12813 NOREF(aMemTotal);
12814 NOREF(aMemFree);
12815 NOREF(aMemBalloon);
12816 NOREF(aMemShared);
12817 NOREF(aMemCache);
12818 NOREF(aPageTotal);
12819 NOREF(aAllocVMM);
12820 NOREF(aFreeVMM);
12821 NOREF(aBalloonedVMM);
12822 NOREF(aSharedVMM);
12823 NOREF(aVmNetRx);
12824 NOREF(aVmNetTx);
12825 return E_NOTIMPL;
12826#endif
12827}
12828
12829////////////////////////////////////////////////////////////////////////////////
12830//
12831// SessionMachine task records
12832//
12833////////////////////////////////////////////////////////////////////////////////
12834
12835/**
12836 * Task record for saving the machine state.
12837 */
12838class SessionMachine::SaveStateTask
12839 : public Machine::Task
12840{
12841public:
12842 SaveStateTask(SessionMachine *m,
12843 Progress *p,
12844 const Utf8Str &t,
12845 Reason_T enmReason,
12846 const Utf8Str &strStateFilePath)
12847 : Task(m, p, t),
12848 m_enmReason(enmReason),
12849 m_strStateFilePath(strStateFilePath)
12850 {}
12851
12852private:
12853 void handler()
12854 {
12855 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12856 }
12857
12858 Reason_T m_enmReason;
12859 Utf8Str m_strStateFilePath;
12860
12861 friend class SessionMachine;
12862};
12863
12864/**
12865 * Task thread implementation for SessionMachine::SaveState(), called from
12866 * SessionMachine::taskHandler().
12867 *
12868 * @note Locks this object for writing.
12869 *
12870 * @param task
12871 * @return
12872 */
12873void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12874{
12875 LogFlowThisFuncEnter();
12876
12877 AutoCaller autoCaller(this);
12878 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12879 if (FAILED(autoCaller.rc()))
12880 {
12881 /* we might have been uninitialized because the session was accidentally
12882 * closed by the client, so don't assert */
12883 HRESULT rc = setError(E_FAIL,
12884 tr("The session has been accidentally closed"));
12885 task.m_pProgress->i_notifyComplete(rc);
12886 LogFlowThisFuncLeave();
12887 return;
12888 }
12889
12890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12891
12892 HRESULT rc = S_OK;
12893
12894 try
12895 {
12896 ComPtr<IInternalSessionControl> directControl;
12897 if (mData->mSession.mLockType == LockType_VM)
12898 directControl = mData->mSession.mDirectControl;
12899 if (directControl.isNull())
12900 throw setError(VBOX_E_INVALID_VM_STATE,
12901 tr("Trying to save state without a running VM"));
12902 alock.release();
12903 BOOL fSuspendedBySave;
12904 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12905 Assert(!fSuspendedBySave);
12906 alock.acquire();
12907
12908 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12909 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12910 throw E_FAIL);
12911
12912 if (SUCCEEDED(rc))
12913 {
12914 mSSData->strStateFilePath = task.m_strStateFilePath;
12915
12916 /* save all VM settings */
12917 rc = i_saveSettings(NULL);
12918 // no need to check whether VirtualBox.xml needs saving also since
12919 // we can't have a name change pending at this point
12920 }
12921 else
12922 {
12923 // On failure, set the state to the state we had at the beginning.
12924 i_setMachineState(task.m_machineStateBackup);
12925 i_updateMachineStateOnClient();
12926
12927 // Delete the saved state file (might have been already created).
12928 // No need to check whether this is shared with a snapshot here
12929 // because we certainly created a fresh saved state file here.
12930 RTFileDelete(task.m_strStateFilePath.c_str());
12931 }
12932 }
12933 catch (HRESULT aRC) { rc = aRC; }
12934
12935 task.m_pProgress->i_notifyComplete(rc);
12936
12937 LogFlowThisFuncLeave();
12938}
12939
12940/**
12941 * @note Locks this object for writing.
12942 */
12943HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12944{
12945 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12946}
12947
12948HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12949{
12950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12951
12952 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12953 if (FAILED(rc)) return rc;
12954
12955 if ( mData->mMachineState != MachineState_Running
12956 && mData->mMachineState != MachineState_Paused
12957 )
12958 return setError(VBOX_E_INVALID_VM_STATE,
12959 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12960 Global::stringifyMachineState(mData->mMachineState));
12961
12962 ComObjPtr<Progress> pProgress;
12963 pProgress.createObject();
12964 rc = pProgress->init(i_getVirtualBox(),
12965 static_cast<IMachine *>(this) /* aInitiator */,
12966 tr("Saving the execution state of the virtual machine"),
12967 FALSE /* aCancelable */);
12968 if (FAILED(rc))
12969 return rc;
12970
12971 Utf8Str strStateFilePath;
12972 i_composeSavedStateFilename(strStateFilePath);
12973
12974 /* create and start the task on a separate thread (note that it will not
12975 * start working until we release alock) */
12976 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12977 rc = pTask->createThread();
12978 if (FAILED(rc))
12979 return rc;
12980
12981 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12982 i_setMachineState(MachineState_Saving);
12983 i_updateMachineStateOnClient();
12984
12985 pProgress.queryInterfaceTo(aProgress.asOutParam());
12986
12987 return S_OK;
12988}
12989
12990/**
12991 * @note Locks this object for writing.
12992 */
12993HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12994{
12995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12996
12997 HRESULT rc = i_checkStateDependency(MutableStateDep);
12998 if (FAILED(rc)) return rc;
12999
13000 if ( mData->mMachineState != MachineState_PoweredOff
13001 && mData->mMachineState != MachineState_Teleported
13002 && mData->mMachineState != MachineState_Aborted
13003 )
13004 return setError(VBOX_E_INVALID_VM_STATE,
13005 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13006 Global::stringifyMachineState(mData->mMachineState));
13007
13008 com::Utf8Str stateFilePathFull;
13009 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13010 if (RT_FAILURE(vrc))
13011 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13012 tr("Invalid saved state file path '%s' (%Rrc)"),
13013 aSavedStateFile.c_str(),
13014 vrc);
13015
13016 mSSData->strStateFilePath = stateFilePathFull;
13017
13018 /* The below i_setMachineState() will detect the state transition and will
13019 * update the settings file */
13020
13021 return i_setMachineState(MachineState_Saved);
13022}
13023
13024/**
13025 * @note Locks this object for writing.
13026 */
13027HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13028{
13029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13030
13031 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13032 if (FAILED(rc)) return rc;
13033
13034 if (mData->mMachineState != MachineState_Saved)
13035 return setError(VBOX_E_INVALID_VM_STATE,
13036 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13037 Global::stringifyMachineState(mData->mMachineState));
13038
13039 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13040
13041 /*
13042 * Saved -> PoweredOff transition will be detected in the SessionMachine
13043 * and properly handled.
13044 */
13045 rc = i_setMachineState(MachineState_PoweredOff);
13046 return rc;
13047}
13048
13049
13050/**
13051 * @note Locks the same as #i_setMachineState() does.
13052 */
13053HRESULT SessionMachine::updateState(MachineState_T aState)
13054{
13055 return i_setMachineState(aState);
13056}
13057
13058/**
13059 * @note Locks this object for writing.
13060 */
13061HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13062{
13063 IProgress *pProgress(aProgress);
13064
13065 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13066
13067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13068
13069 if (mData->mSession.mState != SessionState_Locked)
13070 return VBOX_E_INVALID_OBJECT_STATE;
13071
13072 if (!mData->mSession.mProgress.isNull())
13073 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13074
13075 /* If we didn't reference the NAT network service yet, add a reference to
13076 * force a start */
13077 if (miNATNetworksStarted < 1)
13078 {
13079 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13080 {
13081 BOOL enabled;
13082 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13083 if ( FAILED(hrc)
13084 || !enabled)
13085 continue;
13086
13087 NetworkAttachmentType_T type;
13088 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13089 if ( SUCCEEDED(hrc)
13090 && type == NetworkAttachmentType_NATNetwork)
13091 {
13092 Bstr name;
13093 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13094 if (SUCCEEDED(hrc))
13095 {
13096 Utf8Str strName(name);
13097 LogRel(("VM '%s' starts using NAT network '%s'\n",
13098 mUserData->s.strName.c_str(), strName.c_str()));
13099 mPeer->lockHandle()->unlockWrite();
13100 mParent->i_natNetworkRefInc(strName);
13101#ifdef RT_LOCK_STRICT
13102 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13103#else
13104 mPeer->lockHandle()->lockWrite();
13105#endif
13106 }
13107 }
13108 }
13109 miNATNetworksStarted++;
13110 }
13111
13112 LogFlowThisFunc(("returns S_OK.\n"));
13113 return S_OK;
13114}
13115
13116/**
13117 * @note Locks this object for writing.
13118 */
13119HRESULT SessionMachine::endPowerUp(LONG aResult)
13120{
13121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13122
13123 if (mData->mSession.mState != SessionState_Locked)
13124 return VBOX_E_INVALID_OBJECT_STATE;
13125
13126 /* Finalize the LaunchVMProcess progress object. */
13127 if (mData->mSession.mProgress)
13128 {
13129 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13130 mData->mSession.mProgress.setNull();
13131 }
13132
13133 if (SUCCEEDED((HRESULT)aResult))
13134 {
13135#ifdef VBOX_WITH_RESOURCE_USAGE_API
13136 /* The VM has been powered up successfully, so it makes sense
13137 * now to offer the performance metrics for a running machine
13138 * object. Doing it earlier wouldn't be safe. */
13139 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13140 mData->mSession.mPID);
13141#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13142 }
13143
13144 return S_OK;
13145}
13146
13147/**
13148 * @note Locks this object for writing.
13149 */
13150HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13151{
13152 LogFlowThisFuncEnter();
13153
13154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13155
13156 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13157 E_FAIL);
13158
13159 /* create a progress object to track operation completion */
13160 ComObjPtr<Progress> pProgress;
13161 pProgress.createObject();
13162 pProgress->init(i_getVirtualBox(),
13163 static_cast<IMachine *>(this) /* aInitiator */,
13164 tr("Stopping the virtual machine"),
13165 FALSE /* aCancelable */);
13166
13167 /* fill in the console task data */
13168 mConsoleTaskData.mLastState = mData->mMachineState;
13169 mConsoleTaskData.mProgress = pProgress;
13170
13171 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13172 i_setMachineState(MachineState_Stopping);
13173
13174 pProgress.queryInterfaceTo(aProgress.asOutParam());
13175
13176 return S_OK;
13177}
13178
13179/**
13180 * @note Locks this object for writing.
13181 */
13182HRESULT SessionMachine::endPoweringDown(LONG aResult,
13183 const com::Utf8Str &aErrMsg)
13184{
13185 LogFlowThisFuncEnter();
13186
13187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13188
13189 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13190 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13191 && mConsoleTaskData.mLastState != MachineState_Null,
13192 E_FAIL);
13193
13194 /*
13195 * On failure, set the state to the state we had when BeginPoweringDown()
13196 * was called (this is expected by Console::PowerDown() and the associated
13197 * task). On success the VM process already changed the state to
13198 * MachineState_PoweredOff, so no need to do anything.
13199 */
13200 if (FAILED(aResult))
13201 i_setMachineState(mConsoleTaskData.mLastState);
13202
13203 /* notify the progress object about operation completion */
13204 Assert(mConsoleTaskData.mProgress);
13205 if (SUCCEEDED(aResult))
13206 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13207 else
13208 {
13209 if (aErrMsg.length())
13210 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13211 COM_IIDOF(ISession),
13212 getComponentName(),
13213 aErrMsg.c_str());
13214 else
13215 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13216 }
13217
13218 /* clear out the temporary saved state data */
13219 mConsoleTaskData.mLastState = MachineState_Null;
13220 mConsoleTaskData.mProgress.setNull();
13221
13222 LogFlowThisFuncLeave();
13223 return S_OK;
13224}
13225
13226
13227/**
13228 * Goes through the USB filters of the given machine to see if the given
13229 * device matches any filter or not.
13230 *
13231 * @note Locks the same as USBController::hasMatchingFilter() does.
13232 */
13233HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13234 BOOL *aMatched,
13235 ULONG *aMaskedInterfaces)
13236{
13237 LogFlowThisFunc(("\n"));
13238
13239#ifdef VBOX_WITH_USB
13240 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13241#else
13242 NOREF(aDevice);
13243 NOREF(aMaskedInterfaces);
13244 *aMatched = FALSE;
13245#endif
13246
13247 return S_OK;
13248}
13249
13250/**
13251 * @note Locks the same as Host::captureUSBDevice() does.
13252 */
13253HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13254{
13255 LogFlowThisFunc(("\n"));
13256
13257#ifdef VBOX_WITH_USB
13258 /* if captureDeviceForVM() fails, it must have set extended error info */
13259 clearError();
13260 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13261 if (FAILED(rc)) return rc;
13262
13263 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13264 AssertReturn(service, E_FAIL);
13265 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13266#else
13267 NOREF(aId);
13268 return E_NOTIMPL;
13269#endif
13270}
13271
13272/**
13273 * @note Locks the same as Host::detachUSBDevice() does.
13274 */
13275HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13276 BOOL aDone)
13277{
13278 LogFlowThisFunc(("\n"));
13279
13280#ifdef VBOX_WITH_USB
13281 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13282 AssertReturn(service, E_FAIL);
13283 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13284#else
13285 NOREF(aId);
13286 NOREF(aDone);
13287 return E_NOTIMPL;
13288#endif
13289}
13290
13291/**
13292 * Inserts all machine filters to the USB proxy service and then calls
13293 * Host::autoCaptureUSBDevices().
13294 *
13295 * Called by Console from the VM process upon VM startup.
13296 *
13297 * @note Locks what called methods lock.
13298 */
13299HRESULT SessionMachine::autoCaptureUSBDevices()
13300{
13301 LogFlowThisFunc(("\n"));
13302
13303#ifdef VBOX_WITH_USB
13304 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13305 AssertComRC(rc);
13306 NOREF(rc);
13307
13308 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13309 AssertReturn(service, E_FAIL);
13310 return service->autoCaptureDevicesForVM(this);
13311#else
13312 return S_OK;
13313#endif
13314}
13315
13316/**
13317 * Removes all machine filters from the USB proxy service and then calls
13318 * Host::detachAllUSBDevices().
13319 *
13320 * Called by Console from the VM process upon normal VM termination or by
13321 * SessionMachine::uninit() upon abnormal VM termination (from under the
13322 * Machine/SessionMachine lock).
13323 *
13324 * @note Locks what called methods lock.
13325 */
13326HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330#ifdef VBOX_WITH_USB
13331 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13332 AssertComRC(rc);
13333 NOREF(rc);
13334
13335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13336 AssertReturn(service, E_FAIL);
13337 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13338#else
13339 NOREF(aDone);
13340 return S_OK;
13341#endif
13342}
13343
13344/**
13345 * @note Locks this object for writing.
13346 */
13347HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13348 ComPtr<IProgress> &aProgress)
13349{
13350 LogFlowThisFuncEnter();
13351
13352 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13353 /*
13354 * We don't assert below because it might happen that a non-direct session
13355 * informs us it is closed right after we've been uninitialized -- it's ok.
13356 */
13357
13358 /* get IInternalSessionControl interface */
13359 ComPtr<IInternalSessionControl> control(aSession);
13360
13361 ComAssertRet(!control.isNull(), E_INVALIDARG);
13362
13363 /* Creating a Progress object requires the VirtualBox lock, and
13364 * thus locking it here is required by the lock order rules. */
13365 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13366
13367 if (control == mData->mSession.mDirectControl)
13368 {
13369 /* The direct session is being normally closed by the client process
13370 * ----------------------------------------------------------------- */
13371
13372 /* go to the closing state (essential for all open*Session() calls and
13373 * for #i_checkForDeath()) */
13374 Assert(mData->mSession.mState == SessionState_Locked);
13375 mData->mSession.mState = SessionState_Unlocking;
13376
13377 /* set direct control to NULL to release the remote instance */
13378 mData->mSession.mDirectControl.setNull();
13379 LogFlowThisFunc(("Direct control is set to NULL\n"));
13380
13381 if (mData->mSession.mProgress)
13382 {
13383 /* finalize the progress, someone might wait if a frontend
13384 * closes the session before powering on the VM. */
13385 mData->mSession.mProgress->notifyComplete(E_FAIL,
13386 COM_IIDOF(ISession),
13387 getComponentName(),
13388 tr("The VM session was closed before any attempt to power it on"));
13389 mData->mSession.mProgress.setNull();
13390 }
13391
13392 /* Create the progress object the client will use to wait until
13393 * #i_checkForDeath() is called to uninitialize this session object after
13394 * it releases the IPC semaphore.
13395 * Note! Because we're "reusing" mProgress here, this must be a proxy
13396 * object just like for LaunchVMProcess. */
13397 Assert(mData->mSession.mProgress.isNull());
13398 ComObjPtr<ProgressProxy> progress;
13399 progress.createObject();
13400 ComPtr<IUnknown> pPeer(mPeer);
13401 progress->init(mParent, pPeer,
13402 Bstr(tr("Closing session")).raw(),
13403 FALSE /* aCancelable */);
13404 progress.queryInterfaceTo(aProgress.asOutParam());
13405 mData->mSession.mProgress = progress;
13406 }
13407 else
13408 {
13409 /* the remote session is being normally closed */
13410 bool found = false;
13411 for (Data::Session::RemoteControlList::iterator
13412 it = mData->mSession.mRemoteControls.begin();
13413 it != mData->mSession.mRemoteControls.end();
13414 ++it)
13415 {
13416 if (control == *it)
13417 {
13418 found = true;
13419 // This MUST be erase(it), not remove(*it) as the latter
13420 // triggers a very nasty use after free due to the place where
13421 // the value "lives".
13422 mData->mSession.mRemoteControls.erase(it);
13423 break;
13424 }
13425 }
13426 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13427 E_INVALIDARG);
13428 }
13429
13430 /* signal the client watcher thread, because the client is going away */
13431 mParent->i_updateClientWatcher();
13432
13433 LogFlowThisFuncLeave();
13434 return S_OK;
13435}
13436
13437HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13438{
13439#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13440 ULONG uID;
13441 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13442 if (RT_SUCCESS(rc))
13443 {
13444 if (aID)
13445 *aID = uID;
13446 return S_OK;
13447 }
13448 return E_FAIL;
13449#else
13450 RT_NOREF(aParms, aID);
13451 ReturnComNotImplemented();
13452#endif
13453}
13454
13455HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13456{
13457#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13458 return mParent->i_onClipboardAreaUnregister(aID);
13459#else
13460 RT_NOREF(aID);
13461 ReturnComNotImplemented();
13462#endif
13463}
13464
13465HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13466{
13467#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13468 return mParent->i_onClipboardAreaAttach(aID);
13469#else
13470 RT_NOREF(aID);
13471 ReturnComNotImplemented();
13472#endif
13473}
13474HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13475{
13476#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13477 return mParent->i_onClipboardAreaDetach(aID);
13478#else
13479 RT_NOREF(aID);
13480 ReturnComNotImplemented();
13481#endif
13482}
13483
13484HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13485{
13486#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13487 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13488 if (aID)
13489 *aID = uID;
13490 return S_OK;
13491#else
13492 RT_NOREF(aID);
13493 ReturnComNotImplemented();
13494#endif
13495}
13496
13497HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13498{
13499#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13500 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13501 if (aRefCount)
13502 *aRefCount = uRefCount;
13503 return S_OK;
13504#else
13505 RT_NOREF(aID, aRefCount);
13506 ReturnComNotImplemented();
13507#endif
13508}
13509
13510HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13511 std::vector<com::Utf8Str> &aValues,
13512 std::vector<LONG64> &aTimestamps,
13513 std::vector<com::Utf8Str> &aFlags)
13514{
13515 LogFlowThisFunc(("\n"));
13516
13517#ifdef VBOX_WITH_GUEST_PROPS
13518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13519
13520 size_t cEntries = mHWData->mGuestProperties.size();
13521 aNames.resize(cEntries);
13522 aValues.resize(cEntries);
13523 aTimestamps.resize(cEntries);
13524 aFlags.resize(cEntries);
13525
13526 size_t i = 0;
13527 for (HWData::GuestPropertyMap::const_iterator
13528 it = mHWData->mGuestProperties.begin();
13529 it != mHWData->mGuestProperties.end();
13530 ++it, ++i)
13531 {
13532 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13533 aNames[i] = it->first;
13534 aValues[i] = it->second.strValue;
13535 aTimestamps[i] = it->second.mTimestamp;
13536
13537 /* If it is NULL, keep it NULL. */
13538 if (it->second.mFlags)
13539 {
13540 GuestPropWriteFlags(it->second.mFlags, szFlags);
13541 aFlags[i] = szFlags;
13542 }
13543 else
13544 aFlags[i] = "";
13545 }
13546 return S_OK;
13547#else
13548 ReturnComNotImplemented();
13549#endif
13550}
13551
13552HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13553 const com::Utf8Str &aValue,
13554 LONG64 aTimestamp,
13555 const com::Utf8Str &aFlags)
13556{
13557 LogFlowThisFunc(("\n"));
13558
13559#ifdef VBOX_WITH_GUEST_PROPS
13560 try
13561 {
13562 /*
13563 * Convert input up front.
13564 */
13565 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13566 if (aFlags.length())
13567 {
13568 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13569 AssertRCReturn(vrc, E_INVALIDARG);
13570 }
13571
13572 /*
13573 * Now grab the object lock, validate the state and do the update.
13574 */
13575
13576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13577
13578 if (!Global::IsOnline(mData->mMachineState))
13579 {
13580 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13581 VBOX_E_INVALID_VM_STATE);
13582 }
13583
13584 i_setModified(IsModified_MachineData);
13585 mHWData.backup();
13586
13587 bool fDelete = !aValue.length();
13588 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13589 if (it != mHWData->mGuestProperties.end())
13590 {
13591 if (!fDelete)
13592 {
13593 it->second.strValue = aValue;
13594 it->second.mTimestamp = aTimestamp;
13595 it->second.mFlags = fFlags;
13596 }
13597 else
13598 mHWData->mGuestProperties.erase(it);
13599
13600 mData->mGuestPropertiesModified = TRUE;
13601 }
13602 else if (!fDelete)
13603 {
13604 HWData::GuestProperty prop;
13605 prop.strValue = aValue;
13606 prop.mTimestamp = aTimestamp;
13607 prop.mFlags = fFlags;
13608
13609 mHWData->mGuestProperties[aName] = prop;
13610 mData->mGuestPropertiesModified = TRUE;
13611 }
13612
13613 alock.release();
13614
13615 mParent->i_onGuestPropertyChange(mData->mUuid,
13616 Bstr(aName).raw(),
13617 Bstr(aValue).raw(),
13618 Bstr(aFlags).raw());
13619 }
13620 catch (...)
13621 {
13622 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13623 }
13624 return S_OK;
13625#else
13626 ReturnComNotImplemented();
13627#endif
13628}
13629
13630
13631HRESULT SessionMachine::lockMedia()
13632{
13633 AutoMultiWriteLock2 alock(this->lockHandle(),
13634 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13635
13636 AssertReturn( mData->mMachineState == MachineState_Starting
13637 || mData->mMachineState == MachineState_Restoring
13638 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13639
13640 clearError();
13641 alock.release();
13642 return i_lockMedia();
13643}
13644
13645HRESULT SessionMachine::unlockMedia()
13646{
13647 HRESULT hrc = i_unlockMedia();
13648 return hrc;
13649}
13650
13651HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13652 ComPtr<IMediumAttachment> &aNewAttachment)
13653{
13654 // request the host lock first, since might be calling Host methods for getting host drives;
13655 // next, protect the media tree all the while we're in here, as well as our member variables
13656 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13657 this->lockHandle(),
13658 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13659
13660 IMediumAttachment *iAttach = aAttachment;
13661 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13662
13663 Utf8Str ctrlName;
13664 LONG lPort;
13665 LONG lDevice;
13666 bool fTempEject;
13667 {
13668 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13669
13670 /* Need to query the details first, as the IMediumAttachment reference
13671 * might be to the original settings, which we are going to change. */
13672 ctrlName = pAttach->i_getControllerName();
13673 lPort = pAttach->i_getPort();
13674 lDevice = pAttach->i_getDevice();
13675 fTempEject = pAttach->i_getTempEject();
13676 }
13677
13678 if (!fTempEject)
13679 {
13680 /* Remember previously mounted medium. The medium before taking the
13681 * backup is not necessarily the same thing. */
13682 ComObjPtr<Medium> oldmedium;
13683 oldmedium = pAttach->i_getMedium();
13684
13685 i_setModified(IsModified_Storage);
13686 mMediumAttachments.backup();
13687
13688 // The backup operation makes the pAttach reference point to the
13689 // old settings. Re-get the correct reference.
13690 pAttach = i_findAttachment(*mMediumAttachments.data(),
13691 ctrlName,
13692 lPort,
13693 lDevice);
13694
13695 {
13696 AutoCaller autoAttachCaller(this);
13697 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13698
13699 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13700 if (!oldmedium.isNull())
13701 oldmedium->i_removeBackReference(mData->mUuid);
13702
13703 pAttach->i_updateMedium(NULL);
13704 pAttach->i_updateEjected();
13705 }
13706
13707 i_setModified(IsModified_Storage);
13708 }
13709 else
13710 {
13711 {
13712 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13713 pAttach->i_updateEjected();
13714 }
13715 }
13716
13717 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13718
13719 return S_OK;
13720}
13721
13722HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13723 com::Utf8Str &aResult)
13724{
13725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13726
13727 HRESULT hr = S_OK;
13728
13729 if (!mAuthLibCtx.hAuthLibrary)
13730 {
13731 /* Load the external authentication library. */
13732 Bstr authLibrary;
13733 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13734
13735 Utf8Str filename = authLibrary;
13736
13737 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13738 if (RT_FAILURE(vrc))
13739 hr = setErrorBoth(E_FAIL, vrc,
13740 tr("Could not load the external authentication library '%s' (%Rrc)"),
13741 filename.c_str(), vrc);
13742 }
13743
13744 /* The auth library might need the machine lock. */
13745 alock.release();
13746
13747 if (FAILED(hr))
13748 return hr;
13749
13750 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13751 {
13752 enum VRDEAuthParams
13753 {
13754 parmUuid = 1,
13755 parmGuestJudgement,
13756 parmUser,
13757 parmPassword,
13758 parmDomain,
13759 parmClientId
13760 };
13761
13762 AuthResult result = AuthResultAccessDenied;
13763
13764 Guid uuid(aAuthParams[parmUuid]);
13765 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13766 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13767
13768 result = AuthLibAuthenticate(&mAuthLibCtx,
13769 uuid.raw(), guestJudgement,
13770 aAuthParams[parmUser].c_str(),
13771 aAuthParams[parmPassword].c_str(),
13772 aAuthParams[parmDomain].c_str(),
13773 u32ClientId);
13774
13775 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13776 size_t cbPassword = aAuthParams[parmPassword].length();
13777 if (cbPassword)
13778 {
13779 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13780 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13781 }
13782
13783 if (result == AuthResultAccessGranted)
13784 aResult = "granted";
13785 else
13786 aResult = "denied";
13787
13788 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13789 aAuthParams[parmUser].c_str(), aResult.c_str()));
13790 }
13791 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13792 {
13793 enum VRDEAuthDisconnectParams
13794 {
13795 parmUuid = 1,
13796 parmClientId
13797 };
13798
13799 Guid uuid(aAuthParams[parmUuid]);
13800 uint32_t u32ClientId = 0;
13801 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13802 }
13803 else
13804 {
13805 hr = E_INVALIDARG;
13806 }
13807
13808 return hr;
13809}
13810
13811// public methods only for internal purposes
13812/////////////////////////////////////////////////////////////////////////////
13813
13814#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13815/**
13816 * Called from the client watcher thread to check for expected or unexpected
13817 * death of the client process that has a direct session to this machine.
13818 *
13819 * On Win32 and on OS/2, this method is called only when we've got the
13820 * mutex (i.e. the client has either died or terminated normally) so it always
13821 * returns @c true (the client is terminated, the session machine is
13822 * uninitialized).
13823 *
13824 * On other platforms, the method returns @c true if the client process has
13825 * terminated normally or abnormally and the session machine was uninitialized,
13826 * and @c false if the client process is still alive.
13827 *
13828 * @note Locks this object for writing.
13829 */
13830bool SessionMachine::i_checkForDeath()
13831{
13832 Uninit::Reason reason;
13833 bool terminated = false;
13834
13835 /* Enclose autoCaller with a block because calling uninit() from under it
13836 * will deadlock. */
13837 {
13838 AutoCaller autoCaller(this);
13839 if (!autoCaller.isOk())
13840 {
13841 /* return true if not ready, to cause the client watcher to exclude
13842 * the corresponding session from watching */
13843 LogFlowThisFunc(("Already uninitialized!\n"));
13844 return true;
13845 }
13846
13847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13848
13849 /* Determine the reason of death: if the session state is Closing here,
13850 * everything is fine. Otherwise it means that the client did not call
13851 * OnSessionEnd() before it released the IPC semaphore. This may happen
13852 * either because the client process has abnormally terminated, or
13853 * because it simply forgot to call ISession::Close() before exiting. We
13854 * threat the latter also as an abnormal termination (see
13855 * Session::uninit() for details). */
13856 reason = mData->mSession.mState == SessionState_Unlocking ?
13857 Uninit::Normal :
13858 Uninit::Abnormal;
13859
13860 if (mClientToken)
13861 terminated = mClientToken->release();
13862 } /* AutoCaller block */
13863
13864 if (terminated)
13865 uninit(reason);
13866
13867 return terminated;
13868}
13869
13870void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13871{
13872 LogFlowThisFunc(("\n"));
13873
13874 strTokenId.setNull();
13875
13876 AutoCaller autoCaller(this);
13877 AssertComRCReturnVoid(autoCaller.rc());
13878
13879 Assert(mClientToken);
13880 if (mClientToken)
13881 mClientToken->getId(strTokenId);
13882}
13883#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13884IToken *SessionMachine::i_getToken()
13885{
13886 LogFlowThisFunc(("\n"));
13887
13888 AutoCaller autoCaller(this);
13889 AssertComRCReturn(autoCaller.rc(), NULL);
13890
13891 Assert(mClientToken);
13892 if (mClientToken)
13893 return mClientToken->getToken();
13894 else
13895 return NULL;
13896}
13897#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13898
13899Machine::ClientToken *SessionMachine::i_getClientToken()
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), NULL);
13905
13906 return mClientToken;
13907}
13908
13909
13910/**
13911 * @note Locks this object for reading.
13912 */
13913HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13914{
13915 LogFlowThisFunc(("\n"));
13916
13917 AutoCaller autoCaller(this);
13918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13919
13920 ComPtr<IInternalSessionControl> directControl;
13921 {
13922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13923 if (mData->mSession.mLockType == LockType_VM)
13924 directControl = mData->mSession.mDirectControl;
13925 }
13926
13927 /* ignore notifications sent after #OnSessionEnd() is called */
13928 if (!directControl)
13929 return S_OK;
13930
13931 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13932}
13933
13934/**
13935 * @note Locks this object for reading.
13936 */
13937HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13938 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13939 IN_BSTR aGuestIp, LONG aGuestPort)
13940{
13941 LogFlowThisFunc(("\n"));
13942
13943 AutoCaller autoCaller(this);
13944 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13945
13946 ComPtr<IInternalSessionControl> directControl;
13947 {
13948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13949 if (mData->mSession.mLockType == LockType_VM)
13950 directControl = mData->mSession.mDirectControl;
13951 }
13952
13953 /* ignore notifications sent after #OnSessionEnd() is called */
13954 if (!directControl)
13955 return S_OK;
13956 /*
13957 * instead acting like callback we ask IVirtualBox deliver corresponding event
13958 */
13959
13960 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13961 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13962 return S_OK;
13963}
13964
13965/**
13966 * @note Locks this object for reading.
13967 */
13968HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13974
13975 ComPtr<IInternalSessionControl> directControl;
13976 {
13977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13978 if (mData->mSession.mLockType == LockType_VM)
13979 directControl = mData->mSession.mDirectControl;
13980 }
13981
13982 /* ignore notifications sent after #OnSessionEnd() is called */
13983 if (!directControl)
13984 return S_OK;
13985
13986 return directControl->OnAudioAdapterChange(audioAdapter);
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13998
13999 ComPtr<IInternalSessionControl> directControl;
14000 {
14001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14002 if (mData->mSession.mLockType == LockType_VM)
14003 directControl = mData->mSession.mDirectControl;
14004 }
14005
14006 /* ignore notifications sent after #OnSessionEnd() is called */
14007 if (!directControl)
14008 return S_OK;
14009
14010 return directControl->OnSerialPortChange(serialPort);
14011}
14012
14013/**
14014 * @note Locks this object for reading.
14015 */
14016HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14017{
14018 LogFlowThisFunc(("\n"));
14019
14020 AutoCaller autoCaller(this);
14021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14022
14023 ComPtr<IInternalSessionControl> directControl;
14024 {
14025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14026 if (mData->mSession.mLockType == LockType_VM)
14027 directControl = mData->mSession.mDirectControl;
14028 }
14029
14030 /* ignore notifications sent after #OnSessionEnd() is called */
14031 if (!directControl)
14032 return S_OK;
14033
14034 return directControl->OnParallelPortChange(parallelPort);
14035}
14036
14037/**
14038 * @note Locks this object for reading.
14039 */
14040HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14041{
14042 LogFlowThisFunc(("\n"));
14043
14044 AutoCaller autoCaller(this);
14045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14046
14047 ComPtr<IInternalSessionControl> directControl;
14048 {
14049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14050 if (mData->mSession.mLockType == LockType_VM)
14051 directControl = mData->mSession.mDirectControl;
14052 }
14053
14054 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14055
14056 /* ignore notifications sent after #OnSessionEnd() is called */
14057 if (!directControl)
14058 return S_OK;
14059
14060 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14061}
14062
14063/**
14064 * @note Locks this object for reading.
14065 */
14066HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14072
14073 ComPtr<IInternalSessionControl> directControl;
14074 {
14075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14076 if (mData->mSession.mLockType == LockType_VM)
14077 directControl = mData->mSession.mDirectControl;
14078 }
14079
14080 mParent->i_onMediumChanged(aAttachment);
14081
14082 /* ignore notifications sent after #OnSessionEnd() is called */
14083 if (!directControl)
14084 return S_OK;
14085
14086 return directControl->OnMediumChange(aAttachment, aForce);
14087}
14088
14089HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 if (mData->mSession.mLockType == LockType_VM)
14100 directControl = mData->mSession.mDirectControl;
14101 }
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnVMProcessPriorityChange(aPriority);
14108}
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 if (mData->mSession.mLockType == LockType_VM)
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 /* ignore notifications sent after #OnSessionEnd() is called */
14128 if (!directControl)
14129 return S_OK;
14130
14131 return directControl->OnCPUChange(aCPU, aRemove);
14132}
14133
14134HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14140
14141 ComPtr<IInternalSessionControl> directControl;
14142 {
14143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14144 if (mData->mSession.mLockType == LockType_VM)
14145 directControl = mData->mSession.mDirectControl;
14146 }
14147
14148 /* ignore notifications sent after #OnSessionEnd() is called */
14149 if (!directControl)
14150 return S_OK;
14151
14152 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14153}
14154
14155/**
14156 * @note Locks this object for reading.
14157 */
14158HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnVRDEServerChange(aRestart);
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 if (mData->mSession.mLockType == LockType_VM)
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnRecordingChange(aEnable);
14201}
14202
14203/**
14204 * @note Locks this object for reading.
14205 */
14206HRESULT SessionMachine::i_onUSBControllerChange()
14207{
14208 LogFlowThisFunc(("\n"));
14209
14210 AutoCaller autoCaller(this);
14211 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14212
14213 ComPtr<IInternalSessionControl> directControl;
14214 {
14215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14216 if (mData->mSession.mLockType == LockType_VM)
14217 directControl = mData->mSession.mDirectControl;
14218 }
14219
14220 /* ignore notifications sent after #OnSessionEnd() is called */
14221 if (!directControl)
14222 return S_OK;
14223
14224 return directControl->OnUSBControllerChange();
14225}
14226
14227/**
14228 * @note Locks this object for reading.
14229 */
14230HRESULT SessionMachine::i_onSharedFolderChange()
14231{
14232 LogFlowThisFunc(("\n"));
14233
14234 AutoCaller autoCaller(this);
14235 AssertComRCReturnRC(autoCaller.rc());
14236
14237 ComPtr<IInternalSessionControl> directControl;
14238 {
14239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14240 if (mData->mSession.mLockType == LockType_VM)
14241 directControl = mData->mSession.mDirectControl;
14242 }
14243
14244 /* ignore notifications sent after #OnSessionEnd() is called */
14245 if (!directControl)
14246 return S_OK;
14247
14248 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14249}
14250
14251/**
14252 * @note Locks this object for reading.
14253 */
14254HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14255{
14256 LogFlowThisFunc(("\n"));
14257
14258 AutoCaller autoCaller(this);
14259 AssertComRCReturnRC(autoCaller.rc());
14260
14261 ComPtr<IInternalSessionControl> directControl;
14262 {
14263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14264 if (mData->mSession.mLockType == LockType_VM)
14265 directControl = mData->mSession.mDirectControl;
14266 }
14267
14268 /* ignore notifications sent after #OnSessionEnd() is called */
14269 if (!directControl)
14270 return S_OK;
14271
14272 return directControl->OnClipboardModeChange(aClipboardMode);
14273}
14274
14275/**
14276 * @note Locks this object for reading.
14277 */
14278HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14279{
14280 LogFlowThisFunc(("\n"));
14281
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturnRC(autoCaller.rc());
14284
14285 ComPtr<IInternalSessionControl> directControl;
14286 {
14287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14288 if (mData->mSession.mLockType == LockType_VM)
14289 directControl = mData->mSession.mDirectControl;
14290 }
14291
14292 /* ignore notifications sent after #OnSessionEnd() is called */
14293 if (!directControl)
14294 return S_OK;
14295
14296 return directControl->OnDnDModeChange(aDnDMode);
14297}
14298
14299/**
14300 * @note Locks this object for reading.
14301 */
14302HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14303{
14304 LogFlowThisFunc(("\n"));
14305
14306 AutoCaller autoCaller(this);
14307 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14308
14309 ComPtr<IInternalSessionControl> directControl;
14310 {
14311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14312 if (mData->mSession.mLockType == LockType_VM)
14313 directControl = mData->mSession.mDirectControl;
14314 }
14315
14316 /* ignore notifications sent after #OnSessionEnd() is called */
14317 if (!directControl)
14318 return S_OK;
14319
14320 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14321}
14322
14323/**
14324 * @note Locks this object for reading.
14325 */
14326HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14327{
14328 LogFlowThisFunc(("\n"));
14329
14330 AutoCaller autoCaller(this);
14331 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14332
14333 ComPtr<IInternalSessionControl> directControl;
14334 {
14335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14336 if (mData->mSession.mLockType == LockType_VM)
14337 directControl = mData->mSession.mDirectControl;
14338 }
14339
14340 /* ignore notifications sent after #OnSessionEnd() is called */
14341 if (!directControl)
14342 return S_OK;
14343
14344 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14345}
14346
14347/**
14348 * Returns @c true if this machine's USB controller reports it has a matching
14349 * filter for the given USB device and @c false otherwise.
14350 *
14351 * @note locks this object for reading.
14352 */
14353bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14354{
14355 AutoCaller autoCaller(this);
14356 /* silently return if not ready -- this method may be called after the
14357 * direct machine session has been called */
14358 if (!autoCaller.isOk())
14359 return false;
14360
14361#ifdef VBOX_WITH_USB
14362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14363
14364 switch (mData->mMachineState)
14365 {
14366 case MachineState_Starting:
14367 case MachineState_Restoring:
14368 case MachineState_TeleportingIn:
14369 case MachineState_Paused:
14370 case MachineState_Running:
14371 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14372 * elsewhere... */
14373 alock.release();
14374 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14375 default: break;
14376 }
14377#else
14378 NOREF(aDevice);
14379 NOREF(aMaskedIfs);
14380#endif
14381 return false;
14382}
14383
14384/**
14385 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14386 */
14387HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14388 IVirtualBoxErrorInfo *aError,
14389 ULONG aMaskedIfs,
14390 const com::Utf8Str &aCaptureFilename)
14391{
14392 LogFlowThisFunc(("\n"));
14393
14394 AutoCaller autoCaller(this);
14395
14396 /* This notification may happen after the machine object has been
14397 * uninitialized (the session was closed), so don't assert. */
14398 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14399
14400 ComPtr<IInternalSessionControl> directControl;
14401 {
14402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14403 if (mData->mSession.mLockType == LockType_VM)
14404 directControl = mData->mSession.mDirectControl;
14405 }
14406
14407 /* fail on notifications sent after #OnSessionEnd() is called, it is
14408 * expected by the caller */
14409 if (!directControl)
14410 return E_FAIL;
14411
14412 /* No locks should be held at this point. */
14413 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14414 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14415
14416 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14417}
14418
14419/**
14420 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14421 */
14422HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14423 IVirtualBoxErrorInfo *aError)
14424{
14425 LogFlowThisFunc(("\n"));
14426
14427 AutoCaller autoCaller(this);
14428
14429 /* This notification may happen after the machine object has been
14430 * uninitialized (the session was closed), so don't assert. */
14431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14432
14433 ComPtr<IInternalSessionControl> directControl;
14434 {
14435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14436 if (mData->mSession.mLockType == LockType_VM)
14437 directControl = mData->mSession.mDirectControl;
14438 }
14439
14440 /* fail on notifications sent after #OnSessionEnd() is called, it is
14441 * expected by the caller */
14442 if (!directControl)
14443 return E_FAIL;
14444
14445 /* No locks should be held at this point. */
14446 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14447 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14448
14449 return directControl->OnUSBDeviceDetach(aId, aError);
14450}
14451
14452// protected methods
14453/////////////////////////////////////////////////////////////////////////////
14454
14455/**
14456 * Deletes the given file if it is no longer in use by either the current machine state
14457 * (if the machine is "saved") or any of the machine's snapshots.
14458 *
14459 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14460 * but is different for each SnapshotMachine. When calling this, the order of calling this
14461 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14462 * is therefore critical. I know, it's all rather messy.
14463 *
14464 * @param strStateFile
14465 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14466 * the test for whether the saved state file is in use.
14467 */
14468void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14469 Snapshot *pSnapshotToIgnore)
14470{
14471 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14472 if ( (strStateFile.isNotEmpty())
14473 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14474 )
14475 // ... and it must also not be shared with other snapshots
14476 if ( !mData->mFirstSnapshot
14477 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14478 // this checks the SnapshotMachine's state file paths
14479 )
14480 RTFileDelete(strStateFile.c_str());
14481}
14482
14483/**
14484 * Locks the attached media.
14485 *
14486 * All attached hard disks are locked for writing and DVD/floppy are locked for
14487 * reading. Parents of attached hard disks (if any) are locked for reading.
14488 *
14489 * This method also performs accessibility check of all media it locks: if some
14490 * media is inaccessible, the method will return a failure and a bunch of
14491 * extended error info objects per each inaccessible medium.
14492 *
14493 * Note that this method is atomic: if it returns a success, all media are
14494 * locked as described above; on failure no media is locked at all (all
14495 * succeeded individual locks will be undone).
14496 *
14497 * The caller is responsible for doing the necessary state sanity checks.
14498 *
14499 * The locks made by this method must be undone by calling #unlockMedia() when
14500 * no more needed.
14501 */
14502HRESULT SessionMachine::i_lockMedia()
14503{
14504 AutoCaller autoCaller(this);
14505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14506
14507 AutoMultiWriteLock2 alock(this->lockHandle(),
14508 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14509
14510 /* bail out if trying to lock things with already set up locking */
14511 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14512
14513 MultiResult mrc(S_OK);
14514
14515 /* Collect locking information for all medium objects attached to the VM. */
14516 for (MediumAttachmentList::const_iterator
14517 it = mMediumAttachments->begin();
14518 it != mMediumAttachments->end();
14519 ++it)
14520 {
14521 MediumAttachment *pAtt = *it;
14522 DeviceType_T devType = pAtt->i_getType();
14523 Medium *pMedium = pAtt->i_getMedium();
14524
14525 MediumLockList *pMediumLockList(new MediumLockList());
14526 // There can be attachments without a medium (floppy/dvd), and thus
14527 // it's impossible to create a medium lock list. It still makes sense
14528 // to have the empty medium lock list in the map in case a medium is
14529 // attached later.
14530 if (pMedium != NULL)
14531 {
14532 MediumType_T mediumType = pMedium->i_getType();
14533 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14534 || mediumType == MediumType_Shareable;
14535 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14536
14537 alock.release();
14538 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14539 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14540 false /* fMediumLockWriteAll */,
14541 NULL,
14542 *pMediumLockList);
14543 alock.acquire();
14544 if (FAILED(mrc))
14545 {
14546 delete pMediumLockList;
14547 mData->mSession.mLockedMedia.Clear();
14548 break;
14549 }
14550 }
14551
14552 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14553 if (FAILED(rc))
14554 {
14555 mData->mSession.mLockedMedia.Clear();
14556 mrc = setError(rc,
14557 tr("Collecting locking information for all attached media failed"));
14558 break;
14559 }
14560 }
14561
14562 if (SUCCEEDED(mrc))
14563 {
14564 /* Now lock all media. If this fails, nothing is locked. */
14565 alock.release();
14566 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14567 alock.acquire();
14568 if (FAILED(rc))
14569 {
14570 mrc = setError(rc,
14571 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14572 }
14573 }
14574
14575 return mrc;
14576}
14577
14578/**
14579 * Undoes the locks made by by #lockMedia().
14580 */
14581HRESULT SessionMachine::i_unlockMedia()
14582{
14583 AutoCaller autoCaller(this);
14584 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14585
14586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14587
14588 /* we may be holding important error info on the current thread;
14589 * preserve it */
14590 ErrorInfoKeeper eik;
14591
14592 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14593 AssertComRC(rc);
14594 return rc;
14595}
14596
14597/**
14598 * Helper to change the machine state (reimplementation).
14599 *
14600 * @note Locks this object for writing.
14601 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14602 * it can cause crashes in random places due to unexpectedly committing
14603 * the current settings. The caller is responsible for that. The call
14604 * to saveStateSettings is fine, because this method does not commit.
14605 */
14606HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14607{
14608 LogFlowThisFuncEnter();
14609 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14610
14611 AutoCaller autoCaller(this);
14612 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14613
14614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14615
14616 MachineState_T oldMachineState = mData->mMachineState;
14617
14618 AssertMsgReturn(oldMachineState != aMachineState,
14619 ("oldMachineState=%s, aMachineState=%s\n",
14620 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14621 E_FAIL);
14622
14623 HRESULT rc = S_OK;
14624
14625 int stsFlags = 0;
14626 bool deleteSavedState = false;
14627
14628 /* detect some state transitions */
14629
14630 if ( ( oldMachineState == MachineState_Saved
14631 && aMachineState == MachineState_Restoring)
14632 || ( ( oldMachineState == MachineState_PoweredOff
14633 || oldMachineState == MachineState_Teleported
14634 || oldMachineState == MachineState_Aborted
14635 )
14636 && ( aMachineState == MachineState_TeleportingIn
14637 || aMachineState == MachineState_Starting
14638 )
14639 )
14640 )
14641 {
14642 /* The EMT thread is about to start */
14643
14644 /* Nothing to do here for now... */
14645
14646 /// @todo NEWMEDIA don't let mDVDDrive and other children
14647 /// change anything when in the Starting/Restoring state
14648 }
14649 else if ( ( oldMachineState == MachineState_Running
14650 || oldMachineState == MachineState_Paused
14651 || oldMachineState == MachineState_Teleporting
14652 || oldMachineState == MachineState_OnlineSnapshotting
14653 || oldMachineState == MachineState_LiveSnapshotting
14654 || oldMachineState == MachineState_Stuck
14655 || oldMachineState == MachineState_Starting
14656 || oldMachineState == MachineState_Stopping
14657 || oldMachineState == MachineState_Saving
14658 || oldMachineState == MachineState_Restoring
14659 || oldMachineState == MachineState_TeleportingPausedVM
14660 || oldMachineState == MachineState_TeleportingIn
14661 )
14662 && ( aMachineState == MachineState_PoweredOff
14663 || aMachineState == MachineState_Saved
14664 || aMachineState == MachineState_Teleported
14665 || aMachineState == MachineState_Aborted
14666 )
14667 )
14668 {
14669 /* The EMT thread has just stopped, unlock attached media. Note that as
14670 * opposed to locking that is done from Console, we do unlocking here
14671 * because the VM process may have aborted before having a chance to
14672 * properly unlock all media it locked. */
14673
14674 unlockMedia();
14675 }
14676
14677 if (oldMachineState == MachineState_Restoring)
14678 {
14679 if (aMachineState != MachineState_Saved)
14680 {
14681 /*
14682 * delete the saved state file once the machine has finished
14683 * restoring from it (note that Console sets the state from
14684 * Restoring to Saved if the VM couldn't restore successfully,
14685 * to give the user an ability to fix an error and retry --
14686 * we keep the saved state file in this case)
14687 */
14688 deleteSavedState = true;
14689 }
14690 }
14691 else if ( oldMachineState == MachineState_Saved
14692 && ( aMachineState == MachineState_PoweredOff
14693 || aMachineState == MachineState_Aborted
14694 || aMachineState == MachineState_Teleported
14695 )
14696 )
14697 {
14698 /*
14699 * delete the saved state after SessionMachine::ForgetSavedState() is called
14700 * or if the VM process (owning a direct VM session) crashed while the
14701 * VM was Saved
14702 */
14703
14704 /// @todo (dmik)
14705 // Not sure that deleting the saved state file just because of the
14706 // client death before it attempted to restore the VM is a good
14707 // thing. But when it crashes we need to go to the Aborted state
14708 // which cannot have the saved state file associated... The only
14709 // way to fix this is to make the Aborted condition not a VM state
14710 // but a bool flag: i.e., when a crash occurs, set it to true and
14711 // change the state to PoweredOff or Saved depending on the
14712 // saved state presence.
14713
14714 deleteSavedState = true;
14715 mData->mCurrentStateModified = TRUE;
14716 stsFlags |= SaveSTS_CurStateModified;
14717 }
14718
14719 if ( aMachineState == MachineState_Starting
14720 || aMachineState == MachineState_Restoring
14721 || aMachineState == MachineState_TeleportingIn
14722 )
14723 {
14724 /* set the current state modified flag to indicate that the current
14725 * state is no more identical to the state in the
14726 * current snapshot */
14727 if (!mData->mCurrentSnapshot.isNull())
14728 {
14729 mData->mCurrentStateModified = TRUE;
14730 stsFlags |= SaveSTS_CurStateModified;
14731 }
14732 }
14733
14734 if (deleteSavedState)
14735 {
14736 if (mRemoveSavedState)
14737 {
14738 Assert(!mSSData->strStateFilePath.isEmpty());
14739
14740 // it is safe to delete the saved state file if ...
14741 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14742 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14743 // ... none of the snapshots share the saved state file
14744 )
14745 RTFileDelete(mSSData->strStateFilePath.c_str());
14746 }
14747
14748 mSSData->strStateFilePath.setNull();
14749 stsFlags |= SaveSTS_StateFilePath;
14750 }
14751
14752 /* redirect to the underlying peer machine */
14753 mPeer->i_setMachineState(aMachineState);
14754
14755 if ( oldMachineState != MachineState_RestoringSnapshot
14756 && ( aMachineState == MachineState_PoweredOff
14757 || aMachineState == MachineState_Teleported
14758 || aMachineState == MachineState_Aborted
14759 || aMachineState == MachineState_Saved))
14760 {
14761 /* the machine has stopped execution
14762 * (or the saved state file was adopted) */
14763 stsFlags |= SaveSTS_StateTimeStamp;
14764 }
14765
14766 if ( ( oldMachineState == MachineState_PoweredOff
14767 || oldMachineState == MachineState_Aborted
14768 || oldMachineState == MachineState_Teleported
14769 )
14770 && aMachineState == MachineState_Saved)
14771 {
14772 /* the saved state file was adopted */
14773 Assert(!mSSData->strStateFilePath.isEmpty());
14774 stsFlags |= SaveSTS_StateFilePath;
14775 }
14776
14777#ifdef VBOX_WITH_GUEST_PROPS
14778 if ( aMachineState == MachineState_PoweredOff
14779 || aMachineState == MachineState_Aborted
14780 || aMachineState == MachineState_Teleported)
14781 {
14782 /* Make sure any transient guest properties get removed from the
14783 * property store on shutdown. */
14784 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14785
14786 /* remove it from the settings representation */
14787 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14788 for (settings::GuestPropertiesList::iterator
14789 it = llGuestProperties.begin();
14790 it != llGuestProperties.end();
14791 /*nothing*/)
14792 {
14793 const settings::GuestProperty &prop = *it;
14794 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14795 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14796 {
14797 it = llGuestProperties.erase(it);
14798 fNeedsSaving = true;
14799 }
14800 else
14801 {
14802 ++it;
14803 }
14804 }
14805
14806 /* Additionally remove it from the HWData representation. Required to
14807 * keep everything in sync, as this is what the API keeps using. */
14808 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14809 for (HWData::GuestPropertyMap::iterator
14810 it = llHWGuestProperties.begin();
14811 it != llHWGuestProperties.end();
14812 /*nothing*/)
14813 {
14814 uint32_t fFlags = it->second.mFlags;
14815 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14816 {
14817 /* iterator where we need to continue after the erase call
14818 * (C++03 is a fact still, and it doesn't return the iterator
14819 * which would allow continuing) */
14820 HWData::GuestPropertyMap::iterator it2 = it;
14821 ++it2;
14822 llHWGuestProperties.erase(it);
14823 it = it2;
14824 fNeedsSaving = true;
14825 }
14826 else
14827 {
14828 ++it;
14829 }
14830 }
14831
14832 if (fNeedsSaving)
14833 {
14834 mData->mCurrentStateModified = TRUE;
14835 stsFlags |= SaveSTS_CurStateModified;
14836 }
14837 }
14838#endif /* VBOX_WITH_GUEST_PROPS */
14839
14840 rc = i_saveStateSettings(stsFlags);
14841
14842 if ( ( oldMachineState != MachineState_PoweredOff
14843 && oldMachineState != MachineState_Aborted
14844 && oldMachineState != MachineState_Teleported
14845 )
14846 && ( aMachineState == MachineState_PoweredOff
14847 || aMachineState == MachineState_Aborted
14848 || aMachineState == MachineState_Teleported
14849 )
14850 )
14851 {
14852 /* we've been shut down for any reason */
14853 /* no special action so far */
14854 }
14855
14856 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14857 LogFlowThisFuncLeave();
14858 return rc;
14859}
14860
14861/**
14862 * Sends the current machine state value to the VM process.
14863 *
14864 * @note Locks this object for reading, then calls a client process.
14865 */
14866HRESULT SessionMachine::i_updateMachineStateOnClient()
14867{
14868 AutoCaller autoCaller(this);
14869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14870
14871 ComPtr<IInternalSessionControl> directControl;
14872 {
14873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14874 AssertReturn(!!mData, E_FAIL);
14875 if (mData->mSession.mLockType == LockType_VM)
14876 directControl = mData->mSession.mDirectControl;
14877
14878 /* directControl may be already set to NULL here in #OnSessionEnd()
14879 * called too early by the direct session process while there is still
14880 * some operation (like deleting the snapshot) in progress. The client
14881 * process in this case is waiting inside Session::close() for the
14882 * "end session" process object to complete, while #uninit() called by
14883 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14884 * operation to complete. For now, we accept this inconsistent behavior
14885 * and simply do nothing here. */
14886
14887 if (mData->mSession.mState == SessionState_Unlocking)
14888 return S_OK;
14889 }
14890
14891 /* ignore notifications sent after #OnSessionEnd() is called */
14892 if (!directControl)
14893 return S_OK;
14894
14895 return directControl->UpdateMachineState(mData->mMachineState);
14896}
14897
14898
14899/*static*/
14900HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14901{
14902 va_list args;
14903 va_start(args, pcszMsg);
14904 HRESULT rc = setErrorInternal(aResultCode,
14905 getStaticClassIID(),
14906 getStaticComponentName(),
14907 Utf8Str(pcszMsg, args),
14908 false /* aWarning */,
14909 true /* aLogIt */);
14910 va_end(args);
14911 return rc;
14912}
14913
14914
14915HRESULT Machine::updateState(MachineState_T aState)
14916{
14917 NOREF(aState);
14918 ReturnComNotImplemented();
14919}
14920
14921HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14922{
14923 NOREF(aProgress);
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::endPowerUp(LONG aResult)
14928{
14929 NOREF(aResult);
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14934{
14935 NOREF(aProgress);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::endPoweringDown(LONG aResult,
14940 const com::Utf8Str &aErrMsg)
14941{
14942 NOREF(aResult);
14943 NOREF(aErrMsg);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14948 BOOL *aMatched,
14949 ULONG *aMaskedInterfaces)
14950{
14951 NOREF(aDevice);
14952 NOREF(aMatched);
14953 NOREF(aMaskedInterfaces);
14954 ReturnComNotImplemented();
14955
14956}
14957
14958HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14959{
14960 NOREF(aId); NOREF(aCaptureFilename);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14965 BOOL aDone)
14966{
14967 NOREF(aId);
14968 NOREF(aDone);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::autoCaptureUSBDevices()
14973{
14974 ReturnComNotImplemented();
14975}
14976
14977HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14978{
14979 NOREF(aDone);
14980 ReturnComNotImplemented();
14981}
14982
14983HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14984 ComPtr<IProgress> &aProgress)
14985{
14986 NOREF(aSession);
14987 NOREF(aProgress);
14988 ReturnComNotImplemented();
14989}
14990
14991HRESULT Machine::finishOnlineMergeMedium()
14992{
14993 ReturnComNotImplemented();
14994}
14995
14996HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14997{
14998 RT_NOREF(aParms, aID);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15003{
15004 RT_NOREF(aID);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::clipboardAreaAttach(ULONG aID)
15009{
15010 RT_NOREF(aID);
15011 ReturnComNotImplemented();
15012}
15013HRESULT Machine::clipboardAreaDetach(ULONG aID)
15014{
15015 RT_NOREF(aID);
15016 ReturnComNotImplemented();
15017}
15018
15019HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15020{
15021 RT_NOREF(aID);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15026{
15027 RT_NOREF(aID, aRefCount);
15028 ReturnComNotImplemented();
15029}
15030
15031HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15032 std::vector<com::Utf8Str> &aValues,
15033 std::vector<LONG64> &aTimestamps,
15034 std::vector<com::Utf8Str> &aFlags)
15035{
15036 NOREF(aNames);
15037 NOREF(aValues);
15038 NOREF(aTimestamps);
15039 NOREF(aFlags);
15040 ReturnComNotImplemented();
15041}
15042
15043HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15044 const com::Utf8Str &aValue,
15045 LONG64 aTimestamp,
15046 const com::Utf8Str &aFlags)
15047{
15048 NOREF(aName);
15049 NOREF(aValue);
15050 NOREF(aTimestamp);
15051 NOREF(aFlags);
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::lockMedia()
15056{
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::unlockMedia()
15061{
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15066 ComPtr<IMediumAttachment> &aNewAttachment)
15067{
15068 NOREF(aAttachment);
15069 NOREF(aNewAttachment);
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15074 ULONG aCpuUser,
15075 ULONG aCpuKernel,
15076 ULONG aCpuIdle,
15077 ULONG aMemTotal,
15078 ULONG aMemFree,
15079 ULONG aMemBalloon,
15080 ULONG aMemShared,
15081 ULONG aMemCache,
15082 ULONG aPagedTotal,
15083 ULONG aMemAllocTotal,
15084 ULONG aMemFreeTotal,
15085 ULONG aMemBalloonTotal,
15086 ULONG aMemSharedTotal,
15087 ULONG aVmNetRx,
15088 ULONG aVmNetTx)
15089{
15090 NOREF(aValidStats);
15091 NOREF(aCpuUser);
15092 NOREF(aCpuKernel);
15093 NOREF(aCpuIdle);
15094 NOREF(aMemTotal);
15095 NOREF(aMemFree);
15096 NOREF(aMemBalloon);
15097 NOREF(aMemShared);
15098 NOREF(aMemCache);
15099 NOREF(aPagedTotal);
15100 NOREF(aMemAllocTotal);
15101 NOREF(aMemFreeTotal);
15102 NOREF(aMemBalloonTotal);
15103 NOREF(aMemSharedTotal);
15104 NOREF(aVmNetRx);
15105 NOREF(aVmNetTx);
15106 ReturnComNotImplemented();
15107}
15108
15109HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15110 com::Utf8Str &aResult)
15111{
15112 NOREF(aAuthParams);
15113 NOREF(aResult);
15114 ReturnComNotImplemented();
15115}
15116
15117com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15118{
15119 com::Utf8Str strControllerName = "Unknown";
15120 switch (aBusType)
15121 {
15122 case StorageBus_IDE:
15123 {
15124 strControllerName = "IDE";
15125 break;
15126 }
15127 case StorageBus_SATA:
15128 {
15129 strControllerName = "SATA";
15130 break;
15131 }
15132 case StorageBus_SCSI:
15133 {
15134 strControllerName = "SCSI";
15135 break;
15136 }
15137 case StorageBus_Floppy:
15138 {
15139 strControllerName = "Floppy";
15140 break;
15141 }
15142 case StorageBus_SAS:
15143 {
15144 strControllerName = "SAS";
15145 break;
15146 }
15147 case StorageBus_USB:
15148 {
15149 strControllerName = "USB";
15150 break;
15151 }
15152 default:
15153 break;
15154 }
15155 return strControllerName;
15156}
15157
15158HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15159{
15160 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15161
15162 AutoCaller autoCaller(this);
15163 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15164
15165 HRESULT rc = S_OK;
15166
15167 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15168 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15169 rc = getUSBDeviceFilters(usbDeviceFilters);
15170 if (FAILED(rc)) return rc;
15171
15172 NOREF(aFlags);
15173 com::Utf8Str osTypeId;
15174 ComObjPtr<GuestOSType> osType = NULL;
15175
15176 /* Get the guest os type as a string from the VB. */
15177 rc = getOSTypeId(osTypeId);
15178 if (FAILED(rc)) return rc;
15179
15180 /* Get the os type obj that coresponds, can be used to get
15181 * the defaults for this guest OS. */
15182 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15183 if (FAILED(rc)) return rc;
15184
15185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15186
15187 /* Let the OS type select 64-bit ness. */
15188 mHWData->mLongMode = osType->i_is64Bit()
15189 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15190
15191 /* Let the OS type enable the X2APIC */
15192 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15193
15194 /* This one covers IOAPICEnabled. */
15195 mBIOSSettings->i_applyDefaults(osType);
15196
15197 /* Initialize default record settings. */
15198 mRecordingSettings->i_applyDefaults();
15199
15200 /* Initialize default BIOS settings here */
15201 /* Hardware virtualization must be ON by default */
15202 mHWData->mAPIC = true;
15203 mHWData->mHWVirtExEnabled = true;
15204
15205 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15212 if (FAILED(rc)) return rc;
15213
15214 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15215 if (FAILED(rc)) return rc;
15216
15217 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15218 if (FAILED(rc)) return rc;
15219
15220 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15221 if (FAILED(rc)) return rc;
15222
15223 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15224 if (FAILED(rc)) return rc;
15225
15226 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15227 if (FAILED(rc)) return rc;
15228
15229 BOOL mRTCUseUTC;
15230 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15231 if (FAILED(rc)) return rc;
15232
15233 setRTCUseUTC(mRTCUseUTC);
15234 if (FAILED(rc)) return rc;
15235
15236 /* the setter does more than just the assignment, so use it */
15237 ChipsetType_T enmChipsetType;
15238 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = COMSETTER(ChipsetType)(enmChipsetType);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15245 if (FAILED(rc)) return rc;
15246
15247 /* Apply network adapters defaults */
15248 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15249 mNetworkAdapters[slot]->i_applyDefaults(osType);
15250
15251 /* Apply serial port defaults */
15252 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15253 mSerialPorts[slot]->i_applyDefaults(osType);
15254
15255 /* Apply parallel port defaults - not OS dependent*/
15256 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15257 mParallelPorts[slot]->i_applyDefaults();
15258
15259 /* Audio stuff. */
15260 AudioControllerType_T audioController;
15261 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15262 if (FAILED(rc)) return rc;
15263
15264 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15265 if (FAILED(rc)) return rc;
15266
15267 AudioCodecType_T audioCodec;
15268 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15269 if (FAILED(rc)) return rc;
15270
15271 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15275 if (FAILED(rc)) return rc;
15276
15277 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15278 if (FAILED(rc)) return rc;
15279
15280 /* Storage Controllers */
15281 StorageControllerType_T hdStorageControllerType;
15282 StorageBus_T hdStorageBusType;
15283 StorageControllerType_T dvdStorageControllerType;
15284 StorageBus_T dvdStorageBusType;
15285 BOOL recommendedFloppy;
15286 ComPtr<IStorageController> floppyController;
15287 ComPtr<IStorageController> hdController;
15288 ComPtr<IStorageController> dvdController;
15289 Utf8Str strFloppyName, strDVDName, strHDName;
15290
15291 /* GUI auto generates controller names using bus type. Do the same*/
15292 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15293
15294 /* Floppy recommended? add one. */
15295 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15296 if (FAILED(rc)) return rc;
15297 if (recommendedFloppy)
15298 {
15299 rc = addStorageController(strFloppyName,
15300 StorageBus_Floppy,
15301 floppyController);
15302 if (FAILED(rc)) return rc;
15303 }
15304
15305 /* Setup one DVD storage controller. */
15306 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15307 if (FAILED(rc)) return rc;
15308
15309 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15310 if (FAILED(rc)) return rc;
15311
15312 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15313
15314 rc = addStorageController(strDVDName,
15315 dvdStorageBusType,
15316 dvdController);
15317 if (FAILED(rc)) return rc;
15318
15319 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15320 if (FAILED(rc)) return rc;
15321
15322 /* Setup one HDD storage controller. */
15323 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15324 if (FAILED(rc)) return rc;
15325
15326 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15327 if (FAILED(rc)) return rc;
15328
15329 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15330
15331 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15332 {
15333 rc = addStorageController(strHDName,
15334 hdStorageBusType,
15335 hdController);
15336 if (FAILED(rc)) return rc;
15337
15338 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15339 if (FAILED(rc)) return rc;
15340 }
15341 else
15342 {
15343 /* The HD controller is the same as DVD: */
15344 hdController = dvdController;
15345 }
15346
15347 /* Limit the AHCI port count if it's used because windows has trouble with
15348 * too many ports and other guest (OS X in particular) may take extra long
15349 * boot: */
15350
15351 // pParent = static_cast<Medium*>(aP)
15352 IStorageController *temp = hdController;
15353 ComObjPtr<StorageController> storageController;
15354 storageController = static_cast<StorageController *>(temp);
15355
15356 // tempHDController = aHDController;
15357 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15358 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15359 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15360 storageController->COMSETTER(PortCount)(1);
15361
15362 /* USB stuff */
15363
15364 bool ohciEnabled = false;
15365
15366 ComPtr<IUSBController> usbController;
15367 BOOL recommendedUSB3;
15368 BOOL recommendedUSB;
15369 BOOL usbProxyAvailable;
15370
15371 getUSBProxyAvailable(&usbProxyAvailable);
15372 if (FAILED(rc)) return rc;
15373
15374 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15375 if (FAILED(rc)) return rc;
15376 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15377 if (FAILED(rc)) return rc;
15378
15379 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15380 {
15381#ifdef VBOX_WITH_EXTPACK
15382 /* USB 3.0 is only available if the proper ExtPack is installed. */
15383 ExtPackManager *aManager = mParent->i_getExtPackManager();
15384 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15385 {
15386 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15387 if (FAILED(rc)) return rc;
15388
15389 /* xHci includes OHCI */
15390 ohciEnabled = true;
15391 }
15392#endif
15393 }
15394 if ( !ohciEnabled
15395 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15396 {
15397 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15398 if (FAILED(rc)) return rc;
15399 ohciEnabled = true;
15400
15401#ifdef VBOX_WITH_EXTPACK
15402 /* USB 2.0 is only available if the proper ExtPack is installed.
15403 * Note. Configuring EHCI here and providing messages about
15404 * the missing extpack isn't exactly clean, but it is a
15405 * necessary evil to patch over legacy compatability issues
15406 * introduced by the new distribution model. */
15407 ExtPackManager *manager = mParent->i_getExtPackManager();
15408 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15409 {
15410 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15411 if (FAILED(rc)) return rc;
15412 }
15413#endif
15414 }
15415
15416 /* Set recommended human interface device types: */
15417 BOOL recommendedUSBHID;
15418 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15419 if (FAILED(rc)) return rc;
15420
15421 if (recommendedUSBHID)
15422 {
15423 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15424 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15425 if (!ohciEnabled && !usbDeviceFilters.isNull())
15426 {
15427 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15428 if (FAILED(rc)) return rc;
15429 }
15430 }
15431
15432 BOOL recommendedUSBTablet;
15433 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15434 if (FAILED(rc)) return rc;
15435
15436 if (recommendedUSBTablet)
15437 {
15438 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15439 if (!ohciEnabled && !usbDeviceFilters.isNull())
15440 {
15441 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15442 if (FAILED(rc)) return rc;
15443 }
15444 }
15445 return S_OK;
15446}
15447
15448/* This isn't handled entirely by the wrapper generator yet. */
15449#ifdef VBOX_WITH_XPCOM
15450NS_DECL_CLASSINFO(SessionMachine)
15451NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15452
15453NS_DECL_CLASSINFO(SnapshotMachine)
15454NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15455#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