VirtualBox

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

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.2 KB
Line 
1/* $Id: MachineImpl.cpp 78897 2019-05-31 15:23:14Z 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::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2907{
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 /** @todo deal with running state change. */
2911 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mUserData.backup();
2916 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2917 return S_OK;
2918}
2919
2920HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2925 return S_OK;
2926}
2927
2928HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2929{
2930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2931
2932 /** @todo deal with running state change. */
2933 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2934 if (FAILED(rc)) return rc;
2935
2936 i_setModified(IsModified_MachineData);
2937 mUserData.backup();
2938 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2943{
2944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2947 return S_OK;
2948}
2949
2950HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2951{
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 /** @todo deal with running state change. */
2955 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2956 if (FAILED(rc)) return rc;
2957
2958 i_setModified(IsModified_MachineData);
2959 mUserData.backup();
2960 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2961 return S_OK;
2962}
2963
2964HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2965{
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2969
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /** @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2984
2985 return S_OK;
2986}
2987
2988HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2989{
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2993 return S_OK;
2994}
2995
2996HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2997{
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999
3000 /** @todo deal with running state change. */
3001 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3002 if (FAILED(rc)) return rc;
3003
3004 i_setModified(IsModified_MachineData);
3005 mUserData.backup();
3006 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3007 return S_OK;
3008}
3009
3010HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3015
3016 return S_OK;
3017}
3018
3019HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3020{
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 /* Only allow it to be set to true when PoweredOff or Aborted.
3024 (Clearing it is always permitted.) */
3025 if ( aRTCUseUTC
3026 && mData->mRegistered
3027 && ( !i_isSessionMachine()
3028 || ( mData->mMachineState != MachineState_PoweredOff
3029 && mData->mMachineState != MachineState_Teleported
3030 && mData->mMachineState != MachineState_Aborted
3031 )
3032 )
3033 )
3034 return setError(VBOX_E_INVALID_VM_STATE,
3035 tr("The machine is not powered off (state is %s)"),
3036 Global::stringifyMachineState(mData->mMachineState));
3037
3038 i_setModified(IsModified_MachineData);
3039 mUserData.backup();
3040 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3041
3042 return S_OK;
3043}
3044
3045HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3046{
3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3055{
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 HRESULT rc = i_checkStateDependency(MutableStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3069{
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 *aIOCacheSize = mHWData->mIOCacheSize;
3073
3074 return S_OK;
3075}
3076
3077HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3078{
3079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 HRESULT rc = i_checkStateDependency(MutableStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mHWData.backup();
3086 mHWData->mIOCacheSize = aIOCacheSize;
3087
3088 return S_OK;
3089}
3090
3091
3092/**
3093 * @note Locks objects!
3094 */
3095HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3096 LockType_T aLockType)
3097{
3098 /* check the session state */
3099 SessionState_T state;
3100 HRESULT rc = aSession->COMGETTER(State)(&state);
3101 if (FAILED(rc)) return rc;
3102
3103 if (state != SessionState_Unlocked)
3104 return setError(VBOX_E_INVALID_OBJECT_STATE,
3105 tr("The given session is busy"));
3106
3107 // get the client's IInternalSessionControl interface
3108 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3109 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3110 E_INVALIDARG);
3111
3112 // session name (only used in some code paths)
3113 Utf8Str strSessionName;
3114
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 if (!mData->mRegistered)
3118 return setError(E_UNEXPECTED,
3119 tr("The machine '%s' is not registered"),
3120 mUserData->s.strName.c_str());
3121
3122 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3123
3124 SessionState_T oldState = mData->mSession.mState;
3125 /* Hack: in case the session is closing and there is a progress object
3126 * which allows waiting for the session to be closed, take the opportunity
3127 * and do a limited wait (max. 1 second). This helps a lot when the system
3128 * is busy and thus session closing can take a little while. */
3129 if ( mData->mSession.mState == SessionState_Unlocking
3130 && mData->mSession.mProgress)
3131 {
3132 alock.release();
3133 mData->mSession.mProgress->WaitForCompletion(1000);
3134 alock.acquire();
3135 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3136 }
3137
3138 // try again now
3139 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3140 // (i.e. session machine exists)
3141 && (aLockType == LockType_Shared) // caller wants a shared link to the
3142 // existing session that holds the write lock:
3143 )
3144 {
3145 // OK, share the session... we are now dealing with three processes:
3146 // 1) VBoxSVC (where this code runs);
3147 // 2) process C: the caller's client process (who wants a shared session);
3148 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3149
3150 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3151 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3152 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3153 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3154 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3155
3156 /*
3157 * Release the lock before calling the client process. It's safe here
3158 * since the only thing to do after we get the lock again is to add
3159 * the remote control to the list (which doesn't directly influence
3160 * anything).
3161 */
3162 alock.release();
3163
3164 // get the console of the session holding the write lock (this is a remote call)
3165 ComPtr<IConsole> pConsoleW;
3166 if (mData->mSession.mLockType == LockType_VM)
3167 {
3168 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3169 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3170 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3171 if (FAILED(rc))
3172 // the failure may occur w/o any error info (from RPC), so provide one
3173 return setError(VBOX_E_VM_ERROR,
3174 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3175 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3176 }
3177
3178 // share the session machine and W's console with the caller's session
3179 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3180 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3181 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3182
3183 if (FAILED(rc))
3184 // the failure may occur w/o any error info (from RPC), so provide one
3185 return setError(VBOX_E_VM_ERROR,
3186 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3187 alock.acquire();
3188
3189 // need to revalidate the state after acquiring the lock again
3190 if (mData->mSession.mState != SessionState_Locked)
3191 {
3192 pSessionControl->Uninitialize();
3193 return setError(VBOX_E_INVALID_SESSION_STATE,
3194 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3195 mUserData->s.strName.c_str());
3196 }
3197
3198 // add the caller's session to the list
3199 mData->mSession.mRemoteControls.push_back(pSessionControl);
3200 }
3201 else if ( mData->mSession.mState == SessionState_Locked
3202 || mData->mSession.mState == SessionState_Unlocking
3203 )
3204 {
3205 // sharing not permitted, or machine still unlocking:
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3208 mUserData->s.strName.c_str());
3209 }
3210 else
3211 {
3212 // machine is not locked: then write-lock the machine (create the session machine)
3213
3214 // must not be busy
3215 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3216
3217 // get the caller's session PID
3218 RTPROCESS pid = NIL_RTPROCESS;
3219 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3220 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3221 Assert(pid != NIL_RTPROCESS);
3222
3223 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3224
3225 if (fLaunchingVMProcess)
3226 {
3227 if (mData->mSession.mPID == NIL_RTPROCESS)
3228 {
3229 // two or more clients racing for a lock, the one which set the
3230 // session state to Spawning will win, the others will get an
3231 // error as we can't decide here if waiting a little would help
3232 // (only for shared locks this would avoid an error)
3233 return setError(VBOX_E_INVALID_OBJECT_STATE,
3234 tr("The machine '%s' already has a lock request pending"),
3235 mUserData->s.strName.c_str());
3236 }
3237
3238 // this machine is awaiting for a spawning session to be opened:
3239 // then the calling process must be the one that got started by
3240 // LaunchVMProcess()
3241
3242 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3243 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3244
3245#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3246 /* Hardened windows builds spawns three processes when a VM is
3247 launched, the 3rd one is the one that will end up here. */
3248 RTPROCESS ppid;
3249 int rc = RTProcQueryParent(pid, &ppid);
3250 if (RT_SUCCESS(rc))
3251 rc = RTProcQueryParent(ppid, &ppid);
3252 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3253 || rc == VERR_ACCESS_DENIED)
3254 {
3255 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3256 mData->mSession.mPID = pid;
3257 }
3258#endif
3259
3260 if (mData->mSession.mPID != pid)
3261 return setError(E_ACCESSDENIED,
3262 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3263 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3264 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3265 }
3266
3267 // create the mutable SessionMachine from the current machine
3268 ComObjPtr<SessionMachine> sessionMachine;
3269 sessionMachine.createObject();
3270 rc = sessionMachine->init(this);
3271 AssertComRC(rc);
3272
3273 /* NOTE: doing return from this function after this point but
3274 * before the end is forbidden since it may call SessionMachine::uninit()
3275 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3276 * lock while still holding the Machine lock in alock so that a deadlock
3277 * is possible due to the wrong lock order. */
3278
3279 if (SUCCEEDED(rc))
3280 {
3281 /*
3282 * Set the session state to Spawning to protect against subsequent
3283 * attempts to open a session and to unregister the machine after
3284 * we release the lock.
3285 */
3286 SessionState_T origState = mData->mSession.mState;
3287 mData->mSession.mState = SessionState_Spawning;
3288
3289#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3290 /* Get the client token ID to be passed to the client process */
3291 Utf8Str strTokenId;
3292 sessionMachine->i_getTokenId(strTokenId);
3293 Assert(!strTokenId.isEmpty());
3294#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3295 /* Get the client token to be passed to the client process */
3296 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3297 /* The token is now "owned" by pToken, fix refcount */
3298 if (!pToken.isNull())
3299 pToken->Release();
3300#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3301
3302 /*
3303 * Release the lock before calling the client process -- it will call
3304 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3305 * because the state is Spawning, so that LaunchVMProcess() and
3306 * LockMachine() calls will fail. This method, called before we
3307 * acquire the lock again, will fail because of the wrong PID.
3308 *
3309 * Note that mData->mSession.mRemoteControls accessed outside
3310 * the lock may not be modified when state is Spawning, so it's safe.
3311 */
3312 alock.release();
3313
3314 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3315#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3316 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3317#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3318 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3319 /* Now the token is owned by the client process. */
3320 pToken.setNull();
3321#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3322 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3323
3324 /* The failure may occur w/o any error info (from RPC), so provide one */
3325 if (FAILED(rc))
3326 setError(VBOX_E_VM_ERROR,
3327 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3328
3329 // get session name, either to remember or to compare against
3330 // the already known session name.
3331 {
3332 Bstr bstrSessionName;
3333 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3334 if (SUCCEEDED(rc2))
3335 strSessionName = bstrSessionName;
3336 }
3337
3338 if ( SUCCEEDED(rc)
3339 && fLaunchingVMProcess
3340 )
3341 {
3342 /* complete the remote session initialization */
3343
3344 /* get the console from the direct session */
3345 ComPtr<IConsole> console;
3346 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3347 ComAssertComRC(rc);
3348
3349 if (SUCCEEDED(rc) && !console)
3350 {
3351 ComAssert(!!console);
3352 rc = E_FAIL;
3353 }
3354
3355 /* assign machine & console to the remote session */
3356 if (SUCCEEDED(rc))
3357 {
3358 /*
3359 * after LaunchVMProcess(), the first and the only
3360 * entry in remoteControls is that remote session
3361 */
3362 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3363 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3364 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3365
3366 /* The failure may occur w/o any error info (from RPC), so provide one */
3367 if (FAILED(rc))
3368 setError(VBOX_E_VM_ERROR,
3369 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3370 }
3371
3372 if (FAILED(rc))
3373 pSessionControl->Uninitialize();
3374 }
3375
3376 /* acquire the lock again */
3377 alock.acquire();
3378
3379 /* Restore the session state */
3380 mData->mSession.mState = origState;
3381 }
3382
3383 // finalize spawning anyway (this is why we don't return on errors above)
3384 if (fLaunchingVMProcess)
3385 {
3386 Assert(mData->mSession.mName == strSessionName);
3387 /* Note that the progress object is finalized later */
3388 /** @todo Consider checking mData->mSession.mProgress for cancellation
3389 * around here. */
3390
3391 /* We don't reset mSession.mPID here because it is necessary for
3392 * SessionMachine::uninit() to reap the child process later. */
3393
3394 if (FAILED(rc))
3395 {
3396 /* Close the remote session, remove the remote control from the list
3397 * and reset session state to Closed (@note keep the code in sync
3398 * with the relevant part in checkForSpawnFailure()). */
3399
3400 Assert(mData->mSession.mRemoteControls.size() == 1);
3401 if (mData->mSession.mRemoteControls.size() == 1)
3402 {
3403 ErrorInfoKeeper eik;
3404 mData->mSession.mRemoteControls.front()->Uninitialize();
3405 }
3406
3407 mData->mSession.mRemoteControls.clear();
3408 mData->mSession.mState = SessionState_Unlocked;
3409 }
3410 }
3411 else
3412 {
3413 /* memorize PID of the directly opened session */
3414 if (SUCCEEDED(rc))
3415 mData->mSession.mPID = pid;
3416 }
3417
3418 if (SUCCEEDED(rc))
3419 {
3420 mData->mSession.mLockType = aLockType;
3421 /* memorize the direct session control and cache IUnknown for it */
3422 mData->mSession.mDirectControl = pSessionControl;
3423 mData->mSession.mState = SessionState_Locked;
3424 if (!fLaunchingVMProcess)
3425 mData->mSession.mName = strSessionName;
3426 /* associate the SessionMachine with this Machine */
3427 mData->mSession.mMachine = sessionMachine;
3428
3429 /* request an IUnknown pointer early from the remote party for later
3430 * identity checks (it will be internally cached within mDirectControl
3431 * at least on XPCOM) */
3432 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3433 NOREF(unk);
3434 }
3435
3436 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3437 * would break the lock order */
3438 alock.release();
3439
3440 /* uninitialize the created session machine on failure */
3441 if (FAILED(rc))
3442 sessionMachine->uninit();
3443 }
3444
3445 if (SUCCEEDED(rc))
3446 {
3447 /*
3448 * tell the client watcher thread to update the set of
3449 * machines that have open sessions
3450 */
3451 mParent->i_updateClientWatcher();
3452
3453 if (oldState != SessionState_Locked)
3454 /* fire an event */
3455 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3456 }
3457
3458 return rc;
3459}
3460
3461/**
3462 * @note Locks objects!
3463 */
3464HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3465 const com::Utf8Str &aName,
3466 const com::Utf8Str &aEnvironment,
3467 ComPtr<IProgress> &aProgress)
3468{
3469 Utf8Str strFrontend(aName);
3470 /* "emergencystop" doesn't need the session, so skip the checks/interface
3471 * retrieval. This code doesn't quite fit in here, but introducing a
3472 * special API method would be even more effort, and would require explicit
3473 * support by every API client. It's better to hide the feature a bit. */
3474 if (strFrontend != "emergencystop")
3475 CheckComArgNotNull(aSession);
3476
3477 HRESULT rc = S_OK;
3478 if (strFrontend.isEmpty())
3479 {
3480 Bstr bstrFrontend;
3481 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3482 if (FAILED(rc))
3483 return rc;
3484 strFrontend = bstrFrontend;
3485 if (strFrontend.isEmpty())
3486 {
3487 ComPtr<ISystemProperties> systemProperties;
3488 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3489 if (FAILED(rc))
3490 return rc;
3491 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3492 if (FAILED(rc))
3493 return rc;
3494 strFrontend = bstrFrontend;
3495 }
3496 /* paranoia - emergencystop is not a valid default */
3497 if (strFrontend == "emergencystop")
3498 strFrontend = Utf8Str::Empty;
3499 }
3500 /* default frontend: Qt GUI */
3501 if (strFrontend.isEmpty())
3502 strFrontend = "GUI/Qt";
3503
3504 if (strFrontend != "emergencystop")
3505 {
3506 /* check the session state */
3507 SessionState_T state;
3508 rc = aSession->COMGETTER(State)(&state);
3509 if (FAILED(rc))
3510 return rc;
3511
3512 if (state != SessionState_Unlocked)
3513 return setError(VBOX_E_INVALID_OBJECT_STATE,
3514 tr("The given session is busy"));
3515
3516 /* get the IInternalSessionControl interface */
3517 ComPtr<IInternalSessionControl> control(aSession);
3518 ComAssertMsgRet(!control.isNull(),
3519 ("No IInternalSessionControl interface"),
3520 E_INVALIDARG);
3521
3522 /* get the teleporter enable state for the progress object init. */
3523 BOOL fTeleporterEnabled;
3524 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3525 if (FAILED(rc))
3526 return rc;
3527
3528 /* create a progress object */
3529 ComObjPtr<ProgressProxy> progress;
3530 progress.createObject();
3531 rc = progress->init(mParent,
3532 static_cast<IMachine*>(this),
3533 Bstr(tr("Starting VM")).raw(),
3534 TRUE /* aCancelable */,
3535 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3536 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3537 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3538 2 /* uFirstOperationWeight */,
3539 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3540
3541 if (SUCCEEDED(rc))
3542 {
3543 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3544 if (SUCCEEDED(rc))
3545 {
3546 aProgress = progress;
3547
3548 /* signal the client watcher thread */
3549 mParent->i_updateClientWatcher();
3550
3551 /* fire an event */
3552 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3553 }
3554 }
3555 }
3556 else
3557 {
3558 /* no progress object - either instant success or failure */
3559 aProgress = NULL;
3560
3561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3562
3563 if (mData->mSession.mState != SessionState_Locked)
3564 return setError(VBOX_E_INVALID_OBJECT_STATE,
3565 tr("The machine '%s' is not locked by a session"),
3566 mUserData->s.strName.c_str());
3567
3568 /* must have a VM process associated - do not kill normal API clients
3569 * with an open session */
3570 if (!Global::IsOnline(mData->mMachineState))
3571 return setError(VBOX_E_INVALID_OBJECT_STATE,
3572 tr("The machine '%s' does not have a VM process"),
3573 mUserData->s.strName.c_str());
3574
3575 /* forcibly terminate the VM process */
3576 if (mData->mSession.mPID != NIL_RTPROCESS)
3577 RTProcTerminate(mData->mSession.mPID);
3578
3579 /* signal the client watcher thread, as most likely the client has
3580 * been terminated */
3581 mParent->i_updateClientWatcher();
3582 }
3583
3584 return rc;
3585}
3586
3587HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3588{
3589 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3590 return setError(E_INVALIDARG,
3591 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3592 aPosition, SchemaDefs::MaxBootPosition);
3593
3594 if (aDevice == DeviceType_USB)
3595 return setError(E_NOTIMPL,
3596 tr("Booting from USB device is currently not supported"));
3597
3598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3599
3600 HRESULT rc = i_checkStateDependency(MutableStateDep);
3601 if (FAILED(rc)) return rc;
3602
3603 i_setModified(IsModified_MachineData);
3604 mHWData.backup();
3605 mHWData->mBootOrder[aPosition - 1] = aDevice;
3606
3607 return S_OK;
3608}
3609
3610HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3611{
3612 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3613 return setError(E_INVALIDARG,
3614 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3615 aPosition, SchemaDefs::MaxBootPosition);
3616
3617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3618
3619 *aDevice = mHWData->mBootOrder[aPosition - 1];
3620
3621 return S_OK;
3622}
3623
3624HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3625 LONG aControllerPort,
3626 LONG aDevice,
3627 DeviceType_T aType,
3628 const ComPtr<IMedium> &aMedium)
3629{
3630 IMedium *aM = aMedium;
3631 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3632 aName.c_str(), aControllerPort, aDevice, aType, aM));
3633
3634 // request the host lock first, since might be calling Host methods for getting host drives;
3635 // next, protect the media tree all the while we're in here, as well as our member variables
3636 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3637 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3638
3639 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3640 if (FAILED(rc)) return rc;
3641
3642 /// @todo NEWMEDIA implicit machine registration
3643 if (!mData->mRegistered)
3644 return setError(VBOX_E_INVALID_OBJECT_STATE,
3645 tr("Cannot attach storage devices to an unregistered machine"));
3646
3647 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3648
3649 /* Check for an existing controller. */
3650 ComObjPtr<StorageController> ctl;
3651 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3652 if (FAILED(rc)) return rc;
3653
3654 StorageControllerType_T ctrlType;
3655 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3656 if (FAILED(rc))
3657 return setError(E_FAIL,
3658 tr("Could not get type of controller '%s'"),
3659 aName.c_str());
3660
3661 bool fSilent = false;
3662 Utf8Str strReconfig;
3663
3664 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3665 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3666 if ( mData->mMachineState == MachineState_Paused
3667 && strReconfig == "1")
3668 fSilent = true;
3669
3670 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3671 bool fHotplug = false;
3672 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3673 fHotplug = true;
3674
3675 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3676 return setError(VBOX_E_INVALID_VM_STATE,
3677 tr("Controller '%s' does not support hotplugging"),
3678 aName.c_str());
3679
3680 // check that the port and device are not out of range
3681 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3682 if (FAILED(rc)) return rc;
3683
3684 /* check if the device slot is already busy */
3685 MediumAttachment *pAttachTemp;
3686 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3687 aName,
3688 aControllerPort,
3689 aDevice)))
3690 {
3691 Medium *pMedium = pAttachTemp->i_getMedium();
3692 if (pMedium)
3693 {
3694 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3695 return setError(VBOX_E_OBJECT_IN_USE,
3696 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3697 pMedium->i_getLocationFull().c_str(),
3698 aControllerPort,
3699 aDevice,
3700 aName.c_str());
3701 }
3702 else
3703 return setError(VBOX_E_OBJECT_IN_USE,
3704 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3705 aControllerPort, aDevice, aName.c_str());
3706 }
3707
3708 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3709 if (aMedium && medium.isNull())
3710 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3711
3712 AutoCaller mediumCaller(medium);
3713 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3714
3715 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3716
3717 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3718 && !medium.isNull()
3719 )
3720 return setError(VBOX_E_OBJECT_IN_USE,
3721 tr("Medium '%s' is already attached to this virtual machine"),
3722 medium->i_getLocationFull().c_str());
3723
3724 if (!medium.isNull())
3725 {
3726 MediumType_T mtype = medium->i_getType();
3727 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3728 // For DVDs it's not written to the config file, so needs no global config
3729 // version bump. For floppies it's a new attribute "type", which is ignored
3730 // by older VirtualBox version, so needs no global config version bump either.
3731 // For hard disks this type is not accepted.
3732 if (mtype == MediumType_MultiAttach)
3733 {
3734 // This type is new with VirtualBox 4.0 and therefore requires settings
3735 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3736 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3737 // two reasons: The medium type is a property of the media registry tree, which
3738 // can reside in the global config file (for pre-4.0 media); we would therefore
3739 // possibly need to bump the global config version. We don't want to do that though
3740 // because that might make downgrading to pre-4.0 impossible.
3741 // As a result, we can only use these two new types if the medium is NOT in the
3742 // global registry:
3743 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3744 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3745 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3746 )
3747 return setError(VBOX_E_INVALID_OBJECT_STATE,
3748 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3749 "to machines that were created with VirtualBox 4.0 or later"),
3750 medium->i_getLocationFull().c_str());
3751 }
3752 }
3753
3754 bool fIndirect = false;
3755 if (!medium.isNull())
3756 fIndirect = medium->i_isReadOnly();
3757 bool associate = true;
3758
3759 do
3760 {
3761 if ( aType == DeviceType_HardDisk
3762 && mMediumAttachments.isBackedUp())
3763 {
3764 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3765
3766 /* check if the medium was attached to the VM before we started
3767 * changing attachments in which case the attachment just needs to
3768 * be restored */
3769 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3770 {
3771 AssertReturn(!fIndirect, E_FAIL);
3772
3773 /* see if it's the same bus/channel/device */
3774 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3775 {
3776 /* the simplest case: restore the whole attachment
3777 * and return, nothing else to do */
3778 mMediumAttachments->push_back(pAttachTemp);
3779
3780 /* Reattach the medium to the VM. */
3781 if (fHotplug || fSilent)
3782 {
3783 mediumLock.release();
3784 treeLock.release();
3785 alock.release();
3786
3787 MediumLockList *pMediumLockList(new MediumLockList());
3788
3789 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3790 medium /* pToLockWrite */,
3791 false /* fMediumLockWriteAll */,
3792 NULL,
3793 *pMediumLockList);
3794 alock.acquire();
3795 if (FAILED(rc))
3796 delete pMediumLockList;
3797 else
3798 {
3799 mData->mSession.mLockedMedia.Unlock();
3800 alock.release();
3801 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3802 mData->mSession.mLockedMedia.Lock();
3803 alock.acquire();
3804 }
3805 alock.release();
3806
3807 if (SUCCEEDED(rc))
3808 {
3809 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3810 /* Remove lock list in case of error. */
3811 if (FAILED(rc))
3812 {
3813 mData->mSession.mLockedMedia.Unlock();
3814 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3815 mData->mSession.mLockedMedia.Lock();
3816 }
3817 }
3818 }
3819
3820 return S_OK;
3821 }
3822
3823 /* bus/channel/device differ; we need a new attachment object,
3824 * but don't try to associate it again */
3825 associate = false;
3826 break;
3827 }
3828 }
3829
3830 /* go further only if the attachment is to be indirect */
3831 if (!fIndirect)
3832 break;
3833
3834 /* perform the so called smart attachment logic for indirect
3835 * attachments. Note that smart attachment is only applicable to base
3836 * hard disks. */
3837
3838 if (medium->i_getParent().isNull())
3839 {
3840 /* first, investigate the backup copy of the current hard disk
3841 * attachments to make it possible to re-attach existing diffs to
3842 * another device slot w/o losing their contents */
3843 if (mMediumAttachments.isBackedUp())
3844 {
3845 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3846
3847 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3848 uint32_t foundLevel = 0;
3849
3850 for (MediumAttachmentList::const_iterator
3851 it = oldAtts.begin();
3852 it != oldAtts.end();
3853 ++it)
3854 {
3855 uint32_t level = 0;
3856 MediumAttachment *pAttach = *it;
3857 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3858 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3859 if (pMedium.isNull())
3860 continue;
3861
3862 if (pMedium->i_getBase(&level) == medium)
3863 {
3864 /* skip the hard disk if its currently attached (we
3865 * cannot attach the same hard disk twice) */
3866 if (i_findAttachment(*mMediumAttachments.data(),
3867 pMedium))
3868 continue;
3869
3870 /* matched device, channel and bus (i.e. attached to the
3871 * same place) will win and immediately stop the search;
3872 * otherwise the attachment that has the youngest
3873 * descendant of medium will be used
3874 */
3875 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3876 {
3877 /* the simplest case: restore the whole attachment
3878 * and return, nothing else to do */
3879 mMediumAttachments->push_back(*it);
3880
3881 /* Reattach the medium to the VM. */
3882 if (fHotplug || fSilent)
3883 {
3884 mediumLock.release();
3885 treeLock.release();
3886 alock.release();
3887
3888 MediumLockList *pMediumLockList(new MediumLockList());
3889
3890 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3891 medium /* pToLockWrite */,
3892 false /* fMediumLockWriteAll */,
3893 NULL,
3894 *pMediumLockList);
3895 alock.acquire();
3896 if (FAILED(rc))
3897 delete pMediumLockList;
3898 else
3899 {
3900 mData->mSession.mLockedMedia.Unlock();
3901 alock.release();
3902 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3903 mData->mSession.mLockedMedia.Lock();
3904 alock.acquire();
3905 }
3906 alock.release();
3907
3908 if (SUCCEEDED(rc))
3909 {
3910 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3911 /* Remove lock list in case of error. */
3912 if (FAILED(rc))
3913 {
3914 mData->mSession.mLockedMedia.Unlock();
3915 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3916 mData->mSession.mLockedMedia.Lock();
3917 }
3918 }
3919 }
3920
3921 return S_OK;
3922 }
3923 else if ( foundIt == oldAtts.end()
3924 || level > foundLevel /* prefer younger */
3925 )
3926 {
3927 foundIt = it;
3928 foundLevel = level;
3929 }
3930 }
3931 }
3932
3933 if (foundIt != oldAtts.end())
3934 {
3935 /* use the previously attached hard disk */
3936 medium = (*foundIt)->i_getMedium();
3937 mediumCaller.attach(medium);
3938 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3939 mediumLock.attach(medium);
3940 /* not implicit, doesn't require association with this VM */
3941 fIndirect = false;
3942 associate = false;
3943 /* go right to the MediumAttachment creation */
3944 break;
3945 }
3946 }
3947
3948 /* must give up the medium lock and medium tree lock as below we
3949 * go over snapshots, which needs a lock with higher lock order. */
3950 mediumLock.release();
3951 treeLock.release();
3952
3953 /* then, search through snapshots for the best diff in the given
3954 * hard disk's chain to base the new diff on */
3955
3956 ComObjPtr<Medium> base;
3957 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3958 while (snap)
3959 {
3960 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3961
3962 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3963
3964 MediumAttachment *pAttachFound = NULL;
3965 uint32_t foundLevel = 0;
3966
3967 for (MediumAttachmentList::const_iterator
3968 it = snapAtts.begin();
3969 it != snapAtts.end();
3970 ++it)
3971 {
3972 MediumAttachment *pAttach = *it;
3973 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3974 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3975 if (pMedium.isNull())
3976 continue;
3977
3978 uint32_t level = 0;
3979 if (pMedium->i_getBase(&level) == medium)
3980 {
3981 /* matched device, channel and bus (i.e. attached to the
3982 * same place) will win and immediately stop the search;
3983 * otherwise the attachment that has the youngest
3984 * descendant of medium will be used
3985 */
3986 if ( pAttach->i_getDevice() == aDevice
3987 && pAttach->i_getPort() == aControllerPort
3988 && pAttach->i_getControllerName() == aName
3989 )
3990 {
3991 pAttachFound = pAttach;
3992 break;
3993 }
3994 else if ( !pAttachFound
3995 || level > foundLevel /* prefer younger */
3996 )
3997 {
3998 pAttachFound = pAttach;
3999 foundLevel = level;
4000 }
4001 }
4002 }
4003
4004 if (pAttachFound)
4005 {
4006 base = pAttachFound->i_getMedium();
4007 break;
4008 }
4009
4010 snap = snap->i_getParent();
4011 }
4012
4013 /* re-lock medium tree and the medium, as we need it below */
4014 treeLock.acquire();
4015 mediumLock.acquire();
4016
4017 /* found a suitable diff, use it as a base */
4018 if (!base.isNull())
4019 {
4020 medium = base;
4021 mediumCaller.attach(medium);
4022 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4023 mediumLock.attach(medium);
4024 }
4025 }
4026
4027 Utf8Str strFullSnapshotFolder;
4028 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4029
4030 ComObjPtr<Medium> diff;
4031 diff.createObject();
4032 // store this diff in the same registry as the parent
4033 Guid uuidRegistryParent;
4034 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4035 {
4036 // parent image has no registry: this can happen if we're attaching a new immutable
4037 // image that has not yet been attached (medium then points to the base and we're
4038 // creating the diff image for the immutable, and the parent is not yet registered);
4039 // put the parent in the machine registry then
4040 mediumLock.release();
4041 treeLock.release();
4042 alock.release();
4043 i_addMediumToRegistry(medium);
4044 alock.acquire();
4045 treeLock.acquire();
4046 mediumLock.acquire();
4047 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4048 }
4049 rc = diff->init(mParent,
4050 medium->i_getPreferredDiffFormat(),
4051 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4052 uuidRegistryParent,
4053 DeviceType_HardDisk);
4054 if (FAILED(rc)) return rc;
4055
4056 /* Apply the normal locking logic to the entire chain. */
4057 MediumLockList *pMediumLockList(new MediumLockList());
4058 mediumLock.release();
4059 treeLock.release();
4060 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4061 diff /* pToLockWrite */,
4062 false /* fMediumLockWriteAll */,
4063 medium,
4064 *pMediumLockList);
4065 treeLock.acquire();
4066 mediumLock.acquire();
4067 if (SUCCEEDED(rc))
4068 {
4069 mediumLock.release();
4070 treeLock.release();
4071 rc = pMediumLockList->Lock();
4072 treeLock.acquire();
4073 mediumLock.acquire();
4074 if (FAILED(rc))
4075 setError(rc,
4076 tr("Could not lock medium when creating diff '%s'"),
4077 diff->i_getLocationFull().c_str());
4078 else
4079 {
4080 /* will release the lock before the potentially lengthy
4081 * operation, so protect with the special state */
4082 MachineState_T oldState = mData->mMachineState;
4083 i_setMachineState(MachineState_SettingUp);
4084
4085 mediumLock.release();
4086 treeLock.release();
4087 alock.release();
4088
4089 rc = medium->i_createDiffStorage(diff,
4090 medium->i_getPreferredDiffVariant(),
4091 pMediumLockList,
4092 NULL /* aProgress */,
4093 true /* aWait */,
4094 false /* aNotify */);
4095
4096 alock.acquire();
4097 treeLock.acquire();
4098 mediumLock.acquire();
4099
4100 i_setMachineState(oldState);
4101 }
4102 }
4103
4104 /* Unlock the media and free the associated memory. */
4105 delete pMediumLockList;
4106
4107 if (FAILED(rc)) return rc;
4108
4109 /* use the created diff for the actual attachment */
4110 medium = diff;
4111 mediumCaller.attach(medium);
4112 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4113 mediumLock.attach(medium);
4114 }
4115 while (0);
4116
4117 ComObjPtr<MediumAttachment> attachment;
4118 attachment.createObject();
4119 rc = attachment->init(this,
4120 medium,
4121 aName,
4122 aControllerPort,
4123 aDevice,
4124 aType,
4125 fIndirect,
4126 false /* fPassthrough */,
4127 false /* fTempEject */,
4128 false /* fNonRotational */,
4129 false /* fDiscard */,
4130 fHotplug /* fHotPluggable */,
4131 Utf8Str::Empty);
4132 if (FAILED(rc)) return rc;
4133
4134 if (associate && !medium.isNull())
4135 {
4136 // as the last step, associate the medium to the VM
4137 rc = medium->i_addBackReference(mData->mUuid);
4138 // here we can fail because of Deleting, or being in process of creating a Diff
4139 if (FAILED(rc)) return rc;
4140
4141 mediumLock.release();
4142 treeLock.release();
4143 alock.release();
4144 i_addMediumToRegistry(medium);
4145 alock.acquire();
4146 treeLock.acquire();
4147 mediumLock.acquire();
4148 }
4149
4150 /* success: finally remember the attachment */
4151 i_setModified(IsModified_Storage);
4152 mMediumAttachments.backup();
4153 mMediumAttachments->push_back(attachment);
4154
4155 mediumLock.release();
4156 treeLock.release();
4157 alock.release();
4158
4159 if (fHotplug || fSilent)
4160 {
4161 if (!medium.isNull())
4162 {
4163 MediumLockList *pMediumLockList(new MediumLockList());
4164
4165 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4166 medium /* pToLockWrite */,
4167 false /* fMediumLockWriteAll */,
4168 NULL,
4169 *pMediumLockList);
4170 alock.acquire();
4171 if (FAILED(rc))
4172 delete pMediumLockList;
4173 else
4174 {
4175 mData->mSession.mLockedMedia.Unlock();
4176 alock.release();
4177 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4178 mData->mSession.mLockedMedia.Lock();
4179 alock.acquire();
4180 }
4181 alock.release();
4182 }
4183
4184 if (SUCCEEDED(rc))
4185 {
4186 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4187 /* Remove lock list in case of error. */
4188 if (FAILED(rc))
4189 {
4190 mData->mSession.mLockedMedia.Unlock();
4191 mData->mSession.mLockedMedia.Remove(attachment);
4192 mData->mSession.mLockedMedia.Lock();
4193 }
4194 }
4195 }
4196
4197 /* Save modified registries, but skip this machine as it's the caller's
4198 * job to save its settings like all other settings changes. */
4199 mParent->i_unmarkRegistryModified(i_getId());
4200 mParent->i_saveModifiedRegistries();
4201
4202 if (SUCCEEDED(rc))
4203 {
4204 if (fIndirect && medium != aM)
4205 mParent->i_onMediumConfigChanged(medium);
4206 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4207 }
4208
4209 return rc;
4210}
4211
4212HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4213 LONG aDevice)
4214{
4215 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4216 aName.c_str(), aControllerPort, aDevice));
4217
4218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4219
4220 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4221 if (FAILED(rc)) return rc;
4222
4223 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4224
4225 /* Check for an existing controller. */
4226 ComObjPtr<StorageController> ctl;
4227 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4228 if (FAILED(rc)) return rc;
4229
4230 StorageControllerType_T ctrlType;
4231 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4232 if (FAILED(rc))
4233 return setError(E_FAIL,
4234 tr("Could not get type of controller '%s'"),
4235 aName.c_str());
4236
4237 bool fSilent = false;
4238 Utf8Str strReconfig;
4239
4240 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4241 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4242 if ( mData->mMachineState == MachineState_Paused
4243 && strReconfig == "1")
4244 fSilent = true;
4245
4246 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4247 bool fHotplug = false;
4248 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4249 fHotplug = true;
4250
4251 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4252 return setError(VBOX_E_INVALID_VM_STATE,
4253 tr("Controller '%s' does not support hotplugging"),
4254 aName.c_str());
4255
4256 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4257 aName,
4258 aControllerPort,
4259 aDevice);
4260 if (!pAttach)
4261 return setError(VBOX_E_OBJECT_NOT_FOUND,
4262 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4263 aDevice, aControllerPort, aName.c_str());
4264
4265 if (fHotplug && !pAttach->i_getHotPluggable())
4266 return setError(VBOX_E_NOT_SUPPORTED,
4267 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4268 aDevice, aControllerPort, aName.c_str());
4269
4270 /*
4271 * The VM has to detach the device before we delete any implicit diffs.
4272 * If this fails we can roll back without loosing data.
4273 */
4274 if (fHotplug || fSilent)
4275 {
4276 alock.release();
4277 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4278 alock.acquire();
4279 }
4280 if (FAILED(rc)) return rc;
4281
4282 /* If we are here everything went well and we can delete the implicit now. */
4283 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4284
4285 alock.release();
4286
4287 /* Save modified registries, but skip this machine as it's the caller's
4288 * job to save its settings like all other settings changes. */
4289 mParent->i_unmarkRegistryModified(i_getId());
4290 mParent->i_saveModifiedRegistries();
4291
4292 if (SUCCEEDED(rc))
4293 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4294
4295 return rc;
4296}
4297
4298HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4299 LONG aDevice, BOOL aPassthrough)
4300{
4301 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4302 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 /* Check for an existing controller. */
4312 ComObjPtr<StorageController> ctl;
4313 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4314 if (FAILED(rc)) return rc;
4315
4316 StorageControllerType_T ctrlType;
4317 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4318 if (FAILED(rc))
4319 return setError(E_FAIL,
4320 tr("Could not get type of controller '%s'"),
4321 aName.c_str());
4322
4323 bool fSilent = false;
4324 Utf8Str strReconfig;
4325
4326 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4327 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4328 if ( mData->mMachineState == MachineState_Paused
4329 && strReconfig == "1")
4330 fSilent = true;
4331
4332 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4333 bool fHotplug = false;
4334 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4335 fHotplug = true;
4336
4337 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4338 return setError(VBOX_E_INVALID_VM_STATE,
4339 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4340 aName.c_str());
4341
4342 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4343 aName,
4344 aControllerPort,
4345 aDevice);
4346 if (!pAttach)
4347 return setError(VBOX_E_OBJECT_NOT_FOUND,
4348 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4349 aDevice, aControllerPort, aName.c_str());
4350
4351
4352 i_setModified(IsModified_Storage);
4353 mMediumAttachments.backup();
4354
4355 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4356
4357 if (pAttach->i_getType() != DeviceType_DVD)
4358 return setError(E_INVALIDARG,
4359 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4360 aDevice, aControllerPort, aName.c_str());
4361
4362 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4363
4364 pAttach->i_updatePassthrough(!!aPassthrough);
4365
4366 attLock.release();
4367 alock.release();
4368 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4369 if (SUCCEEDED(rc) && fValueChanged)
4370 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4371
4372 return rc;
4373}
4374
4375HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4376 LONG aDevice, BOOL aTemporaryEject)
4377{
4378
4379 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4380 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4381
4382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4383
4384 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4385 if (FAILED(rc)) return rc;
4386
4387 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4388 aName,
4389 aControllerPort,
4390 aDevice);
4391 if (!pAttach)
4392 return setError(VBOX_E_OBJECT_NOT_FOUND,
4393 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4394 aDevice, aControllerPort, aName.c_str());
4395
4396
4397 i_setModified(IsModified_Storage);
4398 mMediumAttachments.backup();
4399
4400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4401
4402 if (pAttach->i_getType() != DeviceType_DVD)
4403 return setError(E_INVALIDARG,
4404 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4405 aDevice, aControllerPort, aName.c_str());
4406 pAttach->i_updateTempEject(!!aTemporaryEject);
4407
4408 return S_OK;
4409}
4410
4411HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4412 LONG aDevice, BOOL aNonRotational)
4413{
4414
4415 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4416 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4417
4418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4419
4420 HRESULT rc = i_checkStateDependency(MutableStateDep);
4421 if (FAILED(rc)) return rc;
4422
4423 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4424
4425 if (Global::IsOnlineOrTransient(mData->mMachineState))
4426 return setError(VBOX_E_INVALID_VM_STATE,
4427 tr("Invalid machine state: %s"),
4428 Global::stringifyMachineState(mData->mMachineState));
4429
4430 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4431 aName,
4432 aControllerPort,
4433 aDevice);
4434 if (!pAttach)
4435 return setError(VBOX_E_OBJECT_NOT_FOUND,
4436 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4437 aDevice, aControllerPort, aName.c_str());
4438
4439
4440 i_setModified(IsModified_Storage);
4441 mMediumAttachments.backup();
4442
4443 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4444
4445 if (pAttach->i_getType() != DeviceType_HardDisk)
4446 return setError(E_INVALIDARG,
4447 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"),
4448 aDevice, aControllerPort, aName.c_str());
4449 pAttach->i_updateNonRotational(!!aNonRotational);
4450
4451 return S_OK;
4452}
4453
4454HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4455 LONG aDevice, BOOL aDiscard)
4456{
4457
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4459 aName.c_str(), aControllerPort, aDevice, aDiscard));
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = i_checkStateDependency(MutableStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4467
4468 if (Global::IsOnlineOrTransient(mData->mMachineState))
4469 return setError(VBOX_E_INVALID_VM_STATE,
4470 tr("Invalid machine state: %s"),
4471 Global::stringifyMachineState(mData->mMachineState));
4472
4473 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4474 aName,
4475 aControllerPort,
4476 aDevice);
4477 if (!pAttach)
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4480 aDevice, aControllerPort, aName.c_str());
4481
4482
4483 i_setModified(IsModified_Storage);
4484 mMediumAttachments.backup();
4485
4486 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4487
4488 if (pAttach->i_getType() != DeviceType_HardDisk)
4489 return setError(E_INVALIDARG,
4490 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"),
4491 aDevice, aControllerPort, aName.c_str());
4492 pAttach->i_updateDiscard(!!aDiscard);
4493
4494 return S_OK;
4495}
4496
4497HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4498 LONG aDevice, BOOL aHotPluggable)
4499{
4500 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4501 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4502
4503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4504
4505 HRESULT rc = i_checkStateDependency(MutableStateDep);
4506 if (FAILED(rc)) return rc;
4507
4508 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4509
4510 if (Global::IsOnlineOrTransient(mData->mMachineState))
4511 return setError(VBOX_E_INVALID_VM_STATE,
4512 tr("Invalid machine state: %s"),
4513 Global::stringifyMachineState(mData->mMachineState));
4514
4515 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (!pAttach)
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524 /* Check for an existing controller. */
4525 ComObjPtr<StorageController> ctl;
4526 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4527 if (FAILED(rc)) return rc;
4528
4529 StorageControllerType_T ctrlType;
4530 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4531 if (FAILED(rc))
4532 return setError(E_FAIL,
4533 tr("Could not get type of controller '%s'"),
4534 aName.c_str());
4535
4536 if (!i_isControllerHotplugCapable(ctrlType))
4537 return setError(VBOX_E_NOT_SUPPORTED,
4538 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4539 aName.c_str());
4540
4541 i_setModified(IsModified_Storage);
4542 mMediumAttachments.backup();
4543
4544 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4545
4546 if (pAttach->i_getType() == DeviceType_Floppy)
4547 return setError(E_INVALIDARG,
4548 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"),
4549 aDevice, aControllerPort, aName.c_str());
4550 pAttach->i_updateHotPluggable(!!aHotPluggable);
4551
4552 return S_OK;
4553}
4554
4555HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice)
4557{
4558 int rc = S_OK;
4559 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4560 aName.c_str(), aControllerPort, aDevice));
4561
4562 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4563
4564 return rc;
4565}
4566
4567HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4568 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4569{
4570 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4571 aName.c_str(), aControllerPort, aDevice));
4572
4573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4574
4575 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4576 if (FAILED(rc)) return rc;
4577
4578 if (Global::IsOnlineOrTransient(mData->mMachineState))
4579 return setError(VBOX_E_INVALID_VM_STATE,
4580 tr("Invalid machine state: %s"),
4581 Global::stringifyMachineState(mData->mMachineState));
4582
4583 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4584 aName,
4585 aControllerPort,
4586 aDevice);
4587 if (!pAttach)
4588 return setError(VBOX_E_OBJECT_NOT_FOUND,
4589 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4590 aDevice, aControllerPort, aName.c_str());
4591
4592
4593 i_setModified(IsModified_Storage);
4594 mMediumAttachments.backup();
4595
4596 IBandwidthGroup *iB = aBandwidthGroup;
4597 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4598 if (aBandwidthGroup && group.isNull())
4599 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4600
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602
4603 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4604 if (strBandwidthGroupOld.isNotEmpty())
4605 {
4606 /* Get the bandwidth group object and release it - this must not fail. */
4607 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4608 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4609 Assert(SUCCEEDED(rc));
4610
4611 pBandwidthGroupOld->i_release();
4612 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4613 }
4614
4615 if (!group.isNull())
4616 {
4617 group->i_reference();
4618 pAttach->i_updateBandwidthGroup(group->i_getName());
4619 }
4620
4621 return S_OK;
4622}
4623
4624HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4625 LONG aControllerPort,
4626 LONG aDevice,
4627 DeviceType_T aType)
4628{
4629 HRESULT rc = S_OK;
4630
4631 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4632 aName.c_str(), aControllerPort, aDevice, aType));
4633
4634 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4635
4636 return rc;
4637}
4638
4639
4640HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4641 LONG aControllerPort,
4642 LONG aDevice,
4643 BOOL aForce)
4644{
4645 int rc = S_OK;
4646 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4647 aName.c_str(), aControllerPort, aForce));
4648
4649 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4650
4651 return rc;
4652}
4653
4654HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4655 LONG aControllerPort,
4656 LONG aDevice,
4657 const ComPtr<IMedium> &aMedium,
4658 BOOL aForce)
4659{
4660 int rc = S_OK;
4661 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4662 aName.c_str(), aControllerPort, aDevice, aForce));
4663
4664 // request the host lock first, since might be calling Host methods for getting host drives;
4665 // next, protect the media tree all the while we're in here, as well as our member variables
4666 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4667 this->lockHandle(),
4668 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4669
4670 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4671 aName,
4672 aControllerPort,
4673 aDevice);
4674 if (pAttach.isNull())
4675 return setError(VBOX_E_OBJECT_NOT_FOUND,
4676 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4677 aDevice, aControllerPort, aName.c_str());
4678
4679 /* Remember previously mounted medium. The medium before taking the
4680 * backup is not necessarily the same thing. */
4681 ComObjPtr<Medium> oldmedium;
4682 oldmedium = pAttach->i_getMedium();
4683
4684 IMedium *iM = aMedium;
4685 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4686 if (aMedium && pMedium.isNull())
4687 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4688
4689 AutoCaller mediumCaller(pMedium);
4690 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4691
4692 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4693 if (pMedium)
4694 {
4695 DeviceType_T mediumType = pAttach->i_getType();
4696 switch (mediumType)
4697 {
4698 case DeviceType_DVD:
4699 case DeviceType_Floppy:
4700 break;
4701
4702 default:
4703 return setError(VBOX_E_INVALID_OBJECT_STATE,
4704 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4705 aControllerPort,
4706 aDevice,
4707 aName.c_str());
4708 }
4709 }
4710
4711 i_setModified(IsModified_Storage);
4712 mMediumAttachments.backup();
4713
4714 {
4715 // The backup operation makes the pAttach reference point to the
4716 // old settings. Re-get the correct reference.
4717 pAttach = i_findAttachment(*mMediumAttachments.data(),
4718 aName,
4719 aControllerPort,
4720 aDevice);
4721 if (!oldmedium.isNull())
4722 oldmedium->i_removeBackReference(mData->mUuid);
4723 if (!pMedium.isNull())
4724 {
4725 pMedium->i_addBackReference(mData->mUuid);
4726
4727 mediumLock.release();
4728 multiLock.release();
4729 i_addMediumToRegistry(pMedium);
4730 multiLock.acquire();
4731 mediumLock.acquire();
4732 }
4733
4734 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4735 pAttach->i_updateMedium(pMedium);
4736 }
4737
4738 i_setModified(IsModified_Storage);
4739
4740 mediumLock.release();
4741 multiLock.release();
4742 rc = i_onMediumChange(pAttach, aForce);
4743 multiLock.acquire();
4744 mediumLock.acquire();
4745
4746 /* On error roll back this change only. */
4747 if (FAILED(rc))
4748 {
4749 if (!pMedium.isNull())
4750 pMedium->i_removeBackReference(mData->mUuid);
4751 pAttach = i_findAttachment(*mMediumAttachments.data(),
4752 aName,
4753 aControllerPort,
4754 aDevice);
4755 /* If the attachment is gone in the meantime, bail out. */
4756 if (pAttach.isNull())
4757 return rc;
4758 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4759 if (!oldmedium.isNull())
4760 oldmedium->i_addBackReference(mData->mUuid);
4761 pAttach->i_updateMedium(oldmedium);
4762 }
4763
4764 mediumLock.release();
4765 multiLock.release();
4766
4767 /* Save modified registries, but skip this machine as it's the caller's
4768 * job to save its settings like all other settings changes. */
4769 mParent->i_unmarkRegistryModified(i_getId());
4770 mParent->i_saveModifiedRegistries();
4771
4772 return rc;
4773}
4774HRESULT Machine::getMedium(const com::Utf8Str &aName,
4775 LONG aControllerPort,
4776 LONG aDevice,
4777 ComPtr<IMedium> &aMedium)
4778{
4779 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4780 aName.c_str(), aControllerPort, aDevice));
4781
4782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4783
4784 aMedium = NULL;
4785
4786 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4787 aName,
4788 aControllerPort,
4789 aDevice);
4790 if (pAttach.isNull())
4791 return setError(VBOX_E_OBJECT_NOT_FOUND,
4792 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4793 aDevice, aControllerPort, aName.c_str());
4794
4795 aMedium = pAttach->i_getMedium();
4796
4797 return S_OK;
4798}
4799
4800HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4801{
4802
4803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4806
4807 return S_OK;
4808}
4809
4810HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4811{
4812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4813
4814 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4815
4816 return S_OK;
4817}
4818
4819HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4820{
4821 /* Do not assert if slot is out of range, just return the advertised
4822 status. testdriver/vbox.py triggers this in logVmInfo. */
4823 if (aSlot >= mNetworkAdapters.size())
4824 return setError(E_INVALIDARG,
4825 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4826 aSlot, mNetworkAdapters.size());
4827
4828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4831
4832 return S_OK;
4833}
4834
4835HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4836{
4837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4838
4839 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4840 size_t i = 0;
4841 for (settings::StringsMap::const_iterator
4842 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4843 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4844 ++it, ++i)
4845 aKeys[i] = it->first;
4846
4847 return S_OK;
4848}
4849
4850 /**
4851 * @note Locks this object for reading.
4852 */
4853HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4854 com::Utf8Str &aValue)
4855{
4856 /* start with nothing found */
4857 aValue = "";
4858
4859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4860
4861 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4862 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4863 // found:
4864 aValue = it->second; // source is a Utf8Str
4865
4866 /* return the result to caller (may be empty) */
4867 return S_OK;
4868}
4869
4870 /**
4871 * @note Locks mParent for writing + this object for writing.
4872 */
4873HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4874{
4875 /* Because control characters in aKey have caused problems in the settings
4876 * they are rejected unless the key should be deleted. */
4877 if (!aValue.isEmpty())
4878 {
4879 for (size_t i = 0; i < aKey.length(); ++i)
4880 {
4881 char ch = aKey[i];
4882 if (RTLocCIsCntrl(ch))
4883 return E_INVALIDARG;
4884 }
4885 }
4886
4887 Utf8Str strOldValue; // empty
4888
4889 // locking note: we only hold the read lock briefly to look up the old value,
4890 // then release it and call the onExtraCanChange callbacks. There is a small
4891 // chance of a race insofar as the callback might be called twice if two callers
4892 // change the same key at the same time, but that's a much better solution
4893 // than the deadlock we had here before. The actual changing of the extradata
4894 // is then performed under the write lock and race-free.
4895
4896 // look up the old value first; if nothing has changed then we need not do anything
4897 {
4898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4899
4900 // For snapshots don't even think about allowing changes, extradata
4901 // is global for a machine, so there is nothing snapshot specific.
4902 if (i_isSnapshotMachine())
4903 return setError(VBOX_E_INVALID_VM_STATE,
4904 tr("Cannot set extradata for a snapshot"));
4905
4906 // check if the right IMachine instance is used
4907 if (mData->mRegistered && !i_isSessionMachine())
4908 return setError(VBOX_E_INVALID_VM_STATE,
4909 tr("Cannot set extradata for an immutable machine"));
4910
4911 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4912 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4913 strOldValue = it->second;
4914 }
4915
4916 bool fChanged;
4917 if ((fChanged = (strOldValue != aValue)))
4918 {
4919 // ask for permission from all listeners outside the locks;
4920 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4921 // lock to copy the list of callbacks to invoke
4922 Bstr error;
4923 Bstr bstrValue(aValue);
4924
4925 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4926 {
4927 const char *sep = error.isEmpty() ? "" : ": ";
4928 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4929 return setError(E_ACCESSDENIED,
4930 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4931 aKey.c_str(),
4932 aValue.c_str(),
4933 sep,
4934 error.raw());
4935 }
4936
4937 // data is changing and change not vetoed: then write it out under the lock
4938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4939
4940 if (aValue.isEmpty())
4941 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4942 else
4943 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4944 // creates a new key if needed
4945
4946 bool fNeedsGlobalSaveSettings = false;
4947 // This saving of settings is tricky: there is no "old state" for the
4948 // extradata items at all (unlike all other settings), so the old/new
4949 // settings comparison would give a wrong result!
4950 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4951
4952 if (fNeedsGlobalSaveSettings)
4953 {
4954 // save the global settings; for that we should hold only the VirtualBox lock
4955 alock.release();
4956 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4957 mParent->i_saveSettings();
4958 }
4959 }
4960
4961 // fire notification outside the lock
4962 if (fChanged)
4963 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4964
4965 return S_OK;
4966}
4967
4968HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4969{
4970 aProgress = NULL;
4971 NOREF(aSettingsFilePath);
4972 ReturnComNotImplemented();
4973}
4974
4975HRESULT Machine::saveSettings()
4976{
4977 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4978
4979 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4980 if (FAILED(rc)) return rc;
4981
4982 /* the settings file path may never be null */
4983 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4984
4985 /* save all VM data excluding snapshots */
4986 bool fNeedsGlobalSaveSettings = false;
4987 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4988 mlock.release();
4989
4990 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4991 {
4992 // save the global settings; for that we should hold only the VirtualBox lock
4993 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4994 rc = mParent->i_saveSettings();
4995 }
4996
4997 return rc;
4998}
4999
5000
5001HRESULT Machine::discardSettings()
5002{
5003 /*
5004 * We need to take the machine list lock here as well as the machine one
5005 * or we'll get into trouble should any media stuff require rolling back.
5006 *
5007 * Details:
5008 *
5009 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5010 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5011 * 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]
5012 * 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
5013 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5014 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5015 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5016 * 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
5017 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5018 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5019 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5020 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5021 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5022 * 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]
5023 * 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] (*)
5024 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5025 * 0:005> k
5026 * # Child-SP RetAddr Call Site
5027 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5028 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5029 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5030 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5031 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5032 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5033 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5034 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5035 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5036 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5037 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5038 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5039 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5040 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5041 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5042 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5043 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5044 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5045 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5046 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5047 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5048 *
5049 */
5050 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5054 if (FAILED(rc)) return rc;
5055
5056 /*
5057 * during this rollback, the session will be notified if data has
5058 * been actually changed
5059 */
5060 i_rollback(true /* aNotify */);
5061
5062 return S_OK;
5063}
5064
5065/** @note Locks objects! */
5066HRESULT Machine::unregister(AutoCaller &autoCaller,
5067 CleanupMode_T aCleanupMode,
5068 std::vector<ComPtr<IMedium> > &aMedia)
5069{
5070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5071
5072 Guid id(i_getId());
5073
5074 if (mData->mSession.mState != SessionState_Unlocked)
5075 return setError(VBOX_E_INVALID_OBJECT_STATE,
5076 tr("Cannot unregister the machine '%s' while it is locked"),
5077 mUserData->s.strName.c_str());
5078
5079 // wait for state dependents to drop to zero
5080 i_ensureNoStateDependencies();
5081
5082 if (!mData->mAccessible)
5083 {
5084 // inaccessible maschines can only be unregistered; uninitialize ourselves
5085 // here because currently there may be no unregistered that are inaccessible
5086 // (this state combination is not supported). Note releasing the caller and
5087 // leaving the lock before calling uninit()
5088 alock.release();
5089 autoCaller.release();
5090
5091 uninit();
5092
5093 mParent->i_unregisterMachine(this, id);
5094 // calls VirtualBox::i_saveSettings()
5095
5096 return S_OK;
5097 }
5098
5099 HRESULT rc = S_OK;
5100
5101 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5102 // discard saved state
5103 if (mData->mMachineState == MachineState_Saved)
5104 {
5105 // add the saved state file to the list of files the caller should delete
5106 Assert(!mSSData->strStateFilePath.isEmpty());
5107 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5108
5109 mSSData->strStateFilePath.setNull();
5110
5111 // unconditionally set the machine state to powered off, we now
5112 // know no session has locked the machine
5113 mData->mMachineState = MachineState_PoweredOff;
5114 }
5115
5116 size_t cSnapshots = 0;
5117 if (mData->mFirstSnapshot)
5118 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5119 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5120 // fail now before we start detaching media
5121 return setError(VBOX_E_INVALID_OBJECT_STATE,
5122 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5123 mUserData->s.strName.c_str(), cSnapshots);
5124
5125 // This list collects the medium objects from all medium attachments
5126 // which we will detach from the machine and its snapshots, in a specific
5127 // order which allows for closing all media without getting "media in use"
5128 // errors, simply by going through the list from the front to the back:
5129 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5130 // and must be closed before the parent media from the snapshots, or closing the parents
5131 // will fail because they still have children);
5132 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5133 // the root ("first") snapshot of the machine.
5134 MediaList llMedia;
5135
5136 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5137 && mMediumAttachments->size()
5138 )
5139 {
5140 // we have media attachments: detach them all and add the Medium objects to our list
5141 if (aCleanupMode != CleanupMode_UnregisterOnly)
5142 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5143 else
5144 return setError(VBOX_E_INVALID_OBJECT_STATE,
5145 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5146 mUserData->s.strName.c_str(), mMediumAttachments->size());
5147 }
5148
5149 if (cSnapshots)
5150 {
5151 // add the media from the medium attachments of the snapshots to llMedia
5152 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5153 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5154 // into the children first
5155
5156 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5157 MachineState_T oldState = mData->mMachineState;
5158 mData->mMachineState = MachineState_DeletingSnapshot;
5159
5160 // make a copy of the first snapshot so the refcount does not drop to 0
5161 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5162 // because of the AutoCaller voodoo)
5163 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5164
5165 // GO!
5166 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5167
5168 mData->mMachineState = oldState;
5169 }
5170
5171 if (FAILED(rc))
5172 {
5173 i_rollbackMedia();
5174 return rc;
5175 }
5176
5177 // commit all the media changes made above
5178 i_commitMedia();
5179
5180 mData->mRegistered = false;
5181
5182 // machine lock no longer needed
5183 alock.release();
5184
5185 // return media to caller
5186 aMedia.resize(llMedia.size());
5187 size_t i = 0;
5188 for (MediaList::const_iterator
5189 it = llMedia.begin();
5190 it != llMedia.end();
5191 ++it, ++i)
5192 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5193
5194 mParent->i_unregisterMachine(this, id);
5195 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5196
5197 return S_OK;
5198}
5199
5200/**
5201 * Task record for deleting a machine config.
5202 */
5203class Machine::DeleteConfigTask
5204 : public Machine::Task
5205{
5206public:
5207 DeleteConfigTask(Machine *m,
5208 Progress *p,
5209 const Utf8Str &t,
5210 const RTCList<ComPtr<IMedium> > &llMediums,
5211 const StringsList &llFilesToDelete)
5212 : Task(m, p, t),
5213 m_llMediums(llMediums),
5214 m_llFilesToDelete(llFilesToDelete)
5215 {}
5216
5217private:
5218 void handler()
5219 {
5220 try
5221 {
5222 m_pMachine->i_deleteConfigHandler(*this);
5223 }
5224 catch (...)
5225 {
5226 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5227 }
5228 }
5229
5230 RTCList<ComPtr<IMedium> > m_llMediums;
5231 StringsList m_llFilesToDelete;
5232
5233 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5234};
5235
5236/**
5237 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5238 * SessionMachine::taskHandler().
5239 *
5240 * @note Locks this object for writing.
5241 *
5242 * @param task
5243 * @return
5244 */
5245void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5246{
5247 LogFlowThisFuncEnter();
5248
5249 AutoCaller autoCaller(this);
5250 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5251 if (FAILED(autoCaller.rc()))
5252 {
5253 /* we might have been uninitialized because the session was accidentally
5254 * closed by the client, so don't assert */
5255 HRESULT rc = setError(E_FAIL,
5256 tr("The session has been accidentally closed"));
5257 task.m_pProgress->i_notifyComplete(rc);
5258 LogFlowThisFuncLeave();
5259 return;
5260 }
5261
5262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 HRESULT rc = S_OK;
5265
5266 try
5267 {
5268 ULONG uLogHistoryCount = 3;
5269 ComPtr<ISystemProperties> systemProperties;
5270 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5271 if (FAILED(rc)) throw rc;
5272
5273 if (!systemProperties.isNull())
5274 {
5275 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5276 if (FAILED(rc)) throw rc;
5277 }
5278
5279 MachineState_T oldState = mData->mMachineState;
5280 i_setMachineState(MachineState_SettingUp);
5281 alock.release();
5282 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5283 {
5284 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5285 {
5286 AutoCaller mac(pMedium);
5287 if (FAILED(mac.rc())) throw mac.rc();
5288 Utf8Str strLocation = pMedium->i_getLocationFull();
5289 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5290 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5291 if (FAILED(rc)) throw rc;
5292 }
5293 if (pMedium->i_isMediumFormatFile())
5294 {
5295 ComPtr<IProgress> pProgress2;
5296 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5297 if (FAILED(rc)) throw rc;
5298 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5299 if (FAILED(rc)) throw rc;
5300 }
5301
5302 /* Close the medium, deliberately without checking the return
5303 * code, and without leaving any trace in the error info, as
5304 * a failure here is a very minor issue, which shouldn't happen
5305 * as above we even managed to delete the medium. */
5306 {
5307 ErrorInfoKeeper eik;
5308 pMedium->Close();
5309 }
5310 }
5311 i_setMachineState(oldState);
5312 alock.acquire();
5313
5314 // delete the files pushed on the task list by Machine::Delete()
5315 // (this includes saved states of the machine and snapshots and
5316 // medium storage files from the IMedium list passed in, and the
5317 // machine XML file)
5318 for (StringsList::const_iterator
5319 it = task.m_llFilesToDelete.begin();
5320 it != task.m_llFilesToDelete.end();
5321 ++it)
5322 {
5323 const Utf8Str &strFile = *it;
5324 LogFunc(("Deleting file %s\n", strFile.c_str()));
5325 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5326 if (FAILED(rc)) throw rc;
5327
5328 int vrc = RTFileDelete(strFile.c_str());
5329 if (RT_FAILURE(vrc))
5330 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5331 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5332 }
5333
5334 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5335 if (FAILED(rc)) throw rc;
5336
5337 /* delete the settings only when the file actually exists */
5338 if (mData->pMachineConfigFile->fileExists())
5339 {
5340 /* Delete any backup or uncommitted XML files. Ignore failures.
5341 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5342 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5343 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5344 RTFileDelete(otherXml.c_str());
5345 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5346 RTFileDelete(otherXml.c_str());
5347
5348 /* delete the Logs folder, nothing important should be left
5349 * there (we don't check for errors because the user might have
5350 * some private files there that we don't want to delete) */
5351 Utf8Str logFolder;
5352 getLogFolder(logFolder);
5353 Assert(logFolder.length());
5354 if (RTDirExists(logFolder.c_str()))
5355 {
5356 /* Delete all VBox.log[.N] files from the Logs folder
5357 * (this must be in sync with the rotation logic in
5358 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5359 * files that may have been created by the GUI. */
5360 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5361 logFolder.c_str(), RTPATH_DELIMITER);
5362 RTFileDelete(log.c_str());
5363 log = Utf8StrFmt("%s%cVBox.png",
5364 logFolder.c_str(), RTPATH_DELIMITER);
5365 RTFileDelete(log.c_str());
5366 for (int i = uLogHistoryCount; i > 0; i--)
5367 {
5368 log = Utf8StrFmt("%s%cVBox.log.%d",
5369 logFolder.c_str(), RTPATH_DELIMITER, i);
5370 RTFileDelete(log.c_str());
5371 log = Utf8StrFmt("%s%cVBox.png.%d",
5372 logFolder.c_str(), RTPATH_DELIMITER, i);
5373 RTFileDelete(log.c_str());
5374 }
5375#if defined(RT_OS_WINDOWS)
5376 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5377 RTFileDelete(log.c_str());
5378 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5379 RTFileDelete(log.c_str());
5380#endif
5381
5382 RTDirRemove(logFolder.c_str());
5383 }
5384
5385 /* delete the Snapshots folder, nothing important should be left
5386 * there (we don't check for errors because the user might have
5387 * some private files there that we don't want to delete) */
5388 Utf8Str strFullSnapshotFolder;
5389 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5390 Assert(!strFullSnapshotFolder.isEmpty());
5391 if (RTDirExists(strFullSnapshotFolder.c_str()))
5392 RTDirRemove(strFullSnapshotFolder.c_str());
5393
5394 // delete the directory that contains the settings file, but only
5395 // if it matches the VM name
5396 Utf8Str settingsDir;
5397 if (i_isInOwnDir(&settingsDir))
5398 RTDirRemove(settingsDir.c_str());
5399 }
5400
5401 alock.release();
5402
5403 mParent->i_saveModifiedRegistries();
5404 }
5405 catch (HRESULT aRC) { rc = aRC; }
5406
5407 task.m_pProgress->i_notifyComplete(rc);
5408
5409 LogFlowThisFuncLeave();
5410}
5411
5412HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5413{
5414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5415
5416 HRESULT rc = i_checkStateDependency(MutableStateDep);
5417 if (FAILED(rc)) return rc;
5418
5419 if (mData->mRegistered)
5420 return setError(VBOX_E_INVALID_VM_STATE,
5421 tr("Cannot delete settings of a registered machine"));
5422
5423 // collect files to delete
5424 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5425 if (mData->pMachineConfigFile->fileExists())
5426 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5427
5428 RTCList<ComPtr<IMedium> > llMediums;
5429 for (size_t i = 0; i < aMedia.size(); ++i)
5430 {
5431 IMedium *pIMedium(aMedia[i]);
5432 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5433 if (pMedium.isNull())
5434 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5435 SafeArray<BSTR> ids;
5436 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5437 if (FAILED(rc)) return rc;
5438 /* At this point the medium should not have any back references
5439 * anymore. If it has it is attached to another VM and *must* not
5440 * deleted. */
5441 if (ids.size() < 1)
5442 llMediums.append(pMedium);
5443 }
5444
5445 ComObjPtr<Progress> pProgress;
5446 pProgress.createObject();
5447 rc = pProgress->init(i_getVirtualBox(),
5448 static_cast<IMachine*>(this) /* aInitiator */,
5449 tr("Deleting files"),
5450 true /* fCancellable */,
5451 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5452 tr("Collecting file inventory"));
5453 if (FAILED(rc))
5454 return rc;
5455
5456 /* create and start the task on a separate thread (note that it will not
5457 * start working until we release alock) */
5458 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5459 rc = pTask->createThread();
5460 pTask = NULL;
5461 if (FAILED(rc))
5462 return rc;
5463
5464 pProgress.queryInterfaceTo(aProgress.asOutParam());
5465
5466 LogFlowFuncLeave();
5467
5468 return S_OK;
5469}
5470
5471HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5472{
5473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5474
5475 ComObjPtr<Snapshot> pSnapshot;
5476 HRESULT rc;
5477
5478 if (aNameOrId.isEmpty())
5479 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5480 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5481 else
5482 {
5483 Guid uuid(aNameOrId);
5484 if (uuid.isValid())
5485 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5486 else
5487 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5488 }
5489 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5490
5491 return rc;
5492}
5493
5494HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5495 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5496{
5497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5498
5499 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5500 if (FAILED(rc)) return rc;
5501
5502 ComObjPtr<SharedFolder> sharedFolder;
5503 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5504 if (SUCCEEDED(rc))
5505 return setError(VBOX_E_OBJECT_IN_USE,
5506 tr("Shared folder named '%s' already exists"),
5507 aName.c_str());
5508
5509 sharedFolder.createObject();
5510 rc = sharedFolder->init(i_getMachine(),
5511 aName,
5512 aHostPath,
5513 !!aWritable,
5514 !!aAutomount,
5515 aAutoMountPoint,
5516 true /* fFailOnError */);
5517 if (FAILED(rc)) return rc;
5518
5519 i_setModified(IsModified_SharedFolders);
5520 mHWData.backup();
5521 mHWData->mSharedFolders.push_back(sharedFolder);
5522
5523 /* inform the direct session if any */
5524 alock.release();
5525 i_onSharedFolderChange();
5526
5527 return S_OK;
5528}
5529
5530HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5531{
5532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5533
5534 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5535 if (FAILED(rc)) return rc;
5536
5537 ComObjPtr<SharedFolder> sharedFolder;
5538 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5539 if (FAILED(rc)) return rc;
5540
5541 i_setModified(IsModified_SharedFolders);
5542 mHWData.backup();
5543 mHWData->mSharedFolders.remove(sharedFolder);
5544
5545 /* inform the direct session if any */
5546 alock.release();
5547 i_onSharedFolderChange();
5548
5549 return S_OK;
5550}
5551
5552HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5553{
5554 /* start with No */
5555 *aCanShow = FALSE;
5556
5557 ComPtr<IInternalSessionControl> directControl;
5558 {
5559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5560
5561 if (mData->mSession.mState != SessionState_Locked)
5562 return setError(VBOX_E_INVALID_VM_STATE,
5563 tr("Machine is not locked for session (session state: %s)"),
5564 Global::stringifySessionState(mData->mSession.mState));
5565
5566 if (mData->mSession.mLockType == LockType_VM)
5567 directControl = mData->mSession.mDirectControl;
5568 }
5569
5570 /* ignore calls made after #OnSessionEnd() is called */
5571 if (!directControl)
5572 return S_OK;
5573
5574 LONG64 dummy;
5575 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5576}
5577
5578HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5579{
5580 ComPtr<IInternalSessionControl> directControl;
5581 {
5582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5583
5584 if (mData->mSession.mState != SessionState_Locked)
5585 return setError(E_FAIL,
5586 tr("Machine is not locked for session (session state: %s)"),
5587 Global::stringifySessionState(mData->mSession.mState));
5588
5589 if (mData->mSession.mLockType == LockType_VM)
5590 directControl = mData->mSession.mDirectControl;
5591 }
5592
5593 /* ignore calls made after #OnSessionEnd() is called */
5594 if (!directControl)
5595 return S_OK;
5596
5597 BOOL dummy;
5598 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5599}
5600
5601#ifdef VBOX_WITH_GUEST_PROPS
5602/**
5603 * Look up a guest property in VBoxSVC's internal structures.
5604 */
5605HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5606 com::Utf8Str &aValue,
5607 LONG64 *aTimestamp,
5608 com::Utf8Str &aFlags) const
5609{
5610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5611
5612 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5613 if (it != mHWData->mGuestProperties.end())
5614 {
5615 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5616 aValue = it->second.strValue;
5617 *aTimestamp = it->second.mTimestamp;
5618 GuestPropWriteFlags(it->second.mFlags, szFlags);
5619 aFlags = Utf8Str(szFlags);
5620 }
5621
5622 return S_OK;
5623}
5624
5625/**
5626 * Query the VM that a guest property belongs to for the property.
5627 * @returns E_ACCESSDENIED if the VM process is not available or not
5628 * currently handling queries and the lookup should then be done in
5629 * VBoxSVC.
5630 */
5631HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5632 com::Utf8Str &aValue,
5633 LONG64 *aTimestamp,
5634 com::Utf8Str &aFlags) const
5635{
5636 HRESULT rc = S_OK;
5637 Bstr bstrValue;
5638 Bstr bstrFlags;
5639
5640 ComPtr<IInternalSessionControl> directControl;
5641 {
5642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5643 if (mData->mSession.mLockType == LockType_VM)
5644 directControl = mData->mSession.mDirectControl;
5645 }
5646
5647 /* ignore calls made after #OnSessionEnd() is called */
5648 if (!directControl)
5649 rc = E_ACCESSDENIED;
5650 else
5651 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5652 0 /* accessMode */,
5653 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5654
5655 aValue = bstrValue;
5656 aFlags = bstrFlags;
5657
5658 return rc;
5659}
5660#endif // VBOX_WITH_GUEST_PROPS
5661
5662HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5663 com::Utf8Str &aValue,
5664 LONG64 *aTimestamp,
5665 com::Utf8Str &aFlags)
5666{
5667#ifndef VBOX_WITH_GUEST_PROPS
5668 ReturnComNotImplemented();
5669#else // VBOX_WITH_GUEST_PROPS
5670
5671 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5672
5673 if (rc == E_ACCESSDENIED)
5674 /* The VM is not running or the service is not (yet) accessible */
5675 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5676 return rc;
5677#endif // VBOX_WITH_GUEST_PROPS
5678}
5679
5680HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5681{
5682 LONG64 dummyTimestamp;
5683 com::Utf8Str dummyFlags;
5684 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5685 return rc;
5686
5687}
5688HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5689{
5690 com::Utf8Str dummyFlags;
5691 com::Utf8Str dummyValue;
5692 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5693 return rc;
5694}
5695
5696#ifdef VBOX_WITH_GUEST_PROPS
5697/**
5698 * Set a guest property in VBoxSVC's internal structures.
5699 */
5700HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5701 const com::Utf8Str &aFlags, bool fDelete)
5702{
5703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5704 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5705 if (FAILED(rc)) return rc;
5706
5707 try
5708 {
5709 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5710 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5711 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5712
5713 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5714 if (it == mHWData->mGuestProperties.end())
5715 {
5716 if (!fDelete)
5717 {
5718 i_setModified(IsModified_MachineData);
5719 mHWData.backupEx();
5720
5721 RTTIMESPEC time;
5722 HWData::GuestProperty prop;
5723 prop.strValue = Bstr(aValue).raw();
5724 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5725 prop.mFlags = fFlags;
5726 mHWData->mGuestProperties[aName] = prop;
5727 }
5728 }
5729 else
5730 {
5731 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5732 {
5733 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5734 }
5735 else
5736 {
5737 i_setModified(IsModified_MachineData);
5738 mHWData.backupEx();
5739
5740 /* The backupEx() operation invalidates our iterator,
5741 * so get a new one. */
5742 it = mHWData->mGuestProperties.find(aName);
5743 Assert(it != mHWData->mGuestProperties.end());
5744
5745 if (!fDelete)
5746 {
5747 RTTIMESPEC time;
5748 it->second.strValue = aValue;
5749 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5750 it->second.mFlags = fFlags;
5751 }
5752 else
5753 mHWData->mGuestProperties.erase(it);
5754 }
5755 }
5756
5757 if (SUCCEEDED(rc))
5758 {
5759 alock.release();
5760
5761 mParent->i_onGuestPropertyChange(mData->mUuid,
5762 Bstr(aName).raw(),
5763 Bstr(aValue).raw(),
5764 Bstr(aFlags).raw());
5765 }
5766 }
5767 catch (std::bad_alloc &)
5768 {
5769 rc = E_OUTOFMEMORY;
5770 }
5771
5772 return rc;
5773}
5774
5775/**
5776 * Set a property on the VM that that property belongs to.
5777 * @returns E_ACCESSDENIED if the VM process is not available or not
5778 * currently handling queries and the setting should then be done in
5779 * VBoxSVC.
5780 */
5781HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5782 const com::Utf8Str &aFlags, bool fDelete)
5783{
5784 HRESULT rc;
5785
5786 try
5787 {
5788 ComPtr<IInternalSessionControl> directControl;
5789 {
5790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5791 if (mData->mSession.mLockType == LockType_VM)
5792 directControl = mData->mSession.mDirectControl;
5793 }
5794
5795 Bstr dummy1; /* will not be changed (setter) */
5796 Bstr dummy2; /* will not be changed (setter) */
5797 LONG64 dummy64;
5798 if (!directControl)
5799 rc = E_ACCESSDENIED;
5800 else
5801 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5802 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5803 fDelete ? 2 : 1 /* accessMode */,
5804 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5805 }
5806 catch (std::bad_alloc &)
5807 {
5808 rc = E_OUTOFMEMORY;
5809 }
5810
5811 return rc;
5812}
5813#endif // VBOX_WITH_GUEST_PROPS
5814
5815HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5816 const com::Utf8Str &aFlags)
5817{
5818#ifndef VBOX_WITH_GUEST_PROPS
5819 ReturnComNotImplemented();
5820#else // VBOX_WITH_GUEST_PROPS
5821 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5822 if (rc == E_ACCESSDENIED)
5823 /* The VM is not running or the service is not (yet) accessible */
5824 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5825 return rc;
5826#endif // VBOX_WITH_GUEST_PROPS
5827}
5828
5829HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5830{
5831 return setGuestProperty(aProperty, aValue, "");
5832}
5833
5834HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5835{
5836#ifndef VBOX_WITH_GUEST_PROPS
5837 ReturnComNotImplemented();
5838#else // VBOX_WITH_GUEST_PROPS
5839 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5840 if (rc == E_ACCESSDENIED)
5841 /* The VM is not running or the service is not (yet) accessible */
5842 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5843 return rc;
5844#endif // VBOX_WITH_GUEST_PROPS
5845}
5846
5847#ifdef VBOX_WITH_GUEST_PROPS
5848/**
5849 * Enumerate the guest properties in VBoxSVC's internal structures.
5850 */
5851HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5852 std::vector<com::Utf8Str> &aNames,
5853 std::vector<com::Utf8Str> &aValues,
5854 std::vector<LONG64> &aTimestamps,
5855 std::vector<com::Utf8Str> &aFlags)
5856{
5857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5858 Utf8Str strPatterns(aPatterns);
5859
5860 /*
5861 * Look for matching patterns and build up a list.
5862 */
5863 HWData::GuestPropertyMap propMap;
5864 for (HWData::GuestPropertyMap::const_iterator
5865 it = mHWData->mGuestProperties.begin();
5866 it != mHWData->mGuestProperties.end();
5867 ++it)
5868 {
5869 if ( strPatterns.isEmpty()
5870 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5871 RTSTR_MAX,
5872 it->first.c_str(),
5873 RTSTR_MAX,
5874 NULL)
5875 )
5876 propMap.insert(*it);
5877 }
5878
5879 alock.release();
5880
5881 /*
5882 * And build up the arrays for returning the property information.
5883 */
5884 size_t cEntries = propMap.size();
5885
5886 aNames.resize(cEntries);
5887 aValues.resize(cEntries);
5888 aTimestamps.resize(cEntries);
5889 aFlags.resize(cEntries);
5890
5891 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5892 size_t i = 0;
5893 for (HWData::GuestPropertyMap::const_iterator
5894 it = propMap.begin();
5895 it != propMap.end();
5896 ++it, ++i)
5897 {
5898 aNames[i] = it->first;
5899 aValues[i] = it->second.strValue;
5900 aTimestamps[i] = it->second.mTimestamp;
5901 GuestPropWriteFlags(it->second.mFlags, szFlags);
5902 aFlags[i] = Utf8Str(szFlags);
5903 }
5904
5905 return S_OK;
5906}
5907
5908/**
5909 * Enumerate the properties managed by a VM.
5910 * @returns E_ACCESSDENIED if the VM process is not available or not
5911 * currently handling queries and the setting should then be done in
5912 * VBoxSVC.
5913 */
5914HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5915 std::vector<com::Utf8Str> &aNames,
5916 std::vector<com::Utf8Str> &aValues,
5917 std::vector<LONG64> &aTimestamps,
5918 std::vector<com::Utf8Str> &aFlags)
5919{
5920 HRESULT rc;
5921 ComPtr<IInternalSessionControl> directControl;
5922 {
5923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5924 if (mData->mSession.mLockType == LockType_VM)
5925 directControl = mData->mSession.mDirectControl;
5926 }
5927
5928 com::SafeArray<BSTR> bNames;
5929 com::SafeArray<BSTR> bValues;
5930 com::SafeArray<LONG64> bTimestamps;
5931 com::SafeArray<BSTR> bFlags;
5932
5933 if (!directControl)
5934 rc = E_ACCESSDENIED;
5935 else
5936 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5937 ComSafeArrayAsOutParam(bNames),
5938 ComSafeArrayAsOutParam(bValues),
5939 ComSafeArrayAsOutParam(bTimestamps),
5940 ComSafeArrayAsOutParam(bFlags));
5941 size_t i;
5942 aNames.resize(bNames.size());
5943 for (i = 0; i < bNames.size(); ++i)
5944 aNames[i] = Utf8Str(bNames[i]);
5945 aValues.resize(bValues.size());
5946 for (i = 0; i < bValues.size(); ++i)
5947 aValues[i] = Utf8Str(bValues[i]);
5948 aTimestamps.resize(bTimestamps.size());
5949 for (i = 0; i < bTimestamps.size(); ++i)
5950 aTimestamps[i] = bTimestamps[i];
5951 aFlags.resize(bFlags.size());
5952 for (i = 0; i < bFlags.size(); ++i)
5953 aFlags[i] = Utf8Str(bFlags[i]);
5954
5955 return rc;
5956}
5957#endif // VBOX_WITH_GUEST_PROPS
5958HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5959 std::vector<com::Utf8Str> &aNames,
5960 std::vector<com::Utf8Str> &aValues,
5961 std::vector<LONG64> &aTimestamps,
5962 std::vector<com::Utf8Str> &aFlags)
5963{
5964#ifndef VBOX_WITH_GUEST_PROPS
5965 ReturnComNotImplemented();
5966#else // VBOX_WITH_GUEST_PROPS
5967
5968 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5969
5970 if (rc == E_ACCESSDENIED)
5971 /* The VM is not running or the service is not (yet) accessible */
5972 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5973 return rc;
5974#endif // VBOX_WITH_GUEST_PROPS
5975}
5976
5977HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5978 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5979{
5980 MediumAttachmentList atts;
5981
5982 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5983 if (FAILED(rc)) return rc;
5984
5985 aMediumAttachments.resize(atts.size());
5986 size_t i = 0;
5987 for (MediumAttachmentList::const_iterator
5988 it = atts.begin();
5989 it != atts.end();
5990 ++it, ++i)
5991 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5992
5993 return S_OK;
5994}
5995
5996HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5997 LONG aControllerPort,
5998 LONG aDevice,
5999 ComPtr<IMediumAttachment> &aAttachment)
6000{
6001 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6002 aName.c_str(), aControllerPort, aDevice));
6003
6004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6005
6006 aAttachment = NULL;
6007
6008 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6009 aName,
6010 aControllerPort,
6011 aDevice);
6012 if (pAttach.isNull())
6013 return setError(VBOX_E_OBJECT_NOT_FOUND,
6014 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6015 aDevice, aControllerPort, aName.c_str());
6016
6017 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6018
6019 return S_OK;
6020}
6021
6022
6023HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6024 StorageBus_T aConnectionType,
6025 ComPtr<IStorageController> &aController)
6026{
6027 if ( (aConnectionType <= StorageBus_Null)
6028 || (aConnectionType > StorageBus_VirtioSCSI))
6029 return setError(E_INVALIDARG,
6030 tr("Invalid connection type: %d"),
6031 aConnectionType);
6032
6033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 HRESULT rc = i_checkStateDependency(MutableStateDep);
6036 if (FAILED(rc)) return rc;
6037
6038 /* try to find one with the name first. */
6039 ComObjPtr<StorageController> ctrl;
6040
6041 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 return setError(VBOX_E_OBJECT_IN_USE,
6044 tr("Storage controller named '%s' already exists"),
6045 aName.c_str());
6046
6047 ctrl.createObject();
6048
6049 /* get a new instance number for the storage controller */
6050 ULONG ulInstance = 0;
6051 bool fBootable = true;
6052 for (StorageControllerList::const_iterator
6053 it = mStorageControllers->begin();
6054 it != mStorageControllers->end();
6055 ++it)
6056 {
6057 if ((*it)->i_getStorageBus() == aConnectionType)
6058 {
6059 ULONG ulCurInst = (*it)->i_getInstance();
6060
6061 if (ulCurInst >= ulInstance)
6062 ulInstance = ulCurInst + 1;
6063
6064 /* Only one controller of each type can be marked as bootable. */
6065 if ((*it)->i_getBootable())
6066 fBootable = false;
6067 }
6068 }
6069
6070 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6071 if (FAILED(rc)) return rc;
6072
6073 i_setModified(IsModified_Storage);
6074 mStorageControllers.backup();
6075 mStorageControllers->push_back(ctrl);
6076
6077 ctrl.queryInterfaceTo(aController.asOutParam());
6078
6079 /* inform the direct session if any */
6080 alock.release();
6081 i_onStorageControllerChange(i_getId(), aName);
6082
6083 return S_OK;
6084}
6085
6086HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6087 ComPtr<IStorageController> &aStorageController)
6088{
6089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6090
6091 ComObjPtr<StorageController> ctrl;
6092
6093 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6094 if (SUCCEEDED(rc))
6095 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6096
6097 return rc;
6098}
6099
6100HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6101 ULONG aInstance,
6102 ComPtr<IStorageController> &aStorageController)
6103{
6104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6105
6106 for (StorageControllerList::const_iterator
6107 it = mStorageControllers->begin();
6108 it != mStorageControllers->end();
6109 ++it)
6110 {
6111 if ( (*it)->i_getStorageBus() == aConnectionType
6112 && (*it)->i_getInstance() == aInstance)
6113 {
6114 (*it).queryInterfaceTo(aStorageController.asOutParam());
6115 return S_OK;
6116 }
6117 }
6118
6119 return setError(VBOX_E_OBJECT_NOT_FOUND,
6120 tr("Could not find a storage controller with instance number '%lu'"),
6121 aInstance);
6122}
6123
6124HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6125{
6126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6127
6128 HRESULT rc = i_checkStateDependency(MutableStateDep);
6129 if (FAILED(rc)) return rc;
6130
6131 ComObjPtr<StorageController> ctrl;
6132
6133 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6134 if (SUCCEEDED(rc))
6135 {
6136 /* Ensure that only one controller of each type is marked as bootable. */
6137 if (aBootable == TRUE)
6138 {
6139 for (StorageControllerList::const_iterator
6140 it = mStorageControllers->begin();
6141 it != mStorageControllers->end();
6142 ++it)
6143 {
6144 ComObjPtr<StorageController> aCtrl = (*it);
6145
6146 if ( (aCtrl->i_getName() != aName)
6147 && aCtrl->i_getBootable() == TRUE
6148 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6149 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6150 {
6151 aCtrl->i_setBootable(FALSE);
6152 break;
6153 }
6154 }
6155 }
6156
6157 if (SUCCEEDED(rc))
6158 {
6159 ctrl->i_setBootable(aBootable);
6160 i_setModified(IsModified_Storage);
6161 }
6162 }
6163
6164 if (SUCCEEDED(rc))
6165 {
6166 /* inform the direct session if any */
6167 alock.release();
6168 i_onStorageControllerChange(i_getId(), aName);
6169 }
6170
6171 return rc;
6172}
6173
6174HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6175{
6176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6177
6178 HRESULT rc = i_checkStateDependency(MutableStateDep);
6179 if (FAILED(rc)) return rc;
6180
6181 ComObjPtr<StorageController> ctrl;
6182 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6183 if (FAILED(rc)) return rc;
6184
6185 MediumAttachmentList llDetachedAttachments;
6186 {
6187 /* find all attached devices to the appropriate storage controller and detach them all */
6188 // make a temporary list because detachDevice invalidates iterators into
6189 // mMediumAttachments
6190 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6191
6192 for (MediumAttachmentList::const_iterator
6193 it = llAttachments2.begin();
6194 it != llAttachments2.end();
6195 ++it)
6196 {
6197 MediumAttachment *pAttachTemp = *it;
6198
6199 AutoCaller localAutoCaller(pAttachTemp);
6200 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6201
6202 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6203
6204 if (pAttachTemp->i_getControllerName() == aName)
6205 {
6206 llDetachedAttachments.push_back(pAttachTemp);
6207 rc = i_detachDevice(pAttachTemp, alock, NULL);
6208 if (FAILED(rc)) return rc;
6209 }
6210 }
6211 }
6212
6213 /* send event about detached devices before removing parent controller */
6214 for (MediumAttachmentList::const_iterator
6215 it = llDetachedAttachments.begin();
6216 it != llDetachedAttachments.end();
6217 ++it)
6218 {
6219 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6220 }
6221
6222 /* We can remove it now. */
6223 i_setModified(IsModified_Storage);
6224 mStorageControllers.backup();
6225
6226 ctrl->i_unshare();
6227
6228 mStorageControllers->remove(ctrl);
6229
6230 /* inform the direct session if any */
6231 alock.release();
6232 i_onStorageControllerChange(i_getId(), aName);
6233
6234 return S_OK;
6235}
6236
6237HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6238 ComPtr<IUSBController> &aController)
6239{
6240 if ( (aType <= USBControllerType_Null)
6241 || (aType >= USBControllerType_Last))
6242 return setError(E_INVALIDARG,
6243 tr("Invalid USB controller type: %d"),
6244 aType);
6245
6246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 HRESULT rc = i_checkStateDependency(MutableStateDep);
6249 if (FAILED(rc)) return rc;
6250
6251 /* try to find one with the same type first. */
6252 ComObjPtr<USBController> ctrl;
6253
6254 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6255 if (SUCCEEDED(rc))
6256 return setError(VBOX_E_OBJECT_IN_USE,
6257 tr("USB controller named '%s' already exists"),
6258 aName.c_str());
6259
6260 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6261 ULONG maxInstances;
6262 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6263 if (FAILED(rc))
6264 return rc;
6265
6266 ULONG cInstances = i_getUSBControllerCountByType(aType);
6267 if (cInstances >= maxInstances)
6268 return setError(E_INVALIDARG,
6269 tr("Too many USB controllers of this type"));
6270
6271 ctrl.createObject();
6272
6273 rc = ctrl->init(this, aName, aType);
6274 if (FAILED(rc)) return rc;
6275
6276 i_setModified(IsModified_USB);
6277 mUSBControllers.backup();
6278 mUSBControllers->push_back(ctrl);
6279
6280 ctrl.queryInterfaceTo(aController.asOutParam());
6281
6282 /* inform the direct session if any */
6283 alock.release();
6284 i_onUSBControllerChange();
6285
6286 return S_OK;
6287}
6288
6289HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6290{
6291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6292
6293 ComObjPtr<USBController> ctrl;
6294
6295 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6296 if (SUCCEEDED(rc))
6297 ctrl.queryInterfaceTo(aController.asOutParam());
6298
6299 return rc;
6300}
6301
6302HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6303 ULONG *aControllers)
6304{
6305 if ( (aType <= USBControllerType_Null)
6306 || (aType >= USBControllerType_Last))
6307 return setError(E_INVALIDARG,
6308 tr("Invalid USB controller type: %d"),
6309 aType);
6310
6311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6312
6313 ComObjPtr<USBController> ctrl;
6314
6315 *aControllers = i_getUSBControllerCountByType(aType);
6316
6317 return S_OK;
6318}
6319
6320HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6321{
6322
6323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 HRESULT rc = i_checkStateDependency(MutableStateDep);
6326 if (FAILED(rc)) return rc;
6327
6328 ComObjPtr<USBController> ctrl;
6329 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6330 if (FAILED(rc)) return rc;
6331
6332 i_setModified(IsModified_USB);
6333 mUSBControllers.backup();
6334
6335 ctrl->i_unshare();
6336
6337 mUSBControllers->remove(ctrl);
6338
6339 /* inform the direct session if any */
6340 alock.release();
6341 i_onUSBControllerChange();
6342
6343 return S_OK;
6344}
6345
6346HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6347 ULONG *aOriginX,
6348 ULONG *aOriginY,
6349 ULONG *aWidth,
6350 ULONG *aHeight,
6351 BOOL *aEnabled)
6352{
6353 uint32_t u32OriginX= 0;
6354 uint32_t u32OriginY= 0;
6355 uint32_t u32Width = 0;
6356 uint32_t u32Height = 0;
6357 uint16_t u16Flags = 0;
6358
6359 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6360 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6361 if (RT_FAILURE(vrc))
6362 {
6363#ifdef RT_OS_WINDOWS
6364 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6365 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6366 * So just assign fEnable to TRUE again.
6367 * The right fix would be to change GUI API wrappers to make sure that parameters
6368 * are changed only if API succeeds.
6369 */
6370 *aEnabled = TRUE;
6371#endif
6372 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6373 tr("Saved guest size is not available (%Rrc)"),
6374 vrc);
6375 }
6376
6377 *aOriginX = u32OriginX;
6378 *aOriginY = u32OriginY;
6379 *aWidth = u32Width;
6380 *aHeight = u32Height;
6381 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6382
6383 return S_OK;
6384}
6385
6386HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6387 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6388{
6389 if (aScreenId != 0)
6390 return E_NOTIMPL;
6391
6392 if ( aBitmapFormat != BitmapFormat_BGR0
6393 && aBitmapFormat != BitmapFormat_BGRA
6394 && aBitmapFormat != BitmapFormat_RGBA
6395 && aBitmapFormat != BitmapFormat_PNG)
6396 return setError(E_NOTIMPL,
6397 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6398
6399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6400
6401 uint8_t *pu8Data = NULL;
6402 uint32_t cbData = 0;
6403 uint32_t u32Width = 0;
6404 uint32_t u32Height = 0;
6405
6406 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6407
6408 if (RT_FAILURE(vrc))
6409 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6410 tr("Saved thumbnail data is not available (%Rrc)"),
6411 vrc);
6412
6413 HRESULT hr = S_OK;
6414
6415 *aWidth = u32Width;
6416 *aHeight = u32Height;
6417
6418 if (cbData > 0)
6419 {
6420 /* Convert pixels to the format expected by the API caller. */
6421 if (aBitmapFormat == BitmapFormat_BGR0)
6422 {
6423 /* [0] B, [1] G, [2] R, [3] 0. */
6424 aData.resize(cbData);
6425 memcpy(&aData.front(), pu8Data, cbData);
6426 }
6427 else if (aBitmapFormat == BitmapFormat_BGRA)
6428 {
6429 /* [0] B, [1] G, [2] R, [3] A. */
6430 aData.resize(cbData);
6431 for (uint32_t i = 0; i < cbData; i += 4)
6432 {
6433 aData[i] = pu8Data[i];
6434 aData[i + 1] = pu8Data[i + 1];
6435 aData[i + 2] = pu8Data[i + 2];
6436 aData[i + 3] = 0xff;
6437 }
6438 }
6439 else if (aBitmapFormat == BitmapFormat_RGBA)
6440 {
6441 /* [0] R, [1] G, [2] B, [3] A. */
6442 aData.resize(cbData);
6443 for (uint32_t i = 0; i < cbData; i += 4)
6444 {
6445 aData[i] = pu8Data[i + 2];
6446 aData[i + 1] = pu8Data[i + 1];
6447 aData[i + 2] = pu8Data[i];
6448 aData[i + 3] = 0xff;
6449 }
6450 }
6451 else if (aBitmapFormat == BitmapFormat_PNG)
6452 {
6453 uint8_t *pu8PNG = NULL;
6454 uint32_t cbPNG = 0;
6455 uint32_t cxPNG = 0;
6456 uint32_t cyPNG = 0;
6457
6458 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6459
6460 if (RT_SUCCESS(vrc))
6461 {
6462 aData.resize(cbPNG);
6463 if (cbPNG)
6464 memcpy(&aData.front(), pu8PNG, cbPNG);
6465 }
6466 else
6467 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6468 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6469 vrc);
6470
6471 RTMemFree(pu8PNG);
6472 }
6473 }
6474
6475 freeSavedDisplayScreenshot(pu8Data);
6476
6477 return hr;
6478}
6479
6480HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6481 ULONG *aWidth,
6482 ULONG *aHeight,
6483 std::vector<BitmapFormat_T> &aBitmapFormats)
6484{
6485 if (aScreenId != 0)
6486 return E_NOTIMPL;
6487
6488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 uint8_t *pu8Data = NULL;
6491 uint32_t cbData = 0;
6492 uint32_t u32Width = 0;
6493 uint32_t u32Height = 0;
6494
6495 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6496
6497 if (RT_FAILURE(vrc))
6498 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6499 tr("Saved screenshot data is not available (%Rrc)"),
6500 vrc);
6501
6502 *aWidth = u32Width;
6503 *aHeight = u32Height;
6504 aBitmapFormats.resize(1);
6505 aBitmapFormats[0] = BitmapFormat_PNG;
6506
6507 freeSavedDisplayScreenshot(pu8Data);
6508
6509 return S_OK;
6510}
6511
6512HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6513 BitmapFormat_T aBitmapFormat,
6514 ULONG *aWidth,
6515 ULONG *aHeight,
6516 std::vector<BYTE> &aData)
6517{
6518 if (aScreenId != 0)
6519 return E_NOTIMPL;
6520
6521 if (aBitmapFormat != BitmapFormat_PNG)
6522 return E_NOTIMPL;
6523
6524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6525
6526 uint8_t *pu8Data = NULL;
6527 uint32_t cbData = 0;
6528 uint32_t u32Width = 0;
6529 uint32_t u32Height = 0;
6530
6531 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6532
6533 if (RT_FAILURE(vrc))
6534 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6535 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6536 vrc);
6537
6538 *aWidth = u32Width;
6539 *aHeight = u32Height;
6540
6541 aData.resize(cbData);
6542 if (cbData)
6543 memcpy(&aData.front(), pu8Data, cbData);
6544
6545 freeSavedDisplayScreenshot(pu8Data);
6546
6547 return S_OK;
6548}
6549
6550HRESULT Machine::hotPlugCPU(ULONG aCpu)
6551{
6552 HRESULT rc = S_OK;
6553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6554
6555 if (!mHWData->mCPUHotPlugEnabled)
6556 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6557
6558 if (aCpu >= mHWData->mCPUCount)
6559 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6560
6561 if (mHWData->mCPUAttached[aCpu])
6562 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6563
6564 alock.release();
6565 rc = i_onCPUChange(aCpu, false);
6566 alock.acquire();
6567 if (FAILED(rc)) return rc;
6568
6569 i_setModified(IsModified_MachineData);
6570 mHWData.backup();
6571 mHWData->mCPUAttached[aCpu] = true;
6572
6573 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6574 if (Global::IsOnline(mData->mMachineState))
6575 i_saveSettings(NULL);
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6581{
6582 HRESULT rc = S_OK;
6583
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 if (!mHWData->mCPUHotPlugEnabled)
6587 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6588
6589 if (aCpu >= SchemaDefs::MaxCPUCount)
6590 return setError(E_INVALIDARG,
6591 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6592 SchemaDefs::MaxCPUCount);
6593
6594 if (!mHWData->mCPUAttached[aCpu])
6595 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6596
6597 /* CPU 0 can't be detached */
6598 if (aCpu == 0)
6599 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6600
6601 alock.release();
6602 rc = i_onCPUChange(aCpu, true);
6603 alock.acquire();
6604 if (FAILED(rc)) return rc;
6605
6606 i_setModified(IsModified_MachineData);
6607 mHWData.backup();
6608 mHWData->mCPUAttached[aCpu] = false;
6609
6610 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6611 if (Global::IsOnline(mData->mMachineState))
6612 i_saveSettings(NULL);
6613
6614 return S_OK;
6615}
6616
6617HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6618{
6619 *aAttached = false;
6620
6621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6622
6623 /* If hotplug is enabled the CPU is always enabled. */
6624 if (!mHWData->mCPUHotPlugEnabled)
6625 {
6626 if (aCpu < mHWData->mCPUCount)
6627 *aAttached = true;
6628 }
6629 else
6630 {
6631 if (aCpu < SchemaDefs::MaxCPUCount)
6632 *aAttached = mHWData->mCPUAttached[aCpu];
6633 }
6634
6635 return S_OK;
6636}
6637
6638HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6639{
6640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6641
6642 Utf8Str log = i_getLogFilename(aIdx);
6643 if (!RTFileExists(log.c_str()))
6644 log.setNull();
6645 aFilename = log;
6646
6647 return S_OK;
6648}
6649
6650HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6651{
6652 if (aSize < 0)
6653 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6654
6655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 HRESULT rc = S_OK;
6658 Utf8Str log = i_getLogFilename(aIdx);
6659
6660 /* do not unnecessarily hold the lock while doing something which does
6661 * not need the lock and potentially takes a long time. */
6662 alock.release();
6663
6664 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6665 * keeps the SOAP reply size under 1M for the webservice (we're using
6666 * base64 encoded strings for binary data for years now, avoiding the
6667 * expansion of each byte array element to approx. 25 bytes of XML. */
6668 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6669 aData.resize(cbData);
6670
6671 RTFILE LogFile;
6672 int vrc = RTFileOpen(&LogFile, log.c_str(),
6673 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6674 if (RT_SUCCESS(vrc))
6675 {
6676 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6677 if (RT_SUCCESS(vrc))
6678 aData.resize(cbData);
6679 else
6680 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6681 tr("Could not read log file '%s' (%Rrc)"),
6682 log.c_str(), vrc);
6683 RTFileClose(LogFile);
6684 }
6685 else
6686 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6687 tr("Could not open log file '%s' (%Rrc)"),
6688 log.c_str(), vrc);
6689
6690 if (FAILED(rc))
6691 aData.resize(0);
6692
6693 return rc;
6694}
6695
6696
6697/**
6698 * Currently this method doesn't attach device to the running VM,
6699 * just makes sure it's plugged on next VM start.
6700 */
6701HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6702{
6703 // lock scope
6704 {
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706
6707 HRESULT rc = i_checkStateDependency(MutableStateDep);
6708 if (FAILED(rc)) return rc;
6709
6710 ChipsetType_T aChipset = ChipsetType_PIIX3;
6711 COMGETTER(ChipsetType)(&aChipset);
6712
6713 if (aChipset != ChipsetType_ICH9)
6714 {
6715 return setError(E_INVALIDARG,
6716 tr("Host PCI attachment only supported with ICH9 chipset"));
6717 }
6718
6719 // check if device with this host PCI address already attached
6720 for (HWData::PCIDeviceAssignmentList::const_iterator
6721 it = mHWData->mPCIDeviceAssignments.begin();
6722 it != mHWData->mPCIDeviceAssignments.end();
6723 ++it)
6724 {
6725 LONG iHostAddress = -1;
6726 ComPtr<PCIDeviceAttachment> pAttach;
6727 pAttach = *it;
6728 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6729 if (iHostAddress == aHostAddress)
6730 return setError(E_INVALIDARG,
6731 tr("Device with host PCI address already attached to this VM"));
6732 }
6733
6734 ComObjPtr<PCIDeviceAttachment> pda;
6735 char name[32];
6736
6737 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6738 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6739 pda.createObject();
6740 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6741 i_setModified(IsModified_MachineData);
6742 mHWData.backup();
6743 mHWData->mPCIDeviceAssignments.push_back(pda);
6744 }
6745
6746 return S_OK;
6747}
6748
6749/**
6750 * Currently this method doesn't detach device from the running VM,
6751 * just makes sure it's not plugged on next VM start.
6752 */
6753HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6754{
6755 ComObjPtr<PCIDeviceAttachment> pAttach;
6756 bool fRemoved = false;
6757 HRESULT rc;
6758
6759 // lock scope
6760 {
6761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6762
6763 rc = i_checkStateDependency(MutableStateDep);
6764 if (FAILED(rc)) return rc;
6765
6766 for (HWData::PCIDeviceAssignmentList::const_iterator
6767 it = mHWData->mPCIDeviceAssignments.begin();
6768 it != mHWData->mPCIDeviceAssignments.end();
6769 ++it)
6770 {
6771 LONG iHostAddress = -1;
6772 pAttach = *it;
6773 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6774 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6775 {
6776 i_setModified(IsModified_MachineData);
6777 mHWData.backup();
6778 mHWData->mPCIDeviceAssignments.remove(pAttach);
6779 fRemoved = true;
6780 break;
6781 }
6782 }
6783 }
6784
6785
6786 /* Fire event outside of the lock */
6787 if (fRemoved)
6788 {
6789 Assert(!pAttach.isNull());
6790 ComPtr<IEventSource> es;
6791 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6792 Assert(SUCCEEDED(rc));
6793 Bstr mid;
6794 rc = this->COMGETTER(Id)(mid.asOutParam());
6795 Assert(SUCCEEDED(rc));
6796 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6797 }
6798
6799 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6800 tr("No host PCI device %08x attached"),
6801 aHostAddress
6802 );
6803}
6804
6805HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6806{
6807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6808
6809 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6810 size_t i = 0;
6811 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6812 it = mHWData->mPCIDeviceAssignments.begin();
6813 it != mHWData->mPCIDeviceAssignments.end();
6814 ++it, ++i)
6815 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6816
6817 return S_OK;
6818}
6819
6820HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6821{
6822 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6823
6824 return S_OK;
6825}
6826
6827HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6828{
6829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6830
6831 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6832
6833 return S_OK;
6834}
6835
6836HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6837{
6838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6839 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6840 if (SUCCEEDED(hrc))
6841 {
6842 hrc = mHWData.backupEx();
6843 if (SUCCEEDED(hrc))
6844 {
6845 i_setModified(IsModified_MachineData);
6846 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6847 }
6848 }
6849 return hrc;
6850}
6851
6852HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6853{
6854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6855 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6856 return S_OK;
6857}
6858
6859HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6860{
6861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6862 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6863 if (SUCCEEDED(hrc))
6864 {
6865 hrc = mHWData.backupEx();
6866 if (SUCCEEDED(hrc))
6867 {
6868 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6869 if (SUCCEEDED(hrc))
6870 i_setModified(IsModified_MachineData);
6871 }
6872 }
6873 return hrc;
6874}
6875
6876HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6877{
6878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6879
6880 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6881
6882 return S_OK;
6883}
6884
6885HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6886{
6887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6889 if (SUCCEEDED(hrc))
6890 {
6891 hrc = mHWData.backupEx();
6892 if (SUCCEEDED(hrc))
6893 {
6894 i_setModified(IsModified_MachineData);
6895 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6896 }
6897 }
6898 return hrc;
6899}
6900
6901HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6902{
6903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6904
6905 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6906
6907 return S_OK;
6908}
6909
6910HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6911{
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913
6914 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6915 if ( SUCCEEDED(hrc)
6916 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6917 {
6918 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6919 int vrc;
6920
6921 if (aAutostartEnabled)
6922 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6923 else
6924 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6925
6926 if (RT_SUCCESS(vrc))
6927 {
6928 hrc = mHWData.backupEx();
6929 if (SUCCEEDED(hrc))
6930 {
6931 i_setModified(IsModified_MachineData);
6932 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6933 }
6934 }
6935 else if (vrc == VERR_NOT_SUPPORTED)
6936 hrc = setError(VBOX_E_NOT_SUPPORTED,
6937 tr("The VM autostart feature is not supported on this platform"));
6938 else if (vrc == VERR_PATH_NOT_FOUND)
6939 hrc = setError(E_FAIL,
6940 tr("The path to the autostart database is not set"));
6941 else
6942 hrc = setError(E_UNEXPECTED,
6943 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6944 aAutostartEnabled ? "Adding" : "Removing",
6945 mUserData->s.strName.c_str(), vrc);
6946 }
6947 return hrc;
6948}
6949
6950HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6951{
6952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6953
6954 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6955
6956 return S_OK;
6957}
6958
6959HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6960{
6961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6962 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6963 if (SUCCEEDED(hrc))
6964 {
6965 hrc = mHWData.backupEx();
6966 if (SUCCEEDED(hrc))
6967 {
6968 i_setModified(IsModified_MachineData);
6969 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6970 }
6971 }
6972 return hrc;
6973}
6974
6975HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6976{
6977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6980
6981 return S_OK;
6982}
6983
6984HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6985{
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6988 if ( SUCCEEDED(hrc)
6989 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6990 {
6991 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6992 int vrc;
6993
6994 if (aAutostopType != AutostopType_Disabled)
6995 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6996 else
6997 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6998
6999 if (RT_SUCCESS(vrc))
7000 {
7001 hrc = mHWData.backupEx();
7002 if (SUCCEEDED(hrc))
7003 {
7004 i_setModified(IsModified_MachineData);
7005 mHWData->mAutostart.enmAutostopType = aAutostopType;
7006 }
7007 }
7008 else if (vrc == VERR_NOT_SUPPORTED)
7009 hrc = setError(VBOX_E_NOT_SUPPORTED,
7010 tr("The VM autostop feature is not supported on this platform"));
7011 else if (vrc == VERR_PATH_NOT_FOUND)
7012 hrc = setError(E_FAIL,
7013 tr("The path to the autostart database is not set"));
7014 else
7015 hrc = setError(E_UNEXPECTED,
7016 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7017 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7018 mUserData->s.strName.c_str(), vrc);
7019 }
7020 return hrc;
7021}
7022
7023HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7024{
7025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7026
7027 aDefaultFrontend = mHWData->mDefaultFrontend;
7028
7029 return S_OK;
7030}
7031
7032HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7033{
7034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7036 if (SUCCEEDED(hrc))
7037 {
7038 hrc = mHWData.backupEx();
7039 if (SUCCEEDED(hrc))
7040 {
7041 i_setModified(IsModified_MachineData);
7042 mHWData->mDefaultFrontend = aDefaultFrontend;
7043 }
7044 }
7045 return hrc;
7046}
7047
7048HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7049{
7050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7051 size_t cbIcon = mUserData->s.ovIcon.size();
7052 aIcon.resize(cbIcon);
7053 if (cbIcon)
7054 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7055 return S_OK;
7056}
7057
7058HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7059{
7060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7061 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7062 if (SUCCEEDED(hrc))
7063 {
7064 i_setModified(IsModified_MachineData);
7065 mUserData.backup();
7066 size_t cbIcon = aIcon.size();
7067 mUserData->s.ovIcon.resize(cbIcon);
7068 if (cbIcon)
7069 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7070 }
7071 return hrc;
7072}
7073
7074HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7075{
7076#ifdef VBOX_WITH_USB
7077 *aUSBProxyAvailable = true;
7078#else
7079 *aUSBProxyAvailable = false;
7080#endif
7081 return S_OK;
7082}
7083
7084HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7085{
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087
7088 *aVMProcessPriority = mUserData->s.enmVMPriority;
7089
7090 return S_OK;
7091}
7092
7093HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7094{
7095 RT_NOREF(aVMProcessPriority);
7096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7097 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7098 if (SUCCEEDED(hrc))
7099 {
7100 hrc = mUserData.backupEx();
7101 if (SUCCEEDED(hrc))
7102 {
7103 i_setModified(IsModified_MachineData);
7104 mUserData->s.enmVMPriority = aVMProcessPriority;
7105 }
7106 }
7107 alock.release();
7108 if (SUCCEEDED(hrc))
7109 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7110 return hrc;
7111}
7112
7113HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7114 ComPtr<IProgress> &aProgress)
7115{
7116 ComObjPtr<Progress> pP;
7117 Progress *ppP = pP;
7118 IProgress *iP = static_cast<IProgress *>(ppP);
7119 IProgress **pProgress = &iP;
7120
7121 IMachine *pTarget = aTarget;
7122
7123 /* Convert the options. */
7124 RTCList<CloneOptions_T> optList;
7125 if (aOptions.size())
7126 for (size_t i = 0; i < aOptions.size(); ++i)
7127 optList.append(aOptions[i]);
7128
7129 if (optList.contains(CloneOptions_Link))
7130 {
7131 if (!i_isSnapshotMachine())
7132 return setError(E_INVALIDARG,
7133 tr("Linked clone can only be created from a snapshot"));
7134 if (aMode != CloneMode_MachineState)
7135 return setError(E_INVALIDARG,
7136 tr("Linked clone can only be created for a single machine state"));
7137 }
7138 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7139
7140 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7141
7142 HRESULT rc = pWorker->start(pProgress);
7143
7144 pP = static_cast<Progress *>(*pProgress);
7145 pP.queryInterfaceTo(aProgress.asOutParam());
7146
7147 return rc;
7148
7149}
7150
7151HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7152 const com::Utf8Str &aType,
7153 ComPtr<IProgress> &aProgress)
7154{
7155 LogFlowThisFuncEnter();
7156
7157 ComObjPtr<Progress> ptrProgress;
7158 HRESULT hrc = ptrProgress.createObject();
7159 if (SUCCEEDED(hrc))
7160 {
7161 /* Initialize our worker task */
7162 MachineMoveVM *pTask = NULL;
7163 try
7164 {
7165 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7166 }
7167 catch (std::bad_alloc &)
7168 {
7169 return E_OUTOFMEMORY;
7170 }
7171 /** @todo r=bird: Turned out the init() function throws stuff of type int
7172 * without telling anyone. Probably unintentionally. The catching
7173 * here is only temporary until someone can be bothered to fix it
7174 * properly. */
7175 try
7176 {
7177 hrc = pTask->init();
7178 }
7179 catch (...)
7180 {
7181 hrc = setError(E_FAIL, tr("Unknown exception thrown by MachineMoveVM::init"));
7182 }
7183 if (SUCCEEDED(hrc))
7184 {
7185 hrc = pTask->createThread();
7186 pTask = NULL; /* Consumed by createThread(). */
7187 if (SUCCEEDED(hrc))
7188 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7189 else
7190 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7191 }
7192 else
7193 delete pTask;
7194 }
7195
7196 LogFlowThisFuncLeave();
7197 return hrc;
7198
7199}
7200
7201HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7202{
7203 NOREF(aProgress);
7204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 // This check should always fail.
7207 HRESULT rc = i_checkStateDependency(MutableStateDep);
7208 if (FAILED(rc)) return rc;
7209
7210 AssertFailedReturn(E_NOTIMPL);
7211}
7212
7213HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7214{
7215 NOREF(aSavedStateFile);
7216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7217
7218 // This check should always fail.
7219 HRESULT rc = i_checkStateDependency(MutableStateDep);
7220 if (FAILED(rc)) return rc;
7221
7222 AssertFailedReturn(E_NOTIMPL);
7223}
7224
7225HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7226{
7227 NOREF(aFRemoveFile);
7228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7229
7230 // This check should always fail.
7231 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7232 if (FAILED(rc)) return rc;
7233
7234 AssertFailedReturn(E_NOTIMPL);
7235}
7236
7237// public methods for internal purposes
7238/////////////////////////////////////////////////////////////////////////////
7239
7240/**
7241 * Adds the given IsModified_* flag to the dirty flags of the machine.
7242 * This must be called either during i_loadSettings or under the machine write lock.
7243 * @param fl Flag
7244 * @param fAllowStateModification If state modifications are allowed.
7245 */
7246void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7247{
7248 mData->flModifications |= fl;
7249 if (fAllowStateModification && i_isStateModificationAllowed())
7250 mData->mCurrentStateModified = true;
7251}
7252
7253/**
7254 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7255 * care of the write locking.
7256 *
7257 * @param fModification The flag to add.
7258 * @param fAllowStateModification If state modifications are allowed.
7259 */
7260void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7261{
7262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7263 i_setModified(fModification, fAllowStateModification);
7264}
7265
7266/**
7267 * Saves the registry entry of this machine to the given configuration node.
7268 *
7269 * @param data Machine registry data.
7270 *
7271 * @note locks this object for reading.
7272 */
7273HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7274{
7275 AutoLimitedCaller autoCaller(this);
7276 AssertComRCReturnRC(autoCaller.rc());
7277
7278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7279
7280 data.uuid = mData->mUuid;
7281 data.strSettingsFile = mData->m_strConfigFile;
7282
7283 return S_OK;
7284}
7285
7286/**
7287 * Calculates the absolute path of the given path taking the directory of the
7288 * machine settings file as the current directory.
7289 *
7290 * @param strPath Path to calculate the absolute path for.
7291 * @param aResult Where to put the result (used only on success, can be the
7292 * same Utf8Str instance as passed in @a aPath).
7293 * @return IPRT result.
7294 *
7295 * @note Locks this object for reading.
7296 */
7297int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7298{
7299 AutoCaller autoCaller(this);
7300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7301
7302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7303
7304 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7305
7306 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7307
7308 strSettingsDir.stripFilename();
7309 char szFolder[RTPATH_MAX];
7310 size_t cbFolder = sizeof(szFolder);
7311 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7312 if (RT_SUCCESS(vrc))
7313 aResult = szFolder;
7314
7315 return vrc;
7316}
7317
7318/**
7319 * Copies strSource to strTarget, making it relative to the machine folder
7320 * if it is a subdirectory thereof, or simply copying it otherwise.
7321 *
7322 * @param strSource Path to evaluate and copy.
7323 * @param strTarget Buffer to receive target path.
7324 *
7325 * @note Locks this object for reading.
7326 */
7327void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7328 Utf8Str &strTarget)
7329{
7330 AutoCaller autoCaller(this);
7331 AssertComRCReturn(autoCaller.rc(), (void)0);
7332
7333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7334
7335 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7336 // use strTarget as a temporary buffer to hold the machine settings dir
7337 strTarget = mData->m_strConfigFileFull;
7338 strTarget.stripFilename();
7339 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7340 {
7341 // is relative: then append what's left
7342 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7343 // for empty paths (only possible for subdirs) use "." to avoid
7344 // triggering default settings for not present config attributes.
7345 if (strTarget.isEmpty())
7346 strTarget = ".";
7347 }
7348 else
7349 // is not relative: then overwrite
7350 strTarget = strSource;
7351}
7352
7353/**
7354 * Returns the full path to the machine's log folder in the
7355 * \a aLogFolder argument.
7356 */
7357void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7358{
7359 AutoCaller autoCaller(this);
7360 AssertComRCReturnVoid(autoCaller.rc());
7361
7362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7363
7364 char szTmp[RTPATH_MAX];
7365 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7366 if (RT_SUCCESS(vrc))
7367 {
7368 if (szTmp[0] && !mUserData.isNull())
7369 {
7370 char szTmp2[RTPATH_MAX];
7371 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7372 if (RT_SUCCESS(vrc))
7373 aLogFolder = Utf8StrFmt("%s%c%s",
7374 szTmp2,
7375 RTPATH_DELIMITER,
7376 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7377 }
7378 else
7379 vrc = VERR_PATH_IS_RELATIVE;
7380 }
7381
7382 if (RT_FAILURE(vrc))
7383 {
7384 // fallback if VBOX_USER_LOGHOME is not set or invalid
7385 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7386 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7387 aLogFolder.append(RTPATH_DELIMITER);
7388 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7389 }
7390}
7391
7392/**
7393 * Returns the full path to the machine's log file for an given index.
7394 */
7395Utf8Str Machine::i_getLogFilename(ULONG idx)
7396{
7397 Utf8Str logFolder;
7398 getLogFolder(logFolder);
7399 Assert(logFolder.length());
7400
7401 Utf8Str log;
7402 if (idx == 0)
7403 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7404#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7405 else if (idx == 1)
7406 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7407 else
7408 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7409#else
7410 else
7411 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7412#endif
7413 return log;
7414}
7415
7416/**
7417 * Returns the full path to the machine's hardened log file.
7418 */
7419Utf8Str Machine::i_getHardeningLogFilename(void)
7420{
7421 Utf8Str strFilename;
7422 getLogFolder(strFilename);
7423 Assert(strFilename.length());
7424 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7425 return strFilename;
7426}
7427
7428
7429/**
7430 * Composes a unique saved state filename based on the current system time. The filename is
7431 * granular to the second so this will work so long as no more than one snapshot is taken on
7432 * a machine per second.
7433 *
7434 * Before version 4.1, we used this formula for saved state files:
7435 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7436 * which no longer works because saved state files can now be shared between the saved state of the
7437 * "saved" machine and an online snapshot, and the following would cause problems:
7438 * 1) save machine
7439 * 2) create online snapshot from that machine state --> reusing saved state file
7440 * 3) save machine again --> filename would be reused, breaking the online snapshot
7441 *
7442 * So instead we now use a timestamp.
7443 *
7444 * @param strStateFilePath
7445 */
7446
7447void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7448{
7449 AutoCaller autoCaller(this);
7450 AssertComRCReturnVoid(autoCaller.rc());
7451
7452 {
7453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7454 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7455 }
7456
7457 RTTIMESPEC ts;
7458 RTTimeNow(&ts);
7459 RTTIME time;
7460 RTTimeExplode(&time, &ts);
7461
7462 strStateFilePath += RTPATH_DELIMITER;
7463 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7464 time.i32Year, time.u8Month, time.u8MonthDay,
7465 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7466}
7467
7468/**
7469 * Returns whether at least one USB controller is present for the VM.
7470 */
7471bool Machine::i_isUSBControllerPresent()
7472{
7473 AutoCaller autoCaller(this);
7474 AssertComRCReturn(autoCaller.rc(), false);
7475
7476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7477
7478 return (mUSBControllers->size() > 0);
7479}
7480
7481/**
7482 * @note Locks this object for writing, calls the client process
7483 * (inside the lock).
7484 */
7485HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7486 const Utf8Str &strFrontend,
7487 const Utf8Str &strEnvironment,
7488 ProgressProxy *aProgress)
7489{
7490 LogFlowThisFuncEnter();
7491
7492 AssertReturn(aControl, E_FAIL);
7493 AssertReturn(aProgress, E_FAIL);
7494 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7495
7496 AutoCaller autoCaller(this);
7497 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7498
7499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7500
7501 if (!mData->mRegistered)
7502 return setError(E_UNEXPECTED,
7503 tr("The machine '%s' is not registered"),
7504 mUserData->s.strName.c_str());
7505
7506 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7507
7508 /* The process started when launching a VM with separate UI/VM processes is always
7509 * the UI process, i.e. needs special handling as it won't claim the session. */
7510 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7511
7512 if (fSeparate)
7513 {
7514 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7515 return setError(VBOX_E_INVALID_OBJECT_STATE,
7516 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7517 mUserData->s.strName.c_str());
7518 }
7519 else
7520 {
7521 if ( mData->mSession.mState == SessionState_Locked
7522 || mData->mSession.mState == SessionState_Spawning
7523 || mData->mSession.mState == SessionState_Unlocking)
7524 return setError(VBOX_E_INVALID_OBJECT_STATE,
7525 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7526 mUserData->s.strName.c_str());
7527
7528 /* may not be busy */
7529 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7530 }
7531
7532 /* get the path to the executable */
7533 char szPath[RTPATH_MAX];
7534 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7535 size_t cchBufLeft = strlen(szPath);
7536 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7537 szPath[cchBufLeft] = 0;
7538 char *pszNamePart = szPath + cchBufLeft;
7539 cchBufLeft = sizeof(szPath) - cchBufLeft;
7540
7541 int vrc = VINF_SUCCESS;
7542 RTPROCESS pid = NIL_RTPROCESS;
7543
7544 RTENV env = RTENV_DEFAULT;
7545
7546 if (!strEnvironment.isEmpty())
7547 {
7548 char *newEnvStr = NULL;
7549
7550 do
7551 {
7552 /* clone the current environment */
7553 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7554 AssertRCBreakStmt(vrc2, vrc = vrc2);
7555
7556 newEnvStr = RTStrDup(strEnvironment.c_str());
7557 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7558
7559 /* put new variables to the environment
7560 * (ignore empty variable names here since RTEnv API
7561 * intentionally doesn't do that) */
7562 char *var = newEnvStr;
7563 for (char *p = newEnvStr; *p; ++p)
7564 {
7565 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7566 {
7567 *p = '\0';
7568 if (*var)
7569 {
7570 char *val = strchr(var, '=');
7571 if (val)
7572 {
7573 *val++ = '\0';
7574 vrc2 = RTEnvSetEx(env, var, val);
7575 }
7576 else
7577 vrc2 = RTEnvUnsetEx(env, var);
7578 if (RT_FAILURE(vrc2))
7579 break;
7580 }
7581 var = p + 1;
7582 }
7583 }
7584 if (RT_SUCCESS(vrc2) && *var)
7585 vrc2 = RTEnvPutEx(env, var);
7586
7587 AssertRCBreakStmt(vrc2, vrc = vrc2);
7588 }
7589 while (0);
7590
7591 if (newEnvStr != NULL)
7592 RTStrFree(newEnvStr);
7593 }
7594
7595 /* Hardening logging */
7596#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7597 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7598 {
7599 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7600 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7601 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7602 {
7603 Utf8Str strStartupLogDir = strHardeningLogFile;
7604 strStartupLogDir.stripFilename();
7605 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7606 file without stripping the file. */
7607 }
7608 strSupHardeningLogArg.append(strHardeningLogFile);
7609
7610 /* Remove legacy log filename to avoid confusion. */
7611 Utf8Str strOldStartupLogFile;
7612 getLogFolder(strOldStartupLogFile);
7613 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7614 RTFileDelete(strOldStartupLogFile.c_str());
7615 }
7616 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7617#else
7618 const char *pszSupHardeningLogArg = NULL;
7619#endif
7620
7621 Utf8Str strCanonicalName;
7622
7623#ifdef VBOX_WITH_QTGUI
7624 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7625 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7626 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7627 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7628 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7629 {
7630 strCanonicalName = "GUI/Qt";
7631# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7632 /* Modify the base path so that we don't need to use ".." below. */
7633 RTPathStripTrailingSlash(szPath);
7634 RTPathStripFilename(szPath);
7635 cchBufLeft = strlen(szPath);
7636 pszNamePart = szPath + cchBufLeft;
7637 cchBufLeft = sizeof(szPath) - cchBufLeft;
7638
7639# define OSX_APP_NAME "VirtualBoxVM"
7640# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7641
7642 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7643 if ( strAppOverride.contains(".")
7644 || strAppOverride.contains("/")
7645 || strAppOverride.contains("\\")
7646 || strAppOverride.contains(":"))
7647 strAppOverride.setNull();
7648 Utf8Str strAppPath;
7649 if (!strAppOverride.isEmpty())
7650 {
7651 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7652 Utf8Str strFullPath(szPath);
7653 strFullPath.append(strAppPath);
7654 /* there is a race, but people using this deserve the failure */
7655 if (!RTFileExists(strFullPath.c_str()))
7656 strAppOverride.setNull();
7657 }
7658 if (strAppOverride.isEmpty())
7659 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7660 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7661 strcpy(pszNamePart, strAppPath.c_str());
7662# else
7663 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7664 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7665 strcpy(pszNamePart, s_szVirtualBox_exe);
7666# endif
7667
7668 Utf8Str idStr = mData->mUuid.toString();
7669 const char *apszArgs[] =
7670 {
7671 szPath,
7672 "--comment", mUserData->s.strName.c_str(),
7673 "--startvm", idStr.c_str(),
7674 "--no-startvm-errormsgbox",
7675 NULL, /* For "--separate". */
7676 NULL, /* For "--sup-startup-log". */
7677 NULL
7678 };
7679 unsigned iArg = 6;
7680 if (fSeparate)
7681 apszArgs[iArg++] = "--separate";
7682 apszArgs[iArg++] = pszSupHardeningLogArg;
7683
7684 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7685 }
7686#else /* !VBOX_WITH_QTGUI */
7687 if (0)
7688 ;
7689#endif /* VBOX_WITH_QTGUI */
7690
7691 else
7692
7693#ifdef VBOX_WITH_VBOXSDL
7694 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7695 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7696 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7697 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7698 {
7699 strCanonicalName = "GUI/SDL";
7700 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7701 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7702 strcpy(pszNamePart, s_szVBoxSDL_exe);
7703
7704 Utf8Str idStr = mData->mUuid.toString();
7705 const char *apszArgs[] =
7706 {
7707 szPath,
7708 "--comment", mUserData->s.strName.c_str(),
7709 "--startvm", idStr.c_str(),
7710 NULL, /* For "--separate". */
7711 NULL, /* For "--sup-startup-log". */
7712 NULL
7713 };
7714 unsigned iArg = 5;
7715 if (fSeparate)
7716 apszArgs[iArg++] = "--separate";
7717 apszArgs[iArg++] = pszSupHardeningLogArg;
7718
7719 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7720 }
7721#else /* !VBOX_WITH_VBOXSDL */
7722 if (0)
7723 ;
7724#endif /* !VBOX_WITH_VBOXSDL */
7725
7726 else
7727
7728#ifdef VBOX_WITH_HEADLESS
7729 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7730 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7731 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7732 )
7733 {
7734 strCanonicalName = "headless";
7735 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7736 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7737 * and a VM works even if the server has not been installed.
7738 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7739 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7740 * differently in 4.0 and 3.x.
7741 */
7742 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7743 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7744 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7745
7746 Utf8Str idStr = mData->mUuid.toString();
7747 const char *apszArgs[] =
7748 {
7749 szPath,
7750 "--comment", mUserData->s.strName.c_str(),
7751 "--startvm", idStr.c_str(),
7752 "--vrde", "config",
7753 NULL, /* For "--capture". */
7754 NULL, /* For "--sup-startup-log". */
7755 NULL
7756 };
7757 unsigned iArg = 7;
7758 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7759 apszArgs[iArg++] = "--capture";
7760 apszArgs[iArg++] = pszSupHardeningLogArg;
7761
7762# ifdef RT_OS_WINDOWS
7763 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7764# else
7765 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7766# endif
7767 }
7768#else /* !VBOX_WITH_HEADLESS */
7769 if (0)
7770 ;
7771#endif /* !VBOX_WITH_HEADLESS */
7772 else
7773 {
7774 RTEnvDestroy(env);
7775 return setError(E_INVALIDARG,
7776 tr("Invalid frontend name: '%s'"),
7777 strFrontend.c_str());
7778 }
7779
7780 RTEnvDestroy(env);
7781
7782 if (RT_FAILURE(vrc))
7783 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7784 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7785 mUserData->s.strName.c_str(), vrc);
7786
7787 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7788
7789 if (!fSeparate)
7790 {
7791 /*
7792 * Note that we don't release the lock here before calling the client,
7793 * because it doesn't need to call us back if called with a NULL argument.
7794 * Releasing the lock here is dangerous because we didn't prepare the
7795 * launch data yet, but the client we've just started may happen to be
7796 * too fast and call LockMachine() that will fail (because of PID, etc.),
7797 * so that the Machine will never get out of the Spawning session state.
7798 */
7799
7800 /* inform the session that it will be a remote one */
7801 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7802#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7803 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7804#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7805 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7806#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7807 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7808
7809 if (FAILED(rc))
7810 {
7811 /* restore the session state */
7812 mData->mSession.mState = SessionState_Unlocked;
7813 alock.release();
7814 mParent->i_addProcessToReap(pid);
7815 /* The failure may occur w/o any error info (from RPC), so provide one */
7816 return setError(VBOX_E_VM_ERROR,
7817 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7818 }
7819
7820 /* attach launch data to the machine */
7821 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7822 mData->mSession.mRemoteControls.push_back(aControl);
7823 mData->mSession.mProgress = aProgress;
7824 mData->mSession.mPID = pid;
7825 mData->mSession.mState = SessionState_Spawning;
7826 Assert(strCanonicalName.isNotEmpty());
7827 mData->mSession.mName = strCanonicalName;
7828 }
7829 else
7830 {
7831 /* For separate UI process we declare the launch as completed instantly, as the
7832 * actual headless VM start may or may not come. No point in remembering anything
7833 * yet, as what matters for us is when the headless VM gets started. */
7834 aProgress->i_notifyComplete(S_OK);
7835 }
7836
7837 alock.release();
7838 mParent->i_addProcessToReap(pid);
7839
7840 LogFlowThisFuncLeave();
7841 return S_OK;
7842}
7843
7844/**
7845 * Returns @c true if the given session machine instance has an open direct
7846 * session (and optionally also for direct sessions which are closing) and
7847 * returns the session control machine instance if so.
7848 *
7849 * Note that when the method returns @c false, the arguments remain unchanged.
7850 *
7851 * @param aMachine Session machine object.
7852 * @param aControl Direct session control object (optional).
7853 * @param aRequireVM If true then only allow VM sessions.
7854 * @param aAllowClosing If true then additionally a session which is currently
7855 * being closed will also be allowed.
7856 *
7857 * @note locks this object for reading.
7858 */
7859bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7860 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7861 bool aRequireVM /*= false*/,
7862 bool aAllowClosing /*= false*/)
7863{
7864 AutoLimitedCaller autoCaller(this);
7865 AssertComRCReturn(autoCaller.rc(), false);
7866
7867 /* just return false for inaccessible machines */
7868 if (getObjectState().getState() != ObjectState::Ready)
7869 return false;
7870
7871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7872
7873 if ( ( mData->mSession.mState == SessionState_Locked
7874 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7875 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7876 )
7877 {
7878 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7879
7880 aMachine = mData->mSession.mMachine;
7881
7882 if (aControl != NULL)
7883 *aControl = mData->mSession.mDirectControl;
7884
7885 return true;
7886 }
7887
7888 return false;
7889}
7890
7891/**
7892 * Returns @c true if the given machine has an spawning direct session.
7893 *
7894 * @note locks this object for reading.
7895 */
7896bool Machine::i_isSessionSpawning()
7897{
7898 AutoLimitedCaller autoCaller(this);
7899 AssertComRCReturn(autoCaller.rc(), false);
7900
7901 /* just return false for inaccessible machines */
7902 if (getObjectState().getState() != ObjectState::Ready)
7903 return false;
7904
7905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7906
7907 if (mData->mSession.mState == SessionState_Spawning)
7908 return true;
7909
7910 return false;
7911}
7912
7913/**
7914 * Called from the client watcher thread to check for unexpected client process
7915 * death during Session_Spawning state (e.g. before it successfully opened a
7916 * direct session).
7917 *
7918 * On Win32 and on OS/2, this method is called only when we've got the
7919 * direct client's process termination notification, so it always returns @c
7920 * true.
7921 *
7922 * On other platforms, this method returns @c true if the client process is
7923 * terminated and @c false if it's still alive.
7924 *
7925 * @note Locks this object for writing.
7926 */
7927bool Machine::i_checkForSpawnFailure()
7928{
7929 AutoCaller autoCaller(this);
7930 if (!autoCaller.isOk())
7931 {
7932 /* nothing to do */
7933 LogFlowThisFunc(("Already uninitialized!\n"));
7934 return true;
7935 }
7936
7937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7938
7939 if (mData->mSession.mState != SessionState_Spawning)
7940 {
7941 /* nothing to do */
7942 LogFlowThisFunc(("Not spawning any more!\n"));
7943 return true;
7944 }
7945
7946 HRESULT rc = S_OK;
7947
7948 /* PID not yet initialized, skip check. */
7949 if (mData->mSession.mPID == NIL_RTPROCESS)
7950 return false;
7951
7952 RTPROCSTATUS status;
7953 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7954
7955 if (vrc != VERR_PROCESS_RUNNING)
7956 {
7957 Utf8Str strExtraInfo;
7958
7959#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7960 /* If the startup logfile exists and is of non-zero length, tell the
7961 user to look there for more details to encourage them to attach it
7962 when reporting startup issues. */
7963 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7964 uint64_t cbStartupLogFile = 0;
7965 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7966 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7967 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7968#endif
7969
7970 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7971 rc = setError(E_FAIL,
7972 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7973 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7974 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7975 rc = setError(E_FAIL,
7976 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7977 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7978 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7979 rc = setError(E_FAIL,
7980 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7981 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7982 else
7983 rc = setErrorBoth(E_FAIL, vrc,
7984 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7985 i_getName().c_str(), vrc, strExtraInfo.c_str());
7986 }
7987
7988 if (FAILED(rc))
7989 {
7990 /* Close the remote session, remove the remote control from the list
7991 * and reset session state to Closed (@note keep the code in sync with
7992 * the relevant part in LockMachine()). */
7993
7994 Assert(mData->mSession.mRemoteControls.size() == 1);
7995 if (mData->mSession.mRemoteControls.size() == 1)
7996 {
7997 ErrorInfoKeeper eik;
7998 mData->mSession.mRemoteControls.front()->Uninitialize();
7999 }
8000
8001 mData->mSession.mRemoteControls.clear();
8002 mData->mSession.mState = SessionState_Unlocked;
8003
8004 /* finalize the progress after setting the state */
8005 if (!mData->mSession.mProgress.isNull())
8006 {
8007 mData->mSession.mProgress->notifyComplete(rc);
8008 mData->mSession.mProgress.setNull();
8009 }
8010
8011 mData->mSession.mPID = NIL_RTPROCESS;
8012
8013 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8014 return true;
8015 }
8016
8017 return false;
8018}
8019
8020/**
8021 * Checks whether the machine can be registered. If so, commits and saves
8022 * all settings.
8023 *
8024 * @note Must be called from mParent's write lock. Locks this object and
8025 * children for writing.
8026 */
8027HRESULT Machine::i_prepareRegister()
8028{
8029 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8030
8031 AutoLimitedCaller autoCaller(this);
8032 AssertComRCReturnRC(autoCaller.rc());
8033
8034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8035
8036 /* wait for state dependents to drop to zero */
8037 i_ensureNoStateDependencies();
8038
8039 if (!mData->mAccessible)
8040 return setError(VBOX_E_INVALID_OBJECT_STATE,
8041 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8042 mUserData->s.strName.c_str(),
8043 mData->mUuid.toString().c_str());
8044
8045 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8046
8047 if (mData->mRegistered)
8048 return setError(VBOX_E_INVALID_OBJECT_STATE,
8049 tr("The machine '%s' with UUID {%s} is already registered"),
8050 mUserData->s.strName.c_str(),
8051 mData->mUuid.toString().c_str());
8052
8053 HRESULT rc = S_OK;
8054
8055 // Ensure the settings are saved. If we are going to be registered and
8056 // no config file exists yet, create it by calling i_saveSettings() too.
8057 if ( (mData->flModifications)
8058 || (!mData->pMachineConfigFile->fileExists())
8059 )
8060 {
8061 rc = i_saveSettings(NULL);
8062 // no need to check whether VirtualBox.xml needs saving too since
8063 // we can't have a machine XML file rename pending
8064 if (FAILED(rc)) return rc;
8065 }
8066
8067 /* more config checking goes here */
8068
8069 if (SUCCEEDED(rc))
8070 {
8071 /* we may have had implicit modifications we want to fix on success */
8072 i_commit();
8073
8074 mData->mRegistered = true;
8075 }
8076 else
8077 {
8078 /* we may have had implicit modifications we want to cancel on failure*/
8079 i_rollback(false /* aNotify */);
8080 }
8081
8082 return rc;
8083}
8084
8085/**
8086 * Increases the number of objects dependent on the machine state or on the
8087 * registered state. Guarantees that these two states will not change at least
8088 * until #i_releaseStateDependency() is called.
8089 *
8090 * Depending on the @a aDepType value, additional state checks may be made.
8091 * These checks will set extended error info on failure. See
8092 * #i_checkStateDependency() for more info.
8093 *
8094 * If this method returns a failure, the dependency is not added and the caller
8095 * is not allowed to rely on any particular machine state or registration state
8096 * value and may return the failed result code to the upper level.
8097 *
8098 * @param aDepType Dependency type to add.
8099 * @param aState Current machine state (NULL if not interested).
8100 * @param aRegistered Current registered state (NULL if not interested).
8101 *
8102 * @note Locks this object for writing.
8103 */
8104HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8105 MachineState_T *aState /* = NULL */,
8106 BOOL *aRegistered /* = NULL */)
8107{
8108 AutoCaller autoCaller(this);
8109 AssertComRCReturnRC(autoCaller.rc());
8110
8111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8112
8113 HRESULT rc = i_checkStateDependency(aDepType);
8114 if (FAILED(rc)) return rc;
8115
8116 {
8117 if (mData->mMachineStateChangePending != 0)
8118 {
8119 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8120 * drop to zero so don't add more. It may make sense to wait a bit
8121 * and retry before reporting an error (since the pending state
8122 * transition should be really quick) but let's just assert for
8123 * now to see if it ever happens on practice. */
8124
8125 AssertFailed();
8126
8127 return setError(E_ACCESSDENIED,
8128 tr("Machine state change is in progress. Please retry the operation later."));
8129 }
8130
8131 ++mData->mMachineStateDeps;
8132 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8133 }
8134
8135 if (aState)
8136 *aState = mData->mMachineState;
8137 if (aRegistered)
8138 *aRegistered = mData->mRegistered;
8139
8140 return S_OK;
8141}
8142
8143/**
8144 * Decreases the number of objects dependent on the machine state.
8145 * Must always complete the #i_addStateDependency() call after the state
8146 * dependency is no more necessary.
8147 */
8148void Machine::i_releaseStateDependency()
8149{
8150 AutoCaller autoCaller(this);
8151 AssertComRCReturnVoid(autoCaller.rc());
8152
8153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8154
8155 /* releaseStateDependency() w/o addStateDependency()? */
8156 AssertReturnVoid(mData->mMachineStateDeps != 0);
8157 -- mData->mMachineStateDeps;
8158
8159 if (mData->mMachineStateDeps == 0)
8160 {
8161 /* inform i_ensureNoStateDependencies() that there are no more deps */
8162 if (mData->mMachineStateChangePending != 0)
8163 {
8164 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8165 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8166 }
8167 }
8168}
8169
8170Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8171{
8172 /* start with nothing found */
8173 Utf8Str strResult("");
8174
8175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8176
8177 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8178 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8179 // found:
8180 strResult = it->second; // source is a Utf8Str
8181
8182 return strResult;
8183}
8184
8185// protected methods
8186/////////////////////////////////////////////////////////////////////////////
8187
8188/**
8189 * Performs machine state checks based on the @a aDepType value. If a check
8190 * fails, this method will set extended error info, otherwise it will return
8191 * S_OK. It is supposed, that on failure, the caller will immediately return
8192 * the return value of this method to the upper level.
8193 *
8194 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8195 *
8196 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8197 * current state of this machine object allows to change settings of the
8198 * machine (i.e. the machine is not registered, or registered but not running
8199 * and not saved). It is useful to call this method from Machine setters
8200 * before performing any change.
8201 *
8202 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8203 * as for MutableStateDep except that if the machine is saved, S_OK is also
8204 * returned. This is useful in setters which allow changing machine
8205 * properties when it is in the saved state.
8206 *
8207 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8208 * if the current state of this machine object allows to change runtime
8209 * changeable settings of the machine (i.e. the machine is not registered, or
8210 * registered but either running or not running and not saved). It is useful
8211 * to call this method from Machine setters before performing any changes to
8212 * runtime changeable settings.
8213 *
8214 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8215 * the same as for MutableOrRunningStateDep except that if the machine is
8216 * saved, S_OK is also returned. This is useful in setters which allow
8217 * changing runtime and saved state changeable machine properties.
8218 *
8219 * @param aDepType Dependency type to check.
8220 *
8221 * @note Non Machine based classes should use #i_addStateDependency() and
8222 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8223 * template.
8224 *
8225 * @note This method must be called from under this object's read or write
8226 * lock.
8227 */
8228HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8229{
8230 switch (aDepType)
8231 {
8232 case AnyStateDep:
8233 {
8234 break;
8235 }
8236 case MutableStateDep:
8237 {
8238 if ( mData->mRegistered
8239 && ( !i_isSessionMachine()
8240 || ( mData->mMachineState != MachineState_Aborted
8241 && mData->mMachineState != MachineState_Teleported
8242 && mData->mMachineState != MachineState_PoweredOff
8243 )
8244 )
8245 )
8246 return setError(VBOX_E_INVALID_VM_STATE,
8247 tr("The machine is not mutable (state is %s)"),
8248 Global::stringifyMachineState(mData->mMachineState));
8249 break;
8250 }
8251 case MutableOrSavedStateDep:
8252 {
8253 if ( mData->mRegistered
8254 && ( !i_isSessionMachine()
8255 || ( mData->mMachineState != MachineState_Aborted
8256 && mData->mMachineState != MachineState_Teleported
8257 && mData->mMachineState != MachineState_Saved
8258 && mData->mMachineState != MachineState_PoweredOff
8259 )
8260 )
8261 )
8262 return setError(VBOX_E_INVALID_VM_STATE,
8263 tr("The machine is not mutable or saved (state is %s)"),
8264 Global::stringifyMachineState(mData->mMachineState));
8265 break;
8266 }
8267 case MutableOrRunningStateDep:
8268 {
8269 if ( mData->mRegistered
8270 && ( !i_isSessionMachine()
8271 || ( mData->mMachineState != MachineState_Aborted
8272 && mData->mMachineState != MachineState_Teleported
8273 && mData->mMachineState != MachineState_PoweredOff
8274 && !Global::IsOnline(mData->mMachineState)
8275 )
8276 )
8277 )
8278 return setError(VBOX_E_INVALID_VM_STATE,
8279 tr("The machine is not mutable or running (state is %s)"),
8280 Global::stringifyMachineState(mData->mMachineState));
8281 break;
8282 }
8283 case MutableOrSavedOrRunningStateDep:
8284 {
8285 if ( mData->mRegistered
8286 && ( !i_isSessionMachine()
8287 || ( mData->mMachineState != MachineState_Aborted
8288 && mData->mMachineState != MachineState_Teleported
8289 && mData->mMachineState != MachineState_Saved
8290 && mData->mMachineState != MachineState_PoweredOff
8291 && !Global::IsOnline(mData->mMachineState)
8292 )
8293 )
8294 )
8295 return setError(VBOX_E_INVALID_VM_STATE,
8296 tr("The machine is not mutable, saved or running (state is %s)"),
8297 Global::stringifyMachineState(mData->mMachineState));
8298 break;
8299 }
8300 }
8301
8302 return S_OK;
8303}
8304
8305/**
8306 * Helper to initialize all associated child objects and allocate data
8307 * structures.
8308 *
8309 * This method must be called as a part of the object's initialization procedure
8310 * (usually done in the #init() method).
8311 *
8312 * @note Must be called only from #init() or from #i_registeredInit().
8313 */
8314HRESULT Machine::initDataAndChildObjects()
8315{
8316 AutoCaller autoCaller(this);
8317 AssertComRCReturnRC(autoCaller.rc());
8318 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8319 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8320
8321 AssertReturn(!mData->mAccessible, E_FAIL);
8322
8323 /* allocate data structures */
8324 mSSData.allocate();
8325 mUserData.allocate();
8326 mHWData.allocate();
8327 mMediumAttachments.allocate();
8328 mStorageControllers.allocate();
8329 mUSBControllers.allocate();
8330
8331 /* initialize mOSTypeId */
8332 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8333
8334/** @todo r=bird: init() methods never fails, right? Why don't we make them
8335 * return void then! */
8336
8337 /* create associated BIOS settings object */
8338 unconst(mBIOSSettings).createObject();
8339 mBIOSSettings->init(this);
8340
8341 /* create associated record settings object */
8342 unconst(mRecordingSettings).createObject();
8343 mRecordingSettings->init(this);
8344
8345 /* create an associated VRDE object (default is disabled) */
8346 unconst(mVRDEServer).createObject();
8347 mVRDEServer->init(this);
8348
8349 /* create associated serial port objects */
8350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8351 {
8352 unconst(mSerialPorts[slot]).createObject();
8353 mSerialPorts[slot]->init(this, slot);
8354 }
8355
8356 /* create associated parallel port objects */
8357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8358 {
8359 unconst(mParallelPorts[slot]).createObject();
8360 mParallelPorts[slot]->init(this, slot);
8361 }
8362
8363 /* create the audio adapter object (always present, default is disabled) */
8364 unconst(mAudioAdapter).createObject();
8365 mAudioAdapter->init(this);
8366
8367 /* create the USB device filters object (always present) */
8368 unconst(mUSBDeviceFilters).createObject();
8369 mUSBDeviceFilters->init(this);
8370
8371 /* create associated network adapter objects */
8372 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8374 {
8375 unconst(mNetworkAdapters[slot]).createObject();
8376 mNetworkAdapters[slot]->init(this, slot);
8377 }
8378
8379 /* create the bandwidth control */
8380 unconst(mBandwidthControl).createObject();
8381 mBandwidthControl->init(this);
8382
8383 return S_OK;
8384}
8385
8386/**
8387 * Helper to uninitialize all associated child objects and to free all data
8388 * structures.
8389 *
8390 * This method must be called as a part of the object's uninitialization
8391 * procedure (usually done in the #uninit() method).
8392 *
8393 * @note Must be called only from #uninit() or from #i_registeredInit().
8394 */
8395void Machine::uninitDataAndChildObjects()
8396{
8397 AutoCaller autoCaller(this);
8398 AssertComRCReturnVoid(autoCaller.rc());
8399 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8400 || getObjectState().getState() == ObjectState::Limited);
8401
8402 /* tell all our other child objects we've been uninitialized */
8403 if (mBandwidthControl)
8404 {
8405 mBandwidthControl->uninit();
8406 unconst(mBandwidthControl).setNull();
8407 }
8408
8409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8410 {
8411 if (mNetworkAdapters[slot])
8412 {
8413 mNetworkAdapters[slot]->uninit();
8414 unconst(mNetworkAdapters[slot]).setNull();
8415 }
8416 }
8417
8418 if (mUSBDeviceFilters)
8419 {
8420 mUSBDeviceFilters->uninit();
8421 unconst(mUSBDeviceFilters).setNull();
8422 }
8423
8424 if (mAudioAdapter)
8425 {
8426 mAudioAdapter->uninit();
8427 unconst(mAudioAdapter).setNull();
8428 }
8429
8430 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8431 {
8432 if (mParallelPorts[slot])
8433 {
8434 mParallelPorts[slot]->uninit();
8435 unconst(mParallelPorts[slot]).setNull();
8436 }
8437 }
8438
8439 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8440 {
8441 if (mSerialPorts[slot])
8442 {
8443 mSerialPorts[slot]->uninit();
8444 unconst(mSerialPorts[slot]).setNull();
8445 }
8446 }
8447
8448 if (mVRDEServer)
8449 {
8450 mVRDEServer->uninit();
8451 unconst(mVRDEServer).setNull();
8452 }
8453
8454 if (mBIOSSettings)
8455 {
8456 mBIOSSettings->uninit();
8457 unconst(mBIOSSettings).setNull();
8458 }
8459
8460 if (mRecordingSettings)
8461 {
8462 mRecordingSettings->uninit();
8463 unconst(mRecordingSettings).setNull();
8464 }
8465
8466 /* Deassociate media (only when a real Machine or a SnapshotMachine
8467 * instance is uninitialized; SessionMachine instances refer to real
8468 * Machine media). This is necessary for a clean re-initialization of
8469 * the VM after successfully re-checking the accessibility state. Note
8470 * that in case of normal Machine or SnapshotMachine uninitialization (as
8471 * a result of unregistering or deleting the snapshot), outdated media
8472 * attachments will already be uninitialized and deleted, so this
8473 * code will not affect them. */
8474 if ( !mMediumAttachments.isNull()
8475 && !i_isSessionMachine()
8476 )
8477 {
8478 for (MediumAttachmentList::const_iterator
8479 it = mMediumAttachments->begin();
8480 it != mMediumAttachments->end();
8481 ++it)
8482 {
8483 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8484 if (pMedium.isNull())
8485 continue;
8486 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8487 AssertComRC(rc);
8488 }
8489 }
8490
8491 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8492 {
8493 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8494 if (mData->mFirstSnapshot)
8495 {
8496 // snapshots tree is protected by machine write lock; strictly
8497 // this isn't necessary here since we're deleting the entire
8498 // machine, but otherwise we assert in Snapshot::uninit()
8499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8500 mData->mFirstSnapshot->uninit();
8501 mData->mFirstSnapshot.setNull();
8502 }
8503
8504 mData->mCurrentSnapshot.setNull();
8505 }
8506
8507 /* free data structures (the essential mData structure is not freed here
8508 * since it may be still in use) */
8509 mMediumAttachments.free();
8510 mStorageControllers.free();
8511 mUSBControllers.free();
8512 mHWData.free();
8513 mUserData.free();
8514 mSSData.free();
8515}
8516
8517/**
8518 * Returns a pointer to the Machine object for this machine that acts like a
8519 * parent for complex machine data objects such as shared folders, etc.
8520 *
8521 * For primary Machine objects and for SnapshotMachine objects, returns this
8522 * object's pointer itself. For SessionMachine objects, returns the peer
8523 * (primary) machine pointer.
8524 */
8525Machine *Machine::i_getMachine()
8526{
8527 if (i_isSessionMachine())
8528 return (Machine*)mPeer;
8529 return this;
8530}
8531
8532/**
8533 * Makes sure that there are no machine state dependents. If necessary, waits
8534 * for the number of dependents to drop to zero.
8535 *
8536 * Make sure this method is called from under this object's write lock to
8537 * guarantee that no new dependents may be added when this method returns
8538 * control to the caller.
8539 *
8540 * @note Locks this object for writing. The lock will be released while waiting
8541 * (if necessary).
8542 *
8543 * @warning To be used only in methods that change the machine state!
8544 */
8545void Machine::i_ensureNoStateDependencies()
8546{
8547 AssertReturnVoid(isWriteLockOnCurrentThread());
8548
8549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8550
8551 /* Wait for all state dependents if necessary */
8552 if (mData->mMachineStateDeps != 0)
8553 {
8554 /* lazy semaphore creation */
8555 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8556 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8557
8558 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8559 mData->mMachineStateDeps));
8560
8561 ++mData->mMachineStateChangePending;
8562
8563 /* reset the semaphore before waiting, the last dependent will signal
8564 * it */
8565 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8566
8567 alock.release();
8568
8569 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8570
8571 alock.acquire();
8572
8573 -- mData->mMachineStateChangePending;
8574 }
8575}
8576
8577/**
8578 * Changes the machine state and informs callbacks.
8579 *
8580 * This method is not intended to fail so it either returns S_OK or asserts (and
8581 * returns a failure).
8582 *
8583 * @note Locks this object for writing.
8584 */
8585HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8586{
8587 LogFlowThisFuncEnter();
8588 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8589 Assert(aMachineState != MachineState_Null);
8590
8591 AutoCaller autoCaller(this);
8592 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8593
8594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8595
8596 /* wait for state dependents to drop to zero */
8597 i_ensureNoStateDependencies();
8598
8599 MachineState_T const enmOldState = mData->mMachineState;
8600 if (enmOldState != aMachineState)
8601 {
8602 mData->mMachineState = aMachineState;
8603 RTTimeNow(&mData->mLastStateChange);
8604
8605#ifdef VBOX_WITH_DTRACE_R3_MAIN
8606 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8607#endif
8608 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8609 }
8610
8611 LogFlowThisFuncLeave();
8612 return S_OK;
8613}
8614
8615/**
8616 * Searches for a shared folder with the given logical name
8617 * in the collection of shared folders.
8618 *
8619 * @param aName logical name of the shared folder
8620 * @param aSharedFolder where to return the found object
8621 * @param aSetError whether to set the error info if the folder is
8622 * not found
8623 * @return
8624 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8625 *
8626 * @note
8627 * must be called from under the object's lock!
8628 */
8629HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8630 ComObjPtr<SharedFolder> &aSharedFolder,
8631 bool aSetError /* = false */)
8632{
8633 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8634 for (HWData::SharedFolderList::const_iterator
8635 it = mHWData->mSharedFolders.begin();
8636 it != mHWData->mSharedFolders.end();
8637 ++it)
8638 {
8639 SharedFolder *pSF = *it;
8640 AutoCaller autoCaller(pSF);
8641 if (pSF->i_getName() == aName)
8642 {
8643 aSharedFolder = pSF;
8644 rc = S_OK;
8645 break;
8646 }
8647 }
8648
8649 if (aSetError && FAILED(rc))
8650 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8651
8652 return rc;
8653}
8654
8655/**
8656 * Initializes all machine instance data from the given settings structures
8657 * from XML. The exception is the machine UUID which needs special handling
8658 * depending on the caller's use case, so the caller needs to set that herself.
8659 *
8660 * This gets called in several contexts during machine initialization:
8661 *
8662 * -- When machine XML exists on disk already and needs to be loaded into memory,
8663 * for example, from #i_registeredInit() to load all registered machines on
8664 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8665 * attached to the machine should be part of some media registry already.
8666 *
8667 * -- During OVF import, when a machine config has been constructed from an
8668 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8669 * ensure that the media listed as attachments in the config (which have
8670 * been imported from the OVF) receive the correct registry ID.
8671 *
8672 * -- During VM cloning.
8673 *
8674 * @param config Machine settings from XML.
8675 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8676 * for each attached medium in the config.
8677 * @return
8678 */
8679HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8680 const Guid *puuidRegistry)
8681{
8682 // copy name, description, OS type, teleporter, UTC etc.
8683 mUserData->s = config.machineUserData;
8684
8685 // look up the object by Id to check it is valid
8686 ComObjPtr<GuestOSType> pGuestOSType;
8687 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8688 if (!pGuestOSType.isNull())
8689 mUserData->s.strOsType = pGuestOSType->i_id();
8690
8691 // stateFile (optional)
8692 if (config.strStateFile.isEmpty())
8693 mSSData->strStateFilePath.setNull();
8694 else
8695 {
8696 Utf8Str stateFilePathFull(config.strStateFile);
8697 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8698 if (RT_FAILURE(vrc))
8699 return setErrorBoth(E_FAIL, vrc,
8700 tr("Invalid saved state file path '%s' (%Rrc)"),
8701 config.strStateFile.c_str(),
8702 vrc);
8703 mSSData->strStateFilePath = stateFilePathFull;
8704 }
8705
8706 // snapshot folder needs special processing so set it again
8707 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8708 if (FAILED(rc)) return rc;
8709
8710 /* Copy the extra data items (config may or may not be the same as
8711 * mData->pMachineConfigFile) if necessary. When loading the XML files
8712 * from disk they are the same, but not for OVF import. */
8713 if (mData->pMachineConfigFile != &config)
8714 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8715
8716 /* currentStateModified (optional, default is true) */
8717 mData->mCurrentStateModified = config.fCurrentStateModified;
8718
8719 mData->mLastStateChange = config.timeLastStateChange;
8720
8721 /*
8722 * note: all mUserData members must be assigned prior this point because
8723 * we need to commit changes in order to let mUserData be shared by all
8724 * snapshot machine instances.
8725 */
8726 mUserData.commitCopy();
8727
8728 // machine registry, if present (must be loaded before snapshots)
8729 if (config.canHaveOwnMediaRegistry())
8730 {
8731 // determine machine folder
8732 Utf8Str strMachineFolder = i_getSettingsFileFull();
8733 strMachineFolder.stripFilename();
8734 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8735 config.mediaRegistry,
8736 strMachineFolder);
8737 if (FAILED(rc)) return rc;
8738 }
8739
8740 /* Snapshot node (optional) */
8741 size_t cRootSnapshots;
8742 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8743 {
8744 // there must be only one root snapshot
8745 Assert(cRootSnapshots == 1);
8746
8747 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8748
8749 rc = i_loadSnapshot(snap,
8750 config.uuidCurrentSnapshot,
8751 NULL); // no parent == first snapshot
8752 if (FAILED(rc)) return rc;
8753 }
8754
8755 // hardware data
8756 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8757 if (FAILED(rc)) return rc;
8758
8759 /*
8760 * NOTE: the assignment below must be the last thing to do,
8761 * otherwise it will be not possible to change the settings
8762 * somewhere in the code above because all setters will be
8763 * blocked by i_checkStateDependency(MutableStateDep).
8764 */
8765
8766 /* set the machine state to Aborted or Saved when appropriate */
8767 if (config.fAborted)
8768 {
8769 mSSData->strStateFilePath.setNull();
8770
8771 /* no need to use i_setMachineState() during init() */
8772 mData->mMachineState = MachineState_Aborted;
8773 }
8774 else if (!mSSData->strStateFilePath.isEmpty())
8775 {
8776 /* no need to use i_setMachineState() during init() */
8777 mData->mMachineState = MachineState_Saved;
8778 }
8779
8780 // after loading settings, we are no longer different from the XML on disk
8781 mData->flModifications = 0;
8782
8783 return S_OK;
8784}
8785
8786/**
8787 * Recursively loads all snapshots starting from the given.
8788 *
8789 * @param data snapshot settings.
8790 * @param aCurSnapshotId Current snapshot ID from the settings file.
8791 * @param aParentSnapshot Parent snapshot.
8792 */
8793HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8794 const Guid &aCurSnapshotId,
8795 Snapshot *aParentSnapshot)
8796{
8797 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8798 AssertReturn(!i_isSessionMachine(), E_FAIL);
8799
8800 HRESULT rc = S_OK;
8801
8802 Utf8Str strStateFile;
8803 if (!data.strStateFile.isEmpty())
8804 {
8805 /* optional */
8806 strStateFile = data.strStateFile;
8807 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8808 if (RT_FAILURE(vrc))
8809 return setErrorBoth(E_FAIL, vrc,
8810 tr("Invalid saved state file path '%s' (%Rrc)"),
8811 strStateFile.c_str(),
8812 vrc);
8813 }
8814
8815 /* create a snapshot machine object */
8816 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8817 pSnapshotMachine.createObject();
8818 rc = pSnapshotMachine->initFromSettings(this,
8819 data.hardware,
8820 &data.debugging,
8821 &data.autostart,
8822 data.uuid.ref(),
8823 strStateFile);
8824 if (FAILED(rc)) return rc;
8825
8826 /* create a snapshot object */
8827 ComObjPtr<Snapshot> pSnapshot;
8828 pSnapshot.createObject();
8829 /* initialize the snapshot */
8830 rc = pSnapshot->init(mParent, // VirtualBox object
8831 data.uuid,
8832 data.strName,
8833 data.strDescription,
8834 data.timestamp,
8835 pSnapshotMachine,
8836 aParentSnapshot);
8837 if (FAILED(rc)) return rc;
8838
8839 /* memorize the first snapshot if necessary */
8840 if (!mData->mFirstSnapshot)
8841 mData->mFirstSnapshot = pSnapshot;
8842
8843 /* memorize the current snapshot when appropriate */
8844 if ( !mData->mCurrentSnapshot
8845 && pSnapshot->i_getId() == aCurSnapshotId
8846 )
8847 mData->mCurrentSnapshot = pSnapshot;
8848
8849 // now create the children
8850 for (settings::SnapshotsList::const_iterator
8851 it = data.llChildSnapshots.begin();
8852 it != data.llChildSnapshots.end();
8853 ++it)
8854 {
8855 const settings::Snapshot &childData = *it;
8856 // recurse
8857 rc = i_loadSnapshot(childData,
8858 aCurSnapshotId,
8859 pSnapshot); // parent = the one we created above
8860 if (FAILED(rc)) return rc;
8861 }
8862
8863 return rc;
8864}
8865
8866/**
8867 * Loads settings into mHWData.
8868 *
8869 * @param puuidRegistry Registry ID.
8870 * @param puuidSnapshot Snapshot ID
8871 * @param data Reference to the hardware settings.
8872 * @param pDbg Pointer to the debugging settings.
8873 * @param pAutostart Pointer to the autostart settings.
8874 */
8875HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8876 const Guid *puuidSnapshot,
8877 const settings::Hardware &data,
8878 const settings::Debugging *pDbg,
8879 const settings::Autostart *pAutostart)
8880{
8881 AssertReturn(!i_isSessionMachine(), E_FAIL);
8882
8883 HRESULT rc = S_OK;
8884
8885 try
8886 {
8887 ComObjPtr<GuestOSType> pGuestOSType;
8888 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8889
8890 /* The hardware version attribute (optional). */
8891 mHWData->mHWVersion = data.strVersion;
8892 mHWData->mHardwareUUID = data.uuid;
8893
8894 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8895 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8896 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8897 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8898 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8899 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8900 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8901 mHWData->mPAEEnabled = data.fPAE;
8902 mHWData->mLongMode = data.enmLongMode;
8903 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8904 mHWData->mAPIC = data.fAPIC;
8905 mHWData->mX2APIC = data.fX2APIC;
8906 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8907 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8908 mHWData->mSpecCtrl = data.fSpecCtrl;
8909 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8910 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8911 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8912 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8913 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8914 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8915 mHWData->mCPUCount = data.cCPUs;
8916 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8917 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8918 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8919 mHWData->mCpuProfile = data.strCpuProfile;
8920
8921 // cpu
8922 if (mHWData->mCPUHotPlugEnabled)
8923 {
8924 for (settings::CpuList::const_iterator
8925 it = data.llCpus.begin();
8926 it != data.llCpus.end();
8927 ++it)
8928 {
8929 const settings::Cpu &cpu = *it;
8930
8931 mHWData->mCPUAttached[cpu.ulId] = true;
8932 }
8933 }
8934
8935 // cpuid leafs
8936 for (settings::CpuIdLeafsList::const_iterator
8937 it = data.llCpuIdLeafs.begin();
8938 it != data.llCpuIdLeafs.end();
8939 ++it)
8940 {
8941 const settings::CpuIdLeaf &rLeaf= *it;
8942 if ( rLeaf.idx < UINT32_C(0x20)
8943 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8944 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8945 mHWData->mCpuIdLeafList.push_back(rLeaf);
8946 /* else: just ignore */
8947 }
8948
8949 mHWData->mMemorySize = data.ulMemorySizeMB;
8950 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8951
8952 // boot order
8953 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8954 {
8955 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8956 if (it == data.mapBootOrder.end())
8957 mHWData->mBootOrder[i] = DeviceType_Null;
8958 else
8959 mHWData->mBootOrder[i] = it->second;
8960 }
8961
8962 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8963 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8964 mHWData->mMonitorCount = data.cMonitors;
8965 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8966 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8967 mHWData->mFirmwareType = data.firmwareType;
8968 mHWData->mPointingHIDType = data.pointingHIDType;
8969 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8970 mHWData->mChipsetType = data.chipsetType;
8971 mHWData->mParavirtProvider = data.paravirtProvider;
8972 mHWData->mParavirtDebug = data.strParavirtDebug;
8973 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8974 mHWData->mHPETEnabled = data.fHPETEnabled;
8975
8976 /* VRDEServer */
8977 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8978 if (FAILED(rc)) return rc;
8979
8980 /* BIOS */
8981 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8982 if (FAILED(rc)) return rc;
8983
8984 /* Recording settings */
8985 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8986 if (FAILED(rc)) return rc;
8987
8988 // Bandwidth control (must come before network adapters)
8989 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8990 if (FAILED(rc)) return rc;
8991
8992 /* USB controllers */
8993 for (settings::USBControllerList::const_iterator
8994 it = data.usbSettings.llUSBControllers.begin();
8995 it != data.usbSettings.llUSBControllers.end();
8996 ++it)
8997 {
8998 const settings::USBController &settingsCtrl = *it;
8999 ComObjPtr<USBController> newCtrl;
9000
9001 newCtrl.createObject();
9002 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9003 mUSBControllers->push_back(newCtrl);
9004 }
9005
9006 /* USB device filters */
9007 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9008 if (FAILED(rc)) return rc;
9009
9010 // network adapters (establish array size first and apply defaults, to
9011 // ensure reading the same settings as we saved, since the list skips
9012 // adapters having defaults)
9013 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9014 size_t oldCount = mNetworkAdapters.size();
9015 if (newCount > oldCount)
9016 {
9017 mNetworkAdapters.resize(newCount);
9018 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9019 {
9020 unconst(mNetworkAdapters[slot]).createObject();
9021 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9022 }
9023 }
9024 else if (newCount < oldCount)
9025 mNetworkAdapters.resize(newCount);
9026 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9027 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9028 for (settings::NetworkAdaptersList::const_iterator
9029 it = data.llNetworkAdapters.begin();
9030 it != data.llNetworkAdapters.end();
9031 ++it)
9032 {
9033 const settings::NetworkAdapter &nic = *it;
9034
9035 /* slot uniqueness is guaranteed by XML Schema */
9036 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9037 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9038 if (FAILED(rc)) return rc;
9039 }
9040
9041 // serial ports (establish defaults first, to ensure reading the same
9042 // settings as we saved, since the list skips ports having defaults)
9043 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9044 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9045 for (settings::SerialPortsList::const_iterator
9046 it = data.llSerialPorts.begin();
9047 it != data.llSerialPorts.end();
9048 ++it)
9049 {
9050 const settings::SerialPort &s = *it;
9051
9052 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9053 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9054 if (FAILED(rc)) return rc;
9055 }
9056
9057 // parallel ports (establish defaults first, to ensure reading the same
9058 // settings as we saved, since the list skips ports having defaults)
9059 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9060 mParallelPorts[i]->i_applyDefaults();
9061 for (settings::ParallelPortsList::const_iterator
9062 it = data.llParallelPorts.begin();
9063 it != data.llParallelPorts.end();
9064 ++it)
9065 {
9066 const settings::ParallelPort &p = *it;
9067
9068 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9069 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9070 if (FAILED(rc)) return rc;
9071 }
9072
9073 /* AudioAdapter */
9074 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9075 if (FAILED(rc)) return rc;
9076
9077 /* storage controllers */
9078 rc = i_loadStorageControllers(data.storage,
9079 puuidRegistry,
9080 puuidSnapshot);
9081 if (FAILED(rc)) return rc;
9082
9083 /* Shared folders */
9084 for (settings::SharedFoldersList::const_iterator
9085 it = data.llSharedFolders.begin();
9086 it != data.llSharedFolders.end();
9087 ++it)
9088 {
9089 const settings::SharedFolder &sf = *it;
9090
9091 ComObjPtr<SharedFolder> sharedFolder;
9092 /* Check for double entries. Not allowed! */
9093 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9094 if (SUCCEEDED(rc))
9095 return setError(VBOX_E_OBJECT_IN_USE,
9096 tr("Shared folder named '%s' already exists"),
9097 sf.strName.c_str());
9098
9099 /* Create the new shared folder. Don't break on error. This will be
9100 * reported when the machine starts. */
9101 sharedFolder.createObject();
9102 rc = sharedFolder->init(i_getMachine(),
9103 sf.strName,
9104 sf.strHostPath,
9105 RT_BOOL(sf.fWritable),
9106 RT_BOOL(sf.fAutoMount),
9107 sf.strAutoMountPoint,
9108 false /* fFailOnError */);
9109 if (FAILED(rc)) return rc;
9110 mHWData->mSharedFolders.push_back(sharedFolder);
9111 }
9112
9113 // Clipboard
9114 mHWData->mClipboardMode = data.clipboardMode;
9115
9116 // drag'n'drop
9117 mHWData->mDnDMode = data.dndMode;
9118
9119 // guest settings
9120 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9121
9122 // IO settings
9123 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9124 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9125
9126 // Host PCI devices
9127 for (settings::HostPCIDeviceAttachmentList::const_iterator
9128 it = data.pciAttachments.begin();
9129 it != data.pciAttachments.end();
9130 ++it)
9131 {
9132 const settings::HostPCIDeviceAttachment &hpda = *it;
9133 ComObjPtr<PCIDeviceAttachment> pda;
9134
9135 pda.createObject();
9136 pda->i_loadSettings(this, hpda);
9137 mHWData->mPCIDeviceAssignments.push_back(pda);
9138 }
9139
9140 /*
9141 * (The following isn't really real hardware, but it lives in HWData
9142 * for reasons of convenience.)
9143 */
9144
9145#ifdef VBOX_WITH_GUEST_PROPS
9146 /* Guest properties (optional) */
9147
9148 /* Only load transient guest properties for configs which have saved
9149 * state, because there shouldn't be any for powered off VMs. The same
9150 * logic applies for snapshots, as offline snapshots shouldn't have
9151 * any such properties. They confuse the code in various places.
9152 * Note: can't rely on the machine state, as it isn't set yet. */
9153 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9154 /* apologies for the hacky unconst() usage, but this needs hacking
9155 * actually inconsistent settings into consistency, otherwise there
9156 * will be some corner cases where the inconsistency survives
9157 * surprisingly long without getting fixed, especially for snapshots
9158 * as there are no config changes. */
9159 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9160 for (settings::GuestPropertiesList::iterator
9161 it = llGuestProperties.begin();
9162 it != llGuestProperties.end();
9163 /*nothing*/)
9164 {
9165 const settings::GuestProperty &prop = *it;
9166 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9167 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9168 if ( fSkipTransientGuestProperties
9169 && ( fFlags & GUEST_PROP_F_TRANSIENT
9170 || fFlags & GUEST_PROP_F_TRANSRESET))
9171 {
9172 it = llGuestProperties.erase(it);
9173 continue;
9174 }
9175 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9176 mHWData->mGuestProperties[prop.strName] = property;
9177 ++it;
9178 }
9179#endif /* VBOX_WITH_GUEST_PROPS defined */
9180
9181 rc = i_loadDebugging(pDbg);
9182 if (FAILED(rc))
9183 return rc;
9184
9185 mHWData->mAutostart = *pAutostart;
9186
9187 /* default frontend */
9188 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9189 }
9190 catch (std::bad_alloc &)
9191 {
9192 return E_OUTOFMEMORY;
9193 }
9194
9195 AssertComRC(rc);
9196 return rc;
9197}
9198
9199/**
9200 * Called from i_loadHardware() to load the debugging settings of the
9201 * machine.
9202 *
9203 * @param pDbg Pointer to the settings.
9204 */
9205HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9206{
9207 mHWData->mDebugging = *pDbg;
9208 /* no more processing currently required, this will probably change. */
9209 return S_OK;
9210}
9211
9212/**
9213 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9214 *
9215 * @param data storage settings.
9216 * @param puuidRegistry media registry ID to set media to or NULL;
9217 * see Machine::i_loadMachineDataFromSettings()
9218 * @param puuidSnapshot snapshot ID
9219 * @return
9220 */
9221HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9222 const Guid *puuidRegistry,
9223 const Guid *puuidSnapshot)
9224{
9225 AssertReturn(!i_isSessionMachine(), E_FAIL);
9226
9227 HRESULT rc = S_OK;
9228
9229 for (settings::StorageControllersList::const_iterator
9230 it = data.llStorageControllers.begin();
9231 it != data.llStorageControllers.end();
9232 ++it)
9233 {
9234 const settings::StorageController &ctlData = *it;
9235
9236 ComObjPtr<StorageController> pCtl;
9237 /* Try to find one with the name first. */
9238 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9239 if (SUCCEEDED(rc))
9240 return setError(VBOX_E_OBJECT_IN_USE,
9241 tr("Storage controller named '%s' already exists"),
9242 ctlData.strName.c_str());
9243
9244 pCtl.createObject();
9245 rc = pCtl->init(this,
9246 ctlData.strName,
9247 ctlData.storageBus,
9248 ctlData.ulInstance,
9249 ctlData.fBootable);
9250 if (FAILED(rc)) return rc;
9251
9252 mStorageControllers->push_back(pCtl);
9253
9254 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9255 if (FAILED(rc)) return rc;
9256
9257 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9258 if (FAILED(rc)) return rc;
9259
9260 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9261 if (FAILED(rc)) return rc;
9262
9263 /* Load the attached devices now. */
9264 rc = i_loadStorageDevices(pCtl,
9265 ctlData,
9266 puuidRegistry,
9267 puuidSnapshot);
9268 if (FAILED(rc)) return rc;
9269 }
9270
9271 return S_OK;
9272}
9273
9274/**
9275 * Called from i_loadStorageControllers for a controller's devices.
9276 *
9277 * @param aStorageController
9278 * @param data
9279 * @param puuidRegistry media registry ID to set media to or NULL; see
9280 * Machine::i_loadMachineDataFromSettings()
9281 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9282 * @return
9283 */
9284HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9285 const settings::StorageController &data,
9286 const Guid *puuidRegistry,
9287 const Guid *puuidSnapshot)
9288{
9289 HRESULT rc = S_OK;
9290
9291 /* paranoia: detect duplicate attachments */
9292 for (settings::AttachedDevicesList::const_iterator
9293 it = data.llAttachedDevices.begin();
9294 it != data.llAttachedDevices.end();
9295 ++it)
9296 {
9297 const settings::AttachedDevice &ad = *it;
9298
9299 for (settings::AttachedDevicesList::const_iterator it2 = it;
9300 it2 != data.llAttachedDevices.end();
9301 ++it2)
9302 {
9303 if (it == it2)
9304 continue;
9305
9306 const settings::AttachedDevice &ad2 = *it2;
9307
9308 if ( ad.lPort == ad2.lPort
9309 && ad.lDevice == ad2.lDevice)
9310 {
9311 return setError(E_FAIL,
9312 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9313 aStorageController->i_getName().c_str(),
9314 ad.lPort,
9315 ad.lDevice,
9316 mUserData->s.strName.c_str());
9317 }
9318 }
9319 }
9320
9321 for (settings::AttachedDevicesList::const_iterator
9322 it = data.llAttachedDevices.begin();
9323 it != data.llAttachedDevices.end();
9324 ++it)
9325 {
9326 const settings::AttachedDevice &dev = *it;
9327 ComObjPtr<Medium> medium;
9328
9329 switch (dev.deviceType)
9330 {
9331 case DeviceType_Floppy:
9332 case DeviceType_DVD:
9333 if (dev.strHostDriveSrc.isNotEmpty())
9334 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9335 false /* fRefresh */, medium);
9336 else
9337 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9338 dev.uuid,
9339 false /* fRefresh */,
9340 false /* aSetError */,
9341 medium);
9342 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9343 // This is not an error. The host drive or UUID might have vanished, so just go
9344 // ahead without this removeable medium attachment
9345 rc = S_OK;
9346 break;
9347
9348 case DeviceType_HardDisk:
9349 {
9350 /* find a hard disk by UUID */
9351 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9352 if (FAILED(rc))
9353 {
9354 if (i_isSnapshotMachine())
9355 {
9356 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9357 // so the user knows that the bad disk is in a snapshot somewhere
9358 com::ErrorInfo info;
9359 return setError(E_FAIL,
9360 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9361 puuidSnapshot->raw(),
9362 info.getText().raw());
9363 }
9364 else
9365 return rc;
9366 }
9367
9368 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9369
9370 if (medium->i_getType() == MediumType_Immutable)
9371 {
9372 if (i_isSnapshotMachine())
9373 return setError(E_FAIL,
9374 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9375 "of the virtual machine '%s' ('%s')"),
9376 medium->i_getLocationFull().c_str(),
9377 dev.uuid.raw(),
9378 puuidSnapshot->raw(),
9379 mUserData->s.strName.c_str(),
9380 mData->m_strConfigFileFull.c_str());
9381
9382 return setError(E_FAIL,
9383 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9384 medium->i_getLocationFull().c_str(),
9385 dev.uuid.raw(),
9386 mUserData->s.strName.c_str(),
9387 mData->m_strConfigFileFull.c_str());
9388 }
9389
9390 if (medium->i_getType() == MediumType_MultiAttach)
9391 {
9392 if (i_isSnapshotMachine())
9393 return setError(E_FAIL,
9394 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9395 "of the virtual machine '%s' ('%s')"),
9396 medium->i_getLocationFull().c_str(),
9397 dev.uuid.raw(),
9398 puuidSnapshot->raw(),
9399 mUserData->s.strName.c_str(),
9400 mData->m_strConfigFileFull.c_str());
9401
9402 return setError(E_FAIL,
9403 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9404 medium->i_getLocationFull().c_str(),
9405 dev.uuid.raw(),
9406 mUserData->s.strName.c_str(),
9407 mData->m_strConfigFileFull.c_str());
9408 }
9409
9410 if ( !i_isSnapshotMachine()
9411 && medium->i_getChildren().size() != 0
9412 )
9413 return setError(E_FAIL,
9414 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9415 "because it has %d differencing child hard disks"),
9416 medium->i_getLocationFull().c_str(),
9417 dev.uuid.raw(),
9418 mUserData->s.strName.c_str(),
9419 mData->m_strConfigFileFull.c_str(),
9420 medium->i_getChildren().size());
9421
9422 if (i_findAttachment(*mMediumAttachments.data(),
9423 medium))
9424 return setError(E_FAIL,
9425 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9426 medium->i_getLocationFull().c_str(),
9427 dev.uuid.raw(),
9428 mUserData->s.strName.c_str(),
9429 mData->m_strConfigFileFull.c_str());
9430
9431 break;
9432 }
9433
9434 default:
9435 return setError(E_FAIL,
9436 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9437 medium->i_getLocationFull().c_str(),
9438 mUserData->s.strName.c_str(),
9439 mData->m_strConfigFileFull.c_str());
9440 }
9441
9442 if (FAILED(rc))
9443 break;
9444
9445 /* Bandwidth groups are loaded at this point. */
9446 ComObjPtr<BandwidthGroup> pBwGroup;
9447
9448 if (!dev.strBwGroup.isEmpty())
9449 {
9450 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9451 if (FAILED(rc))
9452 return setError(E_FAIL,
9453 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9454 medium->i_getLocationFull().c_str(),
9455 dev.strBwGroup.c_str(),
9456 mUserData->s.strName.c_str(),
9457 mData->m_strConfigFileFull.c_str());
9458 pBwGroup->i_reference();
9459 }
9460
9461 const Utf8Str controllerName = aStorageController->i_getName();
9462 ComObjPtr<MediumAttachment> pAttachment;
9463 pAttachment.createObject();
9464 rc = pAttachment->init(this,
9465 medium,
9466 controllerName,
9467 dev.lPort,
9468 dev.lDevice,
9469 dev.deviceType,
9470 false,
9471 dev.fPassThrough,
9472 dev.fTempEject,
9473 dev.fNonRotational,
9474 dev.fDiscard,
9475 dev.fHotPluggable,
9476 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9477 if (FAILED(rc)) break;
9478
9479 /* associate the medium with this machine and snapshot */
9480 if (!medium.isNull())
9481 {
9482 AutoCaller medCaller(medium);
9483 if (FAILED(medCaller.rc())) return medCaller.rc();
9484 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9485
9486 if (i_isSnapshotMachine())
9487 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9488 else
9489 rc = medium->i_addBackReference(mData->mUuid);
9490 /* If the medium->addBackReference fails it sets an appropriate
9491 * error message, so no need to do any guesswork here. */
9492
9493 if (puuidRegistry)
9494 // caller wants registry ID to be set on all attached media (OVF import case)
9495 medium->i_addRegistry(*puuidRegistry);
9496 }
9497
9498 if (FAILED(rc))
9499 break;
9500
9501 /* back up mMediumAttachments to let registeredInit() properly rollback
9502 * on failure (= limited accessibility) */
9503 i_setModified(IsModified_Storage);
9504 mMediumAttachments.backup();
9505 mMediumAttachments->push_back(pAttachment);
9506 }
9507
9508 return rc;
9509}
9510
9511/**
9512 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9513 *
9514 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9515 * @param aSnapshot where to return the found snapshot
9516 * @param aSetError true to set extended error info on failure
9517 */
9518HRESULT Machine::i_findSnapshotById(const Guid &aId,
9519 ComObjPtr<Snapshot> &aSnapshot,
9520 bool aSetError /* = false */)
9521{
9522 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9523
9524 if (!mData->mFirstSnapshot)
9525 {
9526 if (aSetError)
9527 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9528 return E_FAIL;
9529 }
9530
9531 if (aId.isZero())
9532 aSnapshot = mData->mFirstSnapshot;
9533 else
9534 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9535
9536 if (!aSnapshot)
9537 {
9538 if (aSetError)
9539 return setError(E_FAIL,
9540 tr("Could not find a snapshot with UUID {%s}"),
9541 aId.toString().c_str());
9542 return E_FAIL;
9543 }
9544
9545 return S_OK;
9546}
9547
9548/**
9549 * Returns the snapshot with the given name or fails of no such snapshot.
9550 *
9551 * @param strName snapshot name to find
9552 * @param aSnapshot where to return the found snapshot
9553 * @param aSetError true to set extended error info on failure
9554 */
9555HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9556 ComObjPtr<Snapshot> &aSnapshot,
9557 bool aSetError /* = false */)
9558{
9559 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9560
9561 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9562
9563 if (!mData->mFirstSnapshot)
9564 {
9565 if (aSetError)
9566 return setError(VBOX_E_OBJECT_NOT_FOUND,
9567 tr("This machine does not have any snapshots"));
9568 return VBOX_E_OBJECT_NOT_FOUND;
9569 }
9570
9571 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9572
9573 if (!aSnapshot)
9574 {
9575 if (aSetError)
9576 return setError(VBOX_E_OBJECT_NOT_FOUND,
9577 tr("Could not find a snapshot named '%s'"), strName.c_str());
9578 return VBOX_E_OBJECT_NOT_FOUND;
9579 }
9580
9581 return S_OK;
9582}
9583
9584/**
9585 * Returns a storage controller object with the given name.
9586 *
9587 * @param aName storage controller name to find
9588 * @param aStorageController where to return the found storage controller
9589 * @param aSetError true to set extended error info on failure
9590 */
9591HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9592 ComObjPtr<StorageController> &aStorageController,
9593 bool aSetError /* = false */)
9594{
9595 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9596
9597 for (StorageControllerList::const_iterator
9598 it = mStorageControllers->begin();
9599 it != mStorageControllers->end();
9600 ++it)
9601 {
9602 if ((*it)->i_getName() == aName)
9603 {
9604 aStorageController = (*it);
9605 return S_OK;
9606 }
9607 }
9608
9609 if (aSetError)
9610 return setError(VBOX_E_OBJECT_NOT_FOUND,
9611 tr("Could not find a storage controller named '%s'"),
9612 aName.c_str());
9613 return VBOX_E_OBJECT_NOT_FOUND;
9614}
9615
9616/**
9617 * Returns a USB controller object with the given name.
9618 *
9619 * @param aName USB controller name to find
9620 * @param aUSBController where to return the found USB controller
9621 * @param aSetError true to set extended error info on failure
9622 */
9623HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9624 ComObjPtr<USBController> &aUSBController,
9625 bool aSetError /* = false */)
9626{
9627 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9628
9629 for (USBControllerList::const_iterator
9630 it = mUSBControllers->begin();
9631 it != mUSBControllers->end();
9632 ++it)
9633 {
9634 if ((*it)->i_getName() == aName)
9635 {
9636 aUSBController = (*it);
9637 return S_OK;
9638 }
9639 }
9640
9641 if (aSetError)
9642 return setError(VBOX_E_OBJECT_NOT_FOUND,
9643 tr("Could not find a storage controller named '%s'"),
9644 aName.c_str());
9645 return VBOX_E_OBJECT_NOT_FOUND;
9646}
9647
9648/**
9649 * Returns the number of USB controller instance of the given type.
9650 *
9651 * @param enmType USB controller type.
9652 */
9653ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9654{
9655 ULONG cCtrls = 0;
9656
9657 for (USBControllerList::const_iterator
9658 it = mUSBControllers->begin();
9659 it != mUSBControllers->end();
9660 ++it)
9661 {
9662 if ((*it)->i_getControllerType() == enmType)
9663 cCtrls++;
9664 }
9665
9666 return cCtrls;
9667}
9668
9669HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9670 MediumAttachmentList &atts)
9671{
9672 AutoCaller autoCaller(this);
9673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9674
9675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9676
9677 for (MediumAttachmentList::const_iterator
9678 it = mMediumAttachments->begin();
9679 it != mMediumAttachments->end();
9680 ++it)
9681 {
9682 const ComObjPtr<MediumAttachment> &pAtt = *it;
9683 // should never happen, but deal with NULL pointers in the list.
9684 AssertContinue(!pAtt.isNull());
9685
9686 // getControllerName() needs caller+read lock
9687 AutoCaller autoAttCaller(pAtt);
9688 if (FAILED(autoAttCaller.rc()))
9689 {
9690 atts.clear();
9691 return autoAttCaller.rc();
9692 }
9693 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9694
9695 if (pAtt->i_getControllerName() == aName)
9696 atts.push_back(pAtt);
9697 }
9698
9699 return S_OK;
9700}
9701
9702
9703/**
9704 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9705 * file if the machine name was changed and about creating a new settings file
9706 * if this is a new machine.
9707 *
9708 * @note Must be never called directly but only from #saveSettings().
9709 */
9710HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9711{
9712 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9713
9714 HRESULT rc = S_OK;
9715
9716 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9717
9718 /// @todo need to handle primary group change, too
9719
9720 /* attempt to rename the settings file if machine name is changed */
9721 if ( mUserData->s.fNameSync
9722 && mUserData.isBackedUp()
9723 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9724 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9725 )
9726 {
9727 bool dirRenamed = false;
9728 bool fileRenamed = false;
9729
9730 Utf8Str configFile, newConfigFile;
9731 Utf8Str configFilePrev, newConfigFilePrev;
9732 Utf8Str configDir, newConfigDir;
9733
9734 do
9735 {
9736 int vrc = VINF_SUCCESS;
9737
9738 Utf8Str name = mUserData.backedUpData()->s.strName;
9739 Utf8Str newName = mUserData->s.strName;
9740 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9741 if (group == "/")
9742 group.setNull();
9743 Utf8Str newGroup = mUserData->s.llGroups.front();
9744 if (newGroup == "/")
9745 newGroup.setNull();
9746
9747 configFile = mData->m_strConfigFileFull;
9748
9749 /* first, rename the directory if it matches the group and machine name */
9750 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9751 group.c_str(), RTPATH_DELIMITER, name.c_str());
9752 /** @todo hack, make somehow use of ComposeMachineFilename */
9753 if (mUserData->s.fDirectoryIncludesUUID)
9754 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9755 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9756 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9757 /** @todo hack, make somehow use of ComposeMachineFilename */
9758 if (mUserData->s.fDirectoryIncludesUUID)
9759 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9760 configDir = configFile;
9761 configDir.stripFilename();
9762 newConfigDir = configDir;
9763 if ( configDir.length() >= groupPlusName.length()
9764 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9765 groupPlusName.c_str()))
9766 {
9767 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9768 Utf8Str newConfigBaseDir(newConfigDir);
9769 newConfigDir.append(newGroupPlusName);
9770 /* consistency: use \ if appropriate on the platform */
9771 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9772 /* new dir and old dir cannot be equal here because of 'if'
9773 * above and because name != newName */
9774 Assert(configDir != newConfigDir);
9775 if (!fSettingsFileIsNew)
9776 {
9777 /* perform real rename only if the machine is not new */
9778 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9779 if ( vrc == VERR_FILE_NOT_FOUND
9780 || vrc == VERR_PATH_NOT_FOUND)
9781 {
9782 /* create the parent directory, then retry renaming */
9783 Utf8Str parent(newConfigDir);
9784 parent.stripFilename();
9785 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9786 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9787 }
9788 if (RT_FAILURE(vrc))
9789 {
9790 rc = setErrorBoth(E_FAIL, vrc,
9791 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9792 configDir.c_str(),
9793 newConfigDir.c_str(),
9794 vrc);
9795 break;
9796 }
9797 /* delete subdirectories which are no longer needed */
9798 Utf8Str dir(configDir);
9799 dir.stripFilename();
9800 while (dir != newConfigBaseDir && dir != ".")
9801 {
9802 vrc = RTDirRemove(dir.c_str());
9803 if (RT_FAILURE(vrc))
9804 break;
9805 dir.stripFilename();
9806 }
9807 dirRenamed = true;
9808 }
9809 }
9810
9811 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9812 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9813
9814 /* then try to rename the settings file itself */
9815 if (newConfigFile != configFile)
9816 {
9817 /* get the path to old settings file in renamed directory */
9818 configFile = Utf8StrFmt("%s%c%s",
9819 newConfigDir.c_str(),
9820 RTPATH_DELIMITER,
9821 RTPathFilename(configFile.c_str()));
9822 if (!fSettingsFileIsNew)
9823 {
9824 /* perform real rename only if the machine is not new */
9825 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9826 if (RT_FAILURE(vrc))
9827 {
9828 rc = setErrorBoth(E_FAIL, vrc,
9829 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9830 configFile.c_str(),
9831 newConfigFile.c_str(),
9832 vrc);
9833 break;
9834 }
9835 fileRenamed = true;
9836 configFilePrev = configFile;
9837 configFilePrev += "-prev";
9838 newConfigFilePrev = newConfigFile;
9839 newConfigFilePrev += "-prev";
9840 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9841 }
9842 }
9843
9844 // update m_strConfigFileFull amd mConfigFile
9845 mData->m_strConfigFileFull = newConfigFile;
9846 // compute the relative path too
9847 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9848
9849 // store the old and new so that VirtualBox::i_saveSettings() can update
9850 // the media registry
9851 if ( mData->mRegistered
9852 && (configDir != newConfigDir || configFile != newConfigFile))
9853 {
9854 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9855
9856 if (pfNeedsGlobalSaveSettings)
9857 *pfNeedsGlobalSaveSettings = true;
9858 }
9859
9860 // in the saved state file path, replace the old directory with the new directory
9861 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9862 {
9863 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9864 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9865 }
9866
9867 // and do the same thing for the saved state file paths of all the online snapshots
9868 if (mData->mFirstSnapshot)
9869 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9870 newConfigDir.c_str());
9871 }
9872 while (0);
9873
9874 if (FAILED(rc))
9875 {
9876 /* silently try to rename everything back */
9877 if (fileRenamed)
9878 {
9879 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9880 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9881 }
9882 if (dirRenamed)
9883 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9884 }
9885
9886 if (FAILED(rc)) return rc;
9887 }
9888
9889 if (fSettingsFileIsNew)
9890 {
9891 /* create a virgin config file */
9892 int vrc = VINF_SUCCESS;
9893
9894 /* ensure the settings directory exists */
9895 Utf8Str path(mData->m_strConfigFileFull);
9896 path.stripFilename();
9897 if (!RTDirExists(path.c_str()))
9898 {
9899 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9900 if (RT_FAILURE(vrc))
9901 {
9902 return setErrorBoth(E_FAIL, vrc,
9903 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9904 path.c_str(),
9905 vrc);
9906 }
9907 }
9908
9909 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9910 path = Utf8Str(mData->m_strConfigFileFull);
9911 RTFILE f = NIL_RTFILE;
9912 vrc = RTFileOpen(&f, path.c_str(),
9913 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9914 if (RT_FAILURE(vrc))
9915 return setErrorBoth(E_FAIL, vrc,
9916 tr("Could not create the settings file '%s' (%Rrc)"),
9917 path.c_str(),
9918 vrc);
9919 RTFileClose(f);
9920 }
9921
9922 return rc;
9923}
9924
9925/**
9926 * Saves and commits machine data, user data and hardware data.
9927 *
9928 * Note that on failure, the data remains uncommitted.
9929 *
9930 * @a aFlags may combine the following flags:
9931 *
9932 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9933 * Used when saving settings after an operation that makes them 100%
9934 * correspond to the settings from the current snapshot.
9935 * - SaveS_Force: settings will be saved without doing a deep compare of the
9936 * settings structures. This is used when this is called because snapshots
9937 * have changed to avoid the overhead of the deep compare.
9938 *
9939 * @note Must be called from under this object's write lock. Locks children for
9940 * writing.
9941 *
9942 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9943 * initialized to false and that will be set to true by this function if
9944 * the caller must invoke VirtualBox::i_saveSettings() because the global
9945 * settings have changed. This will happen if a machine rename has been
9946 * saved and the global machine and media registries will therefore need
9947 * updating.
9948 * @param aFlags Flags.
9949 */
9950HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9951 int aFlags /*= 0*/)
9952{
9953 LogFlowThisFuncEnter();
9954
9955 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9956
9957 /* make sure child objects are unable to modify the settings while we are
9958 * saving them */
9959 i_ensureNoStateDependencies();
9960
9961 AssertReturn(!i_isSnapshotMachine(),
9962 E_FAIL);
9963
9964 if (!mData->mAccessible)
9965 return setError(VBOX_E_INVALID_VM_STATE,
9966 tr("The machine is not accessible, so cannot save settings"));
9967
9968 HRESULT rc = S_OK;
9969 bool fNeedsWrite = false;
9970
9971 /* First, prepare to save settings. It will care about renaming the
9972 * settings directory and file if the machine name was changed and about
9973 * creating a new settings file if this is a new machine. */
9974 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9975 if (FAILED(rc)) return rc;
9976
9977 // keep a pointer to the current settings structures
9978 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9979 settings::MachineConfigFile *pNewConfig = NULL;
9980
9981 try
9982 {
9983 // make a fresh one to have everyone write stuff into
9984 pNewConfig = new settings::MachineConfigFile(NULL);
9985 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9986
9987 // now go and copy all the settings data from COM to the settings structures
9988 // (this calls i_saveSettings() on all the COM objects in the machine)
9989 i_copyMachineDataToSettings(*pNewConfig);
9990
9991 if (aFlags & SaveS_ResetCurStateModified)
9992 {
9993 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9994 mData->mCurrentStateModified = FALSE;
9995 fNeedsWrite = true; // always, no need to compare
9996 }
9997 else if (aFlags & SaveS_Force)
9998 {
9999 fNeedsWrite = true; // always, no need to compare
10000 }
10001 else
10002 {
10003 if (!mData->mCurrentStateModified)
10004 {
10005 // do a deep compare of the settings that we just saved with the settings
10006 // previously stored in the config file; this invokes MachineConfigFile::operator==
10007 // which does a deep compare of all the settings, which is expensive but less expensive
10008 // than writing out XML in vain
10009 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10010
10011 // could still be modified if any settings changed
10012 mData->mCurrentStateModified = fAnySettingsChanged;
10013
10014 fNeedsWrite = fAnySettingsChanged;
10015 }
10016 else
10017 fNeedsWrite = true;
10018 }
10019
10020 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10021
10022 if (fNeedsWrite)
10023 // now spit it all out!
10024 pNewConfig->write(mData->m_strConfigFileFull);
10025
10026 mData->pMachineConfigFile = pNewConfig;
10027 delete pOldConfig;
10028 i_commit();
10029
10030 // after saving settings, we are no longer different from the XML on disk
10031 mData->flModifications = 0;
10032 }
10033 catch (HRESULT err)
10034 {
10035 // we assume that error info is set by the thrower
10036 rc = err;
10037
10038 // restore old config
10039 delete pNewConfig;
10040 mData->pMachineConfigFile = pOldConfig;
10041 }
10042 catch (...)
10043 {
10044 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10045 }
10046
10047 if (fNeedsWrite)
10048 {
10049 /* Fire the data change event, even on failure (since we've already
10050 * committed all data). This is done only for SessionMachines because
10051 * mutable Machine instances are always not registered (i.e. private
10052 * to the client process that creates them) and thus don't need to
10053 * inform callbacks. */
10054 if (i_isSessionMachine())
10055 mParent->i_onMachineDataChange(mData->mUuid);
10056 }
10057
10058 LogFlowThisFunc(("rc=%08X\n", rc));
10059 LogFlowThisFuncLeave();
10060 return rc;
10061}
10062
10063/**
10064 * Implementation for saving the machine settings into the given
10065 * settings::MachineConfigFile instance. This copies machine extradata
10066 * from the previous machine config file in the instance data, if any.
10067 *
10068 * This gets called from two locations:
10069 *
10070 * -- Machine::i_saveSettings(), during the regular XML writing;
10071 *
10072 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10073 * exported to OVF and we write the VirtualBox proprietary XML
10074 * into a <vbox:Machine> tag.
10075 *
10076 * This routine fills all the fields in there, including snapshots, *except*
10077 * for the following:
10078 *
10079 * -- fCurrentStateModified. There is some special logic associated with that.
10080 *
10081 * The caller can then call MachineConfigFile::write() or do something else
10082 * with it.
10083 *
10084 * Caller must hold the machine lock!
10085 *
10086 * This throws XML errors and HRESULT, so the caller must have a catch block!
10087 */
10088void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10089{
10090 // deep copy extradata, being extra careful with self assignment (the STL
10091 // map assignment on Mac OS X clang based Xcode isn't checking)
10092 if (&config != mData->pMachineConfigFile)
10093 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10094
10095 config.uuid = mData->mUuid;
10096
10097 // copy name, description, OS type, teleport, UTC etc.
10098 config.machineUserData = mUserData->s;
10099
10100 if ( mData->mMachineState == MachineState_Saved
10101 || mData->mMachineState == MachineState_Restoring
10102 // when doing certain snapshot operations we may or may not have
10103 // a saved state in the current state, so keep everything as is
10104 || ( ( mData->mMachineState == MachineState_Snapshotting
10105 || mData->mMachineState == MachineState_DeletingSnapshot
10106 || mData->mMachineState == MachineState_RestoringSnapshot)
10107 && (!mSSData->strStateFilePath.isEmpty())
10108 )
10109 )
10110 {
10111 Assert(!mSSData->strStateFilePath.isEmpty());
10112 /* try to make the file name relative to the settings file dir */
10113 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10114 }
10115 else
10116 {
10117 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10118 config.strStateFile.setNull();
10119 }
10120
10121 if (mData->mCurrentSnapshot)
10122 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10123 else
10124 config.uuidCurrentSnapshot.clear();
10125
10126 config.timeLastStateChange = mData->mLastStateChange;
10127 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10128 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10129
10130 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10131 if (FAILED(rc)) throw rc;
10132
10133 // save machine's media registry if this is VirtualBox 4.0 or later
10134 if (config.canHaveOwnMediaRegistry())
10135 {
10136 // determine machine folder
10137 Utf8Str strMachineFolder = i_getSettingsFileFull();
10138 strMachineFolder.stripFilename();
10139 mParent->i_saveMediaRegistry(config.mediaRegistry,
10140 i_getId(), // only media with registry ID == machine UUID
10141 strMachineFolder);
10142 // this throws HRESULT
10143 }
10144
10145 // save snapshots
10146 rc = i_saveAllSnapshots(config);
10147 if (FAILED(rc)) throw rc;
10148}
10149
10150/**
10151 * Saves all snapshots of the machine into the given machine config file. Called
10152 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10153 * @param config
10154 * @return
10155 */
10156HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10157{
10158 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10159
10160 HRESULT rc = S_OK;
10161
10162 try
10163 {
10164 config.llFirstSnapshot.clear();
10165
10166 if (mData->mFirstSnapshot)
10167 {
10168 // the settings use a list for "the first snapshot"
10169 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10170
10171 // get reference to the snapshot on the list and work on that
10172 // element straight in the list to avoid excessive copying later
10173 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10174 if (FAILED(rc)) throw rc;
10175 }
10176
10177// if (mType == IsSessionMachine)
10178// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10179
10180 }
10181 catch (HRESULT err)
10182 {
10183 /* we assume that error info is set by the thrower */
10184 rc = err;
10185 }
10186 catch (...)
10187 {
10188 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10189 }
10190
10191 return rc;
10192}
10193
10194/**
10195 * Saves the VM hardware configuration. It is assumed that the
10196 * given node is empty.
10197 *
10198 * @param data Reference to the settings object for the hardware config.
10199 * @param pDbg Pointer to the settings object for the debugging config
10200 * which happens to live in mHWData.
10201 * @param pAutostart Pointer to the settings object for the autostart config
10202 * which happens to live in mHWData.
10203 */
10204HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10205 settings::Autostart *pAutostart)
10206{
10207 HRESULT rc = S_OK;
10208
10209 try
10210 {
10211 /* The hardware version attribute (optional).
10212 Automatically upgrade from 1 to current default hardware version
10213 when there is no saved state. (ugly!) */
10214 if ( mHWData->mHWVersion == "1"
10215 && mSSData->strStateFilePath.isEmpty()
10216 )
10217 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10218
10219 data.strVersion = mHWData->mHWVersion;
10220 data.uuid = mHWData->mHardwareUUID;
10221
10222 // CPU
10223 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10224 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10225 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10226 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10227 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10228 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10229 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10230 data.fPAE = !!mHWData->mPAEEnabled;
10231 data.enmLongMode = mHWData->mLongMode;
10232 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10233 data.fAPIC = !!mHWData->mAPIC;
10234 data.fX2APIC = !!mHWData->mX2APIC;
10235 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10236 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10237 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10238 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10239 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10240 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10241 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10242 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10243 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10244 data.cCPUs = mHWData->mCPUCount;
10245 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10246 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10247 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10248 data.strCpuProfile = mHWData->mCpuProfile;
10249
10250 data.llCpus.clear();
10251 if (data.fCpuHotPlug)
10252 {
10253 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10254 {
10255 if (mHWData->mCPUAttached[idx])
10256 {
10257 settings::Cpu cpu;
10258 cpu.ulId = idx;
10259 data.llCpus.push_back(cpu);
10260 }
10261 }
10262 }
10263
10264 /* Standard and Extended CPUID leafs. */
10265 data.llCpuIdLeafs.clear();
10266 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10267
10268 // memory
10269 data.ulMemorySizeMB = mHWData->mMemorySize;
10270 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10271
10272 // firmware
10273 data.firmwareType = mHWData->mFirmwareType;
10274
10275 // HID
10276 data.pointingHIDType = mHWData->mPointingHIDType;
10277 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10278
10279 // chipset
10280 data.chipsetType = mHWData->mChipsetType;
10281
10282 // paravirt
10283 data.paravirtProvider = mHWData->mParavirtProvider;
10284 data.strParavirtDebug = mHWData->mParavirtDebug;
10285
10286 // emulated USB card reader
10287 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10288
10289 // HPET
10290 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10291
10292 // boot order
10293 data.mapBootOrder.clear();
10294 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10295 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10296
10297 // display
10298 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10299 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10300 data.cMonitors = mHWData->mMonitorCount;
10301 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10302 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10303
10304 /* VRDEServer settings (optional) */
10305 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10306 if (FAILED(rc)) throw rc;
10307
10308 /* BIOS settings (required) */
10309 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10310 if (FAILED(rc)) throw rc;
10311
10312 /* Recording settings (required) */
10313 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10314 if (FAILED(rc)) throw rc;
10315
10316 /* USB Controller (required) */
10317 data.usbSettings.llUSBControllers.clear();
10318 for (USBControllerList::const_iterator
10319 it = mUSBControllers->begin();
10320 it != mUSBControllers->end();
10321 ++it)
10322 {
10323 ComObjPtr<USBController> ctrl = *it;
10324 settings::USBController settingsCtrl;
10325
10326 settingsCtrl.strName = ctrl->i_getName();
10327 settingsCtrl.enmType = ctrl->i_getControllerType();
10328
10329 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10330 }
10331
10332 /* USB device filters (required) */
10333 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10334 if (FAILED(rc)) throw rc;
10335
10336 /* Network adapters (required) */
10337 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10338 data.llNetworkAdapters.clear();
10339 /* Write out only the nominal number of network adapters for this
10340 * chipset type. Since Machine::commit() hasn't been called there
10341 * may be extra NIC settings in the vector. */
10342 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10343 {
10344 settings::NetworkAdapter nic;
10345 nic.ulSlot = (uint32_t)slot;
10346 /* paranoia check... must not be NULL, but must not crash either. */
10347 if (mNetworkAdapters[slot])
10348 {
10349 if (mNetworkAdapters[slot]->i_hasDefaults())
10350 continue;
10351
10352 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10353 if (FAILED(rc)) throw rc;
10354
10355 data.llNetworkAdapters.push_back(nic);
10356 }
10357 }
10358
10359 /* Serial ports */
10360 data.llSerialPorts.clear();
10361 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10362 {
10363 if (mSerialPorts[slot]->i_hasDefaults())
10364 continue;
10365
10366 settings::SerialPort s;
10367 s.ulSlot = slot;
10368 rc = mSerialPorts[slot]->i_saveSettings(s);
10369 if (FAILED(rc)) return rc;
10370
10371 data.llSerialPorts.push_back(s);
10372 }
10373
10374 /* Parallel ports */
10375 data.llParallelPorts.clear();
10376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10377 {
10378 if (mParallelPorts[slot]->i_hasDefaults())
10379 continue;
10380
10381 settings::ParallelPort p;
10382 p.ulSlot = slot;
10383 rc = mParallelPorts[slot]->i_saveSettings(p);
10384 if (FAILED(rc)) return rc;
10385
10386 data.llParallelPorts.push_back(p);
10387 }
10388
10389 /* Audio adapter */
10390 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10391 if (FAILED(rc)) return rc;
10392
10393 rc = i_saveStorageControllers(data.storage);
10394 if (FAILED(rc)) return rc;
10395
10396 /* Shared folders */
10397 data.llSharedFolders.clear();
10398 for (HWData::SharedFolderList::const_iterator
10399 it = mHWData->mSharedFolders.begin();
10400 it != mHWData->mSharedFolders.end();
10401 ++it)
10402 {
10403 SharedFolder *pSF = *it;
10404 AutoCaller sfCaller(pSF);
10405 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10406 settings::SharedFolder sf;
10407 sf.strName = pSF->i_getName();
10408 sf.strHostPath = pSF->i_getHostPath();
10409 sf.fWritable = !!pSF->i_isWritable();
10410 sf.fAutoMount = !!pSF->i_isAutoMounted();
10411 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10412
10413 data.llSharedFolders.push_back(sf);
10414 }
10415
10416 // clipboard
10417 data.clipboardMode = mHWData->mClipboardMode;
10418
10419 // drag'n'drop
10420 data.dndMode = mHWData->mDnDMode;
10421
10422 /* Guest */
10423 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10424
10425 // IO settings
10426 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10427 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10428
10429 /* BandwidthControl (required) */
10430 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10431 if (FAILED(rc)) throw rc;
10432
10433 /* Host PCI devices */
10434 data.pciAttachments.clear();
10435 for (HWData::PCIDeviceAssignmentList::const_iterator
10436 it = mHWData->mPCIDeviceAssignments.begin();
10437 it != mHWData->mPCIDeviceAssignments.end();
10438 ++it)
10439 {
10440 ComObjPtr<PCIDeviceAttachment> pda = *it;
10441 settings::HostPCIDeviceAttachment hpda;
10442
10443 rc = pda->i_saveSettings(hpda);
10444 if (FAILED(rc)) throw rc;
10445
10446 data.pciAttachments.push_back(hpda);
10447 }
10448
10449 // guest properties
10450 data.llGuestProperties.clear();
10451#ifdef VBOX_WITH_GUEST_PROPS
10452 for (HWData::GuestPropertyMap::const_iterator
10453 it = mHWData->mGuestProperties.begin();
10454 it != mHWData->mGuestProperties.end();
10455 ++it)
10456 {
10457 HWData::GuestProperty property = it->second;
10458
10459 /* Remove transient guest properties at shutdown unless we
10460 * are saving state. Note that restoring snapshot intentionally
10461 * keeps them, they will be removed if appropriate once the final
10462 * machine state is set (as crashes etc. need to work). */
10463 if ( ( mData->mMachineState == MachineState_PoweredOff
10464 || mData->mMachineState == MachineState_Aborted
10465 || mData->mMachineState == MachineState_Teleported)
10466 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10467 continue;
10468 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10469 prop.strName = it->first;
10470 prop.strValue = property.strValue;
10471 prop.timestamp = property.mTimestamp;
10472 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10473 GuestPropWriteFlags(property.mFlags, szFlags);
10474 prop.strFlags = szFlags;
10475
10476 data.llGuestProperties.push_back(prop);
10477 }
10478
10479 /* I presume this doesn't require a backup(). */
10480 mData->mGuestPropertiesModified = FALSE;
10481#endif /* VBOX_WITH_GUEST_PROPS defined */
10482
10483 *pDbg = mHWData->mDebugging;
10484 *pAutostart = mHWData->mAutostart;
10485
10486 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10487 }
10488 catch (std::bad_alloc &)
10489 {
10490 return E_OUTOFMEMORY;
10491 }
10492
10493 AssertComRC(rc);
10494 return rc;
10495}
10496
10497/**
10498 * Saves the storage controller configuration.
10499 *
10500 * @param data storage settings.
10501 */
10502HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10503{
10504 data.llStorageControllers.clear();
10505
10506 for (StorageControllerList::const_iterator
10507 it = mStorageControllers->begin();
10508 it != mStorageControllers->end();
10509 ++it)
10510 {
10511 HRESULT rc;
10512 ComObjPtr<StorageController> pCtl = *it;
10513
10514 settings::StorageController ctl;
10515 ctl.strName = pCtl->i_getName();
10516 ctl.controllerType = pCtl->i_getControllerType();
10517 ctl.storageBus = pCtl->i_getStorageBus();
10518 ctl.ulInstance = pCtl->i_getInstance();
10519 ctl.fBootable = pCtl->i_getBootable();
10520
10521 /* Save the port count. */
10522 ULONG portCount;
10523 rc = pCtl->COMGETTER(PortCount)(&portCount);
10524 ComAssertComRCRet(rc, rc);
10525 ctl.ulPortCount = portCount;
10526
10527 /* Save fUseHostIOCache */
10528 BOOL fUseHostIOCache;
10529 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10530 ComAssertComRCRet(rc, rc);
10531 ctl.fUseHostIOCache = !!fUseHostIOCache;
10532
10533 /* save the devices now. */
10534 rc = i_saveStorageDevices(pCtl, ctl);
10535 ComAssertComRCRet(rc, rc);
10536
10537 data.llStorageControllers.push_back(ctl);
10538 }
10539
10540 return S_OK;
10541}
10542
10543/**
10544 * Saves the hard disk configuration.
10545 */
10546HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10547 settings::StorageController &data)
10548{
10549 MediumAttachmentList atts;
10550
10551 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10552 if (FAILED(rc)) return rc;
10553
10554 data.llAttachedDevices.clear();
10555 for (MediumAttachmentList::const_iterator
10556 it = atts.begin();
10557 it != atts.end();
10558 ++it)
10559 {
10560 settings::AttachedDevice dev;
10561 IMediumAttachment *iA = *it;
10562 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10563 Medium *pMedium = pAttach->i_getMedium();
10564
10565 dev.deviceType = pAttach->i_getType();
10566 dev.lPort = pAttach->i_getPort();
10567 dev.lDevice = pAttach->i_getDevice();
10568 dev.fPassThrough = pAttach->i_getPassthrough();
10569 dev.fHotPluggable = pAttach->i_getHotPluggable();
10570 if (pMedium)
10571 {
10572 if (pMedium->i_isHostDrive())
10573 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10574 else
10575 dev.uuid = pMedium->i_getId();
10576 dev.fTempEject = pAttach->i_getTempEject();
10577 dev.fNonRotational = pAttach->i_getNonRotational();
10578 dev.fDiscard = pAttach->i_getDiscard();
10579 }
10580
10581 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10582
10583 data.llAttachedDevices.push_back(dev);
10584 }
10585
10586 return S_OK;
10587}
10588
10589/**
10590 * Saves machine state settings as defined by aFlags
10591 * (SaveSTS_* values).
10592 *
10593 * @param aFlags Combination of SaveSTS_* flags.
10594 *
10595 * @note Locks objects for writing.
10596 */
10597HRESULT Machine::i_saveStateSettings(int aFlags)
10598{
10599 if (aFlags == 0)
10600 return S_OK;
10601
10602 AutoCaller autoCaller(this);
10603 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10604
10605 /* This object's write lock is also necessary to serialize file access
10606 * (prevent concurrent reads and writes) */
10607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10608
10609 HRESULT rc = S_OK;
10610
10611 Assert(mData->pMachineConfigFile);
10612
10613 try
10614 {
10615 if (aFlags & SaveSTS_CurStateModified)
10616 mData->pMachineConfigFile->fCurrentStateModified = true;
10617
10618 if (aFlags & SaveSTS_StateFilePath)
10619 {
10620 if (!mSSData->strStateFilePath.isEmpty())
10621 /* try to make the file name relative to the settings file dir */
10622 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10623 else
10624 mData->pMachineConfigFile->strStateFile.setNull();
10625 }
10626
10627 if (aFlags & SaveSTS_StateTimeStamp)
10628 {
10629 Assert( mData->mMachineState != MachineState_Aborted
10630 || mSSData->strStateFilePath.isEmpty());
10631
10632 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10633
10634 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10635/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10636 }
10637
10638 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10639 }
10640 catch (...)
10641 {
10642 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10643 }
10644
10645 return rc;
10646}
10647
10648/**
10649 * Ensures that the given medium is added to a media registry. If this machine
10650 * was created with 4.0 or later, then the machine registry is used. Otherwise
10651 * the global VirtualBox media registry is used.
10652 *
10653 * Caller must NOT hold machine lock, media tree or any medium locks!
10654 *
10655 * @param pMedium
10656 */
10657void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10658{
10659 /* Paranoia checks: do not hold machine or media tree locks. */
10660 AssertReturnVoid(!isWriteLockOnCurrentThread());
10661 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10662
10663 ComObjPtr<Medium> pBase;
10664 {
10665 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10666 pBase = pMedium->i_getBase();
10667 }
10668
10669 /* Paranoia checks: do not hold medium locks. */
10670 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10671 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10672
10673 // decide which medium registry to use now that the medium is attached:
10674 Guid uuid;
10675 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10676 if (fCanHaveOwnMediaRegistry)
10677 // machine XML is VirtualBox 4.0 or higher:
10678 uuid = i_getId(); // machine UUID
10679 else
10680 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10681
10682 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10683 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10684 if (pMedium->i_addRegistry(uuid))
10685 mParent->i_markRegistryModified(uuid);
10686
10687 /* For more complex hard disk structures it can happen that the base
10688 * medium isn't yet associated with any medium registry. Do that now. */
10689 if (pMedium != pBase)
10690 {
10691 /* Tree lock needed by Medium::addRegistry when recursing. */
10692 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10693 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10694 {
10695 treeLock.release();
10696 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10697 treeLock.acquire();
10698 }
10699 if (pBase->i_addRegistryRecursive(uuid))
10700 {
10701 treeLock.release();
10702 mParent->i_markRegistryModified(uuid);
10703 }
10704 }
10705}
10706
10707/**
10708 * Creates differencing hard disks for all normal hard disks attached to this
10709 * machine and a new set of attachments to refer to created disks.
10710 *
10711 * Used when taking a snapshot or when deleting the current state. Gets called
10712 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10713 *
10714 * This method assumes that mMediumAttachments contains the original hard disk
10715 * attachments it needs to create diffs for. On success, these attachments will
10716 * be replaced with the created diffs.
10717 *
10718 * Attachments with non-normal hard disks are left as is.
10719 *
10720 * If @a aOnline is @c false then the original hard disks that require implicit
10721 * diffs will be locked for reading. Otherwise it is assumed that they are
10722 * already locked for writing (when the VM was started). Note that in the latter
10723 * case it is responsibility of the caller to lock the newly created diffs for
10724 * writing if this method succeeds.
10725 *
10726 * @param aProgress Progress object to run (must contain at least as
10727 * many operations left as the number of hard disks
10728 * attached).
10729 * @param aWeight Weight of this operation.
10730 * @param aOnline Whether the VM was online prior to this operation.
10731 *
10732 * @note The progress object is not marked as completed, neither on success nor
10733 * on failure. This is a responsibility of the caller.
10734 *
10735 * @note Locks this object and the media tree for writing.
10736 */
10737HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10738 ULONG aWeight,
10739 bool aOnline)
10740{
10741 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10742
10743 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10744 AssertReturn(!!pProgressControl, E_INVALIDARG);
10745
10746 AutoCaller autoCaller(this);
10747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10748
10749 AutoMultiWriteLock2 alock(this->lockHandle(),
10750 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10751
10752 /* must be in a protective state because we release the lock below */
10753 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10754 || mData->mMachineState == MachineState_OnlineSnapshotting
10755 || mData->mMachineState == MachineState_LiveSnapshotting
10756 || mData->mMachineState == MachineState_RestoringSnapshot
10757 || mData->mMachineState == MachineState_DeletingSnapshot
10758 , E_FAIL);
10759
10760 HRESULT rc = S_OK;
10761
10762 // use appropriate locked media map (online or offline)
10763 MediumLockListMap lockedMediaOffline;
10764 MediumLockListMap *lockedMediaMap;
10765 if (aOnline)
10766 lockedMediaMap = &mData->mSession.mLockedMedia;
10767 else
10768 lockedMediaMap = &lockedMediaOffline;
10769
10770 try
10771 {
10772 if (!aOnline)
10773 {
10774 /* lock all attached hard disks early to detect "in use"
10775 * situations before creating actual diffs */
10776 for (MediumAttachmentList::const_iterator
10777 it = mMediumAttachments->begin();
10778 it != mMediumAttachments->end();
10779 ++it)
10780 {
10781 MediumAttachment *pAtt = *it;
10782 if (pAtt->i_getType() == DeviceType_HardDisk)
10783 {
10784 Medium *pMedium = pAtt->i_getMedium();
10785 Assert(pMedium);
10786
10787 MediumLockList *pMediumLockList(new MediumLockList());
10788 alock.release();
10789 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10790 NULL /* pToLockWrite */,
10791 false /* fMediumLockWriteAll */,
10792 NULL,
10793 *pMediumLockList);
10794 alock.acquire();
10795 if (FAILED(rc))
10796 {
10797 delete pMediumLockList;
10798 throw rc;
10799 }
10800 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10801 if (FAILED(rc))
10802 {
10803 throw setError(rc,
10804 tr("Collecting locking information for all attached media failed"));
10805 }
10806 }
10807 }
10808
10809 /* Now lock all media. If this fails, nothing is locked. */
10810 alock.release();
10811 rc = lockedMediaMap->Lock();
10812 alock.acquire();
10813 if (FAILED(rc))
10814 {
10815 throw setError(rc,
10816 tr("Locking of attached media failed"));
10817 }
10818 }
10819
10820 /* remember the current list (note that we don't use backup() since
10821 * mMediumAttachments may be already backed up) */
10822 MediumAttachmentList atts = *mMediumAttachments.data();
10823
10824 /* start from scratch */
10825 mMediumAttachments->clear();
10826
10827 /* go through remembered attachments and create diffs for normal hard
10828 * disks and attach them */
10829 for (MediumAttachmentList::const_iterator
10830 it = atts.begin();
10831 it != atts.end();
10832 ++it)
10833 {
10834 MediumAttachment *pAtt = *it;
10835
10836 DeviceType_T devType = pAtt->i_getType();
10837 Medium *pMedium = pAtt->i_getMedium();
10838
10839 if ( devType != DeviceType_HardDisk
10840 || pMedium == NULL
10841 || pMedium->i_getType() != MediumType_Normal)
10842 {
10843 /* copy the attachment as is */
10844
10845 /** @todo the progress object created in SessionMachine::TakeSnaphot
10846 * only expects operations for hard disks. Later other
10847 * device types need to show up in the progress as well. */
10848 if (devType == DeviceType_HardDisk)
10849 {
10850 if (pMedium == NULL)
10851 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10852 aWeight); // weight
10853 else
10854 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10855 pMedium->i_getBase()->i_getName().c_str()).raw(),
10856 aWeight); // weight
10857 }
10858
10859 mMediumAttachments->push_back(pAtt);
10860 continue;
10861 }
10862
10863 /* need a diff */
10864 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10865 pMedium->i_getBase()->i_getName().c_str()).raw(),
10866 aWeight); // weight
10867
10868 Utf8Str strFullSnapshotFolder;
10869 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10870
10871 ComObjPtr<Medium> diff;
10872 diff.createObject();
10873 // store the diff in the same registry as the parent
10874 // (this cannot fail here because we can't create implicit diffs for
10875 // unregistered images)
10876 Guid uuidRegistryParent;
10877 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10878 Assert(fInRegistry); NOREF(fInRegistry);
10879 rc = diff->init(mParent,
10880 pMedium->i_getPreferredDiffFormat(),
10881 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10882 uuidRegistryParent,
10883 DeviceType_HardDisk);
10884 if (FAILED(rc)) throw rc;
10885
10886 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10887 * the push_back? Looks like we're going to release medium with the
10888 * wrong kind of lock (general issue with if we fail anywhere at all)
10889 * and an orphaned VDI in the snapshots folder. */
10890
10891 /* update the appropriate lock list */
10892 MediumLockList *pMediumLockList;
10893 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10894 AssertComRCThrowRC(rc);
10895 if (aOnline)
10896 {
10897 alock.release();
10898 /* The currently attached medium will be read-only, change
10899 * the lock type to read. */
10900 rc = pMediumLockList->Update(pMedium, false);
10901 alock.acquire();
10902 AssertComRCThrowRC(rc);
10903 }
10904
10905 /* release the locks before the potentially lengthy operation */
10906 alock.release();
10907 rc = pMedium->i_createDiffStorage(diff,
10908 pMedium->i_getPreferredDiffVariant(),
10909 pMediumLockList,
10910 NULL /* aProgress */,
10911 true /* aWait */,
10912 false /* aNotify */);
10913 alock.acquire();
10914 if (FAILED(rc)) throw rc;
10915
10916 /* actual lock list update is done in Machine::i_commitMedia */
10917
10918 rc = diff->i_addBackReference(mData->mUuid);
10919 AssertComRCThrowRC(rc);
10920
10921 /* add a new attachment */
10922 ComObjPtr<MediumAttachment> attachment;
10923 attachment.createObject();
10924 rc = attachment->init(this,
10925 diff,
10926 pAtt->i_getControllerName(),
10927 pAtt->i_getPort(),
10928 pAtt->i_getDevice(),
10929 DeviceType_HardDisk,
10930 true /* aImplicit */,
10931 false /* aPassthrough */,
10932 false /* aTempEject */,
10933 pAtt->i_getNonRotational(),
10934 pAtt->i_getDiscard(),
10935 pAtt->i_getHotPluggable(),
10936 pAtt->i_getBandwidthGroup());
10937 if (FAILED(rc)) throw rc;
10938
10939 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10940 AssertComRCThrowRC(rc);
10941 mMediumAttachments->push_back(attachment);
10942 }
10943 }
10944 catch (HRESULT aRC) { rc = aRC; }
10945
10946 /* unlock all hard disks we locked when there is no VM */
10947 if (!aOnline)
10948 {
10949 ErrorInfoKeeper eik;
10950
10951 HRESULT rc1 = lockedMediaMap->Clear();
10952 AssertComRC(rc1);
10953 }
10954
10955 return rc;
10956}
10957
10958/**
10959 * Deletes implicit differencing hard disks created either by
10960 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10961 * mMediumAttachments.
10962 *
10963 * Note that to delete hard disks created by #attachDevice() this method is
10964 * called from #i_rollbackMedia() when the changes are rolled back.
10965 *
10966 * @note Locks this object and the media tree for writing.
10967 */
10968HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10969{
10970 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10971
10972 AutoCaller autoCaller(this);
10973 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10974
10975 AutoMultiWriteLock2 alock(this->lockHandle(),
10976 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10977
10978 /* We absolutely must have backed up state. */
10979 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10980
10981 /* Check if there are any implicitly created diff images. */
10982 bool fImplicitDiffs = false;
10983 for (MediumAttachmentList::const_iterator
10984 it = mMediumAttachments->begin();
10985 it != mMediumAttachments->end();
10986 ++it)
10987 {
10988 const ComObjPtr<MediumAttachment> &pAtt = *it;
10989 if (pAtt->i_isImplicit())
10990 {
10991 fImplicitDiffs = true;
10992 break;
10993 }
10994 }
10995 /* If there is nothing to do, leave early. This saves lots of image locking
10996 * effort. It also avoids a MachineStateChanged event without real reason.
10997 * This is important e.g. when loading a VM config, because there should be
10998 * no events. Otherwise API clients can become thoroughly confused for
10999 * inaccessible VMs (the code for loading VM configs uses this method for
11000 * cleanup if the config makes no sense), as they take such events as an
11001 * indication that the VM is alive, and they would force the VM config to
11002 * be reread, leading to an endless loop. */
11003 if (!fImplicitDiffs)
11004 return S_OK;
11005
11006 HRESULT rc = S_OK;
11007 MachineState_T oldState = mData->mMachineState;
11008
11009 /* will release the lock before the potentially lengthy operation,
11010 * so protect with the special state (unless already protected) */
11011 if ( oldState != MachineState_Snapshotting
11012 && oldState != MachineState_OnlineSnapshotting
11013 && oldState != MachineState_LiveSnapshotting
11014 && oldState != MachineState_RestoringSnapshot
11015 && oldState != MachineState_DeletingSnapshot
11016 && oldState != MachineState_DeletingSnapshotOnline
11017 && oldState != MachineState_DeletingSnapshotPaused
11018 )
11019 i_setMachineState(MachineState_SettingUp);
11020
11021 // use appropriate locked media map (online or offline)
11022 MediumLockListMap lockedMediaOffline;
11023 MediumLockListMap *lockedMediaMap;
11024 if (aOnline)
11025 lockedMediaMap = &mData->mSession.mLockedMedia;
11026 else
11027 lockedMediaMap = &lockedMediaOffline;
11028
11029 try
11030 {
11031 if (!aOnline)
11032 {
11033 /* lock all attached hard disks early to detect "in use"
11034 * situations before deleting actual diffs */
11035 for (MediumAttachmentList::const_iterator
11036 it = mMediumAttachments->begin();
11037 it != mMediumAttachments->end();
11038 ++it)
11039 {
11040 MediumAttachment *pAtt = *it;
11041 if (pAtt->i_getType() == DeviceType_HardDisk)
11042 {
11043 Medium *pMedium = pAtt->i_getMedium();
11044 Assert(pMedium);
11045
11046 MediumLockList *pMediumLockList(new MediumLockList());
11047 alock.release();
11048 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11049 NULL /* pToLockWrite */,
11050 false /* fMediumLockWriteAll */,
11051 NULL,
11052 *pMediumLockList);
11053 alock.acquire();
11054
11055 if (FAILED(rc))
11056 {
11057 delete pMediumLockList;
11058 throw rc;
11059 }
11060
11061 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11062 if (FAILED(rc))
11063 throw rc;
11064 }
11065 }
11066
11067 if (FAILED(rc))
11068 throw rc;
11069 } // end of offline
11070
11071 /* Lock lists are now up to date and include implicitly created media */
11072
11073 /* Go through remembered attachments and delete all implicitly created
11074 * diffs and fix up the attachment information */
11075 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11076 MediumAttachmentList implicitAtts;
11077 for (MediumAttachmentList::const_iterator
11078 it = mMediumAttachments->begin();
11079 it != mMediumAttachments->end();
11080 ++it)
11081 {
11082 ComObjPtr<MediumAttachment> pAtt = *it;
11083 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11084 if (pMedium.isNull())
11085 continue;
11086
11087 // Implicit attachments go on the list for deletion and back references are removed.
11088 if (pAtt->i_isImplicit())
11089 {
11090 /* Deassociate and mark for deletion */
11091 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11092 rc = pMedium->i_removeBackReference(mData->mUuid);
11093 if (FAILED(rc))
11094 throw rc;
11095 implicitAtts.push_back(pAtt);
11096 continue;
11097 }
11098
11099 /* Was this medium attached before? */
11100 if (!i_findAttachment(oldAtts, pMedium))
11101 {
11102 /* no: de-associate */
11103 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11104 rc = pMedium->i_removeBackReference(mData->mUuid);
11105 if (FAILED(rc))
11106 throw rc;
11107 continue;
11108 }
11109 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11110 }
11111
11112 /* If there are implicit attachments to delete, throw away the lock
11113 * map contents (which will unlock all media) since the medium
11114 * attachments will be rolled back. Below we need to completely
11115 * recreate the lock map anyway since it is infinitely complex to
11116 * do this incrementally (would need reconstructing each attachment
11117 * change, which would be extremely hairy). */
11118 if (implicitAtts.size() != 0)
11119 {
11120 ErrorInfoKeeper eik;
11121
11122 HRESULT rc1 = lockedMediaMap->Clear();
11123 AssertComRC(rc1);
11124 }
11125
11126 /* rollback hard disk changes */
11127 mMediumAttachments.rollback();
11128
11129 MultiResult mrc(S_OK);
11130
11131 // Delete unused implicit diffs.
11132 if (implicitAtts.size() != 0)
11133 {
11134 alock.release();
11135
11136 for (MediumAttachmentList::const_iterator
11137 it = implicitAtts.begin();
11138 it != implicitAtts.end();
11139 ++it)
11140 {
11141 // Remove medium associated with this attachment.
11142 ComObjPtr<MediumAttachment> pAtt = *it;
11143 Assert(pAtt);
11144 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11145 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11146 Assert(pMedium);
11147
11148 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11149 // continue on delete failure, just collect error messages
11150 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11151 pMedium->i_getLocationFull().c_str() ));
11152 mrc = rc;
11153 }
11154 // Clear the list of deleted implicit attachments now, while not
11155 // holding the lock, as it will ultimately trigger Medium::uninit()
11156 // calls which assume that the media tree lock isn't held.
11157 implicitAtts.clear();
11158
11159 alock.acquire();
11160
11161 /* if there is a VM recreate media lock map as mentioned above,
11162 * otherwise it is a waste of time and we leave things unlocked */
11163 if (aOnline)
11164 {
11165 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11166 /* must never be NULL, but better safe than sorry */
11167 if (!pMachine.isNull())
11168 {
11169 alock.release();
11170 rc = mData->mSession.mMachine->i_lockMedia();
11171 alock.acquire();
11172 if (FAILED(rc))
11173 throw rc;
11174 }
11175 }
11176 }
11177 }
11178 catch (HRESULT aRC) {rc = aRC;}
11179
11180 if (mData->mMachineState == MachineState_SettingUp)
11181 i_setMachineState(oldState);
11182
11183 /* unlock all hard disks we locked when there is no VM */
11184 if (!aOnline)
11185 {
11186 ErrorInfoKeeper eik;
11187
11188 HRESULT rc1 = lockedMediaMap->Clear();
11189 AssertComRC(rc1);
11190 }
11191
11192 return rc;
11193}
11194
11195
11196/**
11197 * Looks through the given list of media attachments for one with the given parameters
11198 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11199 * can be searched as well if needed.
11200 *
11201 * @param ll
11202 * @param aControllerName
11203 * @param aControllerPort
11204 * @param aDevice
11205 * @return
11206 */
11207MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11208 const Utf8Str &aControllerName,
11209 LONG aControllerPort,
11210 LONG aDevice)
11211{
11212 for (MediumAttachmentList::const_iterator
11213 it = ll.begin();
11214 it != ll.end();
11215 ++it)
11216 {
11217 MediumAttachment *pAttach = *it;
11218 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11219 return pAttach;
11220 }
11221
11222 return NULL;
11223}
11224
11225/**
11226 * Looks through the given list of media attachments for one with the given parameters
11227 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11228 * can be searched as well if needed.
11229 *
11230 * @param ll
11231 * @param pMedium
11232 * @return
11233 */
11234MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11235 ComObjPtr<Medium> pMedium)
11236{
11237 for (MediumAttachmentList::const_iterator
11238 it = ll.begin();
11239 it != ll.end();
11240 ++it)
11241 {
11242 MediumAttachment *pAttach = *it;
11243 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11244 if (pMediumThis == pMedium)
11245 return pAttach;
11246 }
11247
11248 return NULL;
11249}
11250
11251/**
11252 * Looks through the given list of media attachments for one with the given parameters
11253 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11254 * can be searched as well if needed.
11255 *
11256 * @param ll
11257 * @param id
11258 * @return
11259 */
11260MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11261 Guid &id)
11262{
11263 for (MediumAttachmentList::const_iterator
11264 it = ll.begin();
11265 it != ll.end();
11266 ++it)
11267 {
11268 MediumAttachment *pAttach = *it;
11269 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11270 if (pMediumThis->i_getId() == id)
11271 return pAttach;
11272 }
11273
11274 return NULL;
11275}
11276
11277/**
11278 * Main implementation for Machine::DetachDevice. This also gets called
11279 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11280 *
11281 * @param pAttach Medium attachment to detach.
11282 * @param writeLock Machine write lock which the caller must have locked once.
11283 * This may be released temporarily in here.
11284 * @param pSnapshot If NULL, then the detachment is for the current machine.
11285 * Otherwise this is for a SnapshotMachine, and this must be
11286 * its snapshot.
11287 * @return
11288 */
11289HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11290 AutoWriteLock &writeLock,
11291 Snapshot *pSnapshot)
11292{
11293 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11294 DeviceType_T mediumType = pAttach->i_getType();
11295
11296 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11297
11298 if (pAttach->i_isImplicit())
11299 {
11300 /* attempt to implicitly delete the implicitly created diff */
11301
11302 /// @todo move the implicit flag from MediumAttachment to Medium
11303 /// and forbid any hard disk operation when it is implicit. Or maybe
11304 /// a special media state for it to make it even more simple.
11305
11306 Assert(mMediumAttachments.isBackedUp());
11307
11308 /* will release the lock before the potentially lengthy operation, so
11309 * protect with the special state */
11310 MachineState_T oldState = mData->mMachineState;
11311 i_setMachineState(MachineState_SettingUp);
11312
11313 writeLock.release();
11314
11315 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11316 true /*aWait*/,
11317 false /*aNotify*/);
11318
11319 writeLock.acquire();
11320
11321 i_setMachineState(oldState);
11322
11323 if (FAILED(rc)) return rc;
11324 }
11325
11326 i_setModified(IsModified_Storage);
11327 mMediumAttachments.backup();
11328 mMediumAttachments->remove(pAttach);
11329
11330 if (!oldmedium.isNull())
11331 {
11332 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11333 if (pSnapshot)
11334 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11335 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11336 else if (mediumType != DeviceType_HardDisk)
11337 oldmedium->i_removeBackReference(mData->mUuid);
11338 }
11339
11340 return S_OK;
11341}
11342
11343/**
11344 * Goes thru all media of the given list and
11345 *
11346 * 1) calls i_detachDevice() on each of them for this machine and
11347 * 2) adds all Medium objects found in the process to the given list,
11348 * depending on cleanupMode.
11349 *
11350 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11351 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11352 * media to the list.
11353 *
11354 * This gets called from Machine::Unregister, both for the actual Machine and
11355 * the SnapshotMachine objects that might be found in the snapshots.
11356 *
11357 * Requires caller and locking. The machine lock must be passed in because it
11358 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11359 *
11360 * @param writeLock Machine lock from top-level caller; this gets passed to
11361 * i_detachDevice.
11362 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11363 * object if called for a SnapshotMachine.
11364 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11365 * added to llMedia; if Full, then all media get added;
11366 * otherwise no media get added.
11367 * @param llMedia Caller's list to receive Medium objects which got detached so
11368 * caller can close() them, depending on cleanupMode.
11369 * @return
11370 */
11371HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11372 Snapshot *pSnapshot,
11373 CleanupMode_T cleanupMode,
11374 MediaList &llMedia)
11375{
11376 Assert(isWriteLockOnCurrentThread());
11377
11378 HRESULT rc;
11379
11380 // make a temporary list because i_detachDevice invalidates iterators into
11381 // mMediumAttachments
11382 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11383
11384 for (MediumAttachmentList::iterator
11385 it = llAttachments2.begin();
11386 it != llAttachments2.end();
11387 ++it)
11388 {
11389 ComObjPtr<MediumAttachment> &pAttach = *it;
11390 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11391
11392 if (!pMedium.isNull())
11393 {
11394 AutoCaller mac(pMedium);
11395 if (FAILED(mac.rc())) return mac.rc();
11396 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11397 DeviceType_T devType = pMedium->i_getDeviceType();
11398 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11399 && devType == DeviceType_HardDisk)
11400 || (cleanupMode == CleanupMode_Full)
11401 )
11402 {
11403 llMedia.push_back(pMedium);
11404 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11405 /* Not allowed to keep this lock as below we need the parent
11406 * medium lock, and the lock order is parent to child. */
11407 lock.release();
11408 /*
11409 * Search for medias which are not attached to any machine, but
11410 * in the chain to an attached disk. Mediums are only consided
11411 * if they are:
11412 * - have only one child
11413 * - no references to any machines
11414 * - are of normal medium type
11415 */
11416 while (!pParent.isNull())
11417 {
11418 AutoCaller mac1(pParent);
11419 if (FAILED(mac1.rc())) return mac1.rc();
11420 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11421 if (pParent->i_getChildren().size() == 1)
11422 {
11423 if ( pParent->i_getMachineBackRefCount() == 0
11424 && pParent->i_getType() == MediumType_Normal
11425 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11426 llMedia.push_back(pParent);
11427 }
11428 else
11429 break;
11430 pParent = pParent->i_getParent();
11431 }
11432 }
11433 }
11434
11435 // real machine: then we need to use the proper method
11436 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11437
11438 if (FAILED(rc))
11439 return rc;
11440 }
11441
11442 return S_OK;
11443}
11444
11445/**
11446 * Perform deferred hard disk detachments.
11447 *
11448 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11449 * changed (not backed up).
11450 *
11451 * If @a aOnline is @c true then this method will also unlock the old hard
11452 * disks for which the new implicit diffs were created and will lock these new
11453 * diffs for writing.
11454 *
11455 * @param aOnline Whether the VM was online prior to this operation.
11456 *
11457 * @note Locks this object for writing!
11458 */
11459void Machine::i_commitMedia(bool aOnline /*= false*/)
11460{
11461 AutoCaller autoCaller(this);
11462 AssertComRCReturnVoid(autoCaller.rc());
11463
11464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11465
11466 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11467
11468 HRESULT rc = S_OK;
11469
11470 /* no attach/detach operations -- nothing to do */
11471 if (!mMediumAttachments.isBackedUp())
11472 return;
11473
11474 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11475 bool fMediaNeedsLocking = false;
11476
11477 /* enumerate new attachments */
11478 for (MediumAttachmentList::const_iterator
11479 it = mMediumAttachments->begin();
11480 it != mMediumAttachments->end();
11481 ++it)
11482 {
11483 MediumAttachment *pAttach = *it;
11484
11485 pAttach->i_commit();
11486
11487 Medium *pMedium = pAttach->i_getMedium();
11488 bool fImplicit = pAttach->i_isImplicit();
11489
11490 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11491 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11492 fImplicit));
11493
11494 /** @todo convert all this Machine-based voodoo to MediumAttachment
11495 * based commit logic. */
11496 if (fImplicit)
11497 {
11498 /* convert implicit attachment to normal */
11499 pAttach->i_setImplicit(false);
11500
11501 if ( aOnline
11502 && pMedium
11503 && pAttach->i_getType() == DeviceType_HardDisk
11504 )
11505 {
11506 /* update the appropriate lock list */
11507 MediumLockList *pMediumLockList;
11508 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11509 AssertComRC(rc);
11510 if (pMediumLockList)
11511 {
11512 /* unlock if there's a need to change the locking */
11513 if (!fMediaNeedsLocking)
11514 {
11515 rc = mData->mSession.mLockedMedia.Unlock();
11516 AssertComRC(rc);
11517 fMediaNeedsLocking = true;
11518 }
11519 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11520 AssertComRC(rc);
11521 rc = pMediumLockList->Append(pMedium, true);
11522 AssertComRC(rc);
11523 }
11524 }
11525
11526 continue;
11527 }
11528
11529 if (pMedium)
11530 {
11531 /* was this medium attached before? */
11532 for (MediumAttachmentList::iterator
11533 oldIt = oldAtts.begin();
11534 oldIt != oldAtts.end();
11535 ++oldIt)
11536 {
11537 MediumAttachment *pOldAttach = *oldIt;
11538 if (pOldAttach->i_getMedium() == pMedium)
11539 {
11540 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11541
11542 /* yes: remove from old to avoid de-association */
11543 oldAtts.erase(oldIt);
11544 break;
11545 }
11546 }
11547 }
11548 }
11549
11550 /* enumerate remaining old attachments and de-associate from the
11551 * current machine state */
11552 for (MediumAttachmentList::const_iterator
11553 it = oldAtts.begin();
11554 it != oldAtts.end();
11555 ++it)
11556 {
11557 MediumAttachment *pAttach = *it;
11558 Medium *pMedium = pAttach->i_getMedium();
11559
11560 /* Detach only hard disks, since DVD/floppy media is detached
11561 * instantly in MountMedium. */
11562 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11563 {
11564 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11565
11566 /* now de-associate from the current machine state */
11567 rc = pMedium->i_removeBackReference(mData->mUuid);
11568 AssertComRC(rc);
11569
11570 if (aOnline)
11571 {
11572 /* unlock since medium is not used anymore */
11573 MediumLockList *pMediumLockList;
11574 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11575 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11576 {
11577 /* this happens for online snapshots, there the attachment
11578 * is changing, but only to a diff image created under
11579 * the old one, so there is no separate lock list */
11580 Assert(!pMediumLockList);
11581 }
11582 else
11583 {
11584 AssertComRC(rc);
11585 if (pMediumLockList)
11586 {
11587 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11588 AssertComRC(rc);
11589 }
11590 }
11591 }
11592 }
11593 }
11594
11595 /* take media locks again so that the locking state is consistent */
11596 if (fMediaNeedsLocking)
11597 {
11598 Assert(aOnline);
11599 rc = mData->mSession.mLockedMedia.Lock();
11600 AssertComRC(rc);
11601 }
11602
11603 /* commit the hard disk changes */
11604 mMediumAttachments.commit();
11605
11606 if (i_isSessionMachine())
11607 {
11608 /*
11609 * Update the parent machine to point to the new owner.
11610 * This is necessary because the stored parent will point to the
11611 * session machine otherwise and cause crashes or errors later
11612 * when the session machine gets invalid.
11613 */
11614 /** @todo Change the MediumAttachment class to behave like any other
11615 * class in this regard by creating peer MediumAttachment
11616 * objects for session machines and share the data with the peer
11617 * machine.
11618 */
11619 for (MediumAttachmentList::const_iterator
11620 it = mMediumAttachments->begin();
11621 it != mMediumAttachments->end();
11622 ++it)
11623 (*it)->i_updateParentMachine(mPeer);
11624
11625 /* attach new data to the primary machine and reshare it */
11626 mPeer->mMediumAttachments.attach(mMediumAttachments);
11627 }
11628
11629 return;
11630}
11631
11632/**
11633 * Perform deferred deletion of implicitly created diffs.
11634 *
11635 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11636 * changed (not backed up).
11637 *
11638 * @note Locks this object for writing!
11639 */
11640void Machine::i_rollbackMedia()
11641{
11642 AutoCaller autoCaller(this);
11643 AssertComRCReturnVoid(autoCaller.rc());
11644
11645 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11646 LogFlowThisFunc(("Entering rollbackMedia\n"));
11647
11648 HRESULT rc = S_OK;
11649
11650 /* no attach/detach operations -- nothing to do */
11651 if (!mMediumAttachments.isBackedUp())
11652 return;
11653
11654 /* enumerate new attachments */
11655 for (MediumAttachmentList::const_iterator
11656 it = mMediumAttachments->begin();
11657 it != mMediumAttachments->end();
11658 ++it)
11659 {
11660 MediumAttachment *pAttach = *it;
11661 /* Fix up the backrefs for DVD/floppy media. */
11662 if (pAttach->i_getType() != DeviceType_HardDisk)
11663 {
11664 Medium *pMedium = pAttach->i_getMedium();
11665 if (pMedium)
11666 {
11667 rc = pMedium->i_removeBackReference(mData->mUuid);
11668 AssertComRC(rc);
11669 }
11670 }
11671
11672 (*it)->i_rollback();
11673
11674 pAttach = *it;
11675 /* Fix up the backrefs for DVD/floppy media. */
11676 if (pAttach->i_getType() != DeviceType_HardDisk)
11677 {
11678 Medium *pMedium = pAttach->i_getMedium();
11679 if (pMedium)
11680 {
11681 rc = pMedium->i_addBackReference(mData->mUuid);
11682 AssertComRC(rc);
11683 }
11684 }
11685 }
11686
11687 /** @todo convert all this Machine-based voodoo to MediumAttachment
11688 * based rollback logic. */
11689 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11690
11691 return;
11692}
11693
11694/**
11695 * Returns true if the settings file is located in the directory named exactly
11696 * as the machine; this means, among other things, that the machine directory
11697 * should be auto-renamed.
11698 *
11699 * @param aSettingsDir if not NULL, the full machine settings file directory
11700 * name will be assigned there.
11701 *
11702 * @note Doesn't lock anything.
11703 * @note Not thread safe (must be called from this object's lock).
11704 */
11705bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11706{
11707 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11708 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11709 if (aSettingsDir)
11710 *aSettingsDir = strMachineDirName;
11711 strMachineDirName.stripPath(); // vmname
11712 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11713 strConfigFileOnly.stripPath() // vmname.vbox
11714 .stripSuffix(); // vmname
11715 /** @todo hack, make somehow use of ComposeMachineFilename */
11716 if (mUserData->s.fDirectoryIncludesUUID)
11717 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11718
11719 AssertReturn(!strMachineDirName.isEmpty(), false);
11720 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11721
11722 return strMachineDirName == strConfigFileOnly;
11723}
11724
11725/**
11726 * Discards all changes to machine settings.
11727 *
11728 * @param aNotify Whether to notify the direct session about changes or not.
11729 *
11730 * @note Locks objects for writing!
11731 */
11732void Machine::i_rollback(bool aNotify)
11733{
11734 AutoCaller autoCaller(this);
11735 AssertComRCReturn(autoCaller.rc(), (void)0);
11736
11737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11738
11739 if (!mStorageControllers.isNull())
11740 {
11741 if (mStorageControllers.isBackedUp())
11742 {
11743 /* unitialize all new devices (absent in the backed up list). */
11744 StorageControllerList *backedList = mStorageControllers.backedUpData();
11745 for (StorageControllerList::const_iterator
11746 it = mStorageControllers->begin();
11747 it != mStorageControllers->end();
11748 ++it)
11749 {
11750 if ( std::find(backedList->begin(), backedList->end(), *it)
11751 == backedList->end()
11752 )
11753 {
11754 (*it)->uninit();
11755 }
11756 }
11757
11758 /* restore the list */
11759 mStorageControllers.rollback();
11760 }
11761
11762 /* rollback any changes to devices after restoring the list */
11763 if (mData->flModifications & IsModified_Storage)
11764 {
11765 for (StorageControllerList::const_iterator
11766 it = mStorageControllers->begin();
11767 it != mStorageControllers->end();
11768 ++it)
11769 {
11770 (*it)->i_rollback();
11771 }
11772 }
11773 }
11774
11775 if (!mUSBControllers.isNull())
11776 {
11777 if (mUSBControllers.isBackedUp())
11778 {
11779 /* unitialize all new devices (absent in the backed up list). */
11780 USBControllerList *backedList = mUSBControllers.backedUpData();
11781 for (USBControllerList::const_iterator
11782 it = mUSBControllers->begin();
11783 it != mUSBControllers->end();
11784 ++it)
11785 {
11786 if ( std::find(backedList->begin(), backedList->end(), *it)
11787 == backedList->end()
11788 )
11789 {
11790 (*it)->uninit();
11791 }
11792 }
11793
11794 /* restore the list */
11795 mUSBControllers.rollback();
11796 }
11797
11798 /* rollback any changes to devices after restoring the list */
11799 if (mData->flModifications & IsModified_USB)
11800 {
11801 for (USBControllerList::const_iterator
11802 it = mUSBControllers->begin();
11803 it != mUSBControllers->end();
11804 ++it)
11805 {
11806 (*it)->i_rollback();
11807 }
11808 }
11809 }
11810
11811 mUserData.rollback();
11812
11813 mHWData.rollback();
11814
11815 if (mData->flModifications & IsModified_Storage)
11816 i_rollbackMedia();
11817
11818 if (mBIOSSettings)
11819 mBIOSSettings->i_rollback();
11820
11821 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11822 mRecordingSettings->i_rollback();
11823
11824 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11825 mVRDEServer->i_rollback();
11826
11827 if (mAudioAdapter)
11828 mAudioAdapter->i_rollback();
11829
11830 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11831 mUSBDeviceFilters->i_rollback();
11832
11833 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11834 mBandwidthControl->i_rollback();
11835
11836 if (!mHWData.isNull())
11837 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11838 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11839 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11840 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11841
11842 if (mData->flModifications & IsModified_NetworkAdapters)
11843 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11844 if ( mNetworkAdapters[slot]
11845 && mNetworkAdapters[slot]->i_isModified())
11846 {
11847 mNetworkAdapters[slot]->i_rollback();
11848 networkAdapters[slot] = mNetworkAdapters[slot];
11849 }
11850
11851 if (mData->flModifications & IsModified_SerialPorts)
11852 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11853 if ( mSerialPorts[slot]
11854 && mSerialPorts[slot]->i_isModified())
11855 {
11856 mSerialPorts[slot]->i_rollback();
11857 serialPorts[slot] = mSerialPorts[slot];
11858 }
11859
11860 if (mData->flModifications & IsModified_ParallelPorts)
11861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11862 if ( mParallelPorts[slot]
11863 && mParallelPorts[slot]->i_isModified())
11864 {
11865 mParallelPorts[slot]->i_rollback();
11866 parallelPorts[slot] = mParallelPorts[slot];
11867 }
11868
11869 if (aNotify)
11870 {
11871 /* inform the direct session about changes */
11872
11873 ComObjPtr<Machine> that = this;
11874 uint32_t flModifications = mData->flModifications;
11875 alock.release();
11876
11877 if (flModifications & IsModified_SharedFolders)
11878 that->i_onSharedFolderChange();
11879
11880 if (flModifications & IsModified_VRDEServer)
11881 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11882 if (flModifications & IsModified_USB)
11883 that->i_onUSBControllerChange();
11884
11885 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11886 if (networkAdapters[slot])
11887 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11889 if (serialPorts[slot])
11890 that->i_onSerialPortChange(serialPorts[slot]);
11891 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11892 if (parallelPorts[slot])
11893 that->i_onParallelPortChange(parallelPorts[slot]);
11894
11895 if (flModifications & IsModified_Storage)
11896 {
11897 for (StorageControllerList::const_iterator
11898 it = mStorageControllers->begin();
11899 it != mStorageControllers->end();
11900 ++it)
11901 {
11902 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11903 }
11904 }
11905
11906
11907#if 0
11908 if (flModifications & IsModified_BandwidthControl)
11909 that->onBandwidthControlChange();
11910#endif
11911 }
11912}
11913
11914/**
11915 * Commits all the changes to machine settings.
11916 *
11917 * Note that this operation is supposed to never fail.
11918 *
11919 * @note Locks this object and children for writing.
11920 */
11921void Machine::i_commit()
11922{
11923 AutoCaller autoCaller(this);
11924 AssertComRCReturnVoid(autoCaller.rc());
11925
11926 AutoCaller peerCaller(mPeer);
11927 AssertComRCReturnVoid(peerCaller.rc());
11928
11929 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11930
11931 /*
11932 * use safe commit to ensure Snapshot machines (that share mUserData)
11933 * will still refer to a valid memory location
11934 */
11935 mUserData.commitCopy();
11936
11937 mHWData.commit();
11938
11939 if (mMediumAttachments.isBackedUp())
11940 i_commitMedia(Global::IsOnline(mData->mMachineState));
11941
11942 mBIOSSettings->i_commit();
11943 mRecordingSettings->i_commit();
11944 mVRDEServer->i_commit();
11945 mAudioAdapter->i_commit();
11946 mUSBDeviceFilters->i_commit();
11947 mBandwidthControl->i_commit();
11948
11949 /* Since mNetworkAdapters is a list which might have been changed (resized)
11950 * without using the Backupable<> template we need to handle the copying
11951 * of the list entries manually, including the creation of peers for the
11952 * new objects. */
11953 bool commitNetworkAdapters = false;
11954 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11955 if (mPeer)
11956 {
11957 /* commit everything, even the ones which will go away */
11958 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11959 mNetworkAdapters[slot]->i_commit();
11960 /* copy over the new entries, creating a peer and uninit the original */
11961 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11962 for (size_t slot = 0; slot < newSize; slot++)
11963 {
11964 /* look if this adapter has a peer device */
11965 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11966 if (!peer)
11967 {
11968 /* no peer means the adapter is a newly created one;
11969 * create a peer owning data this data share it with */
11970 peer.createObject();
11971 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11972 }
11973 mPeer->mNetworkAdapters[slot] = peer;
11974 }
11975 /* uninit any no longer needed network adapters */
11976 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11977 mNetworkAdapters[slot]->uninit();
11978 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11979 {
11980 if (mPeer->mNetworkAdapters[slot])
11981 mPeer->mNetworkAdapters[slot]->uninit();
11982 }
11983 /* Keep the original network adapter count until this point, so that
11984 * discarding a chipset type change will not lose settings. */
11985 mNetworkAdapters.resize(newSize);
11986 mPeer->mNetworkAdapters.resize(newSize);
11987 }
11988 else
11989 {
11990 /* we have no peer (our parent is the newly created machine);
11991 * just commit changes to the network adapters */
11992 commitNetworkAdapters = true;
11993 }
11994 if (commitNetworkAdapters)
11995 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11996 mNetworkAdapters[slot]->i_commit();
11997
11998 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11999 mSerialPorts[slot]->i_commit();
12000 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12001 mParallelPorts[slot]->i_commit();
12002
12003 bool commitStorageControllers = false;
12004
12005 if (mStorageControllers.isBackedUp())
12006 {
12007 mStorageControllers.commit();
12008
12009 if (mPeer)
12010 {
12011 /* Commit all changes to new controllers (this will reshare data with
12012 * peers for those who have peers) */
12013 StorageControllerList *newList = new StorageControllerList();
12014 for (StorageControllerList::const_iterator
12015 it = mStorageControllers->begin();
12016 it != mStorageControllers->end();
12017 ++it)
12018 {
12019 (*it)->i_commit();
12020
12021 /* look if this controller has a peer device */
12022 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12023 if (!peer)
12024 {
12025 /* no peer means the device is a newly created one;
12026 * create a peer owning data this device share it with */
12027 peer.createObject();
12028 peer->init(mPeer, *it, true /* aReshare */);
12029 }
12030 else
12031 {
12032 /* remove peer from the old list */
12033 mPeer->mStorageControllers->remove(peer);
12034 }
12035 /* and add it to the new list */
12036 newList->push_back(peer);
12037 }
12038
12039 /* uninit old peer's controllers that are left */
12040 for (StorageControllerList::const_iterator
12041 it = mPeer->mStorageControllers->begin();
12042 it != mPeer->mStorageControllers->end();
12043 ++it)
12044 {
12045 (*it)->uninit();
12046 }
12047
12048 /* attach new list of controllers to our peer */
12049 mPeer->mStorageControllers.attach(newList);
12050 }
12051 else
12052 {
12053 /* we have no peer (our parent is the newly created machine);
12054 * just commit changes to devices */
12055 commitStorageControllers = true;
12056 }
12057 }
12058 else
12059 {
12060 /* the list of controllers itself is not changed,
12061 * just commit changes to controllers themselves */
12062 commitStorageControllers = true;
12063 }
12064
12065 if (commitStorageControllers)
12066 {
12067 for (StorageControllerList::const_iterator
12068 it = mStorageControllers->begin();
12069 it != mStorageControllers->end();
12070 ++it)
12071 {
12072 (*it)->i_commit();
12073 }
12074 }
12075
12076 bool commitUSBControllers = false;
12077
12078 if (mUSBControllers.isBackedUp())
12079 {
12080 mUSBControllers.commit();
12081
12082 if (mPeer)
12083 {
12084 /* Commit all changes to new controllers (this will reshare data with
12085 * peers for those who have peers) */
12086 USBControllerList *newList = new USBControllerList();
12087 for (USBControllerList::const_iterator
12088 it = mUSBControllers->begin();
12089 it != mUSBControllers->end();
12090 ++it)
12091 {
12092 (*it)->i_commit();
12093
12094 /* look if this controller has a peer device */
12095 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12096 if (!peer)
12097 {
12098 /* no peer means the device is a newly created one;
12099 * create a peer owning data this device share it with */
12100 peer.createObject();
12101 peer->init(mPeer, *it, true /* aReshare */);
12102 }
12103 else
12104 {
12105 /* remove peer from the old list */
12106 mPeer->mUSBControllers->remove(peer);
12107 }
12108 /* and add it to the new list */
12109 newList->push_back(peer);
12110 }
12111
12112 /* uninit old peer's controllers that are left */
12113 for (USBControllerList::const_iterator
12114 it = mPeer->mUSBControllers->begin();
12115 it != mPeer->mUSBControllers->end();
12116 ++it)
12117 {
12118 (*it)->uninit();
12119 }
12120
12121 /* attach new list of controllers to our peer */
12122 mPeer->mUSBControllers.attach(newList);
12123 }
12124 else
12125 {
12126 /* we have no peer (our parent is the newly created machine);
12127 * just commit changes to devices */
12128 commitUSBControllers = true;
12129 }
12130 }
12131 else
12132 {
12133 /* the list of controllers itself is not changed,
12134 * just commit changes to controllers themselves */
12135 commitUSBControllers = true;
12136 }
12137
12138 if (commitUSBControllers)
12139 {
12140 for (USBControllerList::const_iterator
12141 it = mUSBControllers->begin();
12142 it != mUSBControllers->end();
12143 ++it)
12144 {
12145 (*it)->i_commit();
12146 }
12147 }
12148
12149 if (i_isSessionMachine())
12150 {
12151 /* attach new data to the primary machine and reshare it */
12152 mPeer->mUserData.attach(mUserData);
12153 mPeer->mHWData.attach(mHWData);
12154 /* mmMediumAttachments is reshared by fixupMedia */
12155 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12156 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12157 }
12158}
12159
12160/**
12161 * Copies all the hardware data from the given machine.
12162 *
12163 * Currently, only called when the VM is being restored from a snapshot. In
12164 * particular, this implies that the VM is not running during this method's
12165 * call.
12166 *
12167 * @note This method must be called from under this object's lock.
12168 *
12169 * @note This method doesn't call #i_commit(), so all data remains backed up and
12170 * unsaved.
12171 */
12172void Machine::i_copyFrom(Machine *aThat)
12173{
12174 AssertReturnVoid(!i_isSnapshotMachine());
12175 AssertReturnVoid(aThat->i_isSnapshotMachine());
12176
12177 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12178
12179 mHWData.assignCopy(aThat->mHWData);
12180
12181 // create copies of all shared folders (mHWData after attaching a copy
12182 // contains just references to original objects)
12183 for (HWData::SharedFolderList::iterator
12184 it = mHWData->mSharedFolders.begin();
12185 it != mHWData->mSharedFolders.end();
12186 ++it)
12187 {
12188 ComObjPtr<SharedFolder> folder;
12189 folder.createObject();
12190 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12191 AssertComRC(rc);
12192 *it = folder;
12193 }
12194
12195 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12196 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12197 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12198 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12199 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12200 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12201
12202 /* create private copies of all controllers */
12203 mStorageControllers.backup();
12204 mStorageControllers->clear();
12205 for (StorageControllerList::const_iterator
12206 it = aThat->mStorageControllers->begin();
12207 it != aThat->mStorageControllers->end();
12208 ++it)
12209 {
12210 ComObjPtr<StorageController> ctrl;
12211 ctrl.createObject();
12212 ctrl->initCopy(this, *it);
12213 mStorageControllers->push_back(ctrl);
12214 }
12215
12216 /* create private copies of all USB controllers */
12217 mUSBControllers.backup();
12218 mUSBControllers->clear();
12219 for (USBControllerList::const_iterator
12220 it = aThat->mUSBControllers->begin();
12221 it != aThat->mUSBControllers->end();
12222 ++it)
12223 {
12224 ComObjPtr<USBController> ctrl;
12225 ctrl.createObject();
12226 ctrl->initCopy(this, *it);
12227 mUSBControllers->push_back(ctrl);
12228 }
12229
12230 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12231 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12232 {
12233 if (mNetworkAdapters[slot].isNotNull())
12234 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12235 else
12236 {
12237 unconst(mNetworkAdapters[slot]).createObject();
12238 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12239 }
12240 }
12241 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12242 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12243 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12244 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12245}
12246
12247/**
12248 * Returns whether the given storage controller is hotplug capable.
12249 *
12250 * @returns true if the controller supports hotplugging
12251 * false otherwise.
12252 * @param enmCtrlType The controller type to check for.
12253 */
12254bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12255{
12256 ComPtr<ISystemProperties> systemProperties;
12257 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12258 if (FAILED(rc))
12259 return false;
12260
12261 BOOL aHotplugCapable = FALSE;
12262 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12263
12264 return RT_BOOL(aHotplugCapable);
12265}
12266
12267#ifdef VBOX_WITH_RESOURCE_USAGE_API
12268
12269void Machine::i_getDiskList(MediaList &list)
12270{
12271 for (MediumAttachmentList::const_iterator
12272 it = mMediumAttachments->begin();
12273 it != mMediumAttachments->end();
12274 ++it)
12275 {
12276 MediumAttachment *pAttach = *it;
12277 /* just in case */
12278 AssertContinue(pAttach);
12279
12280 AutoCaller localAutoCallerA(pAttach);
12281 if (FAILED(localAutoCallerA.rc())) continue;
12282
12283 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12284
12285 if (pAttach->i_getType() == DeviceType_HardDisk)
12286 list.push_back(pAttach->i_getMedium());
12287 }
12288}
12289
12290void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12291{
12292 AssertReturnVoid(isWriteLockOnCurrentThread());
12293 AssertPtrReturnVoid(aCollector);
12294
12295 pm::CollectorHAL *hal = aCollector->getHAL();
12296 /* Create sub metrics */
12297 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12298 "Percentage of processor time spent in user mode by the VM process.");
12299 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12300 "Percentage of processor time spent in kernel mode by the VM process.");
12301 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12302 "Size of resident portion of VM process in memory.");
12303 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12304 "Actual size of all VM disks combined.");
12305 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12306 "Network receive rate.");
12307 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12308 "Network transmit rate.");
12309 /* Create and register base metrics */
12310 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12311 cpuLoadUser, cpuLoadKernel);
12312 aCollector->registerBaseMetric(cpuLoad);
12313 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12314 ramUsageUsed);
12315 aCollector->registerBaseMetric(ramUsage);
12316 MediaList disks;
12317 i_getDiskList(disks);
12318 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12319 diskUsageUsed);
12320 aCollector->registerBaseMetric(diskUsage);
12321
12322 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12323 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12324 new pm::AggregateAvg()));
12325 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12326 new pm::AggregateMin()));
12327 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12328 new pm::AggregateMax()));
12329 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12330 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12331 new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12333 new pm::AggregateMin()));
12334 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12335 new pm::AggregateMax()));
12336
12337 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12338 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12339 new pm::AggregateAvg()));
12340 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12341 new pm::AggregateMin()));
12342 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12343 new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12346 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12347 new pm::AggregateAvg()));
12348 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12349 new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12351 new pm::AggregateMax()));
12352
12353
12354 /* Guest metrics collector */
12355 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12356 aCollector->registerGuest(mCollectorGuest);
12357 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12358
12359 /* Create sub metrics */
12360 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12361 "Percentage of processor time spent in user mode as seen by the guest.");
12362 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12363 "Percentage of processor time spent in kernel mode as seen by the guest.");
12364 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12365 "Percentage of processor time spent idling as seen by the guest.");
12366
12367 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12368 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12369 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12370 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12371 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12372 pm::SubMetric *guestMemCache = new pm::SubMetric(
12373 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12374
12375 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12376 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12377
12378 /* Create and register base metrics */
12379 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12380 machineNetRx, machineNetTx);
12381 aCollector->registerBaseMetric(machineNetRate);
12382
12383 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12384 guestLoadUser, guestLoadKernel, guestLoadIdle);
12385 aCollector->registerBaseMetric(guestCpuLoad);
12386
12387 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12388 guestMemTotal, guestMemFree,
12389 guestMemBalloon, guestMemShared,
12390 guestMemCache, guestPagedTotal);
12391 aCollector->registerBaseMetric(guestCpuMem);
12392
12393 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12394 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12395 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12396 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12397
12398 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12399 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12400 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12402
12403 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12404 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12405 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12406 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12407
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12409 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12410 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12411 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12412
12413 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12414 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12415 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12416 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12417
12418 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12419 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12422
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12424 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12427
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12429 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12430 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12431 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12432
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12434 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12435 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12436 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12437
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12439 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12442
12443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12444 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12445 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12446 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12447}
12448
12449void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12450{
12451 AssertReturnVoid(isWriteLockOnCurrentThread());
12452
12453 if (aCollector)
12454 {
12455 aCollector->unregisterMetricsFor(aMachine);
12456 aCollector->unregisterBaseMetricsFor(aMachine);
12457 }
12458}
12459
12460#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12461
12462
12463////////////////////////////////////////////////////////////////////////////////
12464
12465DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12466
12467HRESULT SessionMachine::FinalConstruct()
12468{
12469 LogFlowThisFunc(("\n"));
12470
12471 mClientToken = NULL;
12472
12473 return BaseFinalConstruct();
12474}
12475
12476void SessionMachine::FinalRelease()
12477{
12478 LogFlowThisFunc(("\n"));
12479
12480 Assert(!mClientToken);
12481 /* paranoia, should not hang around any more */
12482 if (mClientToken)
12483 {
12484 delete mClientToken;
12485 mClientToken = NULL;
12486 }
12487
12488 uninit(Uninit::Unexpected);
12489
12490 BaseFinalRelease();
12491}
12492
12493/**
12494 * @note Must be called only by Machine::LockMachine() from its own write lock.
12495 */
12496HRESULT SessionMachine::init(Machine *aMachine)
12497{
12498 LogFlowThisFuncEnter();
12499 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12500
12501 AssertReturn(aMachine, E_INVALIDARG);
12502
12503 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12504
12505 /* Enclose the state transition NotReady->InInit->Ready */
12506 AutoInitSpan autoInitSpan(this);
12507 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12508
12509 HRESULT rc = S_OK;
12510
12511 RT_ZERO(mAuthLibCtx);
12512
12513 /* create the machine client token */
12514 try
12515 {
12516 mClientToken = new ClientToken(aMachine, this);
12517 if (!mClientToken->isReady())
12518 {
12519 delete mClientToken;
12520 mClientToken = NULL;
12521 rc = E_FAIL;
12522 }
12523 }
12524 catch (std::bad_alloc &)
12525 {
12526 rc = E_OUTOFMEMORY;
12527 }
12528 if (FAILED(rc))
12529 return rc;
12530
12531 /* memorize the peer Machine */
12532 unconst(mPeer) = aMachine;
12533 /* share the parent pointer */
12534 unconst(mParent) = aMachine->mParent;
12535
12536 /* take the pointers to data to share */
12537 mData.share(aMachine->mData);
12538 mSSData.share(aMachine->mSSData);
12539
12540 mUserData.share(aMachine->mUserData);
12541 mHWData.share(aMachine->mHWData);
12542 mMediumAttachments.share(aMachine->mMediumAttachments);
12543
12544 mStorageControllers.allocate();
12545 for (StorageControllerList::const_iterator
12546 it = aMachine->mStorageControllers->begin();
12547 it != aMachine->mStorageControllers->end();
12548 ++it)
12549 {
12550 ComObjPtr<StorageController> ctl;
12551 ctl.createObject();
12552 ctl->init(this, *it);
12553 mStorageControllers->push_back(ctl);
12554 }
12555
12556 mUSBControllers.allocate();
12557 for (USBControllerList::const_iterator
12558 it = aMachine->mUSBControllers->begin();
12559 it != aMachine->mUSBControllers->end();
12560 ++it)
12561 {
12562 ComObjPtr<USBController> ctl;
12563 ctl.createObject();
12564 ctl->init(this, *it);
12565 mUSBControllers->push_back(ctl);
12566 }
12567
12568 unconst(mBIOSSettings).createObject();
12569 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12570 unconst(mRecordingSettings).createObject();
12571 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12572 /* create another VRDEServer object that will be mutable */
12573 unconst(mVRDEServer).createObject();
12574 mVRDEServer->init(this, aMachine->mVRDEServer);
12575 /* create another audio adapter object that will be mutable */
12576 unconst(mAudioAdapter).createObject();
12577 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12578 /* create a list of serial ports that will be mutable */
12579 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12580 {
12581 unconst(mSerialPorts[slot]).createObject();
12582 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12583 }
12584 /* create a list of parallel ports that will be mutable */
12585 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12586 {
12587 unconst(mParallelPorts[slot]).createObject();
12588 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12589 }
12590
12591 /* create another USB device filters object that will be mutable */
12592 unconst(mUSBDeviceFilters).createObject();
12593 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12594
12595 /* create a list of network adapters that will be mutable */
12596 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12597 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12598 {
12599 unconst(mNetworkAdapters[slot]).createObject();
12600 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12601 }
12602
12603 /* create another bandwidth control object that will be mutable */
12604 unconst(mBandwidthControl).createObject();
12605 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12606
12607 /* default is to delete saved state on Saved -> PoweredOff transition */
12608 mRemoveSavedState = true;
12609
12610 /* Confirm a successful initialization when it's the case */
12611 autoInitSpan.setSucceeded();
12612
12613 miNATNetworksStarted = 0;
12614
12615 LogFlowThisFuncLeave();
12616 return rc;
12617}
12618
12619/**
12620 * Uninitializes this session object. If the reason is other than
12621 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12622 * or the client watcher code.
12623 *
12624 * @param aReason uninitialization reason
12625 *
12626 * @note Locks mParent + this object for writing.
12627 */
12628void SessionMachine::uninit(Uninit::Reason aReason)
12629{
12630 LogFlowThisFuncEnter();
12631 LogFlowThisFunc(("reason=%d\n", aReason));
12632
12633 /*
12634 * Strongly reference ourselves to prevent this object deletion after
12635 * mData->mSession.mMachine.setNull() below (which can release the last
12636 * reference and call the destructor). Important: this must be done before
12637 * accessing any members (and before AutoUninitSpan that does it as well).
12638 * This self reference will be released as the very last step on return.
12639 */
12640 ComObjPtr<SessionMachine> selfRef;
12641 if (aReason != Uninit::Unexpected)
12642 selfRef = this;
12643
12644 /* Enclose the state transition Ready->InUninit->NotReady */
12645 AutoUninitSpan autoUninitSpan(this);
12646 if (autoUninitSpan.uninitDone())
12647 {
12648 LogFlowThisFunc(("Already uninitialized\n"));
12649 LogFlowThisFuncLeave();
12650 return;
12651 }
12652
12653 if (autoUninitSpan.initFailed())
12654 {
12655 /* We've been called by init() because it's failed. It's not really
12656 * necessary (nor it's safe) to perform the regular uninit sequence
12657 * below, the following is enough.
12658 */
12659 LogFlowThisFunc(("Initialization failed.\n"));
12660 /* destroy the machine client token */
12661 if (mClientToken)
12662 {
12663 delete mClientToken;
12664 mClientToken = NULL;
12665 }
12666 uninitDataAndChildObjects();
12667 mData.free();
12668 unconst(mParent) = NULL;
12669 unconst(mPeer) = NULL;
12670 LogFlowThisFuncLeave();
12671 return;
12672 }
12673
12674 MachineState_T lastState;
12675 {
12676 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12677 lastState = mData->mMachineState;
12678 }
12679 NOREF(lastState);
12680
12681#ifdef VBOX_WITH_USB
12682 // release all captured USB devices, but do this before requesting the locks below
12683 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12684 {
12685 /* Console::captureUSBDevices() is called in the VM process only after
12686 * setting the machine state to Starting or Restoring.
12687 * Console::detachAllUSBDevices() will be called upon successful
12688 * termination. So, we need to release USB devices only if there was
12689 * an abnormal termination of a running VM.
12690 *
12691 * This is identical to SessionMachine::DetachAllUSBDevices except
12692 * for the aAbnormal argument. */
12693 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12694 AssertComRC(rc);
12695 NOREF(rc);
12696
12697 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12698 if (service)
12699 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12700 }
12701#endif /* VBOX_WITH_USB */
12702
12703 // we need to lock this object in uninit() because the lock is shared
12704 // with mPeer (as well as data we modify below). mParent lock is needed
12705 // by several calls to it.
12706 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12707
12708#ifdef VBOX_WITH_RESOURCE_USAGE_API
12709 /*
12710 * It is safe to call Machine::i_unregisterMetrics() here because
12711 * PerformanceCollector::samplerCallback no longer accesses guest methods
12712 * holding the lock.
12713 */
12714 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12715 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12716 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12717 if (mCollectorGuest)
12718 {
12719 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12720 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12721 mCollectorGuest = NULL;
12722 }
12723#endif
12724
12725 if (aReason == Uninit::Abnormal)
12726 {
12727 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12728
12729 /* reset the state to Aborted */
12730 if (mData->mMachineState != MachineState_Aborted)
12731 i_setMachineState(MachineState_Aborted);
12732 }
12733
12734 // any machine settings modified?
12735 if (mData->flModifications)
12736 {
12737 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12738 i_rollback(false /* aNotify */);
12739 }
12740
12741 mData->mSession.mPID = NIL_RTPROCESS;
12742
12743 if (aReason == Uninit::Unexpected)
12744 {
12745 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12746 * client watcher thread to update the set of machines that have open
12747 * sessions. */
12748 mParent->i_updateClientWatcher();
12749 }
12750
12751 /* uninitialize all remote controls */
12752 if (mData->mSession.mRemoteControls.size())
12753 {
12754 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12755 mData->mSession.mRemoteControls.size()));
12756
12757 /* Always restart a the beginning, since the iterator is invalidated
12758 * by using erase(). */
12759 for (Data::Session::RemoteControlList::iterator
12760 it = mData->mSession.mRemoteControls.begin();
12761 it != mData->mSession.mRemoteControls.end();
12762 it = mData->mSession.mRemoteControls.begin())
12763 {
12764 ComPtr<IInternalSessionControl> pControl = *it;
12765 mData->mSession.mRemoteControls.erase(it);
12766 multilock.release();
12767 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12768 HRESULT rc = pControl->Uninitialize();
12769 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12770 if (FAILED(rc))
12771 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12772 multilock.acquire();
12773 }
12774 mData->mSession.mRemoteControls.clear();
12775 }
12776
12777 /* Remove all references to the NAT network service. The service will stop
12778 * if all references (also from other VMs) are removed. */
12779 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12780 {
12781 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12782 {
12783 BOOL enabled;
12784 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12785 if ( FAILED(hrc)
12786 || !enabled)
12787 continue;
12788
12789 NetworkAttachmentType_T type;
12790 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12791 if ( SUCCEEDED(hrc)
12792 && type == NetworkAttachmentType_NATNetwork)
12793 {
12794 Bstr name;
12795 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12796 if (SUCCEEDED(hrc))
12797 {
12798 multilock.release();
12799 Utf8Str strName(name);
12800 LogRel(("VM '%s' stops using NAT network '%s'\n",
12801 mUserData->s.strName.c_str(), strName.c_str()));
12802 mParent->i_natNetworkRefDec(strName);
12803 multilock.acquire();
12804 }
12805 }
12806 }
12807 }
12808
12809 /*
12810 * An expected uninitialization can come only from #i_checkForDeath().
12811 * Otherwise it means that something's gone really wrong (for example,
12812 * the Session implementation has released the VirtualBox reference
12813 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12814 * etc). However, it's also possible, that the client releases the IPC
12815 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12816 * but the VirtualBox release event comes first to the server process.
12817 * This case is practically possible, so we should not assert on an
12818 * unexpected uninit, just log a warning.
12819 */
12820
12821 if (aReason == Uninit::Unexpected)
12822 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12823
12824 if (aReason != Uninit::Normal)
12825 {
12826 mData->mSession.mDirectControl.setNull();
12827 }
12828 else
12829 {
12830 /* this must be null here (see #OnSessionEnd()) */
12831 Assert(mData->mSession.mDirectControl.isNull());
12832 Assert(mData->mSession.mState == SessionState_Unlocking);
12833 Assert(!mData->mSession.mProgress.isNull());
12834 }
12835 if (mData->mSession.mProgress)
12836 {
12837 if (aReason == Uninit::Normal)
12838 mData->mSession.mProgress->i_notifyComplete(S_OK);
12839 else
12840 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12841 COM_IIDOF(ISession),
12842 getComponentName(),
12843 tr("The VM session was aborted"));
12844 mData->mSession.mProgress.setNull();
12845 }
12846
12847 if (mConsoleTaskData.mProgress)
12848 {
12849 Assert(aReason == Uninit::Abnormal);
12850 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12851 COM_IIDOF(ISession),
12852 getComponentName(),
12853 tr("The VM session was aborted"));
12854 mConsoleTaskData.mProgress.setNull();
12855 }
12856
12857 /* remove the association between the peer machine and this session machine */
12858 Assert( (SessionMachine*)mData->mSession.mMachine == this
12859 || aReason == Uninit::Unexpected);
12860
12861 /* reset the rest of session data */
12862 mData->mSession.mLockType = LockType_Null;
12863 mData->mSession.mMachine.setNull();
12864 mData->mSession.mState = SessionState_Unlocked;
12865 mData->mSession.mName.setNull();
12866
12867 /* destroy the machine client token before leaving the exclusive lock */
12868 if (mClientToken)
12869 {
12870 delete mClientToken;
12871 mClientToken = NULL;
12872 }
12873
12874 /* fire an event */
12875 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12876
12877 uninitDataAndChildObjects();
12878
12879 /* free the essential data structure last */
12880 mData.free();
12881
12882 /* release the exclusive lock before setting the below two to NULL */
12883 multilock.release();
12884
12885 unconst(mParent) = NULL;
12886 unconst(mPeer) = NULL;
12887
12888 AuthLibUnload(&mAuthLibCtx);
12889
12890 LogFlowThisFuncLeave();
12891}
12892
12893// util::Lockable interface
12894////////////////////////////////////////////////////////////////////////////////
12895
12896/**
12897 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12898 * with the primary Machine instance (mPeer).
12899 */
12900RWLockHandle *SessionMachine::lockHandle() const
12901{
12902 AssertReturn(mPeer != NULL, NULL);
12903 return mPeer->lockHandle();
12904}
12905
12906// IInternalMachineControl methods
12907////////////////////////////////////////////////////////////////////////////////
12908
12909/**
12910 * Passes collected guest statistics to performance collector object
12911 */
12912HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12913 ULONG aCpuKernel, ULONG aCpuIdle,
12914 ULONG aMemTotal, ULONG aMemFree,
12915 ULONG aMemBalloon, ULONG aMemShared,
12916 ULONG aMemCache, ULONG aPageTotal,
12917 ULONG aAllocVMM, ULONG aFreeVMM,
12918 ULONG aBalloonedVMM, ULONG aSharedVMM,
12919 ULONG aVmNetRx, ULONG aVmNetTx)
12920{
12921#ifdef VBOX_WITH_RESOURCE_USAGE_API
12922 if (mCollectorGuest)
12923 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12924 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12925 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12926 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12927
12928 return S_OK;
12929#else
12930 NOREF(aValidStats);
12931 NOREF(aCpuUser);
12932 NOREF(aCpuKernel);
12933 NOREF(aCpuIdle);
12934 NOREF(aMemTotal);
12935 NOREF(aMemFree);
12936 NOREF(aMemBalloon);
12937 NOREF(aMemShared);
12938 NOREF(aMemCache);
12939 NOREF(aPageTotal);
12940 NOREF(aAllocVMM);
12941 NOREF(aFreeVMM);
12942 NOREF(aBalloonedVMM);
12943 NOREF(aSharedVMM);
12944 NOREF(aVmNetRx);
12945 NOREF(aVmNetTx);
12946 return E_NOTIMPL;
12947#endif
12948}
12949
12950////////////////////////////////////////////////////////////////////////////////
12951//
12952// SessionMachine task records
12953//
12954////////////////////////////////////////////////////////////////////////////////
12955
12956/**
12957 * Task record for saving the machine state.
12958 */
12959class SessionMachine::SaveStateTask
12960 : public Machine::Task
12961{
12962public:
12963 SaveStateTask(SessionMachine *m,
12964 Progress *p,
12965 const Utf8Str &t,
12966 Reason_T enmReason,
12967 const Utf8Str &strStateFilePath)
12968 : Task(m, p, t),
12969 m_enmReason(enmReason),
12970 m_strStateFilePath(strStateFilePath)
12971 {}
12972
12973private:
12974 void handler()
12975 {
12976 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12977 }
12978
12979 Reason_T m_enmReason;
12980 Utf8Str m_strStateFilePath;
12981
12982 friend class SessionMachine;
12983};
12984
12985/**
12986 * Task thread implementation for SessionMachine::SaveState(), called from
12987 * SessionMachine::taskHandler().
12988 *
12989 * @note Locks this object for writing.
12990 *
12991 * @param task
12992 * @return
12993 */
12994void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12995{
12996 LogFlowThisFuncEnter();
12997
12998 AutoCaller autoCaller(this);
12999 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13000 if (FAILED(autoCaller.rc()))
13001 {
13002 /* we might have been uninitialized because the session was accidentally
13003 * closed by the client, so don't assert */
13004 HRESULT rc = setError(E_FAIL,
13005 tr("The session has been accidentally closed"));
13006 task.m_pProgress->i_notifyComplete(rc);
13007 LogFlowThisFuncLeave();
13008 return;
13009 }
13010
13011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13012
13013 HRESULT rc = S_OK;
13014
13015 try
13016 {
13017 ComPtr<IInternalSessionControl> directControl;
13018 if (mData->mSession.mLockType == LockType_VM)
13019 directControl = mData->mSession.mDirectControl;
13020 if (directControl.isNull())
13021 throw setError(VBOX_E_INVALID_VM_STATE,
13022 tr("Trying to save state without a running VM"));
13023 alock.release();
13024 BOOL fSuspendedBySave;
13025 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13026 Assert(!fSuspendedBySave);
13027 alock.acquire();
13028
13029 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13030 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13031 throw E_FAIL);
13032
13033 if (SUCCEEDED(rc))
13034 {
13035 mSSData->strStateFilePath = task.m_strStateFilePath;
13036
13037 /* save all VM settings */
13038 rc = i_saveSettings(NULL);
13039 // no need to check whether VirtualBox.xml needs saving also since
13040 // we can't have a name change pending at this point
13041 }
13042 else
13043 {
13044 // On failure, set the state to the state we had at the beginning.
13045 i_setMachineState(task.m_machineStateBackup);
13046 i_updateMachineStateOnClient();
13047
13048 // Delete the saved state file (might have been already created).
13049 // No need to check whether this is shared with a snapshot here
13050 // because we certainly created a fresh saved state file here.
13051 RTFileDelete(task.m_strStateFilePath.c_str());
13052 }
13053 }
13054 catch (HRESULT aRC) { rc = aRC; }
13055
13056 task.m_pProgress->i_notifyComplete(rc);
13057
13058 LogFlowThisFuncLeave();
13059}
13060
13061/**
13062 * @note Locks this object for writing.
13063 */
13064HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13065{
13066 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13067}
13068
13069HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13070{
13071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13072
13073 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13074 if (FAILED(rc)) return rc;
13075
13076 if ( mData->mMachineState != MachineState_Running
13077 && mData->mMachineState != MachineState_Paused
13078 )
13079 return setError(VBOX_E_INVALID_VM_STATE,
13080 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13081 Global::stringifyMachineState(mData->mMachineState));
13082
13083 ComObjPtr<Progress> pProgress;
13084 pProgress.createObject();
13085 rc = pProgress->init(i_getVirtualBox(),
13086 static_cast<IMachine *>(this) /* aInitiator */,
13087 tr("Saving the execution state of the virtual machine"),
13088 FALSE /* aCancelable */);
13089 if (FAILED(rc))
13090 return rc;
13091
13092 Utf8Str strStateFilePath;
13093 i_composeSavedStateFilename(strStateFilePath);
13094
13095 /* create and start the task on a separate thread (note that it will not
13096 * start working until we release alock) */
13097 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13098 rc = pTask->createThread();
13099 if (FAILED(rc))
13100 return rc;
13101
13102 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13103 i_setMachineState(MachineState_Saving);
13104 i_updateMachineStateOnClient();
13105
13106 pProgress.queryInterfaceTo(aProgress.asOutParam());
13107
13108 return S_OK;
13109}
13110
13111/**
13112 * @note Locks this object for writing.
13113 */
13114HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13115{
13116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13117
13118 HRESULT rc = i_checkStateDependency(MutableStateDep);
13119 if (FAILED(rc)) return rc;
13120
13121 if ( mData->mMachineState != MachineState_PoweredOff
13122 && mData->mMachineState != MachineState_Teleported
13123 && mData->mMachineState != MachineState_Aborted
13124 )
13125 return setError(VBOX_E_INVALID_VM_STATE,
13126 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13127 Global::stringifyMachineState(mData->mMachineState));
13128
13129 com::Utf8Str stateFilePathFull;
13130 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13131 if (RT_FAILURE(vrc))
13132 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13133 tr("Invalid saved state file path '%s' (%Rrc)"),
13134 aSavedStateFile.c_str(),
13135 vrc);
13136
13137 mSSData->strStateFilePath = stateFilePathFull;
13138
13139 /* The below i_setMachineState() will detect the state transition and will
13140 * update the settings file */
13141
13142 return i_setMachineState(MachineState_Saved);
13143}
13144
13145/**
13146 * @note Locks this object for writing.
13147 */
13148HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13149{
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13153 if (FAILED(rc)) return rc;
13154
13155 if (mData->mMachineState != MachineState_Saved)
13156 return setError(VBOX_E_INVALID_VM_STATE,
13157 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13158 Global::stringifyMachineState(mData->mMachineState));
13159
13160 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13161
13162 /*
13163 * Saved -> PoweredOff transition will be detected in the SessionMachine
13164 * and properly handled.
13165 */
13166 rc = i_setMachineState(MachineState_PoweredOff);
13167 return rc;
13168}
13169
13170
13171/**
13172 * @note Locks the same as #i_setMachineState() does.
13173 */
13174HRESULT SessionMachine::updateState(MachineState_T aState)
13175{
13176 return i_setMachineState(aState);
13177}
13178
13179/**
13180 * @note Locks this object for writing.
13181 */
13182HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13183{
13184 IProgress *pProgress(aProgress);
13185
13186 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13187
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 if (mData->mSession.mState != SessionState_Locked)
13191 return VBOX_E_INVALID_OBJECT_STATE;
13192
13193 if (!mData->mSession.mProgress.isNull())
13194 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13195
13196 /* If we didn't reference the NAT network service yet, add a reference to
13197 * force a start */
13198 if (miNATNetworksStarted < 1)
13199 {
13200 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13201 {
13202 BOOL enabled;
13203 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13204 if ( FAILED(hrc)
13205 || !enabled)
13206 continue;
13207
13208 NetworkAttachmentType_T type;
13209 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13210 if ( SUCCEEDED(hrc)
13211 && type == NetworkAttachmentType_NATNetwork)
13212 {
13213 Bstr name;
13214 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13215 if (SUCCEEDED(hrc))
13216 {
13217 Utf8Str strName(name);
13218 LogRel(("VM '%s' starts using NAT network '%s'\n",
13219 mUserData->s.strName.c_str(), strName.c_str()));
13220 mPeer->lockHandle()->unlockWrite();
13221 mParent->i_natNetworkRefInc(strName);
13222#ifdef RT_LOCK_STRICT
13223 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13224#else
13225 mPeer->lockHandle()->lockWrite();
13226#endif
13227 }
13228 }
13229 }
13230 miNATNetworksStarted++;
13231 }
13232
13233 LogFlowThisFunc(("returns S_OK.\n"));
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks this object for writing.
13239 */
13240HRESULT SessionMachine::endPowerUp(LONG aResult)
13241{
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 if (mData->mSession.mState != SessionState_Locked)
13245 return VBOX_E_INVALID_OBJECT_STATE;
13246
13247 /* Finalize the LaunchVMProcess progress object. */
13248 if (mData->mSession.mProgress)
13249 {
13250 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13251 mData->mSession.mProgress.setNull();
13252 }
13253
13254 if (SUCCEEDED((HRESULT)aResult))
13255 {
13256#ifdef VBOX_WITH_RESOURCE_USAGE_API
13257 /* The VM has been powered up successfully, so it makes sense
13258 * now to offer the performance metrics for a running machine
13259 * object. Doing it earlier wouldn't be safe. */
13260 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13261 mData->mSession.mPID);
13262#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13263 }
13264
13265 return S_OK;
13266}
13267
13268/**
13269 * @note Locks this object for writing.
13270 */
13271HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13272{
13273 LogFlowThisFuncEnter();
13274
13275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13276
13277 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13278 E_FAIL);
13279
13280 /* create a progress object to track operation completion */
13281 ComObjPtr<Progress> pProgress;
13282 pProgress.createObject();
13283 pProgress->init(i_getVirtualBox(),
13284 static_cast<IMachine *>(this) /* aInitiator */,
13285 tr("Stopping the virtual machine"),
13286 FALSE /* aCancelable */);
13287
13288 /* fill in the console task data */
13289 mConsoleTaskData.mLastState = mData->mMachineState;
13290 mConsoleTaskData.mProgress = pProgress;
13291
13292 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13293 i_setMachineState(MachineState_Stopping);
13294
13295 pProgress.queryInterfaceTo(aProgress.asOutParam());
13296
13297 return S_OK;
13298}
13299
13300/**
13301 * @note Locks this object for writing.
13302 */
13303HRESULT SessionMachine::endPoweringDown(LONG aResult,
13304 const com::Utf8Str &aErrMsg)
13305{
13306 LogFlowThisFuncEnter();
13307
13308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13309
13310 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13311 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13312 && mConsoleTaskData.mLastState != MachineState_Null,
13313 E_FAIL);
13314
13315 /*
13316 * On failure, set the state to the state we had when BeginPoweringDown()
13317 * was called (this is expected by Console::PowerDown() and the associated
13318 * task). On success the VM process already changed the state to
13319 * MachineState_PoweredOff, so no need to do anything.
13320 */
13321 if (FAILED(aResult))
13322 i_setMachineState(mConsoleTaskData.mLastState);
13323
13324 /* notify the progress object about operation completion */
13325 Assert(mConsoleTaskData.mProgress);
13326 if (SUCCEEDED(aResult))
13327 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13328 else
13329 {
13330 if (aErrMsg.length())
13331 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13332 COM_IIDOF(ISession),
13333 getComponentName(),
13334 aErrMsg.c_str());
13335 else
13336 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13337 }
13338
13339 /* clear out the temporary saved state data */
13340 mConsoleTaskData.mLastState = MachineState_Null;
13341 mConsoleTaskData.mProgress.setNull();
13342
13343 LogFlowThisFuncLeave();
13344 return S_OK;
13345}
13346
13347
13348/**
13349 * Goes through the USB filters of the given machine to see if the given
13350 * device matches any filter or not.
13351 *
13352 * @note Locks the same as USBController::hasMatchingFilter() does.
13353 */
13354HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13355 BOOL *aMatched,
13356 ULONG *aMaskedInterfaces)
13357{
13358 LogFlowThisFunc(("\n"));
13359
13360#ifdef VBOX_WITH_USB
13361 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13362#else
13363 NOREF(aDevice);
13364 NOREF(aMaskedInterfaces);
13365 *aMatched = FALSE;
13366#endif
13367
13368 return S_OK;
13369}
13370
13371/**
13372 * @note Locks the same as Host::captureUSBDevice() does.
13373 */
13374HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378#ifdef VBOX_WITH_USB
13379 /* if captureDeviceForVM() fails, it must have set extended error info */
13380 clearError();
13381 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13382 if (FAILED(rc)) return rc;
13383
13384 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13385 AssertReturn(service, E_FAIL);
13386 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13387#else
13388 NOREF(aId);
13389 return E_NOTIMPL;
13390#endif
13391}
13392
13393/**
13394 * @note Locks the same as Host::detachUSBDevice() does.
13395 */
13396HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13397 BOOL aDone)
13398{
13399 LogFlowThisFunc(("\n"));
13400
13401#ifdef VBOX_WITH_USB
13402 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13403 AssertReturn(service, E_FAIL);
13404 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13405#else
13406 NOREF(aId);
13407 NOREF(aDone);
13408 return E_NOTIMPL;
13409#endif
13410}
13411
13412/**
13413 * Inserts all machine filters to the USB proxy service and then calls
13414 * Host::autoCaptureUSBDevices().
13415 *
13416 * Called by Console from the VM process upon VM startup.
13417 *
13418 * @note Locks what called methods lock.
13419 */
13420HRESULT SessionMachine::autoCaptureUSBDevices()
13421{
13422 LogFlowThisFunc(("\n"));
13423
13424#ifdef VBOX_WITH_USB
13425 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13426 AssertComRC(rc);
13427 NOREF(rc);
13428
13429 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13430 AssertReturn(service, E_FAIL);
13431 return service->autoCaptureDevicesForVM(this);
13432#else
13433 return S_OK;
13434#endif
13435}
13436
13437/**
13438 * Removes all machine filters from the USB proxy service and then calls
13439 * Host::detachAllUSBDevices().
13440 *
13441 * Called by Console from the VM process upon normal VM termination or by
13442 * SessionMachine::uninit() upon abnormal VM termination (from under the
13443 * Machine/SessionMachine lock).
13444 *
13445 * @note Locks what called methods lock.
13446 */
13447HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451#ifdef VBOX_WITH_USB
13452 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13453 AssertComRC(rc);
13454 NOREF(rc);
13455
13456 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13457 AssertReturn(service, E_FAIL);
13458 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13459#else
13460 NOREF(aDone);
13461 return S_OK;
13462#endif
13463}
13464
13465/**
13466 * @note Locks this object for writing.
13467 */
13468HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13469 ComPtr<IProgress> &aProgress)
13470{
13471 LogFlowThisFuncEnter();
13472
13473 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13474 /*
13475 * We don't assert below because it might happen that a non-direct session
13476 * informs us it is closed right after we've been uninitialized -- it's ok.
13477 */
13478
13479 /* get IInternalSessionControl interface */
13480 ComPtr<IInternalSessionControl> control(aSession);
13481
13482 ComAssertRet(!control.isNull(), E_INVALIDARG);
13483
13484 /* Creating a Progress object requires the VirtualBox lock, and
13485 * thus locking it here is required by the lock order rules. */
13486 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13487
13488 if (control == mData->mSession.mDirectControl)
13489 {
13490 /* The direct session is being normally closed by the client process
13491 * ----------------------------------------------------------------- */
13492
13493 /* go to the closing state (essential for all open*Session() calls and
13494 * for #i_checkForDeath()) */
13495 Assert(mData->mSession.mState == SessionState_Locked);
13496 mData->mSession.mState = SessionState_Unlocking;
13497
13498 /* set direct control to NULL to release the remote instance */
13499 mData->mSession.mDirectControl.setNull();
13500 LogFlowThisFunc(("Direct control is set to NULL\n"));
13501
13502 if (mData->mSession.mProgress)
13503 {
13504 /* finalize the progress, someone might wait if a frontend
13505 * closes the session before powering on the VM. */
13506 mData->mSession.mProgress->notifyComplete(E_FAIL,
13507 COM_IIDOF(ISession),
13508 getComponentName(),
13509 tr("The VM session was closed before any attempt to power it on"));
13510 mData->mSession.mProgress.setNull();
13511 }
13512
13513 /* Create the progress object the client will use to wait until
13514 * #i_checkForDeath() is called to uninitialize this session object after
13515 * it releases the IPC semaphore.
13516 * Note! Because we're "reusing" mProgress here, this must be a proxy
13517 * object just like for LaunchVMProcess. */
13518 Assert(mData->mSession.mProgress.isNull());
13519 ComObjPtr<ProgressProxy> progress;
13520 progress.createObject();
13521 ComPtr<IUnknown> pPeer(mPeer);
13522 progress->init(mParent, pPeer,
13523 Bstr(tr("Closing session")).raw(),
13524 FALSE /* aCancelable */);
13525 progress.queryInterfaceTo(aProgress.asOutParam());
13526 mData->mSession.mProgress = progress;
13527 }
13528 else
13529 {
13530 /* the remote session is being normally closed */
13531 bool found = false;
13532 for (Data::Session::RemoteControlList::iterator
13533 it = mData->mSession.mRemoteControls.begin();
13534 it != mData->mSession.mRemoteControls.end();
13535 ++it)
13536 {
13537 if (control == *it)
13538 {
13539 found = true;
13540 // This MUST be erase(it), not remove(*it) as the latter
13541 // triggers a very nasty use after free due to the place where
13542 // the value "lives".
13543 mData->mSession.mRemoteControls.erase(it);
13544 break;
13545 }
13546 }
13547 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13548 E_INVALIDARG);
13549 }
13550
13551 /* signal the client watcher thread, because the client is going away */
13552 mParent->i_updateClientWatcher();
13553
13554 LogFlowThisFuncLeave();
13555 return S_OK;
13556}
13557
13558HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13559{
13560#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13561 ULONG uID;
13562 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13563 if (RT_SUCCESS(rc))
13564 {
13565 if (aID)
13566 *aID = uID;
13567 return S_OK;
13568 }
13569 return E_FAIL;
13570#else
13571 RT_NOREF(aParms, aID);
13572 ReturnComNotImplemented();
13573#endif
13574}
13575
13576HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13577{
13578#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13579 return mParent->i_onClipboardAreaUnregister(aID);
13580#else
13581 RT_NOREF(aID);
13582 ReturnComNotImplemented();
13583#endif
13584}
13585
13586HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13587{
13588#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13589 return mParent->i_onClipboardAreaAttach(aID);
13590#else
13591 RT_NOREF(aID);
13592 ReturnComNotImplemented();
13593#endif
13594}
13595HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13596{
13597#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13598 return mParent->i_onClipboardAreaDetach(aID);
13599#else
13600 RT_NOREF(aID);
13601 ReturnComNotImplemented();
13602#endif
13603}
13604
13605HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13606{
13607#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13608 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13609 if (aID)
13610 *aID = uID;
13611 return S_OK;
13612#else
13613 RT_NOREF(aID);
13614 ReturnComNotImplemented();
13615#endif
13616}
13617
13618HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13619{
13620#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13621 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13622 if (aRefCount)
13623 *aRefCount = uRefCount;
13624 return S_OK;
13625#else
13626 RT_NOREF(aID, aRefCount);
13627 ReturnComNotImplemented();
13628#endif
13629}
13630
13631HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13632 std::vector<com::Utf8Str> &aValues,
13633 std::vector<LONG64> &aTimestamps,
13634 std::vector<com::Utf8Str> &aFlags)
13635{
13636 LogFlowThisFunc(("\n"));
13637
13638#ifdef VBOX_WITH_GUEST_PROPS
13639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13640
13641 size_t cEntries = mHWData->mGuestProperties.size();
13642 aNames.resize(cEntries);
13643 aValues.resize(cEntries);
13644 aTimestamps.resize(cEntries);
13645 aFlags.resize(cEntries);
13646
13647 size_t i = 0;
13648 for (HWData::GuestPropertyMap::const_iterator
13649 it = mHWData->mGuestProperties.begin();
13650 it != mHWData->mGuestProperties.end();
13651 ++it, ++i)
13652 {
13653 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13654 aNames[i] = it->first;
13655 aValues[i] = it->second.strValue;
13656 aTimestamps[i] = it->second.mTimestamp;
13657
13658 /* If it is NULL, keep it NULL. */
13659 if (it->second.mFlags)
13660 {
13661 GuestPropWriteFlags(it->second.mFlags, szFlags);
13662 aFlags[i] = szFlags;
13663 }
13664 else
13665 aFlags[i] = "";
13666 }
13667 return S_OK;
13668#else
13669 ReturnComNotImplemented();
13670#endif
13671}
13672
13673HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13674 const com::Utf8Str &aValue,
13675 LONG64 aTimestamp,
13676 const com::Utf8Str &aFlags)
13677{
13678 LogFlowThisFunc(("\n"));
13679
13680#ifdef VBOX_WITH_GUEST_PROPS
13681 try
13682 {
13683 /*
13684 * Convert input up front.
13685 */
13686 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13687 if (aFlags.length())
13688 {
13689 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13690 AssertRCReturn(vrc, E_INVALIDARG);
13691 }
13692
13693 /*
13694 * Now grab the object lock, validate the state and do the update.
13695 */
13696
13697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13698
13699 if (!Global::IsOnline(mData->mMachineState))
13700 {
13701 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13702 VBOX_E_INVALID_VM_STATE);
13703 }
13704
13705 i_setModified(IsModified_MachineData);
13706 mHWData.backup();
13707
13708 bool fDelete = !aValue.length();
13709 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13710 if (it != mHWData->mGuestProperties.end())
13711 {
13712 if (!fDelete)
13713 {
13714 it->second.strValue = aValue;
13715 it->second.mTimestamp = aTimestamp;
13716 it->second.mFlags = fFlags;
13717 }
13718 else
13719 mHWData->mGuestProperties.erase(it);
13720
13721 mData->mGuestPropertiesModified = TRUE;
13722 }
13723 else if (!fDelete)
13724 {
13725 HWData::GuestProperty prop;
13726 prop.strValue = aValue;
13727 prop.mTimestamp = aTimestamp;
13728 prop.mFlags = fFlags;
13729
13730 mHWData->mGuestProperties[aName] = prop;
13731 mData->mGuestPropertiesModified = TRUE;
13732 }
13733
13734 alock.release();
13735
13736 mParent->i_onGuestPropertyChange(mData->mUuid,
13737 Bstr(aName).raw(),
13738 Bstr(aValue).raw(),
13739 Bstr(aFlags).raw());
13740 }
13741 catch (...)
13742 {
13743 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13744 }
13745 return S_OK;
13746#else
13747 ReturnComNotImplemented();
13748#endif
13749}
13750
13751
13752HRESULT SessionMachine::lockMedia()
13753{
13754 AutoMultiWriteLock2 alock(this->lockHandle(),
13755 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13756
13757 AssertReturn( mData->mMachineState == MachineState_Starting
13758 || mData->mMachineState == MachineState_Restoring
13759 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13760
13761 clearError();
13762 alock.release();
13763 return i_lockMedia();
13764}
13765
13766HRESULT SessionMachine::unlockMedia()
13767{
13768 HRESULT hrc = i_unlockMedia();
13769 return hrc;
13770}
13771
13772HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13773 ComPtr<IMediumAttachment> &aNewAttachment)
13774{
13775 // request the host lock first, since might be calling Host methods for getting host drives;
13776 // next, protect the media tree all the while we're in here, as well as our member variables
13777 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13778 this->lockHandle(),
13779 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13780
13781 IMediumAttachment *iAttach = aAttachment;
13782 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13783
13784 Utf8Str ctrlName;
13785 LONG lPort;
13786 LONG lDevice;
13787 bool fTempEject;
13788 {
13789 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13790
13791 /* Need to query the details first, as the IMediumAttachment reference
13792 * might be to the original settings, which we are going to change. */
13793 ctrlName = pAttach->i_getControllerName();
13794 lPort = pAttach->i_getPort();
13795 lDevice = pAttach->i_getDevice();
13796 fTempEject = pAttach->i_getTempEject();
13797 }
13798
13799 if (!fTempEject)
13800 {
13801 /* Remember previously mounted medium. The medium before taking the
13802 * backup is not necessarily the same thing. */
13803 ComObjPtr<Medium> oldmedium;
13804 oldmedium = pAttach->i_getMedium();
13805
13806 i_setModified(IsModified_Storage);
13807 mMediumAttachments.backup();
13808
13809 // The backup operation makes the pAttach reference point to the
13810 // old settings. Re-get the correct reference.
13811 pAttach = i_findAttachment(*mMediumAttachments.data(),
13812 ctrlName,
13813 lPort,
13814 lDevice);
13815
13816 {
13817 AutoCaller autoAttachCaller(this);
13818 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13819
13820 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13821 if (!oldmedium.isNull())
13822 oldmedium->i_removeBackReference(mData->mUuid);
13823
13824 pAttach->i_updateMedium(NULL);
13825 pAttach->i_updateEjected();
13826 }
13827
13828 i_setModified(IsModified_Storage);
13829 }
13830 else
13831 {
13832 {
13833 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13834 pAttach->i_updateEjected();
13835 }
13836 }
13837
13838 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13839
13840 return S_OK;
13841}
13842
13843HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13844 com::Utf8Str &aResult)
13845{
13846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13847
13848 HRESULT hr = S_OK;
13849
13850 if (!mAuthLibCtx.hAuthLibrary)
13851 {
13852 /* Load the external authentication library. */
13853 Bstr authLibrary;
13854 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13855
13856 Utf8Str filename = authLibrary;
13857
13858 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13859 if (RT_FAILURE(vrc))
13860 hr = setErrorBoth(E_FAIL, vrc,
13861 tr("Could not load the external authentication library '%s' (%Rrc)"),
13862 filename.c_str(), vrc);
13863 }
13864
13865 /* The auth library might need the machine lock. */
13866 alock.release();
13867
13868 if (FAILED(hr))
13869 return hr;
13870
13871 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13872 {
13873 enum VRDEAuthParams
13874 {
13875 parmUuid = 1,
13876 parmGuestJudgement,
13877 parmUser,
13878 parmPassword,
13879 parmDomain,
13880 parmClientId
13881 };
13882
13883 AuthResult result = AuthResultAccessDenied;
13884
13885 Guid uuid(aAuthParams[parmUuid]);
13886 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13887 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13888
13889 result = AuthLibAuthenticate(&mAuthLibCtx,
13890 uuid.raw(), guestJudgement,
13891 aAuthParams[parmUser].c_str(),
13892 aAuthParams[parmPassword].c_str(),
13893 aAuthParams[parmDomain].c_str(),
13894 u32ClientId);
13895
13896 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13897 size_t cbPassword = aAuthParams[parmPassword].length();
13898 if (cbPassword)
13899 {
13900 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13901 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13902 }
13903
13904 if (result == AuthResultAccessGranted)
13905 aResult = "granted";
13906 else
13907 aResult = "denied";
13908
13909 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13910 aAuthParams[parmUser].c_str(), aResult.c_str()));
13911 }
13912 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13913 {
13914 enum VRDEAuthDisconnectParams
13915 {
13916 parmUuid = 1,
13917 parmClientId
13918 };
13919
13920 Guid uuid(aAuthParams[parmUuid]);
13921 uint32_t u32ClientId = 0;
13922 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13923 }
13924 else
13925 {
13926 hr = E_INVALIDARG;
13927 }
13928
13929 return hr;
13930}
13931
13932// public methods only for internal purposes
13933/////////////////////////////////////////////////////////////////////////////
13934
13935#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13936/**
13937 * Called from the client watcher thread to check for expected or unexpected
13938 * death of the client process that has a direct session to this machine.
13939 *
13940 * On Win32 and on OS/2, this method is called only when we've got the
13941 * mutex (i.e. the client has either died or terminated normally) so it always
13942 * returns @c true (the client is terminated, the session machine is
13943 * uninitialized).
13944 *
13945 * On other platforms, the method returns @c true if the client process has
13946 * terminated normally or abnormally and the session machine was uninitialized,
13947 * and @c false if the client process is still alive.
13948 *
13949 * @note Locks this object for writing.
13950 */
13951bool SessionMachine::i_checkForDeath()
13952{
13953 Uninit::Reason reason;
13954 bool terminated = false;
13955
13956 /* Enclose autoCaller with a block because calling uninit() from under it
13957 * will deadlock. */
13958 {
13959 AutoCaller autoCaller(this);
13960 if (!autoCaller.isOk())
13961 {
13962 /* return true if not ready, to cause the client watcher to exclude
13963 * the corresponding session from watching */
13964 LogFlowThisFunc(("Already uninitialized!\n"));
13965 return true;
13966 }
13967
13968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13969
13970 /* Determine the reason of death: if the session state is Closing here,
13971 * everything is fine. Otherwise it means that the client did not call
13972 * OnSessionEnd() before it released the IPC semaphore. This may happen
13973 * either because the client process has abnormally terminated, or
13974 * because it simply forgot to call ISession::Close() before exiting. We
13975 * threat the latter also as an abnormal termination (see
13976 * Session::uninit() for details). */
13977 reason = mData->mSession.mState == SessionState_Unlocking ?
13978 Uninit::Normal :
13979 Uninit::Abnormal;
13980
13981 if (mClientToken)
13982 terminated = mClientToken->release();
13983 } /* AutoCaller block */
13984
13985 if (terminated)
13986 uninit(reason);
13987
13988 return terminated;
13989}
13990
13991void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13992{
13993 LogFlowThisFunc(("\n"));
13994
13995 strTokenId.setNull();
13996
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturnVoid(autoCaller.rc());
13999
14000 Assert(mClientToken);
14001 if (mClientToken)
14002 mClientToken->getId(strTokenId);
14003}
14004#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14005IToken *SessionMachine::i_getToken()
14006{
14007 LogFlowThisFunc(("\n"));
14008
14009 AutoCaller autoCaller(this);
14010 AssertComRCReturn(autoCaller.rc(), NULL);
14011
14012 Assert(mClientToken);
14013 if (mClientToken)
14014 return mClientToken->getToken();
14015 else
14016 return NULL;
14017}
14018#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14019
14020Machine::ClientToken *SessionMachine::i_getClientToken()
14021{
14022 LogFlowThisFunc(("\n"));
14023
14024 AutoCaller autoCaller(this);
14025 AssertComRCReturn(autoCaller.rc(), NULL);
14026
14027 return mClientToken;
14028}
14029
14030
14031/**
14032 * @note Locks this object for reading.
14033 */
14034HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14040
14041 ComPtr<IInternalSessionControl> directControl;
14042 {
14043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14044 if (mData->mSession.mLockType == LockType_VM)
14045 directControl = mData->mSession.mDirectControl;
14046 }
14047
14048 /* ignore notifications sent after #OnSessionEnd() is called */
14049 if (!directControl)
14050 return S_OK;
14051
14052 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14053}
14054
14055/**
14056 * @note Locks this object for reading.
14057 */
14058HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14059 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14060 IN_BSTR aGuestIp, LONG aGuestPort)
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 AutoCaller autoCaller(this);
14065 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14066
14067 ComPtr<IInternalSessionControl> directControl;
14068 {
14069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14070 if (mData->mSession.mLockType == LockType_VM)
14071 directControl = mData->mSession.mDirectControl;
14072 }
14073
14074 /* ignore notifications sent after #OnSessionEnd() is called */
14075 if (!directControl)
14076 return S_OK;
14077 /*
14078 * instead acting like callback we ask IVirtualBox deliver corresponding event
14079 */
14080
14081 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14082 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14083 return S_OK;
14084}
14085
14086/**
14087 * @note Locks this object for reading.
14088 */
14089HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
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->OnAudioAdapterChange(audioAdapter);
14108}
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
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->OnSerialPortChange(serialPort);
14132}
14133
14134/**
14135 * @note Locks this object for reading.
14136 */
14137HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14138{
14139 LogFlowThisFunc(("\n"));
14140
14141 AutoCaller autoCaller(this);
14142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnParallelPortChange(parallelPort);
14156}
14157
14158/**
14159 * @note Locks this object for reading.
14160 */
14161HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 if (mData->mSession.mLockType == LockType_VM)
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 mParent->i_onMediumChanged(aAttachment);
14202
14203 /* ignore notifications sent after #OnSessionEnd() is called */
14204 if (!directControl)
14205 return S_OK;
14206
14207 return directControl->OnMediumChange(aAttachment, aForce);
14208}
14209
14210HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnVMProcessPriorityChange(aPriority);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnCPUChange(aCPU, aRemove);
14253}
14254
14255HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 if (mData->mSession.mLockType == LockType_VM)
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 /* ignore notifications sent after #OnSessionEnd() is called */
14270 if (!directControl)
14271 return S_OK;
14272
14273 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14274}
14275
14276/**
14277 * @note Locks this object for reading.
14278 */
14279HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14280{
14281 LogFlowThisFunc(("\n"));
14282
14283 AutoCaller autoCaller(this);
14284 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14285
14286 ComPtr<IInternalSessionControl> directControl;
14287 {
14288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14289 if (mData->mSession.mLockType == LockType_VM)
14290 directControl = mData->mSession.mDirectControl;
14291 }
14292
14293 /* ignore notifications sent after #OnSessionEnd() is called */
14294 if (!directControl)
14295 return S_OK;
14296
14297 return directControl->OnVRDEServerChange(aRestart);
14298}
14299
14300/**
14301 * @note Locks this object for reading.
14302 */
14303HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14304{
14305 LogFlowThisFunc(("\n"));
14306
14307 AutoCaller autoCaller(this);
14308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14309
14310 ComPtr<IInternalSessionControl> directControl;
14311 {
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313 if (mData->mSession.mLockType == LockType_VM)
14314 directControl = mData->mSession.mDirectControl;
14315 }
14316
14317 /* ignore notifications sent after #OnSessionEnd() is called */
14318 if (!directControl)
14319 return S_OK;
14320
14321 return directControl->OnRecordingChange(aEnable);
14322}
14323
14324/**
14325 * @note Locks this object for reading.
14326 */
14327HRESULT SessionMachine::i_onUSBControllerChange()
14328{
14329 LogFlowThisFunc(("\n"));
14330
14331 AutoCaller autoCaller(this);
14332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14333
14334 ComPtr<IInternalSessionControl> directControl;
14335 {
14336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14337 if (mData->mSession.mLockType == LockType_VM)
14338 directControl = mData->mSession.mDirectControl;
14339 }
14340
14341 /* ignore notifications sent after #OnSessionEnd() is called */
14342 if (!directControl)
14343 return S_OK;
14344
14345 return directControl->OnUSBControllerChange();
14346}
14347
14348/**
14349 * @note Locks this object for reading.
14350 */
14351HRESULT SessionMachine::i_onSharedFolderChange()
14352{
14353 LogFlowThisFunc(("\n"));
14354
14355 AutoCaller autoCaller(this);
14356 AssertComRCReturnRC(autoCaller.rc());
14357
14358 ComPtr<IInternalSessionControl> directControl;
14359 {
14360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14361 if (mData->mSession.mLockType == LockType_VM)
14362 directControl = mData->mSession.mDirectControl;
14363 }
14364
14365 /* ignore notifications sent after #OnSessionEnd() is called */
14366 if (!directControl)
14367 return S_OK;
14368
14369 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14370}
14371
14372/**
14373 * @note Locks this object for reading.
14374 */
14375HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14376{
14377 LogFlowThisFunc(("\n"));
14378
14379 AutoCaller autoCaller(this);
14380 AssertComRCReturnRC(autoCaller.rc());
14381
14382 ComPtr<IInternalSessionControl> directControl;
14383 {
14384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14385 if (mData->mSession.mLockType == LockType_VM)
14386 directControl = mData->mSession.mDirectControl;
14387 }
14388
14389 /* ignore notifications sent after #OnSessionEnd() is called */
14390 if (!directControl)
14391 return S_OK;
14392
14393 return directControl->OnClipboardModeChange(aClipboardMode);
14394}
14395
14396/**
14397 * @note Locks this object for reading.
14398 */
14399HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14400{
14401 LogFlowThisFunc(("\n"));
14402
14403 AutoCaller autoCaller(this);
14404 AssertComRCReturnRC(autoCaller.rc());
14405
14406 ComPtr<IInternalSessionControl> directControl;
14407 {
14408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14409 if (mData->mSession.mLockType == LockType_VM)
14410 directControl = mData->mSession.mDirectControl;
14411 }
14412
14413 /* ignore notifications sent after #OnSessionEnd() is called */
14414 if (!directControl)
14415 return S_OK;
14416
14417 return directControl->OnDnDModeChange(aDnDMode);
14418}
14419
14420/**
14421 * @note Locks this object for reading.
14422 */
14423HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14424{
14425 LogFlowThisFunc(("\n"));
14426
14427 AutoCaller autoCaller(this);
14428 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14429
14430 ComPtr<IInternalSessionControl> directControl;
14431 {
14432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14433 if (mData->mSession.mLockType == LockType_VM)
14434 directControl = mData->mSession.mDirectControl;
14435 }
14436
14437 /* ignore notifications sent after #OnSessionEnd() is called */
14438 if (!directControl)
14439 return S_OK;
14440
14441 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14442}
14443
14444/**
14445 * @note Locks this object for reading.
14446 */
14447HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14448{
14449 LogFlowThisFunc(("\n"));
14450
14451 AutoCaller autoCaller(this);
14452 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14453
14454 ComPtr<IInternalSessionControl> directControl;
14455 {
14456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14457 if (mData->mSession.mLockType == LockType_VM)
14458 directControl = mData->mSession.mDirectControl;
14459 }
14460
14461 /* ignore notifications sent after #OnSessionEnd() is called */
14462 if (!directControl)
14463 return S_OK;
14464
14465 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14466}
14467
14468/**
14469 * Returns @c true if this machine's USB controller reports it has a matching
14470 * filter for the given USB device and @c false otherwise.
14471 *
14472 * @note locks this object for reading.
14473 */
14474bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14475{
14476 AutoCaller autoCaller(this);
14477 /* silently return if not ready -- this method may be called after the
14478 * direct machine session has been called */
14479 if (!autoCaller.isOk())
14480 return false;
14481
14482#ifdef VBOX_WITH_USB
14483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14484
14485 switch (mData->mMachineState)
14486 {
14487 case MachineState_Starting:
14488 case MachineState_Restoring:
14489 case MachineState_TeleportingIn:
14490 case MachineState_Paused:
14491 case MachineState_Running:
14492 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14493 * elsewhere... */
14494 alock.release();
14495 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14496 default: break;
14497 }
14498#else
14499 NOREF(aDevice);
14500 NOREF(aMaskedIfs);
14501#endif
14502 return false;
14503}
14504
14505/**
14506 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14507 */
14508HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14509 IVirtualBoxErrorInfo *aError,
14510 ULONG aMaskedIfs,
14511 const com::Utf8Str &aCaptureFilename)
14512{
14513 LogFlowThisFunc(("\n"));
14514
14515 AutoCaller autoCaller(this);
14516
14517 /* This notification may happen after the machine object has been
14518 * uninitialized (the session was closed), so don't assert. */
14519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14520
14521 ComPtr<IInternalSessionControl> directControl;
14522 {
14523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14524 if (mData->mSession.mLockType == LockType_VM)
14525 directControl = mData->mSession.mDirectControl;
14526 }
14527
14528 /* fail on notifications sent after #OnSessionEnd() is called, it is
14529 * expected by the caller */
14530 if (!directControl)
14531 return E_FAIL;
14532
14533 /* No locks should be held at this point. */
14534 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14535 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14536
14537 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14538}
14539
14540/**
14541 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14542 */
14543HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14544 IVirtualBoxErrorInfo *aError)
14545{
14546 LogFlowThisFunc(("\n"));
14547
14548 AutoCaller autoCaller(this);
14549
14550 /* This notification may happen after the machine object has been
14551 * uninitialized (the session was closed), so don't assert. */
14552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14553
14554 ComPtr<IInternalSessionControl> directControl;
14555 {
14556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14557 if (mData->mSession.mLockType == LockType_VM)
14558 directControl = mData->mSession.mDirectControl;
14559 }
14560
14561 /* fail on notifications sent after #OnSessionEnd() is called, it is
14562 * expected by the caller */
14563 if (!directControl)
14564 return E_FAIL;
14565
14566 /* No locks should be held at this point. */
14567 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14568 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14569
14570 return directControl->OnUSBDeviceDetach(aId, aError);
14571}
14572
14573// protected methods
14574/////////////////////////////////////////////////////////////////////////////
14575
14576/**
14577 * Deletes the given file if it is no longer in use by either the current machine state
14578 * (if the machine is "saved") or any of the machine's snapshots.
14579 *
14580 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14581 * but is different for each SnapshotMachine. When calling this, the order of calling this
14582 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14583 * is therefore critical. I know, it's all rather messy.
14584 *
14585 * @param strStateFile
14586 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14587 * the test for whether the saved state file is in use.
14588 */
14589void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14590 Snapshot *pSnapshotToIgnore)
14591{
14592 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14593 if ( (strStateFile.isNotEmpty())
14594 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14595 )
14596 // ... and it must also not be shared with other snapshots
14597 if ( !mData->mFirstSnapshot
14598 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14599 // this checks the SnapshotMachine's state file paths
14600 )
14601 RTFileDelete(strStateFile.c_str());
14602}
14603
14604/**
14605 * Locks the attached media.
14606 *
14607 * All attached hard disks are locked for writing and DVD/floppy are locked for
14608 * reading. Parents of attached hard disks (if any) are locked for reading.
14609 *
14610 * This method also performs accessibility check of all media it locks: if some
14611 * media is inaccessible, the method will return a failure and a bunch of
14612 * extended error info objects per each inaccessible medium.
14613 *
14614 * Note that this method is atomic: if it returns a success, all media are
14615 * locked as described above; on failure no media is locked at all (all
14616 * succeeded individual locks will be undone).
14617 *
14618 * The caller is responsible for doing the necessary state sanity checks.
14619 *
14620 * The locks made by this method must be undone by calling #unlockMedia() when
14621 * no more needed.
14622 */
14623HRESULT SessionMachine::i_lockMedia()
14624{
14625 AutoCaller autoCaller(this);
14626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14627
14628 AutoMultiWriteLock2 alock(this->lockHandle(),
14629 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14630
14631 /* bail out if trying to lock things with already set up locking */
14632 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14633
14634 MultiResult mrc(S_OK);
14635
14636 /* Collect locking information for all medium objects attached to the VM. */
14637 for (MediumAttachmentList::const_iterator
14638 it = mMediumAttachments->begin();
14639 it != mMediumAttachments->end();
14640 ++it)
14641 {
14642 MediumAttachment *pAtt = *it;
14643 DeviceType_T devType = pAtt->i_getType();
14644 Medium *pMedium = pAtt->i_getMedium();
14645
14646 MediumLockList *pMediumLockList(new MediumLockList());
14647 // There can be attachments without a medium (floppy/dvd), and thus
14648 // it's impossible to create a medium lock list. It still makes sense
14649 // to have the empty medium lock list in the map in case a medium is
14650 // attached later.
14651 if (pMedium != NULL)
14652 {
14653 MediumType_T mediumType = pMedium->i_getType();
14654 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14655 || mediumType == MediumType_Shareable;
14656 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14657
14658 alock.release();
14659 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14660 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14661 false /* fMediumLockWriteAll */,
14662 NULL,
14663 *pMediumLockList);
14664 alock.acquire();
14665 if (FAILED(mrc))
14666 {
14667 delete pMediumLockList;
14668 mData->mSession.mLockedMedia.Clear();
14669 break;
14670 }
14671 }
14672
14673 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14674 if (FAILED(rc))
14675 {
14676 mData->mSession.mLockedMedia.Clear();
14677 mrc = setError(rc,
14678 tr("Collecting locking information for all attached media failed"));
14679 break;
14680 }
14681 }
14682
14683 if (SUCCEEDED(mrc))
14684 {
14685 /* Now lock all media. If this fails, nothing is locked. */
14686 alock.release();
14687 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14688 alock.acquire();
14689 if (FAILED(rc))
14690 {
14691 mrc = setError(rc,
14692 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14693 }
14694 }
14695
14696 return mrc;
14697}
14698
14699/**
14700 * Undoes the locks made by by #lockMedia().
14701 */
14702HRESULT SessionMachine::i_unlockMedia()
14703{
14704 AutoCaller autoCaller(this);
14705 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14706
14707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14708
14709 /* we may be holding important error info on the current thread;
14710 * preserve it */
14711 ErrorInfoKeeper eik;
14712
14713 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14714 AssertComRC(rc);
14715 return rc;
14716}
14717
14718/**
14719 * Helper to change the machine state (reimplementation).
14720 *
14721 * @note Locks this object for writing.
14722 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14723 * it can cause crashes in random places due to unexpectedly committing
14724 * the current settings. The caller is responsible for that. The call
14725 * to saveStateSettings is fine, because this method does not commit.
14726 */
14727HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14728{
14729 LogFlowThisFuncEnter();
14730 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14731
14732 AutoCaller autoCaller(this);
14733 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14734
14735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14736
14737 MachineState_T oldMachineState = mData->mMachineState;
14738
14739 AssertMsgReturn(oldMachineState != aMachineState,
14740 ("oldMachineState=%s, aMachineState=%s\n",
14741 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14742 E_FAIL);
14743
14744 HRESULT rc = S_OK;
14745
14746 int stsFlags = 0;
14747 bool deleteSavedState = false;
14748
14749 /* detect some state transitions */
14750
14751 if ( ( oldMachineState == MachineState_Saved
14752 && aMachineState == MachineState_Restoring)
14753 || ( ( oldMachineState == MachineState_PoweredOff
14754 || oldMachineState == MachineState_Teleported
14755 || oldMachineState == MachineState_Aborted
14756 )
14757 && ( aMachineState == MachineState_TeleportingIn
14758 || aMachineState == MachineState_Starting
14759 )
14760 )
14761 )
14762 {
14763 /* The EMT thread is about to start */
14764
14765 /* Nothing to do here for now... */
14766
14767 /// @todo NEWMEDIA don't let mDVDDrive and other children
14768 /// change anything when in the Starting/Restoring state
14769 }
14770 else if ( ( oldMachineState == MachineState_Running
14771 || oldMachineState == MachineState_Paused
14772 || oldMachineState == MachineState_Teleporting
14773 || oldMachineState == MachineState_OnlineSnapshotting
14774 || oldMachineState == MachineState_LiveSnapshotting
14775 || oldMachineState == MachineState_Stuck
14776 || oldMachineState == MachineState_Starting
14777 || oldMachineState == MachineState_Stopping
14778 || oldMachineState == MachineState_Saving
14779 || oldMachineState == MachineState_Restoring
14780 || oldMachineState == MachineState_TeleportingPausedVM
14781 || oldMachineState == MachineState_TeleportingIn
14782 )
14783 && ( aMachineState == MachineState_PoweredOff
14784 || aMachineState == MachineState_Saved
14785 || aMachineState == MachineState_Teleported
14786 || aMachineState == MachineState_Aborted
14787 )
14788 )
14789 {
14790 /* The EMT thread has just stopped, unlock attached media. Note that as
14791 * opposed to locking that is done from Console, we do unlocking here
14792 * because the VM process may have aborted before having a chance to
14793 * properly unlock all media it locked. */
14794
14795 unlockMedia();
14796 }
14797
14798 if (oldMachineState == MachineState_Restoring)
14799 {
14800 if (aMachineState != MachineState_Saved)
14801 {
14802 /*
14803 * delete the saved state file once the machine has finished
14804 * restoring from it (note that Console sets the state from
14805 * Restoring to Saved if the VM couldn't restore successfully,
14806 * to give the user an ability to fix an error and retry --
14807 * we keep the saved state file in this case)
14808 */
14809 deleteSavedState = true;
14810 }
14811 }
14812 else if ( oldMachineState == MachineState_Saved
14813 && ( aMachineState == MachineState_PoweredOff
14814 || aMachineState == MachineState_Aborted
14815 || aMachineState == MachineState_Teleported
14816 )
14817 )
14818 {
14819 /*
14820 * delete the saved state after SessionMachine::ForgetSavedState() is called
14821 * or if the VM process (owning a direct VM session) crashed while the
14822 * VM was Saved
14823 */
14824
14825 /// @todo (dmik)
14826 // Not sure that deleting the saved state file just because of the
14827 // client death before it attempted to restore the VM is a good
14828 // thing. But when it crashes we need to go to the Aborted state
14829 // which cannot have the saved state file associated... The only
14830 // way to fix this is to make the Aborted condition not a VM state
14831 // but a bool flag: i.e., when a crash occurs, set it to true and
14832 // change the state to PoweredOff or Saved depending on the
14833 // saved state presence.
14834
14835 deleteSavedState = true;
14836 mData->mCurrentStateModified = TRUE;
14837 stsFlags |= SaveSTS_CurStateModified;
14838 }
14839
14840 if ( aMachineState == MachineState_Starting
14841 || aMachineState == MachineState_Restoring
14842 || aMachineState == MachineState_TeleportingIn
14843 )
14844 {
14845 /* set the current state modified flag to indicate that the current
14846 * state is no more identical to the state in the
14847 * current snapshot */
14848 if (!mData->mCurrentSnapshot.isNull())
14849 {
14850 mData->mCurrentStateModified = TRUE;
14851 stsFlags |= SaveSTS_CurStateModified;
14852 }
14853 }
14854
14855 if (deleteSavedState)
14856 {
14857 if (mRemoveSavedState)
14858 {
14859 Assert(!mSSData->strStateFilePath.isEmpty());
14860
14861 // it is safe to delete the saved state file if ...
14862 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14863 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14864 // ... none of the snapshots share the saved state file
14865 )
14866 RTFileDelete(mSSData->strStateFilePath.c_str());
14867 }
14868
14869 mSSData->strStateFilePath.setNull();
14870 stsFlags |= SaveSTS_StateFilePath;
14871 }
14872
14873 /* redirect to the underlying peer machine */
14874 mPeer->i_setMachineState(aMachineState);
14875
14876 if ( oldMachineState != MachineState_RestoringSnapshot
14877 && ( aMachineState == MachineState_PoweredOff
14878 || aMachineState == MachineState_Teleported
14879 || aMachineState == MachineState_Aborted
14880 || aMachineState == MachineState_Saved))
14881 {
14882 /* the machine has stopped execution
14883 * (or the saved state file was adopted) */
14884 stsFlags |= SaveSTS_StateTimeStamp;
14885 }
14886
14887 if ( ( oldMachineState == MachineState_PoweredOff
14888 || oldMachineState == MachineState_Aborted
14889 || oldMachineState == MachineState_Teleported
14890 )
14891 && aMachineState == MachineState_Saved)
14892 {
14893 /* the saved state file was adopted */
14894 Assert(!mSSData->strStateFilePath.isEmpty());
14895 stsFlags |= SaveSTS_StateFilePath;
14896 }
14897
14898#ifdef VBOX_WITH_GUEST_PROPS
14899 if ( aMachineState == MachineState_PoweredOff
14900 || aMachineState == MachineState_Aborted
14901 || aMachineState == MachineState_Teleported)
14902 {
14903 /* Make sure any transient guest properties get removed from the
14904 * property store on shutdown. */
14905 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14906
14907 /* remove it from the settings representation */
14908 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14909 for (settings::GuestPropertiesList::iterator
14910 it = llGuestProperties.begin();
14911 it != llGuestProperties.end();
14912 /*nothing*/)
14913 {
14914 const settings::GuestProperty &prop = *it;
14915 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14916 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14917 {
14918 it = llGuestProperties.erase(it);
14919 fNeedsSaving = true;
14920 }
14921 else
14922 {
14923 ++it;
14924 }
14925 }
14926
14927 /* Additionally remove it from the HWData representation. Required to
14928 * keep everything in sync, as this is what the API keeps using. */
14929 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14930 for (HWData::GuestPropertyMap::iterator
14931 it = llHWGuestProperties.begin();
14932 it != llHWGuestProperties.end();
14933 /*nothing*/)
14934 {
14935 uint32_t fFlags = it->second.mFlags;
14936 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14937 {
14938 /* iterator where we need to continue after the erase call
14939 * (C++03 is a fact still, and it doesn't return the iterator
14940 * which would allow continuing) */
14941 HWData::GuestPropertyMap::iterator it2 = it;
14942 ++it2;
14943 llHWGuestProperties.erase(it);
14944 it = it2;
14945 fNeedsSaving = true;
14946 }
14947 else
14948 {
14949 ++it;
14950 }
14951 }
14952
14953 if (fNeedsSaving)
14954 {
14955 mData->mCurrentStateModified = TRUE;
14956 stsFlags |= SaveSTS_CurStateModified;
14957 }
14958 }
14959#endif /* VBOX_WITH_GUEST_PROPS */
14960
14961 rc = i_saveStateSettings(stsFlags);
14962
14963 if ( ( oldMachineState != MachineState_PoweredOff
14964 && oldMachineState != MachineState_Aborted
14965 && oldMachineState != MachineState_Teleported
14966 )
14967 && ( aMachineState == MachineState_PoweredOff
14968 || aMachineState == MachineState_Aborted
14969 || aMachineState == MachineState_Teleported
14970 )
14971 )
14972 {
14973 /* we've been shut down for any reason */
14974 /* no special action so far */
14975 }
14976
14977 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14978 LogFlowThisFuncLeave();
14979 return rc;
14980}
14981
14982/**
14983 * Sends the current machine state value to the VM process.
14984 *
14985 * @note Locks this object for reading, then calls a client process.
14986 */
14987HRESULT SessionMachine::i_updateMachineStateOnClient()
14988{
14989 AutoCaller autoCaller(this);
14990 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14991
14992 ComPtr<IInternalSessionControl> directControl;
14993 {
14994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14995 AssertReturn(!!mData, E_FAIL);
14996 if (mData->mSession.mLockType == LockType_VM)
14997 directControl = mData->mSession.mDirectControl;
14998
14999 /* directControl may be already set to NULL here in #OnSessionEnd()
15000 * called too early by the direct session process while there is still
15001 * some operation (like deleting the snapshot) in progress. The client
15002 * process in this case is waiting inside Session::close() for the
15003 * "end session" process object to complete, while #uninit() called by
15004 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15005 * operation to complete. For now, we accept this inconsistent behavior
15006 * and simply do nothing here. */
15007
15008 if (mData->mSession.mState == SessionState_Unlocking)
15009 return S_OK;
15010 }
15011
15012 /* ignore notifications sent after #OnSessionEnd() is called */
15013 if (!directControl)
15014 return S_OK;
15015
15016 return directControl->UpdateMachineState(mData->mMachineState);
15017}
15018
15019
15020/*static*/
15021HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15022{
15023 va_list args;
15024 va_start(args, pcszMsg);
15025 HRESULT rc = setErrorInternal(aResultCode,
15026 getStaticClassIID(),
15027 getStaticComponentName(),
15028 Utf8Str(pcszMsg, args),
15029 false /* aWarning */,
15030 true /* aLogIt */);
15031 va_end(args);
15032 return rc;
15033}
15034
15035
15036HRESULT Machine::updateState(MachineState_T aState)
15037{
15038 NOREF(aState);
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15043{
15044 NOREF(aProgress);
15045 ReturnComNotImplemented();
15046}
15047
15048HRESULT Machine::endPowerUp(LONG aResult)
15049{
15050 NOREF(aResult);
15051 ReturnComNotImplemented();
15052}
15053
15054HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15055{
15056 NOREF(aProgress);
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::endPoweringDown(LONG aResult,
15061 const com::Utf8Str &aErrMsg)
15062{
15063 NOREF(aResult);
15064 NOREF(aErrMsg);
15065 ReturnComNotImplemented();
15066}
15067
15068HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15069 BOOL *aMatched,
15070 ULONG *aMaskedInterfaces)
15071{
15072 NOREF(aDevice);
15073 NOREF(aMatched);
15074 NOREF(aMaskedInterfaces);
15075 ReturnComNotImplemented();
15076
15077}
15078
15079HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15080{
15081 NOREF(aId); NOREF(aCaptureFilename);
15082 ReturnComNotImplemented();
15083}
15084
15085HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15086 BOOL aDone)
15087{
15088 NOREF(aId);
15089 NOREF(aDone);
15090 ReturnComNotImplemented();
15091}
15092
15093HRESULT Machine::autoCaptureUSBDevices()
15094{
15095 ReturnComNotImplemented();
15096}
15097
15098HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15099{
15100 NOREF(aDone);
15101 ReturnComNotImplemented();
15102}
15103
15104HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15105 ComPtr<IProgress> &aProgress)
15106{
15107 NOREF(aSession);
15108 NOREF(aProgress);
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::finishOnlineMergeMedium()
15113{
15114 ReturnComNotImplemented();
15115}
15116
15117HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
15118{
15119 RT_NOREF(aParms, aID);
15120 ReturnComNotImplemented();
15121}
15122
15123HRESULT Machine::clipboardAreaUnregister(ULONG aID)
15124{
15125 RT_NOREF(aID);
15126 ReturnComNotImplemented();
15127}
15128
15129HRESULT Machine::clipboardAreaAttach(ULONG aID)
15130{
15131 RT_NOREF(aID);
15132 ReturnComNotImplemented();
15133}
15134HRESULT Machine::clipboardAreaDetach(ULONG aID)
15135{
15136 RT_NOREF(aID);
15137 ReturnComNotImplemented();
15138}
15139
15140HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15141{
15142 RT_NOREF(aID);
15143 ReturnComNotImplemented();
15144}
15145
15146HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15147{
15148 RT_NOREF(aID, aRefCount);
15149 ReturnComNotImplemented();
15150}
15151
15152HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15153 std::vector<com::Utf8Str> &aValues,
15154 std::vector<LONG64> &aTimestamps,
15155 std::vector<com::Utf8Str> &aFlags)
15156{
15157 NOREF(aNames);
15158 NOREF(aValues);
15159 NOREF(aTimestamps);
15160 NOREF(aFlags);
15161 ReturnComNotImplemented();
15162}
15163
15164HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15165 const com::Utf8Str &aValue,
15166 LONG64 aTimestamp,
15167 const com::Utf8Str &aFlags)
15168{
15169 NOREF(aName);
15170 NOREF(aValue);
15171 NOREF(aTimestamp);
15172 NOREF(aFlags);
15173 ReturnComNotImplemented();
15174}
15175
15176HRESULT Machine::lockMedia()
15177{
15178 ReturnComNotImplemented();
15179}
15180
15181HRESULT Machine::unlockMedia()
15182{
15183 ReturnComNotImplemented();
15184}
15185
15186HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15187 ComPtr<IMediumAttachment> &aNewAttachment)
15188{
15189 NOREF(aAttachment);
15190 NOREF(aNewAttachment);
15191 ReturnComNotImplemented();
15192}
15193
15194HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15195 ULONG aCpuUser,
15196 ULONG aCpuKernel,
15197 ULONG aCpuIdle,
15198 ULONG aMemTotal,
15199 ULONG aMemFree,
15200 ULONG aMemBalloon,
15201 ULONG aMemShared,
15202 ULONG aMemCache,
15203 ULONG aPagedTotal,
15204 ULONG aMemAllocTotal,
15205 ULONG aMemFreeTotal,
15206 ULONG aMemBalloonTotal,
15207 ULONG aMemSharedTotal,
15208 ULONG aVmNetRx,
15209 ULONG aVmNetTx)
15210{
15211 NOREF(aValidStats);
15212 NOREF(aCpuUser);
15213 NOREF(aCpuKernel);
15214 NOREF(aCpuIdle);
15215 NOREF(aMemTotal);
15216 NOREF(aMemFree);
15217 NOREF(aMemBalloon);
15218 NOREF(aMemShared);
15219 NOREF(aMemCache);
15220 NOREF(aPagedTotal);
15221 NOREF(aMemAllocTotal);
15222 NOREF(aMemFreeTotal);
15223 NOREF(aMemBalloonTotal);
15224 NOREF(aMemSharedTotal);
15225 NOREF(aVmNetRx);
15226 NOREF(aVmNetTx);
15227 ReturnComNotImplemented();
15228}
15229
15230HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15231 com::Utf8Str &aResult)
15232{
15233 NOREF(aAuthParams);
15234 NOREF(aResult);
15235 ReturnComNotImplemented();
15236}
15237
15238com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15239{
15240 com::Utf8Str strControllerName = "Unknown";
15241 switch (aBusType)
15242 {
15243 case StorageBus_IDE:
15244 {
15245 strControllerName = "IDE";
15246 break;
15247 }
15248 case StorageBus_SATA:
15249 {
15250 strControllerName = "SATA";
15251 break;
15252 }
15253 case StorageBus_SCSI:
15254 {
15255 strControllerName = "SCSI";
15256 break;
15257 }
15258 case StorageBus_Floppy:
15259 {
15260 strControllerName = "Floppy";
15261 break;
15262 }
15263 case StorageBus_SAS:
15264 {
15265 strControllerName = "SAS";
15266 break;
15267 }
15268 case StorageBus_USB:
15269 {
15270 strControllerName = "USB";
15271 break;
15272 }
15273 default:
15274 break;
15275 }
15276 return strControllerName;
15277}
15278
15279HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15280{
15281 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15282
15283 AutoCaller autoCaller(this);
15284 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15285
15286 HRESULT rc = S_OK;
15287
15288 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15289 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15290 rc = getUSBDeviceFilters(usbDeviceFilters);
15291 if (FAILED(rc)) return rc;
15292
15293 NOREF(aFlags);
15294 com::Utf8Str osTypeId;
15295 ComObjPtr<GuestOSType> osType = NULL;
15296
15297 /* Get the guest os type as a string from the VB. */
15298 rc = getOSTypeId(osTypeId);
15299 if (FAILED(rc)) return rc;
15300
15301 /* Get the os type obj that coresponds, can be used to get
15302 * the defaults for this guest OS. */
15303 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15304 if (FAILED(rc)) return rc;
15305
15306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15307
15308 /* Let the OS type select 64-bit ness. */
15309 mHWData->mLongMode = osType->i_is64Bit()
15310 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15311
15312 /* Let the OS type enable the X2APIC */
15313 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15314
15315 /* This one covers IOAPICEnabled. */
15316 mBIOSSettings->i_applyDefaults(osType);
15317
15318 /* Initialize default record settings. */
15319 mRecordingSettings->i_applyDefaults();
15320
15321 /* Initialize default BIOS settings here */
15322 /* Hardware virtualization must be ON by default */
15323 mHWData->mAPIC = true;
15324 mHWData->mHWVirtExEnabled = true;
15325
15326 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15327 if (FAILED(rc)) return rc;
15328
15329 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15330 if (FAILED(rc)) return rc;
15331
15332 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15333 if (FAILED(rc)) return rc;
15334
15335 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15336 if (FAILED(rc)) return rc;
15337
15338 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15339 if (FAILED(rc)) return rc;
15340
15341 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15342 if (FAILED(rc)) return rc;
15343
15344 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15345 if (FAILED(rc)) return rc;
15346
15347 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15348 if (FAILED(rc)) return rc;
15349
15350 BOOL mRTCUseUTC;
15351 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15352 if (FAILED(rc)) return rc;
15353
15354 setRTCUseUTC(mRTCUseUTC);
15355 if (FAILED(rc)) return rc;
15356
15357 /* the setter does more than just the assignment, so use it */
15358 ChipsetType_T enmChipsetType;
15359 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15360 if (FAILED(rc)) return rc;
15361
15362 rc = COMSETTER(ChipsetType)(enmChipsetType);
15363 if (FAILED(rc)) return rc;
15364
15365 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15366 if (FAILED(rc)) return rc;
15367
15368 /* Apply network adapters defaults */
15369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15370 mNetworkAdapters[slot]->i_applyDefaults(osType);
15371
15372 /* Apply serial port defaults */
15373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15374 mSerialPorts[slot]->i_applyDefaults(osType);
15375
15376 /* Apply parallel port defaults - not OS dependent*/
15377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15378 mParallelPorts[slot]->i_applyDefaults();
15379
15380 /* Audio stuff. */
15381 AudioControllerType_T audioController;
15382 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15383 if (FAILED(rc)) return rc;
15384
15385 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15386 if (FAILED(rc)) return rc;
15387
15388 AudioCodecType_T audioCodec;
15389 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15390 if (FAILED(rc)) return rc;
15391
15392 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15393 if (FAILED(rc)) return rc;
15394
15395 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15396 if (FAILED(rc)) return rc;
15397
15398 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15399 if (FAILED(rc)) return rc;
15400
15401 /* Storage Controllers */
15402 StorageControllerType_T hdStorageControllerType;
15403 StorageBus_T hdStorageBusType;
15404 StorageControllerType_T dvdStorageControllerType;
15405 StorageBus_T dvdStorageBusType;
15406 BOOL recommendedFloppy;
15407 ComPtr<IStorageController> floppyController;
15408 ComPtr<IStorageController> hdController;
15409 ComPtr<IStorageController> dvdController;
15410 Utf8Str strFloppyName, strDVDName, strHDName;
15411
15412 /* GUI auto generates controller names using bus type. Do the same*/
15413 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15414
15415 /* Floppy recommended? add one. */
15416 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15417 if (FAILED(rc)) return rc;
15418 if (recommendedFloppy)
15419 {
15420 rc = addStorageController(strFloppyName,
15421 StorageBus_Floppy,
15422 floppyController);
15423 if (FAILED(rc)) return rc;
15424 }
15425
15426 /* Setup one DVD storage controller. */
15427 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15428 if (FAILED(rc)) return rc;
15429
15430 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15431 if (FAILED(rc)) return rc;
15432
15433 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15434
15435 rc = addStorageController(strDVDName,
15436 dvdStorageBusType,
15437 dvdController);
15438 if (FAILED(rc)) return rc;
15439
15440 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15441 if (FAILED(rc)) return rc;
15442
15443 /* Setup one HDD storage controller. */
15444 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15445 if (FAILED(rc)) return rc;
15446
15447 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15448 if (FAILED(rc)) return rc;
15449
15450 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15451
15452 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15453 {
15454 rc = addStorageController(strHDName,
15455 hdStorageBusType,
15456 hdController);
15457 if (FAILED(rc)) return rc;
15458
15459 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15460 if (FAILED(rc)) return rc;
15461 }
15462 else
15463 {
15464 /* The HD controller is the same as DVD: */
15465 hdController = dvdController;
15466 }
15467
15468 /* Limit the AHCI port count if it's used because windows has trouble with
15469 * too many ports and other guest (OS X in particular) may take extra long
15470 * boot: */
15471
15472 // pParent = static_cast<Medium*>(aP)
15473 IStorageController *temp = hdController;
15474 ComObjPtr<StorageController> storageController;
15475 storageController = static_cast<StorageController *>(temp);
15476
15477 // tempHDController = aHDController;
15478 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15479 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15480 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15481 storageController->COMSETTER(PortCount)(1);
15482
15483 /* USB stuff */
15484
15485 bool ohciEnabled = false;
15486
15487 ComPtr<IUSBController> usbController;
15488 BOOL recommendedUSB3;
15489 BOOL recommendedUSB;
15490 BOOL usbProxyAvailable;
15491
15492 getUSBProxyAvailable(&usbProxyAvailable);
15493 if (FAILED(rc)) return rc;
15494
15495 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15496 if (FAILED(rc)) return rc;
15497 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15498 if (FAILED(rc)) return rc;
15499
15500 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15501 {
15502#ifdef VBOX_WITH_EXTPACK
15503 /* USB 3.0 is only available if the proper ExtPack is installed. */
15504 ExtPackManager *aManager = mParent->i_getExtPackManager();
15505 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15506 {
15507 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15508 if (FAILED(rc)) return rc;
15509
15510 /* xHci includes OHCI */
15511 ohciEnabled = true;
15512 }
15513#endif
15514 }
15515 if ( !ohciEnabled
15516 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15517 {
15518 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15519 if (FAILED(rc)) return rc;
15520 ohciEnabled = true;
15521
15522#ifdef VBOX_WITH_EXTPACK
15523 /* USB 2.0 is only available if the proper ExtPack is installed.
15524 * Note. Configuring EHCI here and providing messages about
15525 * the missing extpack isn't exactly clean, but it is a
15526 * necessary evil to patch over legacy compatability issues
15527 * introduced by the new distribution model. */
15528 ExtPackManager *manager = mParent->i_getExtPackManager();
15529 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15530 {
15531 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15532 if (FAILED(rc)) return rc;
15533 }
15534#endif
15535 }
15536
15537 /* Set recommended human interface device types: */
15538 BOOL recommendedUSBHID;
15539 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15540 if (FAILED(rc)) return rc;
15541
15542 if (recommendedUSBHID)
15543 {
15544 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15545 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15546 if (!ohciEnabled && !usbDeviceFilters.isNull())
15547 {
15548 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15549 if (FAILED(rc)) return rc;
15550 }
15551 }
15552
15553 BOOL recommendedUSBTablet;
15554 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15555 if (FAILED(rc)) return rc;
15556
15557 if (recommendedUSBTablet)
15558 {
15559 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15560 if (!ohciEnabled && !usbDeviceFilters.isNull())
15561 {
15562 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15563 if (FAILED(rc)) return rc;
15564 }
15565 }
15566 return S_OK;
15567}
15568
15569/* This isn't handled entirely by the wrapper generator yet. */
15570#ifdef VBOX_WITH_XPCOM
15571NS_DECL_CLASSINFO(SessionMachine)
15572NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15573
15574NS_DECL_CLASSINFO(SnapshotMachine)
15575NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15576#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