VirtualBox

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

Last change on this file since 80008 was 79955, checked in by vboxsync, 6 years ago

bugref:8345. Second part of refining and cleaning up the code.

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

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