VirtualBox

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

Last change on this file since 76785 was 76678, checked in by vboxsync, 6 years ago

Port r124260, r124263, r124271, r124273, r124277, r124278, r124279, r124284, r124285, r124286, r124287, r124288, r124289 and r124290 (Ported fixes over from 5.2, see bugref:9179 for more information)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 531.2 KB
Line 
1/* $Id: MachineImpl.cpp 76678 2019-01-07 13:48:16Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mParavirtProvider = ParavirtProvider_Default;
221 mEmulatedUSBCardReaderEnabled = FALSE;
222
223 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
224 mCPUAttached[i] = false;
225
226 mIOCacheEnabled = true;
227 mIOCacheSize = 5; /* 5MB */
228}
229
230Machine::HWData::~HWData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine() :
242#ifdef VBOX_WITH_RESOURCE_USAGE_API
243 mCollectorGuest(NULL),
244#endif
245 mPeer(NULL),
246 mParent(NULL),
247 mSerialPorts(),
248 mParallelPorts(),
249 uRegistryNeedsSaving(0)
250{}
251
252Machine::~Machine()
253{}
254
255HRESULT Machine::FinalConstruct()
256{
257 LogFlowThisFunc(("\n"));
258 return BaseFinalConstruct();
259}
260
261void Machine::FinalRelease()
262{
263 LogFlowThisFunc(("\n"));
264 uninit();
265 BaseFinalRelease();
266}
267
268/**
269 * Initializes a new machine instance; this init() variant creates a new, empty machine.
270 * This gets called from VirtualBox::CreateMachine().
271 *
272 * @param aParent Associated parent object
273 * @param strConfigFile Local file system path to the VM settings file (can
274 * be relative to the VirtualBox config directory).
275 * @param strName name for the machine
276 * @param llGroups list of groups for the machine
277 * @param strOsType OS Type string (stored as is if aOsType is NULL).
278 * @param aOsType OS Type of this machine or NULL.
279 * @param aId UUID for the new machine.
280 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
281 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
282 * scheme (includes the UUID).
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 const Utf8Str &strOsType,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 if (llGroups.size())
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Let the OS type select 64-bit ness. */
345 mHWData->mLongMode = aOsType->i_is64Bit()
346 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
347
348 /* Let the OS type enable the X2APIC */
349 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
350 }
351 else if (!strOsType.isEmpty())
352 {
353 /* Store OS type */
354 mUserData->s.strOsType = strOsType;
355
356 /* No guest OS type object. Pick some plausible defaults which the
357 * host can handle. There's no way to know or validate anything. */
358 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 mHWData->mX2APIC = false;
360 }
361
362 /* Apply BIOS defaults. */
363 mBIOSSettings->i_applyDefaults(aOsType);
364
365 /* Apply record defaults. */
366 mRecordingSettings->i_applyDefaults();
367
368 /* Apply network adapters defaults */
369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
370 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
371
372 /* Apply serial port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
374 mSerialPorts[slot]->i_applyDefaults(aOsType);
375
376 /* Apply parallel port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
378 mParallelPorts[slot]->i_applyDefaults();
379
380 /* At this point the changing of the current state modification
381 * flag is allowed. */
382 i_allowStateModification();
383
384 /* commit all changes made during the initialization */
385 i_commit();
386 }
387
388 /* Confirm a successful initialization when it's the case */
389 if (SUCCEEDED(rc))
390 {
391 if (mData->mAccessible)
392 autoInitSpan.setSucceeded();
393 else
394 autoInitSpan.setLimited();
395 }
396
397 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
398 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
399 mData->mRegistered,
400 mData->mAccessible,
401 rc));
402
403 LogFlowThisFuncLeave();
404
405 return rc;
406}
407
408/**
409 * Initializes a new instance with data from machine XML (formerly Init_Registered).
410 * Gets called in two modes:
411 *
412 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
413 * UUID is specified and we mark the machine as "registered";
414 *
415 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
416 * and the machine remains unregistered until RegisterMachine() is called.
417 *
418 * @param aParent Associated parent object
419 * @param strConfigFile Local file system path to the VM settings file (can
420 * be relative to the VirtualBox config directory).
421 * @param aId UUID of the machine or NULL (see above).
422 *
423 * @return Success indicator. if not S_OK, the machine object is invalid
424 */
425HRESULT Machine::initFromSettings(VirtualBox *aParent,
426 const Utf8Str &strConfigFile,
427 const Guid *aId)
428{
429 LogFlowThisFuncEnter();
430 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 HRESULT rc = initImpl(aParent, strConfigFile);
437 if (FAILED(rc)) return rc;
438
439 if (aId)
440 {
441 // loading a registered VM:
442 unconst(mData->mUuid) = *aId;
443 mData->mRegistered = TRUE;
444 // now load the settings from XML:
445 rc = i_registeredInit();
446 // this calls initDataAndChildObjects() and loadSettings()
447 }
448 else
449 {
450 // opening an unregistered VM (VirtualBox::OpenMachine()):
451 rc = initDataAndChildObjects();
452
453 if (SUCCEEDED(rc))
454 {
455 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
456 mData->mAccessible = TRUE;
457
458 try
459 {
460 // load and parse machine XML; this will throw on XML or logic errors
461 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
462
463 // reject VM UUID duplicates, they can happen if someone
464 // tries to register an already known VM config again
465 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
466 true /* fPermitInaccessible */,
467 false /* aDoSetError */,
468 NULL) != VBOX_E_OBJECT_NOT_FOUND)
469 {
470 throw setError(E_FAIL,
471 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
472 mData->m_strConfigFile.c_str());
473 }
474
475 // use UUID from machine config
476 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
477
478 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
479 NULL /* puuidRegistry */);
480 if (FAILED(rc)) throw rc;
481
482 /* At this point the changing of the current state modification
483 * flag is allowed. */
484 i_allowStateModification();
485
486 i_commit();
487 }
488 catch (HRESULT err)
489 {
490 /* we assume that error info is set by the thrower */
491 rc = err;
492 }
493 catch (...)
494 {
495 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
496 }
497 }
498 }
499
500 /* Confirm a successful initialization when it's the case */
501 if (SUCCEEDED(rc))
502 {
503 if (mData->mAccessible)
504 autoInitSpan.setSucceeded();
505 else
506 {
507 autoInitSpan.setLimited();
508
509 // uninit media from this machine's media registry, or else
510 // reloading the settings will fail
511 mParent->i_unregisterMachineMedia(i_getId());
512 }
513 }
514
515 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
516 "rc=%08X\n",
517 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
518 mData->mRegistered, mData->mAccessible, rc));
519
520 LogFlowThisFuncLeave();
521
522 return rc;
523}
524
525/**
526 * Initializes a new instance from a machine config that is already in memory
527 * (import OVF case). Since we are importing, the UUID in the machine
528 * config is ignored and we always generate a fresh one.
529 *
530 * @param aParent Associated parent object.
531 * @param strName Name for the new machine; this overrides what is specified in config.
532 * @param strSettingsFilename File name of .vbox file.
533 * @param config Machine configuration loaded and parsed from XML.
534 *
535 * @return Success indicator. if not S_OK, the machine object is invalid
536 */
537HRESULT Machine::init(VirtualBox *aParent,
538 const Utf8Str &strName,
539 const Utf8Str &strSettingsFilename,
540 const settings::MachineConfigFile &config)
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 HRESULT rc = initImpl(aParent, strSettingsFilename);
549 if (FAILED(rc)) return rc;
550
551 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
552 if (FAILED(rc)) return rc;
553
554 rc = initDataAndChildObjects();
555
556 if (SUCCEEDED(rc))
557 {
558 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
559 mData->mAccessible = TRUE;
560
561 // create empty machine config for instance data
562 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
563
564 // generate fresh UUID, ignore machine config
565 unconst(mData->mUuid).create();
566
567 rc = i_loadMachineDataFromSettings(config,
568 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
569
570 // override VM name as well, it may be different
571 mUserData->s.strName = strName;
572
573 if (SUCCEEDED(rc))
574 {
575 /* At this point the changing of the current state modification
576 * flag is allowed. */
577 i_allowStateModification();
578
579 /* commit all changes made during the initialization */
580 i_commit();
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 if (SUCCEEDED(rc))
586 {
587 if (mData->mAccessible)
588 autoInitSpan.setSucceeded();
589 else
590 {
591 /* Ignore all errors from unregistering, they would destroy
592- * the more interesting error information we already have,
593- * pinpointing the issue with the VM config. */
594 ErrorInfoKeeper eik;
595
596 autoInitSpan.setLimited();
597
598 // uninit media from this machine's media registry, or else
599 // reloading the settings will fail
600 mParent->i_unregisterMachineMedia(i_getId());
601 }
602 }
603
604 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
605 "rc=%08X\n",
606 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
607 mData->mRegistered, mData->mAccessible, rc));
608
609 LogFlowThisFuncLeave();
610
611 return rc;
612}
613
614/**
615 * Shared code between the various init() implementations.
616 * @param aParent The VirtualBox object.
617 * @param strConfigFile Settings file.
618 * @return
619 */
620HRESULT Machine::initImpl(VirtualBox *aParent,
621 const Utf8Str &strConfigFile)
622{
623 LogFlowThisFuncEnter();
624
625 AssertReturn(aParent, E_INVALIDARG);
626 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
627
628 HRESULT rc = S_OK;
629
630 /* share the parent weakly */
631 unconst(mParent) = aParent;
632
633 /* allocate the essential machine data structure (the rest will be
634 * allocated later by initDataAndChildObjects() */
635 mData.allocate();
636
637 /* memorize the config file name (as provided) */
638 mData->m_strConfigFile = strConfigFile;
639
640 /* get the full file name */
641 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
642 if (RT_FAILURE(vrc1))
643 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
644 tr("Invalid machine settings file name '%s' (%Rrc)"),
645 strConfigFile.c_str(),
646 vrc1);
647
648 LogFlowThisFuncLeave();
649
650 return rc;
651}
652
653/**
654 * Tries to create a machine settings file in the path stored in the machine
655 * instance data. Used when a new machine is created to fail gracefully if
656 * the settings file could not be written (e.g. because machine dir is read-only).
657 * @return
658 */
659HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
660{
661 HRESULT rc = S_OK;
662
663 // when we create a new machine, we must be able to create the settings file
664 RTFILE f = NIL_RTFILE;
665 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
666 if ( RT_SUCCESS(vrc)
667 || vrc == VERR_SHARING_VIOLATION
668 )
669 {
670 if (RT_SUCCESS(vrc))
671 RTFileClose(f);
672 if (!fForceOverwrite)
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Machine settings file '%s' already exists"),
675 mData->m_strConfigFileFull.c_str());
676 else
677 {
678 /* try to delete the config file, as otherwise the creation
679 * of a new settings file will fail. */
680 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
681 if (RT_FAILURE(vrc2))
682 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
683 tr("Could not delete the existing settings file '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(), vrc2);
685 }
686 }
687 else if ( vrc != VERR_FILE_NOT_FOUND
688 && vrc != VERR_PATH_NOT_FOUND
689 )
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
691 tr("Invalid machine settings file name '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(),
693 vrc);
694 return rc;
695}
696
697/**
698 * Initializes the registered machine by loading the settings file.
699 * This method is separated from #init() in order to make it possible to
700 * retry the operation after VirtualBox startup instead of refusing to
701 * startup the whole VirtualBox server in case if the settings file of some
702 * registered VM is invalid or inaccessible.
703 *
704 * @note Must be always called from this object's write lock
705 * (unless called from #init() that doesn't need any locking).
706 * @note Locks the mUSBController method for writing.
707 * @note Subclasses must not call this method.
708 */
709HRESULT Machine::i_registeredInit()
710{
711 AssertReturn(!i_isSessionMachine(), E_FAIL);
712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
713 AssertReturn(mData->mUuid.isValid(), E_FAIL);
714 AssertReturn(!mData->mAccessible, E_FAIL);
715
716 HRESULT rc = initDataAndChildObjects();
717
718 if (SUCCEEDED(rc))
719 {
720 /* Temporarily reset the registered flag in order to let setters
721 * potentially called from loadSettings() succeed (isMutable() used in
722 * all setters will return FALSE for a Machine instance if mRegistered
723 * is TRUE). */
724 mData->mRegistered = FALSE;
725
726 try
727 {
728 // load and parse machine XML; this will throw on XML or logic errors
729 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
730
731 if (mData->mUuid != mData->pMachineConfigFile->uuid)
732 throw setError(E_FAIL,
733 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
734 mData->pMachineConfigFile->uuid.raw(),
735 mData->m_strConfigFileFull.c_str(),
736 mData->mUuid.toString().c_str(),
737 mParent->i_settingsFilePath().c_str());
738
739 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
740 NULL /* const Guid *puuidRegistry */);
741 if (FAILED(rc)) throw rc;
742 }
743 catch (HRESULT err)
744 {
745 /* we assume that error info is set by the thrower */
746 rc = err;
747 }
748 catch (...)
749 {
750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
751 }
752
753 /* Restore the registered flag (even on failure) */
754 mData->mRegistered = TRUE;
755 }
756
757 if (SUCCEEDED(rc))
758 {
759 /* Set mAccessible to TRUE only if we successfully locked and loaded
760 * the settings file */
761 mData->mAccessible = TRUE;
762
763 /* commit all changes made during loading the settings file */
764 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
765 /// @todo r=klaus for some reason the settings loading logic backs up
766 // the settings, and therefore a commit is needed. Should probably be changed.
767 }
768 else
769 {
770 /* If the machine is registered, then, instead of returning a
771 * failure, we mark it as inaccessible and set the result to
772 * success to give it a try later */
773
774 /* fetch the current error info */
775 mData->mAccessError = com::ErrorInfo();
776 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
777
778 /* rollback all changes */
779 i_rollback(false /* aNotify */);
780
781 // uninit media from this machine's media registry, or else
782 // reloading the settings will fail
783 mParent->i_unregisterMachineMedia(i_getId());
784
785 /* uninitialize the common part to make sure all data is reset to
786 * default (null) values */
787 uninitDataAndChildObjects();
788
789 rc = S_OK;
790 }
791
792 return rc;
793}
794
795/**
796 * Uninitializes the instance.
797 * Called either from FinalRelease() or by the parent when it gets destroyed.
798 *
799 * @note The caller of this method must make sure that this object
800 * a) doesn't have active callers on the current thread and b) is not locked
801 * by the current thread; otherwise uninit() will hang either a) due to
802 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
803 * a dead-lock caused by this thread waiting for all callers on the other
804 * threads are done but preventing them from doing so by holding a lock.
805 */
806void Machine::uninit()
807{
808 LogFlowThisFuncEnter();
809
810 Assert(!isWriteLockOnCurrentThread());
811
812 Assert(!uRegistryNeedsSaving);
813 if (uRegistryNeedsSaving)
814 {
815 AutoCaller autoCaller(this);
816 if (SUCCEEDED(autoCaller.rc()))
817 {
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819 i_saveSettings(NULL, Machine::SaveS_Force);
820 }
821 }
822
823 /* Enclose the state transition Ready->InUninit->NotReady */
824 AutoUninitSpan autoUninitSpan(this);
825 if (autoUninitSpan.uninitDone())
826 return;
827
828 Assert(!i_isSnapshotMachine());
829 Assert(!i_isSessionMachine());
830 Assert(!!mData);
831
832 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
833 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 if (!mData->mSession.mMachine.isNull())
838 {
839 /* Theoretically, this can only happen if the VirtualBox server has been
840 * terminated while there were clients running that owned open direct
841 * sessions. Since in this case we are definitely called by
842 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
843 * won't happen on the client watcher thread (because it has a
844 * VirtualBox caller for the duration of the
845 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
846 * cannot happen until the VirtualBox caller is released). This is
847 * important, because SessionMachine::uninit() cannot correctly operate
848 * after we return from this method (it expects the Machine instance is
849 * still valid). We'll call it ourselves below.
850 */
851 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
852 (SessionMachine*)mData->mSession.mMachine));
853
854 if (Global::IsOnlineOrTransient(mData->mMachineState))
855 {
856 Log1WarningThisFunc(("Setting state to Aborted!\n"));
857 /* set machine state using SessionMachine reimplementation */
858 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
859 }
860
861 /*
862 * Uninitialize SessionMachine using public uninit() to indicate
863 * an unexpected uninitialization.
864 */
865 mData->mSession.mMachine->uninit();
866 /* SessionMachine::uninit() must set mSession.mMachine to null */
867 Assert(mData->mSession.mMachine.isNull());
868 }
869
870 // uninit media from this machine's media registry, if they're still there
871 Guid uuidMachine(i_getId());
872
873 /* the lock is no more necessary (SessionMachine is uninitialized) */
874 alock.release();
875
876 /* XXX This will fail with
877 * "cannot be closed because it is still attached to 1 virtual machines"
878 * because at this point we did not call uninitDataAndChildObjects() yet
879 * and therefore also removeBackReference() for all these mediums was not called! */
880
881 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
882 mParent->i_unregisterMachineMedia(uuidMachine);
883
884 // has machine been modified?
885 if (mData->flModifications)
886 {
887 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
888 i_rollback(false /* aNotify */);
889 }
890
891 if (mData->mAccessible)
892 uninitDataAndChildObjects();
893
894 /* free the essential data structure last */
895 mData.free();
896
897 LogFlowThisFuncLeave();
898}
899
900// Wrapped IMachine properties
901/////////////////////////////////////////////////////////////////////////////
902HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
903{
904 /* mParent is constant during life time, no need to lock */
905 ComObjPtr<VirtualBox> pVirtualBox(mParent);
906 aParent = pVirtualBox;
907
908 return S_OK;
909}
910
911
912HRESULT Machine::getAccessible(BOOL *aAccessible)
913{
914 /* In some cases (medium registry related), it is necessary to be able to
915 * go through the list of all machines. Happens when an inaccessible VM
916 * has a sensible medium registry. */
917 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
919
920 HRESULT rc = S_OK;
921
922 if (!mData->mAccessible)
923 {
924 /* try to initialize the VM once more if not accessible */
925
926 AutoReinitSpan autoReinitSpan(this);
927 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
928
929#ifdef DEBUG
930 LogFlowThisFunc(("Dumping media backreferences\n"));
931 mParent->i_dumpAllBackRefs();
932#endif
933
934 if (mData->pMachineConfigFile)
935 {
936 // reset the XML file to force loadSettings() (called from i_registeredInit())
937 // to parse it again; the file might have changed
938 delete mData->pMachineConfigFile;
939 mData->pMachineConfigFile = NULL;
940 }
941
942 rc = i_registeredInit();
943
944 if (SUCCEEDED(rc) && mData->mAccessible)
945 {
946 autoReinitSpan.setSucceeded();
947
948 /* make sure interesting parties will notice the accessibility
949 * state change */
950 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
951 mParent->i_onMachineDataChange(mData->mUuid);
952 }
953 }
954
955 if (SUCCEEDED(rc))
956 *aAccessible = mData->mAccessible;
957
958 LogFlowThisFuncLeave();
959
960 return rc;
961}
962
963HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
964{
965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
968 {
969 /* return shortly */
970 aAccessError = NULL;
971 return S_OK;
972 }
973
974 HRESULT rc = S_OK;
975
976 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
977 rc = errorInfo.createObject();
978 if (SUCCEEDED(rc))
979 {
980 errorInfo->init(mData->mAccessError.getResultCode(),
981 mData->mAccessError.getInterfaceID().ref(),
982 Utf8Str(mData->mAccessError.getComponent()).c_str(),
983 Utf8Str(mData->mAccessError.getText()));
984 aAccessError = errorInfo;
985 }
986
987 return rc;
988}
989
990HRESULT Machine::getName(com::Utf8Str &aName)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 aName = mUserData->s.strName;
995
996 return S_OK;
997}
998
999HRESULT Machine::setName(const com::Utf8Str &aName)
1000{
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = i_checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 i_setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1021{
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 aDescription = mUserData->s.strDescription;
1025
1026 return S_OK;
1027}
1028
1029HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1030{
1031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1032
1033 // this can be done in principle in any state as it doesn't affect the VM
1034 // significantly, but play safe by not messing around while complex
1035 // activities are going on
1036 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1037 if (FAILED(rc)) return rc;
1038
1039 i_setModified(IsModified_MachineData);
1040 mUserData.backup();
1041 mUserData->s.strDescription = aDescription;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getId(com::Guid &aId)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 aId = mData->mUuid;
1051
1052 return S_OK;
1053}
1054
1055HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1056{
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058 aGroups.resize(mUserData->s.llGroups.size());
1059 size_t i = 0;
1060 for (StringsList::const_iterator
1061 it = mUserData->s.llGroups.begin();
1062 it != mUserData->s.llGroups.end();
1063 ++it, ++i)
1064 aGroups[i] = (*it);
1065
1066 return S_OK;
1067}
1068
1069HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1070{
1071 StringsList llGroups;
1072 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1073 if (FAILED(rc))
1074 return rc;
1075
1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 rc = i_checkStateDependency(MutableOrSavedStateDep);
1079 if (FAILED(rc)) return rc;
1080
1081 i_setModified(IsModified_MachineData);
1082 mUserData.backup();
1083 mUserData->s.llGroups = llGroups;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1089{
1090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 aOSTypeId = mUserData->s.strOsType;
1093
1094 return S_OK;
1095}
1096
1097HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1098{
1099 /* look up the object by Id to check it is valid */
1100 ComObjPtr<GuestOSType> pGuestOSType;
1101 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1102
1103 /* when setting, always use the "etalon" value for consistency -- lookup
1104 * by ID is case-insensitive and the input value may have different case */
1105 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 HRESULT rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComObjPtr<GuestOSType> pGuestOSType;
1298 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1299 pGuestOSType);
1300 if (FAILED(hrc2) || pGuestOSType.isNull())
1301 {
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1307 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1308
1309 switch (mHWData->mParavirtProvider)
1310 {
1311 case ParavirtProvider_Legacy:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else
1316 *aParavirtProvider = ParavirtProvider_None;
1317 break;
1318 }
1319
1320 case ParavirtProvider_Default:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else if ( mUserData->s.strOsType == "Windows10"
1325 || mUserData->s.strOsType == "Windows10_64"
1326 || mUserData->s.strOsType == "Windows81"
1327 || mUserData->s.strOsType == "Windows81_64"
1328 || mUserData->s.strOsType == "Windows8"
1329 || mUserData->s.strOsType == "Windows8_64"
1330 || mUserData->s.strOsType == "Windows7"
1331 || mUserData->s.strOsType == "Windows7_64"
1332 || mUserData->s.strOsType == "WindowsVista"
1333 || mUserData->s.strOsType == "WindowsVista_64"
1334 || mUserData->s.strOsType == "Windows2012"
1335 || mUserData->s.strOsType == "Windows2012_64"
1336 || mUserData->s.strOsType == "Windows2008"
1337 || mUserData->s.strOsType == "Windows2008_64")
1338 {
1339 *aParavirtProvider = ParavirtProvider_HyperV;
1340 }
1341 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1342 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1343 || mUserData->s.strOsType == "Linux"
1344 || mUserData->s.strOsType == "Linux_64"
1345 || mUserData->s.strOsType == "ArchLinux"
1346 || mUserData->s.strOsType == "ArchLinux_64"
1347 || mUserData->s.strOsType == "Debian"
1348 || mUserData->s.strOsType == "Debian_64"
1349 || mUserData->s.strOsType == "Fedora"
1350 || mUserData->s.strOsType == "Fedora_64"
1351 || mUserData->s.strOsType == "Gentoo"
1352 || mUserData->s.strOsType == "Gentoo_64"
1353 || mUserData->s.strOsType == "Mandriva"
1354 || mUserData->s.strOsType == "Mandriva_64"
1355 || mUserData->s.strOsType == "OpenSUSE"
1356 || mUserData->s.strOsType == "OpenSUSE_64"
1357 || mUserData->s.strOsType == "Oracle"
1358 || mUserData->s.strOsType == "Oracle_64"
1359 || mUserData->s.strOsType == "RedHat"
1360 || mUserData->s.strOsType == "RedHat_64"
1361 || mUserData->s.strOsType == "Turbolinux"
1362 || mUserData->s.strOsType == "Turbolinux_64"
1363 || mUserData->s.strOsType == "Ubuntu"
1364 || mUserData->s.strOsType == "Ubuntu_64"
1365 || mUserData->s.strOsType == "Xandros"
1366 || mUserData->s.strOsType == "Xandros_64")
1367 {
1368 *aParavirtProvider = ParavirtProvider_KVM;
1369 }
1370 else
1371 *aParavirtProvider = ParavirtProvider_None;
1372 break;
1373 }
1374
1375 default: AssertFailedBreak(); /* Shut up MSC. */
1376 }
1377 break;
1378 }
1379 }
1380
1381 Assert( *aParavirtProvider == ParavirtProvider_None
1382 || *aParavirtProvider == ParavirtProvider_Minimal
1383 || *aParavirtProvider == ParavirtProvider_HyperV
1384 || *aParavirtProvider == ParavirtProvider_KVM);
1385 return S_OK;
1386}
1387
1388HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1389{
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 aHardwareVersion = mHWData->mHWVersion;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1398{
1399 /* check known version */
1400 Utf8Str hwVersion = aHardwareVersion;
1401 if ( hwVersion.compare("1") != 0
1402 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1403 return setError(E_INVALIDARG,
1404 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1405
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mHWVersion = aHardwareVersion;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 if (!mHWData->mHardwareUUID.isZero())
1423 aHardwareUUID = mHWData->mHardwareUUID;
1424 else
1425 aHardwareUUID = mData->mUuid;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1431{
1432 if (!aHardwareUUID.isValid())
1433 return E_INVALIDARG;
1434
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 if (aHardwareUUID == mData->mUuid)
1443 mHWData->mHardwareUUID.clear();
1444 else
1445 mHWData->mHardwareUUID = aHardwareUUID;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aMemorySize = mHWData->mMemorySize;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setMemorySize(ULONG aMemorySize)
1460{
1461 /* check RAM limits */
1462 if ( aMemorySize < MM_RAM_MIN_IN_MB
1463 || aMemorySize > MM_RAM_MAX_IN_MB
1464 )
1465 return setError(E_INVALIDARG,
1466 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1467 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mMemorySize = aMemorySize;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUCount = mHWData->mCPUCount;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUCount(ULONG aCPUCount)
1491{
1492 /* check CPU limits */
1493 if ( aCPUCount < SchemaDefs::MinCPUCount
1494 || aCPUCount > SchemaDefs::MaxCPUCount
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1498 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1503 if (mHWData->mCPUHotPlugEnabled)
1504 {
1505 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1506 {
1507 if (mHWData->mCPUAttached[idx])
1508 return setError(E_INVALIDARG,
1509 tr("There is still a CPU attached to socket %lu."
1510 "Detach the CPU before removing the socket"),
1511 aCPUCount, idx+1);
1512 }
1513 }
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mCPUCount = aCPUCount;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1535{
1536 HRESULT rc = S_OK;
1537
1538 /* check throttle limits */
1539 if ( aCPUExecutionCap < 1
1540 || aCPUExecutionCap > 100
1541 )
1542 return setError(E_INVALIDARG,
1543 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1544 aCPUExecutionCap, 1, 100);
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 alock.release();
1549 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1550 alock.acquire();
1551 if (FAILED(rc)) return rc;
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1556
1557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1558 if (Global::IsOnline(mData->mMachineState))
1559 i_saveSettings(NULL);
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1574{
1575 HRESULT rc = S_OK;
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 rc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(rc)) return rc;
1581
1582 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1583 {
1584 if (aCPUHotPlugEnabled)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588
1589 /* Add the amount of CPUs currently attached */
1590 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1591 mHWData->mCPUAttached[i] = true;
1592 }
1593 else
1594 {
1595 /*
1596 * We can disable hotplug only if the amount of maximum CPUs is equal
1597 * to the amount of attached CPUs
1598 */
1599 unsigned cCpusAttached = 0;
1600 unsigned iHighestId = 0;
1601
1602 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1603 {
1604 if (mHWData->mCPUAttached[i])
1605 {
1606 cCpusAttached++;
1607 iHighestId = i;
1608 }
1609 }
1610
1611 if ( (cCpusAttached != mHWData->mCPUCount)
1612 || (iHighestId >= mHWData->mCPUCount))
1613 return setError(E_INVALIDARG,
1614 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 }
1619 }
1620
1621 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1622
1623 return rc;
1624}
1625
1626HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1636{
1637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1640 if (SUCCEEDED(hrc))
1641 {
1642 i_setModified(IsModified_MachineData);
1643 mHWData.backup();
1644 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1645 }
1646 return hrc;
1647}
1648
1649HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1650{
1651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 aCPUProfile = mHWData->mCpuProfile;
1653 return S_OK;
1654}
1655
1656HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1657{
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1660 if (SUCCEEDED(hrc))
1661 {
1662 i_setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 /* Empty equals 'host'. */
1665 if (aCPUProfile.isNotEmpty())
1666 mHWData->mCpuProfile = aCPUProfile;
1667 else
1668 mHWData->mCpuProfile = "host";
1669 }
1670 return hrc;
1671}
1672
1673HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1679
1680 return S_OK;
1681#else
1682 NOREF(aEmulatedUSBCardReaderEnabled);
1683 return E_NOTIMPL;
1684#endif
1685}
1686
1687HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1688{
1689#ifdef VBOX_WITH_USB_CARDREADER
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1698
1699 return S_OK;
1700#else
1701 NOREF(aEmulatedUSBCardReaderEnabled);
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *aHPETEnabled = mHWData->mHPETEnabled;
1711
1712 return S_OK;
1713}
1714
1715HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 rc = i_checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 i_setModified(IsModified_MachineData);
1725 mHWData.backup();
1726
1727 mHWData->mHPETEnabled = aHPETEnabled;
1728
1729 return rc;
1730}
1731
1732HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1742{
1743 switch (aGraphicsControllerType)
1744 {
1745 case GraphicsControllerType_Null:
1746 case GraphicsControllerType_VBoxVGA:
1747#ifdef VBOX_WITH_VMSVGA
1748 case GraphicsControllerType_VMSVGA:
1749 case GraphicsControllerType_VBoxSVGA:
1750#endif
1751 break;
1752 default:
1753 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1754 }
1755
1756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 HRESULT rc = i_checkStateDependency(MutableStateDep);
1759 if (FAILED(rc)) return rc;
1760
1761 i_setModified(IsModified_MachineData);
1762 mHWData.backup();
1763 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1769{
1770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 *aVRAMSize = mHWData->mVRAMSize;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1778{
1779 /* check VRAM limits */
1780 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1781 return setError(E_INVALIDARG,
1782 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1783 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 HRESULT rc = i_checkStateDependency(MutableStateDep);
1788 if (FAILED(rc)) return rc;
1789
1790 i_setModified(IsModified_MachineData);
1791 mHWData.backup();
1792 mHWData->mVRAMSize = aVRAMSize;
1793
1794 return S_OK;
1795}
1796
1797/** @todo this method should not be public */
1798HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1803
1804 return S_OK;
1805}
1806
1807/**
1808 * Set the memory balloon size.
1809 *
1810 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1811 * we have to make sure that we never call IGuest from here.
1812 */
1813HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1814{
1815 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1816#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1817 /* check limits */
1818 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1819 return setError(E_INVALIDARG,
1820 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1821 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 i_setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1828
1829 return S_OK;
1830#else
1831 NOREF(aMemoryBalloonSize);
1832 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1837{
1838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1845{
1846#ifdef VBOX_WITH_PAGE_SHARING
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1853 return S_OK;
1854#else
1855 NOREF(aPageFusionEnabled);
1856 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1857#endif
1858}
1859
1860HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 HRESULT rc = i_checkStateDependency(MutableStateDep);
1874 if (FAILED(rc)) return rc;
1875
1876 /** @todo check validity! */
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1881
1882 return S_OK;
1883}
1884
1885
1886HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1896{
1897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 HRESULT rc = i_checkStateDependency(MutableStateDep);
1900 if (FAILED(rc)) return rc;
1901
1902 /** @todo check validity! */
1903 i_setModified(IsModified_MachineData);
1904 mHWData.backup();
1905 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1906
1907 return S_OK;
1908}
1909
1910HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1911{
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 *aMonitorCount = mHWData->mMonitorCount;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1920{
1921 /* make sure monitor count is a sensible number */
1922 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1923 return setError(E_INVALIDARG,
1924 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1925 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1926
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mMonitorCount = aMonitorCount;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1940{
1941 /* mBIOSSettings is constant during life time, no need to lock */
1942 aBIOSSettings = mBIOSSettings;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1948{
1949 /* mRecordingSettings is constant during life time, no need to lock */
1950 aRecordingSettings = mRecordingSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1956{
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 switch (aProperty)
1960 {
1961 case CPUPropertyType_PAE:
1962 *aValue = mHWData->mPAEEnabled;
1963 break;
1964
1965 case CPUPropertyType_LongMode:
1966 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1967 *aValue = TRUE;
1968 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1969 *aValue = FALSE;
1970#if HC_ARCH_BITS == 64
1971 else
1972 *aValue = TRUE;
1973#else
1974 else
1975 {
1976 *aValue = FALSE;
1977
1978 ComObjPtr<GuestOSType> pGuestOSType;
1979 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1980 pGuestOSType);
1981 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1982 {
1983 if (pGuestOSType->i_is64Bit())
1984 {
1985 ComObjPtr<Host> pHost = mParent->i_host();
1986 alock.release();
1987
1988 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1989 if (FAILED(hrc2))
1990 *aValue = FALSE;
1991 }
1992 }
1993 }
1994#endif
1995 break;
1996
1997 case CPUPropertyType_TripleFaultReset:
1998 *aValue = mHWData->mTripleFaultReset;
1999 break;
2000
2001 case CPUPropertyType_APIC:
2002 *aValue = mHWData->mAPIC;
2003 break;
2004
2005 case CPUPropertyType_X2APIC:
2006 *aValue = mHWData->mX2APIC;
2007 break;
2008
2009 case CPUPropertyType_IBPBOnVMExit:
2010 *aValue = mHWData->mIBPBOnVMExit;
2011 break;
2012
2013 case CPUPropertyType_IBPBOnVMEntry:
2014 *aValue = mHWData->mIBPBOnVMEntry;
2015 break;
2016
2017 case CPUPropertyType_SpecCtrl:
2018 *aValue = mHWData->mSpecCtrl;
2019 break;
2020
2021 case CPUPropertyType_SpecCtrlByHost:
2022 *aValue = mHWData->mSpecCtrlByHost;
2023 break;
2024
2025 case CPUPropertyType_HWVirt:
2026 *aValue = mHWData->mNestedHWVirt;
2027 break;
2028
2029 case CPUPropertyType_L1DFlushOnEMTScheduling:
2030 *aValue = mHWData->mL1DFlushOnSched;
2031 break;
2032
2033 case CPUPropertyType_L1DFlushOnVMEntry:
2034 *aValue = mHWData->mL1DFlushOnVMEntry;
2035 break;
2036
2037 default:
2038 return E_INVALIDARG;
2039 }
2040 return S_OK;
2041}
2042
2043HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2044{
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = i_checkStateDependency(MutableStateDep);
2048 if (FAILED(rc)) return rc;
2049
2050 switch (aProperty)
2051 {
2052 case CPUPropertyType_PAE:
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mPAEEnabled = !!aValue;
2056 break;
2057
2058 case CPUPropertyType_LongMode:
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2062 break;
2063
2064 case CPUPropertyType_TripleFaultReset:
2065 i_setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mTripleFaultReset = !!aValue;
2068 break;
2069
2070 case CPUPropertyType_APIC:
2071 if (mHWData->mX2APIC)
2072 aValue = TRUE;
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mAPIC = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_X2APIC:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mX2APIC = !!aValue;
2082 if (aValue)
2083 mHWData->mAPIC = !!aValue;
2084 break;
2085
2086 case CPUPropertyType_IBPBOnVMExit:
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mIBPBOnVMExit = !!aValue;
2090 break;
2091
2092 case CPUPropertyType_IBPBOnVMEntry:
2093 i_setModified(IsModified_MachineData);
2094 mHWData.backup();
2095 mHWData->mIBPBOnVMEntry = !!aValue;
2096 break;
2097
2098 case CPUPropertyType_SpecCtrl:
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mSpecCtrl = !!aValue;
2102 break;
2103
2104 case CPUPropertyType_SpecCtrlByHost:
2105 i_setModified(IsModified_MachineData);
2106 mHWData.backup();
2107 mHWData->mSpecCtrlByHost = !!aValue;
2108 break;
2109
2110 case CPUPropertyType_HWVirt:
2111 i_setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mNestedHWVirt = !!aValue;
2114 break;
2115
2116 case CPUPropertyType_L1DFlushOnEMTScheduling:
2117 i_setModified(IsModified_MachineData);
2118 mHWData.backup();
2119 mHWData->mL1DFlushOnSched = !!aValue;
2120 break;
2121
2122 case CPUPropertyType_L1DFlushOnVMEntry:
2123 i_setModified(IsModified_MachineData);
2124 mHWData.backup();
2125 mHWData->mL1DFlushOnVMEntry = !!aValue;
2126 break;
2127
2128 default:
2129 return E_INVALIDARG;
2130 }
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2135 ULONG *aValEcx, ULONG *aValEdx)
2136{
2137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2138 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2139 {
2140 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2141 it != mHWData->mCpuIdLeafList.end();
2142 ++it)
2143 {
2144 if (aOrdinal == 0)
2145 {
2146 const settings::CpuIdLeaf &rLeaf= *it;
2147 *aIdx = rLeaf.idx;
2148 *aSubIdx = rLeaf.idxSub;
2149 *aValEax = rLeaf.uEax;
2150 *aValEbx = rLeaf.uEbx;
2151 *aValEcx = rLeaf.uEcx;
2152 *aValEdx = rLeaf.uEdx;
2153 return S_OK;
2154 }
2155 aOrdinal--;
2156 }
2157 }
2158 return E_INVALIDARG;
2159}
2160
2161HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2162{
2163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 /*
2166 * Search the list.
2167 */
2168 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2169 {
2170 const settings::CpuIdLeaf &rLeaf= *it;
2171 if ( rLeaf.idx == aIdx
2172 && ( aSubIdx == UINT32_MAX
2173 || rLeaf.idxSub == aSubIdx) )
2174 {
2175 *aValEax = rLeaf.uEax;
2176 *aValEbx = rLeaf.uEbx;
2177 *aValEcx = rLeaf.uEcx;
2178 *aValEdx = rLeaf.uEdx;
2179 return S_OK;
2180 }
2181 }
2182
2183 return E_INVALIDARG;
2184}
2185
2186
2187HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2188{
2189 /*
2190 * Validate input before taking locks and checking state.
2191 */
2192 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2193 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2194 if ( aIdx >= UINT32_C(0x20)
2195 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2196 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2197 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2198
2199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2200 HRESULT rc = i_checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 /*
2204 * Impose a maximum number of leaves.
2205 */
2206 if (mHWData->mCpuIdLeafList.size() > 256)
2207 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2208
2209 /*
2210 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2211 */
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214
2215 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2216 {
2217 settings::CpuIdLeaf &rLeaf= *it;
2218 if ( rLeaf.idx == aIdx
2219 && ( aSubIdx == UINT32_MAX
2220 || rLeaf.idxSub == aSubIdx) )
2221 it = mHWData->mCpuIdLeafList.erase(it);
2222 else
2223 ++it;
2224 }
2225
2226 settings::CpuIdLeaf NewLeaf;
2227 NewLeaf.idx = aIdx;
2228 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2229 NewLeaf.uEax = aValEax;
2230 NewLeaf.uEbx = aValEbx;
2231 NewLeaf.uEcx = aValEcx;
2232 NewLeaf.uEdx = aValEdx;
2233 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2234 return S_OK;
2235}
2236
2237HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2238{
2239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2240
2241 HRESULT rc = i_checkStateDependency(MutableStateDep);
2242 if (FAILED(rc)) return rc;
2243
2244 /*
2245 * Do the removal.
2246 */
2247 bool fModified = false;
2248 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2249 {
2250 settings::CpuIdLeaf &rLeaf= *it;
2251 if ( rLeaf.idx == aIdx
2252 && ( aSubIdx == UINT32_MAX
2253 || rLeaf.idxSub == aSubIdx) )
2254 {
2255 if (!fModified)
2256 {
2257 fModified = true;
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 }
2261 it = mHWData->mCpuIdLeafList.erase(it);
2262 }
2263 else
2264 ++it;
2265 }
2266
2267 return S_OK;
2268}
2269
2270HRESULT Machine::removeAllCPUIDLeaves()
2271{
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 HRESULT rc = i_checkStateDependency(MutableStateDep);
2275 if (FAILED(rc)) return rc;
2276
2277 if (mHWData->mCpuIdLeafList.size() > 0)
2278 {
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281
2282 mHWData->mCpuIdLeafList.clear();
2283 }
2284
2285 return S_OK;
2286}
2287HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2288{
2289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2290
2291 switch(aProperty)
2292 {
2293 case HWVirtExPropertyType_Enabled:
2294 *aValue = mHWData->mHWVirtExEnabled;
2295 break;
2296
2297 case HWVirtExPropertyType_VPID:
2298 *aValue = mHWData->mHWVirtExVPIDEnabled;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2303 break;
2304
2305 case HWVirtExPropertyType_UnrestrictedExecution:
2306 *aValue = mHWData->mHWVirtExUXEnabled;
2307 break;
2308
2309 case HWVirtExPropertyType_LargePages:
2310 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2311#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2312 *aValue = FALSE;
2313#endif
2314 break;
2315
2316 case HWVirtExPropertyType_Force:
2317 *aValue = mHWData->mHWVirtExForceEnabled;
2318 break;
2319
2320 case HWVirtExPropertyType_UseNativeApi:
2321 *aValue = mHWData->mHWVirtExUseNativeApi;
2322 break;
2323
2324 default:
2325 return E_INVALIDARG;
2326 }
2327 return S_OK;
2328}
2329
2330HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2331{
2332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2333
2334 HRESULT rc = i_checkStateDependency(MutableStateDep);
2335 if (FAILED(rc)) return rc;
2336
2337 switch (aProperty)
2338 {
2339 case HWVirtExPropertyType_Enabled:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mHWVirtExEnabled = !!aValue;
2343 break;
2344
2345 case HWVirtExPropertyType_VPID:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2349 break;
2350
2351 case HWVirtExPropertyType_NestedPaging:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2355 break;
2356
2357 case HWVirtExPropertyType_UnrestrictedExecution:
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mHWVirtExUXEnabled = !!aValue;
2361 break;
2362
2363 case HWVirtExPropertyType_LargePages:
2364 i_setModified(IsModified_MachineData);
2365 mHWData.backup();
2366 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2367 break;
2368
2369 case HWVirtExPropertyType_Force:
2370 i_setModified(IsModified_MachineData);
2371 mHWData.backup();
2372 mHWData->mHWVirtExForceEnabled = !!aValue;
2373 break;
2374
2375 case HWVirtExPropertyType_UseNativeApi:
2376 i_setModified(IsModified_MachineData);
2377 mHWData.backup();
2378 mHWData->mHWVirtExUseNativeApi = !!aValue;
2379 break;
2380
2381 default:
2382 return E_INVALIDARG;
2383 }
2384
2385 return S_OK;
2386}
2387
2388HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2389{
2390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2393
2394 return S_OK;
2395}
2396
2397HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2398{
2399 /** @todo (r=dmik):
2400 * 1. Allow to change the name of the snapshot folder containing snapshots
2401 * 2. Rename the folder on disk instead of just changing the property
2402 * value (to be smart and not to leave garbage). Note that it cannot be
2403 * done here because the change may be rolled back. Thus, the right
2404 * place is #saveSettings().
2405 */
2406
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 if (!mData->mCurrentSnapshot.isNull())
2413 return setError(E_FAIL,
2414 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2415
2416 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2417
2418 if (strSnapshotFolder.isEmpty())
2419 strSnapshotFolder = "Snapshots";
2420 int vrc = i_calculateFullPath(strSnapshotFolder,
2421 strSnapshotFolder);
2422 if (RT_FAILURE(vrc))
2423 return setErrorBoth(E_FAIL, vrc,
2424 tr("Invalid snapshot folder '%s' (%Rrc)"),
2425 strSnapshotFolder.c_str(), vrc);
2426
2427 i_setModified(IsModified_MachineData);
2428 mUserData.backup();
2429
2430 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2431
2432 return S_OK;
2433}
2434
2435HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2436{
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 aMediumAttachments.resize(mMediumAttachments->size());
2440 size_t i = 0;
2441 for (MediumAttachmentList::const_iterator
2442 it = mMediumAttachments->begin();
2443 it != mMediumAttachments->end();
2444 ++it, ++i)
2445 aMediumAttachments[i] = *it;
2446
2447 return S_OK;
2448}
2449
2450HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 Assert(!!mVRDEServer);
2455
2456 aVRDEServer = mVRDEServer;
2457
2458 return S_OK;
2459}
2460
2461HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2462{
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 aAudioAdapter = mAudioAdapter;
2466
2467 return S_OK;
2468}
2469
2470HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2471{
2472#ifdef VBOX_WITH_VUSB
2473 clearError();
2474 MultiResult rc(S_OK);
2475
2476# ifdef VBOX_WITH_USB
2477 rc = mParent->i_host()->i_checkUSBProxyService();
2478 if (FAILED(rc)) return rc;
2479# endif
2480
2481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2482
2483 aUSBControllers.resize(mUSBControllers->size());
2484 size_t i = 0;
2485 for (USBControllerList::const_iterator
2486 it = mUSBControllers->begin();
2487 it != mUSBControllers->end();
2488 ++it, ++i)
2489 aUSBControllers[i] = *it;
2490
2491 return S_OK;
2492#else
2493 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2494 * extended error info to indicate that USB is simply not available
2495 * (w/o treating it as a failure), for example, as in OSE */
2496 NOREF(aUSBControllers);
2497 ReturnComNotImplemented();
2498#endif /* VBOX_WITH_VUSB */
2499}
2500
2501HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2502{
2503#ifdef VBOX_WITH_VUSB
2504 clearError();
2505 MultiResult rc(S_OK);
2506
2507# ifdef VBOX_WITH_USB
2508 rc = mParent->i_host()->i_checkUSBProxyService();
2509 if (FAILED(rc)) return rc;
2510# endif
2511
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 aUSBDeviceFilters = mUSBDeviceFilters;
2515 return rc;
2516#else
2517 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2518 * extended error info to indicate that USB is simply not available
2519 * (w/o treating it as a failure), for example, as in OSE */
2520 NOREF(aUSBDeviceFilters);
2521 ReturnComNotImplemented();
2522#endif /* VBOX_WITH_VUSB */
2523}
2524
2525HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2526{
2527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2528
2529 aSettingsFilePath = mData->m_strConfigFileFull;
2530
2531 return S_OK;
2532}
2533
2534HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2535{
2536 RT_NOREF(aSettingsFilePath);
2537 ReturnComNotImplemented();
2538}
2539
2540HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2545 if (FAILED(rc)) return rc;
2546
2547 if (!mData->pMachineConfigFile->fileExists())
2548 // this is a new machine, and no config file exists yet:
2549 *aSettingsModified = TRUE;
2550 else
2551 *aSettingsModified = (mData->flModifications != 0);
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 *aSessionState = mData->mSession.mState;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 aSessionName = mData->mSession.mName;
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 *aSessionPID = mData->mSession.mPID;
2579
2580 return S_OK;
2581}
2582
2583HRESULT Machine::getState(MachineState_T *aState)
2584{
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 *aState = mData->mMachineState;
2588 Assert(mData->mMachineState != MachineState_Null);
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2594{
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2598
2599 return S_OK;
2600}
2601
2602HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2603{
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 aStateFilePath = mSSData->strStateFilePath;
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 i_getLogFolder(aLogFolder);
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2621{
2622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 aCurrentSnapshot = mData->mCurrentSnapshot;
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2630{
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2634 ? 0
2635 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2641{
2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 /* Note: for machines with no snapshots, we always return FALSE
2645 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2646 * reasons :) */
2647
2648 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2649 ? FALSE
2650 : mData->mCurrentStateModified;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 aSharedFolders.resize(mHWData->mSharedFolders.size());
2660 size_t i = 0;
2661 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2662 it = mHWData->mSharedFolders.begin();
2663 it != mHWData->mSharedFolders.end();
2664 ++it, ++i)
2665 aSharedFolders[i] = *it;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 *aClipboardMode = mHWData->mClipboardMode;
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2680{
2681 HRESULT rc = S_OK;
2682
2683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 alock.release();
2686 rc = i_onClipboardModeChange(aClipboardMode);
2687 alock.acquire();
2688 if (FAILED(rc)) return rc;
2689
2690 i_setModified(IsModified_MachineData);
2691 mHWData.backup();
2692 mHWData->mClipboardMode = aClipboardMode;
2693
2694 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2695 if (Global::IsOnline(mData->mMachineState))
2696 i_saveSettings(NULL);
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aDnDMode = mHWData->mDnDMode;
2706
2707 return S_OK;
2708}
2709
2710HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2711{
2712 HRESULT rc = S_OK;
2713
2714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 alock.release();
2717 rc = i_onDnDModeChange(aDnDMode);
2718
2719 alock.acquire();
2720 if (FAILED(rc)) return rc;
2721
2722 i_setModified(IsModified_MachineData);
2723 mHWData.backup();
2724 mHWData->mDnDMode = aDnDMode;
2725
2726 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2727 if (Global::IsOnline(mData->mMachineState))
2728 i_saveSettings(NULL);
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2734{
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 aStorageControllers.resize(mStorageControllers->size());
2738 size_t i = 0;
2739 for (StorageControllerList::const_iterator
2740 it = mStorageControllers->begin();
2741 it != mStorageControllers->end();
2742 ++it, ++i)
2743 aStorageControllers[i] = *it;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 *aEnabled = mUserData->s.fTeleporterEnabled;
2753
2754 return S_OK;
2755}
2756
2757HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2758{
2759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 /* Only allow it to be set to true when PoweredOff or Aborted.
2762 (Clearing it is always permitted.) */
2763 if ( aTeleporterEnabled
2764 && mData->mRegistered
2765 && ( !i_isSessionMachine()
2766 || ( mData->mMachineState != MachineState_PoweredOff
2767 && mData->mMachineState != MachineState_Teleported
2768 && mData->mMachineState != MachineState_Aborted
2769 )
2770 )
2771 )
2772 return setError(VBOX_E_INVALID_VM_STATE,
2773 tr("The machine is not powered off (state is %s)"),
2774 Global::stringifyMachineState(mData->mMachineState));
2775
2776 i_setModified(IsModified_MachineData);
2777 mUserData.backup();
2778 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2793{
2794 if (aTeleporterPort >= _64K)
2795 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2796
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2800 if (FAILED(rc)) return rc;
2801
2802 i_setModified(IsModified_MachineData);
2803 mUserData.backup();
2804 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2819{
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2823 if (FAILED(rc)) return rc;
2824
2825 i_setModified(IsModified_MachineData);
2826 mUserData.backup();
2827 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2836
2837 return S_OK;
2838}
2839
2840HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2841{
2842 /*
2843 * Hash the password first.
2844 */
2845 com::Utf8Str aT = aTeleporterPassword;
2846
2847 if (!aT.isEmpty())
2848 {
2849 if (VBoxIsPasswordHashed(&aT))
2850 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2851 VBoxHashPassword(&aT);
2852 }
2853
2854 /*
2855 * Do the update.
2856 */
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2859 if (SUCCEEDED(hrc))
2860 {
2861 i_setModified(IsModified_MachineData);
2862 mUserData.backup();
2863 mUserData->s.strTeleporterPassword = aT;
2864 }
2865
2866 return hrc;
2867}
2868
2869HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /** @todo deal with running state change. */
2882 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /** @todo deal with running state change. */
2904 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2905 if (FAILED(rc)) return rc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mUserData.backup();
2909 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2918 return S_OK;
2919}
2920
2921HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2922{
2923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2924
2925 /** @todo deal with running state change. */
2926 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2927 if (FAILED(rc)) return rc;
2928
2929 i_setModified(IsModified_MachineData);
2930 mUserData.backup();
2931 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2945{
2946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2947
2948 /** @todo deal with running state change. */
2949 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2950 if (FAILED(rc)) return rc;
2951
2952 i_setModified(IsModified_MachineData);
2953 mUserData.backup();
2954 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2960{
2961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2964 return S_OK;
2965}
2966
2967HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2968{
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 /** @todo deal with running state change. */
2972 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2973 if (FAILED(rc)) return rc;
2974
2975 i_setModified(IsModified_MachineData);
2976 mUserData.backup();
2977 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2978 return S_OK;
2979}
2980
2981HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2982{
2983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2986
2987 return S_OK;
2988}
2989
2990HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2991{
2992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2993
2994 /* Only allow it to be set to true when PoweredOff or Aborted.
2995 (Clearing it is always permitted.) */
2996 if ( aRTCUseUTC
2997 && mData->mRegistered
2998 && ( !i_isSessionMachine()
2999 || ( mData->mMachineState != MachineState_PoweredOff
3000 && mData->mMachineState != MachineState_Teleported
3001 && mData->mMachineState != MachineState_Aborted
3002 )
3003 )
3004 )
3005 return setError(VBOX_E_INVALID_VM_STATE,
3006 tr("The machine is not powered off (state is %s)"),
3007 Global::stringifyMachineState(mData->mMachineState));
3008
3009 i_setModified(IsModified_MachineData);
3010 mUserData.backup();
3011 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3017{
3018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3021
3022 return S_OK;
3023}
3024
3025HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3026{
3027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3028
3029 HRESULT rc = i_checkStateDependency(MutableStateDep);
3030 if (FAILED(rc)) return rc;
3031
3032 i_setModified(IsModified_MachineData);
3033 mHWData.backup();
3034 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3035
3036 return S_OK;
3037}
3038
3039HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3040{
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 *aIOCacheSize = mHWData->mIOCacheSize;
3044
3045 return S_OK;
3046}
3047
3048HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3049{
3050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3051
3052 HRESULT rc = i_checkStateDependency(MutableStateDep);
3053 if (FAILED(rc)) return rc;
3054
3055 i_setModified(IsModified_MachineData);
3056 mHWData.backup();
3057 mHWData->mIOCacheSize = aIOCacheSize;
3058
3059 return S_OK;
3060}
3061
3062
3063/**
3064 * @note Locks objects!
3065 */
3066HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3067 LockType_T aLockType)
3068{
3069 /* check the session state */
3070 SessionState_T state;
3071 HRESULT rc = aSession->COMGETTER(State)(&state);
3072 if (FAILED(rc)) return rc;
3073
3074 if (state != SessionState_Unlocked)
3075 return setError(VBOX_E_INVALID_OBJECT_STATE,
3076 tr("The given session is busy"));
3077
3078 // get the client's IInternalSessionControl interface
3079 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3080 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3081 E_INVALIDARG);
3082
3083 // session name (only used in some code paths)
3084 Utf8Str strSessionName;
3085
3086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 if (!mData->mRegistered)
3089 return setError(E_UNEXPECTED,
3090 tr("The machine '%s' is not registered"),
3091 mUserData->s.strName.c_str());
3092
3093 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3094
3095 SessionState_T oldState = mData->mSession.mState;
3096 /* Hack: in case the session is closing and there is a progress object
3097 * which allows waiting for the session to be closed, take the opportunity
3098 * and do a limited wait (max. 1 second). This helps a lot when the system
3099 * is busy and thus session closing can take a little while. */
3100 if ( mData->mSession.mState == SessionState_Unlocking
3101 && mData->mSession.mProgress)
3102 {
3103 alock.release();
3104 mData->mSession.mProgress->WaitForCompletion(1000);
3105 alock.acquire();
3106 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3107 }
3108
3109 // try again now
3110 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3111 // (i.e. session machine exists)
3112 && (aLockType == LockType_Shared) // caller wants a shared link to the
3113 // existing session that holds the write lock:
3114 )
3115 {
3116 // OK, share the session... we are now dealing with three processes:
3117 // 1) VBoxSVC (where this code runs);
3118 // 2) process C: the caller's client process (who wants a shared session);
3119 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3120
3121 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3122 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3123 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3124 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3125 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3126
3127 /*
3128 * Release the lock before calling the client process. It's safe here
3129 * since the only thing to do after we get the lock again is to add
3130 * the remote control to the list (which doesn't directly influence
3131 * anything).
3132 */
3133 alock.release();
3134
3135 // get the console of the session holding the write lock (this is a remote call)
3136 ComPtr<IConsole> pConsoleW;
3137 if (mData->mSession.mLockType == LockType_VM)
3138 {
3139 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3140 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3141 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3142 if (FAILED(rc))
3143 // the failure may occur w/o any error info (from RPC), so provide one
3144 return setError(VBOX_E_VM_ERROR,
3145 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3146 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3147 }
3148
3149 // share the session machine and W's console with the caller's session
3150 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3151 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3152 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3153
3154 if (FAILED(rc))
3155 // the failure may occur w/o any error info (from RPC), so provide one
3156 return setError(VBOX_E_VM_ERROR,
3157 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3158 alock.acquire();
3159
3160 // need to revalidate the state after acquiring the lock again
3161 if (mData->mSession.mState != SessionState_Locked)
3162 {
3163 pSessionControl->Uninitialize();
3164 return setError(VBOX_E_INVALID_SESSION_STATE,
3165 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3166 mUserData->s.strName.c_str());
3167 }
3168
3169 // add the caller's session to the list
3170 mData->mSession.mRemoteControls.push_back(pSessionControl);
3171 }
3172 else if ( mData->mSession.mState == SessionState_Locked
3173 || mData->mSession.mState == SessionState_Unlocking
3174 )
3175 {
3176 // sharing not permitted, or machine still unlocking:
3177 return setError(VBOX_E_INVALID_OBJECT_STATE,
3178 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3179 mUserData->s.strName.c_str());
3180 }
3181 else
3182 {
3183 // machine is not locked: then write-lock the machine (create the session machine)
3184
3185 // must not be busy
3186 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3187
3188 // get the caller's session PID
3189 RTPROCESS pid = NIL_RTPROCESS;
3190 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3191 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3192 Assert(pid != NIL_RTPROCESS);
3193
3194 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3195
3196 if (fLaunchingVMProcess)
3197 {
3198 if (mData->mSession.mPID == NIL_RTPROCESS)
3199 {
3200 // two or more clients racing for a lock, the one which set the
3201 // session state to Spawning will win, the others will get an
3202 // error as we can't decide here if waiting a little would help
3203 // (only for shared locks this would avoid an error)
3204 return setError(VBOX_E_INVALID_OBJECT_STATE,
3205 tr("The machine '%s' already has a lock request pending"),
3206 mUserData->s.strName.c_str());
3207 }
3208
3209 // this machine is awaiting for a spawning session to be opened:
3210 // then the calling process must be the one that got started by
3211 // LaunchVMProcess()
3212
3213 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3214 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3215
3216#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3217 /* Hardened windows builds spawns three processes when a VM is
3218 launched, the 3rd one is the one that will end up here. */
3219 RTPROCESS ppid;
3220 int rc = RTProcQueryParent(pid, &ppid);
3221 if (RT_SUCCESS(rc))
3222 rc = RTProcQueryParent(ppid, &ppid);
3223 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3224 || rc == VERR_ACCESS_DENIED)
3225 {
3226 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3227 mData->mSession.mPID = pid;
3228 }
3229#endif
3230
3231 if (mData->mSession.mPID != pid)
3232 return setError(E_ACCESSDENIED,
3233 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3234 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3235 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3236 }
3237
3238 // create the mutable SessionMachine from the current machine
3239 ComObjPtr<SessionMachine> sessionMachine;
3240 sessionMachine.createObject();
3241 rc = sessionMachine->init(this);
3242 AssertComRC(rc);
3243
3244 /* NOTE: doing return from this function after this point but
3245 * before the end is forbidden since it may call SessionMachine::uninit()
3246 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3247 * lock while still holding the Machine lock in alock so that a deadlock
3248 * is possible due to the wrong lock order. */
3249
3250 if (SUCCEEDED(rc))
3251 {
3252 /*
3253 * Set the session state to Spawning to protect against subsequent
3254 * attempts to open a session and to unregister the machine after
3255 * we release the lock.
3256 */
3257 SessionState_T origState = mData->mSession.mState;
3258 mData->mSession.mState = SessionState_Spawning;
3259
3260#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3261 /* Get the client token ID to be passed to the client process */
3262 Utf8Str strTokenId;
3263 sessionMachine->i_getTokenId(strTokenId);
3264 Assert(!strTokenId.isEmpty());
3265#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3266 /* Get the client token to be passed to the client process */
3267 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3268 /* The token is now "owned" by pToken, fix refcount */
3269 if (!pToken.isNull())
3270 pToken->Release();
3271#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3272
3273 /*
3274 * Release the lock before calling the client process -- it will call
3275 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3276 * because the state is Spawning, so that LaunchVMProcess() and
3277 * LockMachine() calls will fail. This method, called before we
3278 * acquire the lock again, will fail because of the wrong PID.
3279 *
3280 * Note that mData->mSession.mRemoteControls accessed outside
3281 * the lock may not be modified when state is Spawning, so it's safe.
3282 */
3283 alock.release();
3284
3285 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3286#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3287 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3288#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3289 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3290 /* Now the token is owned by the client process. */
3291 pToken.setNull();
3292#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3293 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3294
3295 /* The failure may occur w/o any error info (from RPC), so provide one */
3296 if (FAILED(rc))
3297 setError(VBOX_E_VM_ERROR,
3298 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3299
3300 // get session name, either to remember or to compare against
3301 // the already known session name.
3302 {
3303 Bstr bstrSessionName;
3304 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3305 if (SUCCEEDED(rc2))
3306 strSessionName = bstrSessionName;
3307 }
3308
3309 if ( SUCCEEDED(rc)
3310 && fLaunchingVMProcess
3311 )
3312 {
3313 /* complete the remote session initialization */
3314
3315 /* get the console from the direct session */
3316 ComPtr<IConsole> console;
3317 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3318 ComAssertComRC(rc);
3319
3320 if (SUCCEEDED(rc) && !console)
3321 {
3322 ComAssert(!!console);
3323 rc = E_FAIL;
3324 }
3325
3326 /* assign machine & console to the remote session */
3327 if (SUCCEEDED(rc))
3328 {
3329 /*
3330 * after LaunchVMProcess(), the first and the only
3331 * entry in remoteControls is that remote session
3332 */
3333 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3334 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3335 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3336
3337 /* The failure may occur w/o any error info (from RPC), so provide one */
3338 if (FAILED(rc))
3339 setError(VBOX_E_VM_ERROR,
3340 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3341 }
3342
3343 if (FAILED(rc))
3344 pSessionControl->Uninitialize();
3345 }
3346
3347 /* acquire the lock again */
3348 alock.acquire();
3349
3350 /* Restore the session state */
3351 mData->mSession.mState = origState;
3352 }
3353
3354 // finalize spawning anyway (this is why we don't return on errors above)
3355 if (fLaunchingVMProcess)
3356 {
3357 Assert(mData->mSession.mName == strSessionName);
3358 /* Note that the progress object is finalized later */
3359 /** @todo Consider checking mData->mSession.mProgress for cancellation
3360 * around here. */
3361
3362 /* We don't reset mSession.mPID here because it is necessary for
3363 * SessionMachine::uninit() to reap the child process later. */
3364
3365 if (FAILED(rc))
3366 {
3367 /* Close the remote session, remove the remote control from the list
3368 * and reset session state to Closed (@note keep the code in sync
3369 * with the relevant part in checkForSpawnFailure()). */
3370
3371 Assert(mData->mSession.mRemoteControls.size() == 1);
3372 if (mData->mSession.mRemoteControls.size() == 1)
3373 {
3374 ErrorInfoKeeper eik;
3375 mData->mSession.mRemoteControls.front()->Uninitialize();
3376 }
3377
3378 mData->mSession.mRemoteControls.clear();
3379 mData->mSession.mState = SessionState_Unlocked;
3380 }
3381 }
3382 else
3383 {
3384 /* memorize PID of the directly opened session */
3385 if (SUCCEEDED(rc))
3386 mData->mSession.mPID = pid;
3387 }
3388
3389 if (SUCCEEDED(rc))
3390 {
3391 mData->mSession.mLockType = aLockType;
3392 /* memorize the direct session control and cache IUnknown for it */
3393 mData->mSession.mDirectControl = pSessionControl;
3394 mData->mSession.mState = SessionState_Locked;
3395 if (!fLaunchingVMProcess)
3396 mData->mSession.mName = strSessionName;
3397 /* associate the SessionMachine with this Machine */
3398 mData->mSession.mMachine = sessionMachine;
3399
3400 /* request an IUnknown pointer early from the remote party for later
3401 * identity checks (it will be internally cached within mDirectControl
3402 * at least on XPCOM) */
3403 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3404 NOREF(unk);
3405 }
3406
3407 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3408 * would break the lock order */
3409 alock.release();
3410
3411 /* uninitialize the created session machine on failure */
3412 if (FAILED(rc))
3413 sessionMachine->uninit();
3414 }
3415
3416 if (SUCCEEDED(rc))
3417 {
3418 /*
3419 * tell the client watcher thread to update the set of
3420 * machines that have open sessions
3421 */
3422 mParent->i_updateClientWatcher();
3423
3424 if (oldState != SessionState_Locked)
3425 /* fire an event */
3426 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3427 }
3428
3429 return rc;
3430}
3431
3432/**
3433 * @note Locks objects!
3434 */
3435HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3436 const com::Utf8Str &aName,
3437 const com::Utf8Str &aEnvironment,
3438 ComPtr<IProgress> &aProgress)
3439{
3440 Utf8Str strFrontend(aName);
3441 /* "emergencystop" doesn't need the session, so skip the checks/interface
3442 * retrieval. This code doesn't quite fit in here, but introducing a
3443 * special API method would be even more effort, and would require explicit
3444 * support by every API client. It's better to hide the feature a bit. */
3445 if (strFrontend != "emergencystop")
3446 CheckComArgNotNull(aSession);
3447
3448 HRESULT rc = S_OK;
3449 if (strFrontend.isEmpty())
3450 {
3451 Bstr bstrFrontend;
3452 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3453 if (FAILED(rc))
3454 return rc;
3455 strFrontend = bstrFrontend;
3456 if (strFrontend.isEmpty())
3457 {
3458 ComPtr<ISystemProperties> systemProperties;
3459 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3460 if (FAILED(rc))
3461 return rc;
3462 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3463 if (FAILED(rc))
3464 return rc;
3465 strFrontend = bstrFrontend;
3466 }
3467 /* paranoia - emergencystop is not a valid default */
3468 if (strFrontend == "emergencystop")
3469 strFrontend = Utf8Str::Empty;
3470 }
3471 /* default frontend: Qt GUI */
3472 if (strFrontend.isEmpty())
3473 strFrontend = "GUI/Qt";
3474
3475 if (strFrontend != "emergencystop")
3476 {
3477 /* check the session state */
3478 SessionState_T state;
3479 rc = aSession->COMGETTER(State)(&state);
3480 if (FAILED(rc))
3481 return rc;
3482
3483 if (state != SessionState_Unlocked)
3484 return setError(VBOX_E_INVALID_OBJECT_STATE,
3485 tr("The given session is busy"));
3486
3487 /* get the IInternalSessionControl interface */
3488 ComPtr<IInternalSessionControl> control(aSession);
3489 ComAssertMsgRet(!control.isNull(),
3490 ("No IInternalSessionControl interface"),
3491 E_INVALIDARG);
3492
3493 /* get the teleporter enable state for the progress object init. */
3494 BOOL fTeleporterEnabled;
3495 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3496 if (FAILED(rc))
3497 return rc;
3498
3499 /* create a progress object */
3500 ComObjPtr<ProgressProxy> progress;
3501 progress.createObject();
3502 rc = progress->init(mParent,
3503 static_cast<IMachine*>(this),
3504 Bstr(tr("Starting VM")).raw(),
3505 TRUE /* aCancelable */,
3506 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3507 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3508 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3509 2 /* uFirstOperationWeight */,
3510 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3511
3512 if (SUCCEEDED(rc))
3513 {
3514 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3515 if (SUCCEEDED(rc))
3516 {
3517 aProgress = progress;
3518
3519 /* signal the client watcher thread */
3520 mParent->i_updateClientWatcher();
3521
3522 /* fire an event */
3523 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3524 }
3525 }
3526 }
3527 else
3528 {
3529 /* no progress object - either instant success or failure */
3530 aProgress = NULL;
3531
3532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3533
3534 if (mData->mSession.mState != SessionState_Locked)
3535 return setError(VBOX_E_INVALID_OBJECT_STATE,
3536 tr("The machine '%s' is not locked by a session"),
3537 mUserData->s.strName.c_str());
3538
3539 /* must have a VM process associated - do not kill normal API clients
3540 * with an open session */
3541 if (!Global::IsOnline(mData->mMachineState))
3542 return setError(VBOX_E_INVALID_OBJECT_STATE,
3543 tr("The machine '%s' does not have a VM process"),
3544 mUserData->s.strName.c_str());
3545
3546 /* forcibly terminate the VM process */
3547 if (mData->mSession.mPID != NIL_RTPROCESS)
3548 RTProcTerminate(mData->mSession.mPID);
3549
3550 /* signal the client watcher thread, as most likely the client has
3551 * been terminated */
3552 mParent->i_updateClientWatcher();
3553 }
3554
3555 return rc;
3556}
3557
3558HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3559{
3560 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3561 return setError(E_INVALIDARG,
3562 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3563 aPosition, SchemaDefs::MaxBootPosition);
3564
3565 if (aDevice == DeviceType_USB)
3566 return setError(E_NOTIMPL,
3567 tr("Booting from USB device is currently not supported"));
3568
3569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3570
3571 HRESULT rc = i_checkStateDependency(MutableStateDep);
3572 if (FAILED(rc)) return rc;
3573
3574 i_setModified(IsModified_MachineData);
3575 mHWData.backup();
3576 mHWData->mBootOrder[aPosition - 1] = aDevice;
3577
3578 return S_OK;
3579}
3580
3581HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3582{
3583 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3584 return setError(E_INVALIDARG,
3585 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3586 aPosition, SchemaDefs::MaxBootPosition);
3587
3588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3589
3590 *aDevice = mHWData->mBootOrder[aPosition - 1];
3591
3592 return S_OK;
3593}
3594
3595HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3596 LONG aControllerPort,
3597 LONG aDevice,
3598 DeviceType_T aType,
3599 const ComPtr<IMedium> &aMedium)
3600{
3601 IMedium *aM = aMedium;
3602 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3603 aName.c_str(), aControllerPort, aDevice, aType, aM));
3604
3605 // request the host lock first, since might be calling Host methods for getting host drives;
3606 // next, protect the media tree all the while we're in here, as well as our member variables
3607 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3608 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3609
3610 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3611 if (FAILED(rc)) return rc;
3612
3613 /// @todo NEWMEDIA implicit machine registration
3614 if (!mData->mRegistered)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("Cannot attach storage devices to an unregistered machine"));
3617
3618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3619
3620 /* Check for an existing controller. */
3621 ComObjPtr<StorageController> ctl;
3622 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3623 if (FAILED(rc)) return rc;
3624
3625 StorageControllerType_T ctrlType;
3626 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3627 if (FAILED(rc))
3628 return setError(E_FAIL,
3629 tr("Could not get type of controller '%s'"),
3630 aName.c_str());
3631
3632 bool fSilent = false;
3633 Utf8Str strReconfig;
3634
3635 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3636 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3637 if ( mData->mMachineState == MachineState_Paused
3638 && strReconfig == "1")
3639 fSilent = true;
3640
3641 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3642 bool fHotplug = false;
3643 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3644 fHotplug = true;
3645
3646 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3647 return setError(VBOX_E_INVALID_VM_STATE,
3648 tr("Controller '%s' does not support hotplugging"),
3649 aName.c_str());
3650
3651 // check that the port and device are not out of range
3652 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3653 if (FAILED(rc)) return rc;
3654
3655 /* check if the device slot is already busy */
3656 MediumAttachment *pAttachTemp;
3657 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3658 aName,
3659 aControllerPort,
3660 aDevice)))
3661 {
3662 Medium *pMedium = pAttachTemp->i_getMedium();
3663 if (pMedium)
3664 {
3665 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3666 return setError(VBOX_E_OBJECT_IN_USE,
3667 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3668 pMedium->i_getLocationFull().c_str(),
3669 aControllerPort,
3670 aDevice,
3671 aName.c_str());
3672 }
3673 else
3674 return setError(VBOX_E_OBJECT_IN_USE,
3675 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3676 aControllerPort, aDevice, aName.c_str());
3677 }
3678
3679 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3680 if (aMedium && medium.isNull())
3681 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3682
3683 AutoCaller mediumCaller(medium);
3684 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3685
3686 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3687
3688 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3689 && !medium.isNull()
3690 )
3691 return setError(VBOX_E_OBJECT_IN_USE,
3692 tr("Medium '%s' is already attached to this virtual machine"),
3693 medium->i_getLocationFull().c_str());
3694
3695 if (!medium.isNull())
3696 {
3697 MediumType_T mtype = medium->i_getType();
3698 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3699 // For DVDs it's not written to the config file, so needs no global config
3700 // version bump. For floppies it's a new attribute "type", which is ignored
3701 // by older VirtualBox version, so needs no global config version bump either.
3702 // For hard disks this type is not accepted.
3703 if (mtype == MediumType_MultiAttach)
3704 {
3705 // This type is new with VirtualBox 4.0 and therefore requires settings
3706 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3707 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3708 // two reasons: The medium type is a property of the media registry tree, which
3709 // can reside in the global config file (for pre-4.0 media); we would therefore
3710 // possibly need to bump the global config version. We don't want to do that though
3711 // because that might make downgrading to pre-4.0 impossible.
3712 // As a result, we can only use these two new types if the medium is NOT in the
3713 // global registry:
3714 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3715 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3716 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3717 )
3718 return setError(VBOX_E_INVALID_OBJECT_STATE,
3719 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3720 "to machines that were created with VirtualBox 4.0 or later"),
3721 medium->i_getLocationFull().c_str());
3722 }
3723 }
3724
3725 bool fIndirect = false;
3726 if (!medium.isNull())
3727 fIndirect = medium->i_isReadOnly();
3728 bool associate = true;
3729
3730 do
3731 {
3732 if ( aType == DeviceType_HardDisk
3733 && mMediumAttachments.isBackedUp())
3734 {
3735 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3736
3737 /* check if the medium was attached to the VM before we started
3738 * changing attachments in which case the attachment just needs to
3739 * be restored */
3740 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3741 {
3742 AssertReturn(!fIndirect, E_FAIL);
3743
3744 /* see if it's the same bus/channel/device */
3745 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3746 {
3747 /* the simplest case: restore the whole attachment
3748 * and return, nothing else to do */
3749 mMediumAttachments->push_back(pAttachTemp);
3750
3751 /* Reattach the medium to the VM. */
3752 if (fHotplug || fSilent)
3753 {
3754 mediumLock.release();
3755 treeLock.release();
3756 alock.release();
3757
3758 MediumLockList *pMediumLockList(new MediumLockList());
3759
3760 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3761 medium /* pToLockWrite */,
3762 false /* fMediumLockWriteAll */,
3763 NULL,
3764 *pMediumLockList);
3765 alock.acquire();
3766 if (FAILED(rc))
3767 delete pMediumLockList;
3768 else
3769 {
3770 mData->mSession.mLockedMedia.Unlock();
3771 alock.release();
3772 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3773 mData->mSession.mLockedMedia.Lock();
3774 alock.acquire();
3775 }
3776 alock.release();
3777
3778 if (SUCCEEDED(rc))
3779 {
3780 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3781 /* Remove lock list in case of error. */
3782 if (FAILED(rc))
3783 {
3784 mData->mSession.mLockedMedia.Unlock();
3785 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3786 mData->mSession.mLockedMedia.Lock();
3787 }
3788 }
3789 }
3790
3791 return S_OK;
3792 }
3793
3794 /* bus/channel/device differ; we need a new attachment object,
3795 * but don't try to associate it again */
3796 associate = false;
3797 break;
3798 }
3799 }
3800
3801 /* go further only if the attachment is to be indirect */
3802 if (!fIndirect)
3803 break;
3804
3805 /* perform the so called smart attachment logic for indirect
3806 * attachments. Note that smart attachment is only applicable to base
3807 * hard disks. */
3808
3809 if (medium->i_getParent().isNull())
3810 {
3811 /* first, investigate the backup copy of the current hard disk
3812 * attachments to make it possible to re-attach existing diffs to
3813 * another device slot w/o losing their contents */
3814 if (mMediumAttachments.isBackedUp())
3815 {
3816 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3817
3818 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3819 uint32_t foundLevel = 0;
3820
3821 for (MediumAttachmentList::const_iterator
3822 it = oldAtts.begin();
3823 it != oldAtts.end();
3824 ++it)
3825 {
3826 uint32_t level = 0;
3827 MediumAttachment *pAttach = *it;
3828 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3829 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3830 if (pMedium.isNull())
3831 continue;
3832
3833 if (pMedium->i_getBase(&level) == medium)
3834 {
3835 /* skip the hard disk if its currently attached (we
3836 * cannot attach the same hard disk twice) */
3837 if (i_findAttachment(*mMediumAttachments.data(),
3838 pMedium))
3839 continue;
3840
3841 /* matched device, channel and bus (i.e. attached to the
3842 * same place) will win and immediately stop the search;
3843 * otherwise the attachment that has the youngest
3844 * descendant of medium will be used
3845 */
3846 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3847 {
3848 /* the simplest case: restore the whole attachment
3849 * and return, nothing else to do */
3850 mMediumAttachments->push_back(*it);
3851
3852 /* Reattach the medium to the VM. */
3853 if (fHotplug || fSilent)
3854 {
3855 mediumLock.release();
3856 treeLock.release();
3857 alock.release();
3858
3859 MediumLockList *pMediumLockList(new MediumLockList());
3860
3861 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3862 medium /* pToLockWrite */,
3863 false /* fMediumLockWriteAll */,
3864 NULL,
3865 *pMediumLockList);
3866 alock.acquire();
3867 if (FAILED(rc))
3868 delete pMediumLockList;
3869 else
3870 {
3871 mData->mSession.mLockedMedia.Unlock();
3872 alock.release();
3873 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3874 mData->mSession.mLockedMedia.Lock();
3875 alock.acquire();
3876 }
3877 alock.release();
3878
3879 if (SUCCEEDED(rc))
3880 {
3881 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3882 /* Remove lock list in case of error. */
3883 if (FAILED(rc))
3884 {
3885 mData->mSession.mLockedMedia.Unlock();
3886 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3887 mData->mSession.mLockedMedia.Lock();
3888 }
3889 }
3890 }
3891
3892 return S_OK;
3893 }
3894 else if ( foundIt == oldAtts.end()
3895 || level > foundLevel /* prefer younger */
3896 )
3897 {
3898 foundIt = it;
3899 foundLevel = level;
3900 }
3901 }
3902 }
3903
3904 if (foundIt != oldAtts.end())
3905 {
3906 /* use the previously attached hard disk */
3907 medium = (*foundIt)->i_getMedium();
3908 mediumCaller.attach(medium);
3909 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3910 mediumLock.attach(medium);
3911 /* not implicit, doesn't require association with this VM */
3912 fIndirect = false;
3913 associate = false;
3914 /* go right to the MediumAttachment creation */
3915 break;
3916 }
3917 }
3918
3919 /* must give up the medium lock and medium tree lock as below we
3920 * go over snapshots, which needs a lock with higher lock order. */
3921 mediumLock.release();
3922 treeLock.release();
3923
3924 /* then, search through snapshots for the best diff in the given
3925 * hard disk's chain to base the new diff on */
3926
3927 ComObjPtr<Medium> base;
3928 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3929 while (snap)
3930 {
3931 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3932
3933 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3934
3935 MediumAttachment *pAttachFound = NULL;
3936 uint32_t foundLevel = 0;
3937
3938 for (MediumAttachmentList::const_iterator
3939 it = snapAtts.begin();
3940 it != snapAtts.end();
3941 ++it)
3942 {
3943 MediumAttachment *pAttach = *it;
3944 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3945 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3946 if (pMedium.isNull())
3947 continue;
3948
3949 uint32_t level = 0;
3950 if (pMedium->i_getBase(&level) == medium)
3951 {
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if ( pAttach->i_getDevice() == aDevice
3958 && pAttach->i_getPort() == aControllerPort
3959 && pAttach->i_getControllerName() == aName
3960 )
3961 {
3962 pAttachFound = pAttach;
3963 break;
3964 }
3965 else if ( !pAttachFound
3966 || level > foundLevel /* prefer younger */
3967 )
3968 {
3969 pAttachFound = pAttach;
3970 foundLevel = level;
3971 }
3972 }
3973 }
3974
3975 if (pAttachFound)
3976 {
3977 base = pAttachFound->i_getMedium();
3978 break;
3979 }
3980
3981 snap = snap->i_getParent();
3982 }
3983
3984 /* re-lock medium tree and the medium, as we need it below */
3985 treeLock.acquire();
3986 mediumLock.acquire();
3987
3988 /* found a suitable diff, use it as a base */
3989 if (!base.isNull())
3990 {
3991 medium = base;
3992 mediumCaller.attach(medium);
3993 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3994 mediumLock.attach(medium);
3995 }
3996 }
3997
3998 Utf8Str strFullSnapshotFolder;
3999 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4000
4001 ComObjPtr<Medium> diff;
4002 diff.createObject();
4003 // store this diff in the same registry as the parent
4004 Guid uuidRegistryParent;
4005 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4006 {
4007 // parent image has no registry: this can happen if we're attaching a new immutable
4008 // image that has not yet been attached (medium then points to the base and we're
4009 // creating the diff image for the immutable, and the parent is not yet registered);
4010 // put the parent in the machine registry then
4011 mediumLock.release();
4012 treeLock.release();
4013 alock.release();
4014 i_addMediumToRegistry(medium);
4015 alock.acquire();
4016 treeLock.acquire();
4017 mediumLock.acquire();
4018 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4019 }
4020 rc = diff->init(mParent,
4021 medium->i_getPreferredDiffFormat(),
4022 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4023 uuidRegistryParent,
4024 DeviceType_HardDisk);
4025 if (FAILED(rc)) return rc;
4026
4027 /* Apply the normal locking logic to the entire chain. */
4028 MediumLockList *pMediumLockList(new MediumLockList());
4029 mediumLock.release();
4030 treeLock.release();
4031 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4032 diff /* pToLockWrite */,
4033 false /* fMediumLockWriteAll */,
4034 medium,
4035 *pMediumLockList);
4036 treeLock.acquire();
4037 mediumLock.acquire();
4038 if (SUCCEEDED(rc))
4039 {
4040 mediumLock.release();
4041 treeLock.release();
4042 rc = pMediumLockList->Lock();
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045 if (FAILED(rc))
4046 setError(rc,
4047 tr("Could not lock medium when creating diff '%s'"),
4048 diff->i_getLocationFull().c_str());
4049 else
4050 {
4051 /* will release the lock before the potentially lengthy
4052 * operation, so protect with the special state */
4053 MachineState_T oldState = mData->mMachineState;
4054 i_setMachineState(MachineState_SettingUp);
4055
4056 mediumLock.release();
4057 treeLock.release();
4058 alock.release();
4059
4060 rc = medium->i_createDiffStorage(diff,
4061 medium->i_getPreferredDiffVariant(),
4062 pMediumLockList,
4063 NULL /* aProgress */,
4064 true /* aWait */,
4065 false /* aNotify */);
4066
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070
4071 i_setMachineState(oldState);
4072 }
4073 }
4074
4075 /* Unlock the media and free the associated memory. */
4076 delete pMediumLockList;
4077
4078 if (FAILED(rc)) return rc;
4079
4080 /* use the created diff for the actual attachment */
4081 medium = diff;
4082 mediumCaller.attach(medium);
4083 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4084 mediumLock.attach(medium);
4085 }
4086 while (0);
4087
4088 ComObjPtr<MediumAttachment> attachment;
4089 attachment.createObject();
4090 rc = attachment->init(this,
4091 medium,
4092 aName,
4093 aControllerPort,
4094 aDevice,
4095 aType,
4096 fIndirect,
4097 false /* fPassthrough */,
4098 false /* fTempEject */,
4099 false /* fNonRotational */,
4100 false /* fDiscard */,
4101 fHotplug /* fHotPluggable */,
4102 Utf8Str::Empty);
4103 if (FAILED(rc)) return rc;
4104
4105 if (associate && !medium.isNull())
4106 {
4107 // as the last step, associate the medium to the VM
4108 rc = medium->i_addBackReference(mData->mUuid);
4109 // here we can fail because of Deleting, or being in process of creating a Diff
4110 if (FAILED(rc)) return rc;
4111
4112 mediumLock.release();
4113 treeLock.release();
4114 alock.release();
4115 i_addMediumToRegistry(medium);
4116 alock.acquire();
4117 treeLock.acquire();
4118 mediumLock.acquire();
4119 }
4120
4121 /* success: finally remember the attachment */
4122 i_setModified(IsModified_Storage);
4123 mMediumAttachments.backup();
4124 mMediumAttachments->push_back(attachment);
4125
4126 mediumLock.release();
4127 treeLock.release();
4128 alock.release();
4129
4130 if (fHotplug || fSilent)
4131 {
4132 if (!medium.isNull())
4133 {
4134 MediumLockList *pMediumLockList(new MediumLockList());
4135
4136 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4137 medium /* pToLockWrite */,
4138 false /* fMediumLockWriteAll */,
4139 NULL,
4140 *pMediumLockList);
4141 alock.acquire();
4142 if (FAILED(rc))
4143 delete pMediumLockList;
4144 else
4145 {
4146 mData->mSession.mLockedMedia.Unlock();
4147 alock.release();
4148 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4149 mData->mSession.mLockedMedia.Lock();
4150 alock.acquire();
4151 }
4152 alock.release();
4153 }
4154
4155 if (SUCCEEDED(rc))
4156 {
4157 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4158 /* Remove lock list in case of error. */
4159 if (FAILED(rc))
4160 {
4161 mData->mSession.mLockedMedia.Unlock();
4162 mData->mSession.mLockedMedia.Remove(attachment);
4163 mData->mSession.mLockedMedia.Lock();
4164 }
4165 }
4166 }
4167
4168 /* Save modified registries, but skip this machine as it's the caller's
4169 * job to save its settings like all other settings changes. */
4170 mParent->i_unmarkRegistryModified(i_getId());
4171 mParent->i_saveModifiedRegistries();
4172
4173 if (aM)
4174 mParent->i_onMediumConfigChanged(aM);
4175 return rc;
4176}
4177
4178HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4179 LONG aDevice)
4180{
4181 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4182 aName.c_str(), aControllerPort, aDevice));
4183
4184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4185
4186 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4187 if (FAILED(rc)) return rc;
4188
4189 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4190
4191 /* Check for an existing controller. */
4192 ComObjPtr<StorageController> ctl;
4193 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4194 if (FAILED(rc)) return rc;
4195
4196 StorageControllerType_T ctrlType;
4197 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4198 if (FAILED(rc))
4199 return setError(E_FAIL,
4200 tr("Could not get type of controller '%s'"),
4201 aName.c_str());
4202
4203 bool fSilent = false;
4204 Utf8Str strReconfig;
4205
4206 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4207 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4208 if ( mData->mMachineState == MachineState_Paused
4209 && strReconfig == "1")
4210 fSilent = true;
4211
4212 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4213 bool fHotplug = false;
4214 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4215 fHotplug = true;
4216
4217 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4218 return setError(VBOX_E_INVALID_VM_STATE,
4219 tr("Controller '%s' does not support hotplugging"),
4220 aName.c_str());
4221
4222 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4223 aName,
4224 aControllerPort,
4225 aDevice);
4226 if (!pAttach)
4227 return setError(VBOX_E_OBJECT_NOT_FOUND,
4228 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4229 aDevice, aControllerPort, aName.c_str());
4230
4231 if (fHotplug && !pAttach->i_getHotPluggable())
4232 return setError(VBOX_E_NOT_SUPPORTED,
4233 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4234 aDevice, aControllerPort, aName.c_str());
4235
4236 /*
4237 * The VM has to detach the device before we delete any implicit diffs.
4238 * If this fails we can roll back without loosing data.
4239 */
4240 if (fHotplug || fSilent)
4241 {
4242 alock.release();
4243 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4244 alock.acquire();
4245 }
4246 if (FAILED(rc)) return rc;
4247
4248 /* If we are here everything went well and we can delete the implicit now. */
4249 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4250
4251 alock.release();
4252
4253 /* Save modified registries, but skip this machine as it's the caller's
4254 * job to save its settings like all other settings changes. */
4255 mParent->i_unmarkRegistryModified(i_getId());
4256 mParent->i_saveModifiedRegistries();
4257
4258 return rc;
4259}
4260
4261HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4262 LONG aDevice, BOOL aPassthrough)
4263{
4264 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4265 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4266
4267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4268
4269 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4270 if (FAILED(rc)) return rc;
4271
4272 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4273
4274 /* Check for an existing controller. */
4275 ComObjPtr<StorageController> ctl;
4276 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4277 if (FAILED(rc)) return rc;
4278
4279 StorageControllerType_T ctrlType;
4280 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4281 if (FAILED(rc))
4282 return setError(E_FAIL,
4283 tr("Could not get type of controller '%s'"),
4284 aName.c_str());
4285
4286 bool fSilent = false;
4287 Utf8Str strReconfig;
4288
4289 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4290 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4291 if ( mData->mMachineState == MachineState_Paused
4292 && strReconfig == "1")
4293 fSilent = true;
4294
4295 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4296 bool fHotplug = false;
4297 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4298 fHotplug = true;
4299
4300 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4301 return setError(VBOX_E_INVALID_VM_STATE,
4302 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4303 aName.c_str());
4304
4305 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4306 aName,
4307 aControllerPort,
4308 aDevice);
4309 if (!pAttach)
4310 return setError(VBOX_E_OBJECT_NOT_FOUND,
4311 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4312 aDevice, aControllerPort, aName.c_str());
4313
4314
4315 i_setModified(IsModified_Storage);
4316 mMediumAttachments.backup();
4317
4318 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4319
4320 if (pAttach->i_getType() != DeviceType_DVD)
4321 return setError(E_INVALIDARG,
4322 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4323 aDevice, aControllerPort, aName.c_str());
4324 pAttach->i_updatePassthrough(!!aPassthrough);
4325
4326 attLock.release();
4327 alock.release();
4328 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4329
4330 return rc;
4331}
4332
4333HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4334 LONG aDevice, BOOL aTemporaryEject)
4335{
4336
4337 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4338 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4339
4340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4341
4342 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4343 if (FAILED(rc)) return rc;
4344
4345 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4346 aName,
4347 aControllerPort,
4348 aDevice);
4349 if (!pAttach)
4350 return setError(VBOX_E_OBJECT_NOT_FOUND,
4351 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4352 aDevice, aControllerPort, aName.c_str());
4353
4354
4355 i_setModified(IsModified_Storage);
4356 mMediumAttachments.backup();
4357
4358 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4359
4360 if (pAttach->i_getType() != DeviceType_DVD)
4361 return setError(E_INVALIDARG,
4362 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4363 aDevice, aControllerPort, aName.c_str());
4364 pAttach->i_updateTempEject(!!aTemporaryEject);
4365
4366 return S_OK;
4367}
4368
4369HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4370 LONG aDevice, BOOL aNonRotational)
4371{
4372
4373 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4374 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4375
4376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4377
4378 HRESULT rc = i_checkStateDependency(MutableStateDep);
4379 if (FAILED(rc)) return rc;
4380
4381 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4382
4383 if (Global::IsOnlineOrTransient(mData->mMachineState))
4384 return setError(VBOX_E_INVALID_VM_STATE,
4385 tr("Invalid machine state: %s"),
4386 Global::stringifyMachineState(mData->mMachineState));
4387
4388 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4389 aName,
4390 aControllerPort,
4391 aDevice);
4392 if (!pAttach)
4393 return setError(VBOX_E_OBJECT_NOT_FOUND,
4394 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4395 aDevice, aControllerPort, aName.c_str());
4396
4397
4398 i_setModified(IsModified_Storage);
4399 mMediumAttachments.backup();
4400
4401 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4402
4403 if (pAttach->i_getType() != DeviceType_HardDisk)
4404 return setError(E_INVALIDARG,
4405 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"),
4406 aDevice, aControllerPort, aName.c_str());
4407 pAttach->i_updateNonRotational(!!aNonRotational);
4408
4409 return S_OK;
4410}
4411
4412HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4413 LONG aDevice, BOOL aDiscard)
4414{
4415
4416 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4417 aName.c_str(), aControllerPort, aDevice, aDiscard));
4418
4419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4420
4421 HRESULT rc = i_checkStateDependency(MutableStateDep);
4422 if (FAILED(rc)) return rc;
4423
4424 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4425
4426 if (Global::IsOnlineOrTransient(mData->mMachineState))
4427 return setError(VBOX_E_INVALID_VM_STATE,
4428 tr("Invalid machine state: %s"),
4429 Global::stringifyMachineState(mData->mMachineState));
4430
4431 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4432 aName,
4433 aControllerPort,
4434 aDevice);
4435 if (!pAttach)
4436 return setError(VBOX_E_OBJECT_NOT_FOUND,
4437 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4438 aDevice, aControllerPort, aName.c_str());
4439
4440
4441 i_setModified(IsModified_Storage);
4442 mMediumAttachments.backup();
4443
4444 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4445
4446 if (pAttach->i_getType() != DeviceType_HardDisk)
4447 return setError(E_INVALIDARG,
4448 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"),
4449 aDevice, aControllerPort, aName.c_str());
4450 pAttach->i_updateDiscard(!!aDiscard);
4451
4452 return S_OK;
4453}
4454
4455HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4456 LONG aDevice, BOOL aHotPluggable)
4457{
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4459 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
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 /* Check for an existing controller. */
4483 ComObjPtr<StorageController> ctl;
4484 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4485 if (FAILED(rc)) return rc;
4486
4487 StorageControllerType_T ctrlType;
4488 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4489 if (FAILED(rc))
4490 return setError(E_FAIL,
4491 tr("Could not get type of controller '%s'"),
4492 aName.c_str());
4493
4494 if (!i_isControllerHotplugCapable(ctrlType))
4495 return setError(VBOX_E_NOT_SUPPORTED,
4496 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4497 aName.c_str());
4498
4499 i_setModified(IsModified_Storage);
4500 mMediumAttachments.backup();
4501
4502 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4503
4504 if (pAttach->i_getType() == DeviceType_Floppy)
4505 return setError(E_INVALIDARG,
4506 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"),
4507 aDevice, aControllerPort, aName.c_str());
4508 pAttach->i_updateHotPluggable(!!aHotPluggable);
4509
4510 return S_OK;
4511}
4512
4513HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4514 LONG aDevice)
4515{
4516 int rc = S_OK;
4517 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4518 aName.c_str(), aControllerPort, aDevice));
4519
4520 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4521
4522 return rc;
4523}
4524
4525HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4526 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4527{
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4529 aName.c_str(), aControllerPort, aDevice));
4530
4531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4532
4533 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4534 if (FAILED(rc)) return rc;
4535
4536 if (Global::IsOnlineOrTransient(mData->mMachineState))
4537 return setError(VBOX_E_INVALID_VM_STATE,
4538 tr("Invalid machine state: %s"),
4539 Global::stringifyMachineState(mData->mMachineState));
4540
4541 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4542 aName,
4543 aControllerPort,
4544 aDevice);
4545 if (!pAttach)
4546 return setError(VBOX_E_OBJECT_NOT_FOUND,
4547 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550
4551 i_setModified(IsModified_Storage);
4552 mMediumAttachments.backup();
4553
4554 IBandwidthGroup *iB = aBandwidthGroup;
4555 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4556 if (aBandwidthGroup && group.isNull())
4557 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4558
4559 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4560
4561 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4562 if (strBandwidthGroupOld.isNotEmpty())
4563 {
4564 /* Get the bandwidth group object and release it - this must not fail. */
4565 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4566 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4567 Assert(SUCCEEDED(rc));
4568
4569 pBandwidthGroupOld->i_release();
4570 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4571 }
4572
4573 if (!group.isNull())
4574 {
4575 group->i_reference();
4576 pAttach->i_updateBandwidthGroup(group->i_getName());
4577 }
4578
4579 return S_OK;
4580}
4581
4582HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4583 LONG aControllerPort,
4584 LONG aDevice,
4585 DeviceType_T aType)
4586{
4587 HRESULT rc = S_OK;
4588
4589 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4590 aName.c_str(), aControllerPort, aDevice, aType));
4591
4592 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4593
4594 return rc;
4595}
4596
4597
4598HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4599 LONG aControllerPort,
4600 LONG aDevice,
4601 BOOL aForce)
4602{
4603 int rc = S_OK;
4604 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4605 aName.c_str(), aControllerPort, aForce));
4606
4607 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4608
4609 return rc;
4610}
4611
4612HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 const ComPtr<IMedium> &aMedium,
4616 BOOL aForce)
4617{
4618 int rc = S_OK;
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4620 aName.c_str(), aControllerPort, aDevice, aForce));
4621
4622 // request the host lock first, since might be calling Host methods for getting host drives;
4623 // next, protect the media tree all the while we're in here, as well as our member variables
4624 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4625 this->lockHandle(),
4626 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4627
4628 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4629 aName,
4630 aControllerPort,
4631 aDevice);
4632 if (pAttach.isNull())
4633 return setError(VBOX_E_OBJECT_NOT_FOUND,
4634 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4635 aDevice, aControllerPort, aName.c_str());
4636
4637 /* Remember previously mounted medium. The medium before taking the
4638 * backup is not necessarily the same thing. */
4639 ComObjPtr<Medium> oldmedium;
4640 oldmedium = pAttach->i_getMedium();
4641
4642 IMedium *iM = aMedium;
4643 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4644 if (aMedium && pMedium.isNull())
4645 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4646
4647 AutoCaller mediumCaller(pMedium);
4648 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4649
4650 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4651 if (pMedium)
4652 {
4653 DeviceType_T mediumType = pAttach->i_getType();
4654 switch (mediumType)
4655 {
4656 case DeviceType_DVD:
4657 case DeviceType_Floppy:
4658 break;
4659
4660 default:
4661 return setError(VBOX_E_INVALID_OBJECT_STATE,
4662 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4663 aControllerPort,
4664 aDevice,
4665 aName.c_str());
4666 }
4667 }
4668
4669 i_setModified(IsModified_Storage);
4670 mMediumAttachments.backup();
4671
4672 {
4673 // The backup operation makes the pAttach reference point to the
4674 // old settings. Re-get the correct reference.
4675 pAttach = i_findAttachment(*mMediumAttachments.data(),
4676 aName,
4677 aControllerPort,
4678 aDevice);
4679 if (!oldmedium.isNull())
4680 oldmedium->i_removeBackReference(mData->mUuid);
4681 if (!pMedium.isNull())
4682 {
4683 pMedium->i_addBackReference(mData->mUuid);
4684
4685 mediumLock.release();
4686 multiLock.release();
4687 i_addMediumToRegistry(pMedium);
4688 multiLock.acquire();
4689 mediumLock.acquire();
4690 }
4691
4692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4693 pAttach->i_updateMedium(pMedium);
4694 }
4695
4696 i_setModified(IsModified_Storage);
4697
4698 mediumLock.release();
4699 multiLock.release();
4700 rc = i_onMediumChange(pAttach, aForce);
4701 multiLock.acquire();
4702 mediumLock.acquire();
4703
4704 /* On error roll back this change only. */
4705 if (FAILED(rc))
4706 {
4707 if (!pMedium.isNull())
4708 pMedium->i_removeBackReference(mData->mUuid);
4709 pAttach = i_findAttachment(*mMediumAttachments.data(),
4710 aName,
4711 aControllerPort,
4712 aDevice);
4713 /* If the attachment is gone in the meantime, bail out. */
4714 if (pAttach.isNull())
4715 return rc;
4716 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4717 if (!oldmedium.isNull())
4718 oldmedium->i_addBackReference(mData->mUuid);
4719 pAttach->i_updateMedium(oldmedium);
4720 }
4721
4722 mediumLock.release();
4723 multiLock.release();
4724
4725 /* Save modified registries, but skip this machine as it's the caller's
4726 * job to save its settings like all other settings changes. */
4727 mParent->i_unmarkRegistryModified(i_getId());
4728 mParent->i_saveModifiedRegistries();
4729
4730 return rc;
4731}
4732HRESULT Machine::getMedium(const com::Utf8Str &aName,
4733 LONG aControllerPort,
4734 LONG aDevice,
4735 ComPtr<IMedium> &aMedium)
4736{
4737 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4738 aName.c_str(), aControllerPort, aDevice));
4739
4740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4741
4742 aMedium = NULL;
4743
4744 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4745 aName,
4746 aControllerPort,
4747 aDevice);
4748 if (pAttach.isNull())
4749 return setError(VBOX_E_OBJECT_NOT_FOUND,
4750 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4751 aDevice, aControllerPort, aName.c_str());
4752
4753 aMedium = pAttach->i_getMedium();
4754
4755 return S_OK;
4756}
4757
4758HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4759{
4760
4761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4762
4763 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4764
4765 return S_OK;
4766}
4767
4768HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4769{
4770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4773
4774 return S_OK;
4775}
4776
4777HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4778{
4779 /* Do not assert if slot is out of range, just return the advertised
4780 status. testdriver/vbox.py triggers this in logVmInfo. */
4781 if (aSlot >= mNetworkAdapters.size())
4782 return setError(E_INVALIDARG,
4783 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4784 aSlot, mNetworkAdapters.size());
4785
4786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4789
4790 return S_OK;
4791}
4792
4793HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4794{
4795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4796
4797 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4798 size_t i = 0;
4799 for (settings::StringsMap::const_iterator
4800 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4801 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4802 ++it, ++i)
4803 aKeys[i] = it->first;
4804
4805 return S_OK;
4806}
4807
4808 /**
4809 * @note Locks this object for reading.
4810 */
4811HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4812 com::Utf8Str &aValue)
4813{
4814 /* start with nothing found */
4815 aValue = "";
4816
4817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4820 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4821 // found:
4822 aValue = it->second; // source is a Utf8Str
4823
4824 /* return the result to caller (may be empty) */
4825 return S_OK;
4826}
4827
4828 /**
4829 * @note Locks mParent for writing + this object for writing.
4830 */
4831HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4832{
4833 /* Because non-ASCII characters in aKey have caused problems in the settings
4834 * they are rejected unless the key should be deleted. */
4835 if (!aValue.isEmpty())
4836 {
4837 for (size_t i = 0; i < aKey.length(); ++i)
4838 {
4839 char ch = aKey[i];
4840 if (!RTLocCIsPrint(ch))
4841 return E_INVALIDARG;
4842 }
4843 }
4844
4845 Utf8Str strOldValue; // empty
4846
4847 // locking note: we only hold the read lock briefly to look up the old value,
4848 // then release it and call the onExtraCanChange callbacks. There is a small
4849 // chance of a race insofar as the callback might be called twice if two callers
4850 // change the same key at the same time, but that's a much better solution
4851 // than the deadlock we had here before. The actual changing of the extradata
4852 // is then performed under the write lock and race-free.
4853
4854 // look up the old value first; if nothing has changed then we need not do anything
4855 {
4856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4857
4858 // For snapshots don't even think about allowing changes, extradata
4859 // is global for a machine, so there is nothing snapshot specific.
4860 if (i_isSnapshotMachine())
4861 return setError(VBOX_E_INVALID_VM_STATE,
4862 tr("Cannot set extradata for a snapshot"));
4863
4864 // check if the right IMachine instance is used
4865 if (mData->mRegistered && !i_isSessionMachine())
4866 return setError(VBOX_E_INVALID_VM_STATE,
4867 tr("Cannot set extradata for an immutable machine"));
4868
4869 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4870 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4871 strOldValue = it->second;
4872 }
4873
4874 bool fChanged;
4875 if ((fChanged = (strOldValue != aValue)))
4876 {
4877 // ask for permission from all listeners outside the locks;
4878 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4879 // lock to copy the list of callbacks to invoke
4880 Bstr error;
4881 Bstr bstrValue(aValue);
4882
4883 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4884 {
4885 const char *sep = error.isEmpty() ? "" : ": ";
4886 CBSTR err = error.raw();
4887 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4888 return setError(E_ACCESSDENIED,
4889 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4890 aKey.c_str(),
4891 aValue.c_str(),
4892 sep,
4893 err);
4894 }
4895
4896 // data is changing and change not vetoed: then write it out under the lock
4897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 if (aValue.isEmpty())
4900 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4901 else
4902 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4903 // creates a new key if needed
4904
4905 bool fNeedsGlobalSaveSettings = false;
4906 // This saving of settings is tricky: there is no "old state" for the
4907 // extradata items at all (unlike all other settings), so the old/new
4908 // settings comparison would give a wrong result!
4909 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4910
4911 if (fNeedsGlobalSaveSettings)
4912 {
4913 // save the global settings; for that we should hold only the VirtualBox lock
4914 alock.release();
4915 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4916 mParent->i_saveSettings();
4917 }
4918 }
4919
4920 // fire notification outside the lock
4921 if (fChanged)
4922 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4923
4924 return S_OK;
4925}
4926
4927HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4928{
4929 aProgress = NULL;
4930 NOREF(aSettingsFilePath);
4931 ReturnComNotImplemented();
4932}
4933
4934HRESULT Machine::saveSettings()
4935{
4936 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4937
4938 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4939 if (FAILED(rc)) return rc;
4940
4941 /* the settings file path may never be null */
4942 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4943
4944 /* save all VM data excluding snapshots */
4945 bool fNeedsGlobalSaveSettings = false;
4946 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4947 mlock.release();
4948
4949 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4950 {
4951 // save the global settings; for that we should hold only the VirtualBox lock
4952 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4953 rc = mParent->i_saveSettings();
4954 }
4955
4956 return rc;
4957}
4958
4959
4960HRESULT Machine::discardSettings()
4961{
4962 /*
4963 * We need to take the machine list lock here as well as the machine one
4964 * or we'll get into trouble should any media stuff require rolling back.
4965 *
4966 * Details:
4967 *
4968 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4969 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4970 * 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]
4971 * 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
4972 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4973 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4974 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4975 * 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
4976 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4977 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4978 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4979 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4980 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4981 * 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]
4982 * 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] (*)
4983 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4984 * 0:005> k
4985 * # Child-SP RetAddr Call Site
4986 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4987 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4988 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4989 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4990 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4991 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4992 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4993 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4994 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4995 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4996 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4997 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4998 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4999 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5000 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5001 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5002 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5003 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5004 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5005 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5006 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5007 *
5008 */
5009 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5011
5012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5013 if (FAILED(rc)) return rc;
5014
5015 /*
5016 * during this rollback, the session will be notified if data has
5017 * been actually changed
5018 */
5019 i_rollback(true /* aNotify */);
5020
5021 return S_OK;
5022}
5023
5024/** @note Locks objects! */
5025HRESULT Machine::unregister(AutoCaller &autoCaller,
5026 CleanupMode_T aCleanupMode,
5027 std::vector<ComPtr<IMedium> > &aMedia)
5028{
5029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 Guid id(i_getId());
5032
5033 if (mData->mSession.mState != SessionState_Unlocked)
5034 return setError(VBOX_E_INVALID_OBJECT_STATE,
5035 tr("Cannot unregister the machine '%s' while it is locked"),
5036 mUserData->s.strName.c_str());
5037
5038 // wait for state dependents to drop to zero
5039 i_ensureNoStateDependencies();
5040
5041 if (!mData->mAccessible)
5042 {
5043 // inaccessible maschines can only be unregistered; uninitialize ourselves
5044 // here because currently there may be no unregistered that are inaccessible
5045 // (this state combination is not supported). Note releasing the caller and
5046 // leaving the lock before calling uninit()
5047 alock.release();
5048 autoCaller.release();
5049
5050 uninit();
5051
5052 mParent->i_unregisterMachine(this, id);
5053 // calls VirtualBox::i_saveSettings()
5054
5055 return S_OK;
5056 }
5057
5058 HRESULT rc = S_OK;
5059
5060 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5061 // discard saved state
5062 if (mData->mMachineState == MachineState_Saved)
5063 {
5064 // add the saved state file to the list of files the caller should delete
5065 Assert(!mSSData->strStateFilePath.isEmpty());
5066 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5067
5068 mSSData->strStateFilePath.setNull();
5069
5070 // unconditionally set the machine state to powered off, we now
5071 // know no session has locked the machine
5072 mData->mMachineState = MachineState_PoweredOff;
5073 }
5074
5075 size_t cSnapshots = 0;
5076 if (mData->mFirstSnapshot)
5077 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5078 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5079 // fail now before we start detaching media
5080 return setError(VBOX_E_INVALID_OBJECT_STATE,
5081 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5082 mUserData->s.strName.c_str(), cSnapshots);
5083
5084 // This list collects the medium objects from all medium attachments
5085 // which we will detach from the machine and its snapshots, in a specific
5086 // order which allows for closing all media without getting "media in use"
5087 // errors, simply by going through the list from the front to the back:
5088 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5089 // and must be closed before the parent media from the snapshots, or closing the parents
5090 // will fail because they still have children);
5091 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5092 // the root ("first") snapshot of the machine.
5093 MediaList llMedia;
5094
5095 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5096 && mMediumAttachments->size()
5097 )
5098 {
5099 // we have media attachments: detach them all and add the Medium objects to our list
5100 if (aCleanupMode != CleanupMode_UnregisterOnly)
5101 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5102 else
5103 return setError(VBOX_E_INVALID_OBJECT_STATE,
5104 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5105 mUserData->s.strName.c_str(), mMediumAttachments->size());
5106 }
5107
5108 if (cSnapshots)
5109 {
5110 // add the media from the medium attachments of the snapshots to llMedia
5111 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5112 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5113 // into the children first
5114
5115 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5116 MachineState_T oldState = mData->mMachineState;
5117 mData->mMachineState = MachineState_DeletingSnapshot;
5118
5119 // make a copy of the first snapshot so the refcount does not drop to 0
5120 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5121 // because of the AutoCaller voodoo)
5122 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5123
5124 // GO!
5125 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5126
5127 mData->mMachineState = oldState;
5128 }
5129
5130 if (FAILED(rc))
5131 {
5132 i_rollbackMedia();
5133 return rc;
5134 }
5135
5136 // commit all the media changes made above
5137 i_commitMedia();
5138
5139 mData->mRegistered = false;
5140
5141 // machine lock no longer needed
5142 alock.release();
5143
5144 // return media to caller
5145 aMedia.resize(llMedia.size());
5146 size_t i = 0;
5147 for (MediaList::const_iterator
5148 it = llMedia.begin();
5149 it != llMedia.end();
5150 ++it, ++i)
5151 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5152
5153 mParent->i_unregisterMachine(this, id);
5154 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5155
5156 return S_OK;
5157}
5158
5159/**
5160 * Task record for deleting a machine config.
5161 */
5162class Machine::DeleteConfigTask
5163 : public Machine::Task
5164{
5165public:
5166 DeleteConfigTask(Machine *m,
5167 Progress *p,
5168 const Utf8Str &t,
5169 const RTCList<ComPtr<IMedium> > &llMediums,
5170 const StringsList &llFilesToDelete)
5171 : Task(m, p, t),
5172 m_llMediums(llMediums),
5173 m_llFilesToDelete(llFilesToDelete)
5174 {}
5175
5176private:
5177 void handler()
5178 {
5179 try
5180 {
5181 m_pMachine->i_deleteConfigHandler(*this);
5182 }
5183 catch (...)
5184 {
5185 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5186 }
5187 }
5188
5189 RTCList<ComPtr<IMedium> > m_llMediums;
5190 StringsList m_llFilesToDelete;
5191
5192 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5193};
5194
5195/**
5196 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5197 * SessionMachine::taskHandler().
5198 *
5199 * @note Locks this object for writing.
5200 *
5201 * @param task
5202 * @return
5203 */
5204void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5205{
5206 LogFlowThisFuncEnter();
5207
5208 AutoCaller autoCaller(this);
5209 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5210 if (FAILED(autoCaller.rc()))
5211 {
5212 /* we might have been uninitialized because the session was accidentally
5213 * closed by the client, so don't assert */
5214 HRESULT rc = setError(E_FAIL,
5215 tr("The session has been accidentally closed"));
5216 task.m_pProgress->i_notifyComplete(rc);
5217 LogFlowThisFuncLeave();
5218 return;
5219 }
5220
5221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5222
5223 HRESULT rc = S_OK;
5224
5225 try
5226 {
5227 ULONG uLogHistoryCount = 3;
5228 ComPtr<ISystemProperties> systemProperties;
5229 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5230 if (FAILED(rc)) throw rc;
5231
5232 if (!systemProperties.isNull())
5233 {
5234 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5235 if (FAILED(rc)) throw rc;
5236 }
5237
5238 MachineState_T oldState = mData->mMachineState;
5239 i_setMachineState(MachineState_SettingUp);
5240 alock.release();
5241 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5242 {
5243 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5244 {
5245 AutoCaller mac(pMedium);
5246 if (FAILED(mac.rc())) throw mac.rc();
5247 Utf8Str strLocation = pMedium->i_getLocationFull();
5248 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5249 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5250 if (FAILED(rc)) throw rc;
5251 }
5252 if (pMedium->i_isMediumFormatFile())
5253 {
5254 ComPtr<IProgress> pProgress2;
5255 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5256 if (FAILED(rc)) throw rc;
5257 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5258 if (FAILED(rc)) throw rc;
5259 }
5260
5261 /* Close the medium, deliberately without checking the return
5262 * code, and without leaving any trace in the error info, as
5263 * a failure here is a very minor issue, which shouldn't happen
5264 * as above we even managed to delete the medium. */
5265 {
5266 ErrorInfoKeeper eik;
5267 pMedium->Close();
5268 }
5269 }
5270 i_setMachineState(oldState);
5271 alock.acquire();
5272
5273 // delete the files pushed on the task list by Machine::Delete()
5274 // (this includes saved states of the machine and snapshots and
5275 // medium storage files from the IMedium list passed in, and the
5276 // machine XML file)
5277 for (StringsList::const_iterator
5278 it = task.m_llFilesToDelete.begin();
5279 it != task.m_llFilesToDelete.end();
5280 ++it)
5281 {
5282 const Utf8Str &strFile = *it;
5283 LogFunc(("Deleting file %s\n", strFile.c_str()));
5284 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5285 if (FAILED(rc)) throw rc;
5286
5287 int vrc = RTFileDelete(strFile.c_str());
5288 if (RT_FAILURE(vrc))
5289 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5290 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5291 }
5292
5293 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5294 if (FAILED(rc)) throw rc;
5295
5296 /* delete the settings only when the file actually exists */
5297 if (mData->pMachineConfigFile->fileExists())
5298 {
5299 /* Delete any backup or uncommitted XML files. Ignore failures.
5300 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5301 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5302 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5303 RTFileDelete(otherXml.c_str());
5304 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5305 RTFileDelete(otherXml.c_str());
5306
5307 /* delete the Logs folder, nothing important should be left
5308 * there (we don't check for errors because the user might have
5309 * some private files there that we don't want to delete) */
5310 Utf8Str logFolder;
5311 getLogFolder(logFolder);
5312 Assert(logFolder.length());
5313 if (RTDirExists(logFolder.c_str()))
5314 {
5315 /* Delete all VBox.log[.N] files from the Logs folder
5316 * (this must be in sync with the rotation logic in
5317 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5318 * files that may have been created by the GUI. */
5319 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5320 logFolder.c_str(), RTPATH_DELIMITER);
5321 RTFileDelete(log.c_str());
5322 log = Utf8StrFmt("%s%cVBox.png",
5323 logFolder.c_str(), RTPATH_DELIMITER);
5324 RTFileDelete(log.c_str());
5325 for (int i = uLogHistoryCount; i > 0; i--)
5326 {
5327 log = Utf8StrFmt("%s%cVBox.log.%d",
5328 logFolder.c_str(), RTPATH_DELIMITER, i);
5329 RTFileDelete(log.c_str());
5330 log = Utf8StrFmt("%s%cVBox.png.%d",
5331 logFolder.c_str(), RTPATH_DELIMITER, i);
5332 RTFileDelete(log.c_str());
5333 }
5334#if defined(RT_OS_WINDOWS)
5335 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5336 RTFileDelete(log.c_str());
5337 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5338 RTFileDelete(log.c_str());
5339#endif
5340
5341 RTDirRemove(logFolder.c_str());
5342 }
5343
5344 /* delete the Snapshots folder, nothing important should be left
5345 * there (we don't check for errors because the user might have
5346 * some private files there that we don't want to delete) */
5347 Utf8Str strFullSnapshotFolder;
5348 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5349 Assert(!strFullSnapshotFolder.isEmpty());
5350 if (RTDirExists(strFullSnapshotFolder.c_str()))
5351 RTDirRemove(strFullSnapshotFolder.c_str());
5352
5353 // delete the directory that contains the settings file, but only
5354 // if it matches the VM name
5355 Utf8Str settingsDir;
5356 if (i_isInOwnDir(&settingsDir))
5357 RTDirRemove(settingsDir.c_str());
5358 }
5359
5360 alock.release();
5361
5362 mParent->i_saveModifiedRegistries();
5363 }
5364 catch (HRESULT aRC) { rc = aRC; }
5365
5366 task.m_pProgress->i_notifyComplete(rc);
5367
5368 LogFlowThisFuncLeave();
5369}
5370
5371HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5372{
5373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5374
5375 HRESULT rc = i_checkStateDependency(MutableStateDep);
5376 if (FAILED(rc)) return rc;
5377
5378 if (mData->mRegistered)
5379 return setError(VBOX_E_INVALID_VM_STATE,
5380 tr("Cannot delete settings of a registered machine"));
5381
5382 // collect files to delete
5383 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5384 if (mData->pMachineConfigFile->fileExists())
5385 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5386
5387 RTCList<ComPtr<IMedium> > llMediums;
5388 for (size_t i = 0; i < aMedia.size(); ++i)
5389 {
5390 IMedium *pIMedium(aMedia[i]);
5391 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5392 if (pMedium.isNull())
5393 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5394 SafeArray<BSTR> ids;
5395 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5396 if (FAILED(rc)) return rc;
5397 /* At this point the medium should not have any back references
5398 * anymore. If it has it is attached to another VM and *must* not
5399 * deleted. */
5400 if (ids.size() < 1)
5401 llMediums.append(pMedium);
5402 }
5403
5404 ComObjPtr<Progress> pProgress;
5405 pProgress.createObject();
5406 rc = pProgress->init(i_getVirtualBox(),
5407 static_cast<IMachine*>(this) /* aInitiator */,
5408 tr("Deleting files"),
5409 true /* fCancellable */,
5410 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5411 tr("Collecting file inventory"));
5412 if (FAILED(rc))
5413 return rc;
5414
5415 /* create and start the task on a separate thread (note that it will not
5416 * start working until we release alock) */
5417 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5418 rc = pTask->createThread();
5419 if (FAILED(rc))
5420 return rc;
5421
5422 pProgress.queryInterfaceTo(aProgress.asOutParam());
5423
5424 LogFlowFuncLeave();
5425
5426 return S_OK;
5427}
5428
5429HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5430{
5431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5432
5433 ComObjPtr<Snapshot> pSnapshot;
5434 HRESULT rc;
5435
5436 if (aNameOrId.isEmpty())
5437 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5438 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5439 else
5440 {
5441 Guid uuid(aNameOrId);
5442 if (uuid.isValid())
5443 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5444 else
5445 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5446 }
5447 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5448
5449 return rc;
5450}
5451
5452HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5453 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5454{
5455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5456
5457 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5458 if (FAILED(rc)) return rc;
5459
5460 ComObjPtr<SharedFolder> sharedFolder;
5461 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5462 if (SUCCEEDED(rc))
5463 return setError(VBOX_E_OBJECT_IN_USE,
5464 tr("Shared folder named '%s' already exists"),
5465 aName.c_str());
5466
5467 sharedFolder.createObject();
5468 rc = sharedFolder->init(i_getMachine(),
5469 aName,
5470 aHostPath,
5471 !!aWritable,
5472 !!aAutomount,
5473 aAutoMountPoint,
5474 true /* fFailOnError */);
5475 if (FAILED(rc)) return rc;
5476
5477 i_setModified(IsModified_SharedFolders);
5478 mHWData.backup();
5479 mHWData->mSharedFolders.push_back(sharedFolder);
5480
5481 /* inform the direct session if any */
5482 alock.release();
5483 i_onSharedFolderChange();
5484
5485 return S_OK;
5486}
5487
5488HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5489{
5490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5491
5492 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5493 if (FAILED(rc)) return rc;
5494
5495 ComObjPtr<SharedFolder> sharedFolder;
5496 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5497 if (FAILED(rc)) return rc;
5498
5499 i_setModified(IsModified_SharedFolders);
5500 mHWData.backup();
5501 mHWData->mSharedFolders.remove(sharedFolder);
5502
5503 /* inform the direct session if any */
5504 alock.release();
5505 i_onSharedFolderChange();
5506
5507 return S_OK;
5508}
5509
5510HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5511{
5512 /* start with No */
5513 *aCanShow = FALSE;
5514
5515 ComPtr<IInternalSessionControl> directControl;
5516 {
5517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5518
5519 if (mData->mSession.mState != SessionState_Locked)
5520 return setError(VBOX_E_INVALID_VM_STATE,
5521 tr("Machine is not locked for session (session state: %s)"),
5522 Global::stringifySessionState(mData->mSession.mState));
5523
5524 if (mData->mSession.mLockType == LockType_VM)
5525 directControl = mData->mSession.mDirectControl;
5526 }
5527
5528 /* ignore calls made after #OnSessionEnd() is called */
5529 if (!directControl)
5530 return S_OK;
5531
5532 LONG64 dummy;
5533 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5534}
5535
5536HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5537{
5538 ComPtr<IInternalSessionControl> directControl;
5539 {
5540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5541
5542 if (mData->mSession.mState != SessionState_Locked)
5543 return setError(E_FAIL,
5544 tr("Machine is not locked for session (session state: %s)"),
5545 Global::stringifySessionState(mData->mSession.mState));
5546
5547 if (mData->mSession.mLockType == LockType_VM)
5548 directControl = mData->mSession.mDirectControl;
5549 }
5550
5551 /* ignore calls made after #OnSessionEnd() is called */
5552 if (!directControl)
5553 return S_OK;
5554
5555 BOOL dummy;
5556 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5557}
5558
5559#ifdef VBOX_WITH_GUEST_PROPS
5560/**
5561 * Look up a guest property in VBoxSVC's internal structures.
5562 */
5563HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5564 com::Utf8Str &aValue,
5565 LONG64 *aTimestamp,
5566 com::Utf8Str &aFlags) const
5567{
5568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5569
5570 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5571 if (it != mHWData->mGuestProperties.end())
5572 {
5573 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5574 aValue = it->second.strValue;
5575 *aTimestamp = it->second.mTimestamp;
5576 GuestPropWriteFlags(it->second.mFlags, szFlags);
5577 aFlags = Utf8Str(szFlags);
5578 }
5579
5580 return S_OK;
5581}
5582
5583/**
5584 * Query the VM that a guest property belongs to for the property.
5585 * @returns E_ACCESSDENIED if the VM process is not available or not
5586 * currently handling queries and the lookup should then be done in
5587 * VBoxSVC.
5588 */
5589HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5590 com::Utf8Str &aValue,
5591 LONG64 *aTimestamp,
5592 com::Utf8Str &aFlags) const
5593{
5594 HRESULT rc = S_OK;
5595 BSTR bValue = NULL;
5596 BSTR bFlags = NULL;
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601 if (mData->mSession.mLockType == LockType_VM)
5602 directControl = mData->mSession.mDirectControl;
5603 }
5604
5605 /* ignore calls made after #OnSessionEnd() is called */
5606 if (!directControl)
5607 rc = E_ACCESSDENIED;
5608 else
5609 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5610 0 /* accessMode */,
5611 &bValue, aTimestamp, &bFlags);
5612
5613 aValue = bValue;
5614 aFlags = bFlags;
5615
5616 return rc;
5617}
5618#endif // VBOX_WITH_GUEST_PROPS
5619
5620HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5621 com::Utf8Str &aValue,
5622 LONG64 *aTimestamp,
5623 com::Utf8Str &aFlags)
5624{
5625#ifndef VBOX_WITH_GUEST_PROPS
5626 ReturnComNotImplemented();
5627#else // VBOX_WITH_GUEST_PROPS
5628
5629 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5630
5631 if (rc == E_ACCESSDENIED)
5632 /* The VM is not running or the service is not (yet) accessible */
5633 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5634 return rc;
5635#endif // VBOX_WITH_GUEST_PROPS
5636}
5637
5638HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5639{
5640 LONG64 dummyTimestamp;
5641 com::Utf8Str dummyFlags;
5642 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5643 return rc;
5644
5645}
5646HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5647{
5648 com::Utf8Str dummyFlags;
5649 com::Utf8Str dummyValue;
5650 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5651 return rc;
5652}
5653
5654#ifdef VBOX_WITH_GUEST_PROPS
5655/**
5656 * Set a guest property in VBoxSVC's internal structures.
5657 */
5658HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5659 const com::Utf8Str &aFlags, bool fDelete)
5660{
5661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5662 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5663 if (FAILED(rc)) return rc;
5664
5665 try
5666 {
5667 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5668 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5669 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5670
5671 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5672 if (it == mHWData->mGuestProperties.end())
5673 {
5674 if (!fDelete)
5675 {
5676 i_setModified(IsModified_MachineData);
5677 mHWData.backupEx();
5678
5679 RTTIMESPEC time;
5680 HWData::GuestProperty prop;
5681 prop.strValue = Bstr(aValue).raw();
5682 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5683 prop.mFlags = fFlags;
5684 mHWData->mGuestProperties[aName] = prop;
5685 }
5686 }
5687 else
5688 {
5689 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5690 {
5691 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5692 }
5693 else
5694 {
5695 i_setModified(IsModified_MachineData);
5696 mHWData.backupEx();
5697
5698 /* The backupEx() operation invalidates our iterator,
5699 * so get a new one. */
5700 it = mHWData->mGuestProperties.find(aName);
5701 Assert(it != mHWData->mGuestProperties.end());
5702
5703 if (!fDelete)
5704 {
5705 RTTIMESPEC time;
5706 it->second.strValue = aValue;
5707 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5708 it->second.mFlags = fFlags;
5709 }
5710 else
5711 mHWData->mGuestProperties.erase(it);
5712 }
5713 }
5714
5715 if (SUCCEEDED(rc))
5716 {
5717 alock.release();
5718
5719 mParent->i_onGuestPropertyChange(mData->mUuid,
5720 Bstr(aName).raw(),
5721 Bstr(aValue).raw(),
5722 Bstr(aFlags).raw());
5723 }
5724 }
5725 catch (std::bad_alloc &)
5726 {
5727 rc = E_OUTOFMEMORY;
5728 }
5729
5730 return rc;
5731}
5732
5733/**
5734 * Set a property on the VM that that property belongs to.
5735 * @returns E_ACCESSDENIED if the VM process is not available or not
5736 * currently handling queries and the setting should then be done in
5737 * VBoxSVC.
5738 */
5739HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5740 const com::Utf8Str &aFlags, bool fDelete)
5741{
5742 HRESULT rc;
5743
5744 try
5745 {
5746 ComPtr<IInternalSessionControl> directControl;
5747 {
5748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5749 if (mData->mSession.mLockType == LockType_VM)
5750 directControl = mData->mSession.mDirectControl;
5751 }
5752
5753 BSTR dummy = NULL; /* will not be changed (setter) */
5754 LONG64 dummy64;
5755 if (!directControl)
5756 rc = E_ACCESSDENIED;
5757 else
5758 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5759 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5760 fDelete? 2: 1 /* accessMode */,
5761 &dummy, &dummy64, &dummy);
5762 }
5763 catch (std::bad_alloc &)
5764 {
5765 rc = E_OUTOFMEMORY;
5766 }
5767
5768 return rc;
5769}
5770#endif // VBOX_WITH_GUEST_PROPS
5771
5772HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5773 const com::Utf8Str &aFlags)
5774{
5775#ifndef VBOX_WITH_GUEST_PROPS
5776 ReturnComNotImplemented();
5777#else // VBOX_WITH_GUEST_PROPS
5778 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5779 if (rc == E_ACCESSDENIED)
5780 /* The VM is not running or the service is not (yet) accessible */
5781 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5782 return rc;
5783#endif // VBOX_WITH_GUEST_PROPS
5784}
5785
5786HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5787{
5788 return setGuestProperty(aProperty, aValue, "");
5789}
5790
5791HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5792{
5793#ifndef VBOX_WITH_GUEST_PROPS
5794 ReturnComNotImplemented();
5795#else // VBOX_WITH_GUEST_PROPS
5796 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5797 if (rc == E_ACCESSDENIED)
5798 /* The VM is not running or the service is not (yet) accessible */
5799 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5800 return rc;
5801#endif // VBOX_WITH_GUEST_PROPS
5802}
5803
5804#ifdef VBOX_WITH_GUEST_PROPS
5805/**
5806 * Enumerate the guest properties in VBoxSVC's internal structures.
5807 */
5808HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5809 std::vector<com::Utf8Str> &aNames,
5810 std::vector<com::Utf8Str> &aValues,
5811 std::vector<LONG64> &aTimestamps,
5812 std::vector<com::Utf8Str> &aFlags)
5813{
5814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5815 Utf8Str strPatterns(aPatterns);
5816
5817 /*
5818 * Look for matching patterns and build up a list.
5819 */
5820 HWData::GuestPropertyMap propMap;
5821 for (HWData::GuestPropertyMap::const_iterator
5822 it = mHWData->mGuestProperties.begin();
5823 it != mHWData->mGuestProperties.end();
5824 ++it)
5825 {
5826 if ( strPatterns.isEmpty()
5827 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5828 RTSTR_MAX,
5829 it->first.c_str(),
5830 RTSTR_MAX,
5831 NULL)
5832 )
5833 propMap.insert(*it);
5834 }
5835
5836 alock.release();
5837
5838 /*
5839 * And build up the arrays for returning the property information.
5840 */
5841 size_t cEntries = propMap.size();
5842
5843 aNames.resize(cEntries);
5844 aValues.resize(cEntries);
5845 aTimestamps.resize(cEntries);
5846 aFlags.resize(cEntries);
5847
5848 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5849 size_t i = 0;
5850 for (HWData::GuestPropertyMap::const_iterator
5851 it = propMap.begin();
5852 it != propMap.end();
5853 ++it, ++i)
5854 {
5855 aNames[i] = it->first;
5856 aValues[i] = it->second.strValue;
5857 aTimestamps[i] = it->second.mTimestamp;
5858 GuestPropWriteFlags(it->second.mFlags, szFlags);
5859 aFlags[i] = Utf8Str(szFlags);
5860 }
5861
5862 return S_OK;
5863}
5864
5865/**
5866 * Enumerate the properties managed by a VM.
5867 * @returns E_ACCESSDENIED if the VM process is not available or not
5868 * currently handling queries and the setting should then be done in
5869 * VBoxSVC.
5870 */
5871HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5872 std::vector<com::Utf8Str> &aNames,
5873 std::vector<com::Utf8Str> &aValues,
5874 std::vector<LONG64> &aTimestamps,
5875 std::vector<com::Utf8Str> &aFlags)
5876{
5877 HRESULT rc;
5878 ComPtr<IInternalSessionControl> directControl;
5879 {
5880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5881 if (mData->mSession.mLockType == LockType_VM)
5882 directControl = mData->mSession.mDirectControl;
5883 }
5884
5885 com::SafeArray<BSTR> bNames;
5886 com::SafeArray<BSTR> bValues;
5887 com::SafeArray<LONG64> bTimestamps;
5888 com::SafeArray<BSTR> bFlags;
5889
5890 if (!directControl)
5891 rc = E_ACCESSDENIED;
5892 else
5893 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5894 ComSafeArrayAsOutParam(bNames),
5895 ComSafeArrayAsOutParam(bValues),
5896 ComSafeArrayAsOutParam(bTimestamps),
5897 ComSafeArrayAsOutParam(bFlags));
5898 size_t i;
5899 aNames.resize(bNames.size());
5900 for (i = 0; i < bNames.size(); ++i)
5901 aNames[i] = Utf8Str(bNames[i]);
5902 aValues.resize(bValues.size());
5903 for (i = 0; i < bValues.size(); ++i)
5904 aValues[i] = Utf8Str(bValues[i]);
5905 aTimestamps.resize(bTimestamps.size());
5906 for (i = 0; i < bTimestamps.size(); ++i)
5907 aTimestamps[i] = bTimestamps[i];
5908 aFlags.resize(bFlags.size());
5909 for (i = 0; i < bFlags.size(); ++i)
5910 aFlags[i] = Utf8Str(bFlags[i]);
5911
5912 return rc;
5913}
5914#endif // VBOX_WITH_GUEST_PROPS
5915HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5916 std::vector<com::Utf8Str> &aNames,
5917 std::vector<com::Utf8Str> &aValues,
5918 std::vector<LONG64> &aTimestamps,
5919 std::vector<com::Utf8Str> &aFlags)
5920{
5921#ifndef VBOX_WITH_GUEST_PROPS
5922 ReturnComNotImplemented();
5923#else // VBOX_WITH_GUEST_PROPS
5924
5925 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5926
5927 if (rc == E_ACCESSDENIED)
5928 /* The VM is not running or the service is not (yet) accessible */
5929 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5930 return rc;
5931#endif // VBOX_WITH_GUEST_PROPS
5932}
5933
5934HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5935 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5936{
5937 MediumAttachmentList atts;
5938
5939 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5940 if (FAILED(rc)) return rc;
5941
5942 aMediumAttachments.resize(atts.size());
5943 size_t i = 0;
5944 for (MediumAttachmentList::const_iterator
5945 it = atts.begin();
5946 it != atts.end();
5947 ++it, ++i)
5948 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5949
5950 return S_OK;
5951}
5952
5953HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5954 LONG aControllerPort,
5955 LONG aDevice,
5956 ComPtr<IMediumAttachment> &aAttachment)
5957{
5958 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5959 aName.c_str(), aControllerPort, aDevice));
5960
5961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5962
5963 aAttachment = NULL;
5964
5965 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5966 aName,
5967 aControllerPort,
5968 aDevice);
5969 if (pAttach.isNull())
5970 return setError(VBOX_E_OBJECT_NOT_FOUND,
5971 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5972 aDevice, aControllerPort, aName.c_str());
5973
5974 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5975
5976 return S_OK;
5977}
5978
5979
5980HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5981 StorageBus_T aConnectionType,
5982 ComPtr<IStorageController> &aController)
5983{
5984 if ( (aConnectionType <= StorageBus_Null)
5985 || (aConnectionType > StorageBus_PCIe))
5986 return setError(E_INVALIDARG,
5987 tr("Invalid connection type: %d"),
5988 aConnectionType);
5989
5990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5991
5992 HRESULT rc = i_checkStateDependency(MutableStateDep);
5993 if (FAILED(rc)) return rc;
5994
5995 /* try to find one with the name first. */
5996 ComObjPtr<StorageController> ctrl;
5997
5998 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5999 if (SUCCEEDED(rc))
6000 return setError(VBOX_E_OBJECT_IN_USE,
6001 tr("Storage controller named '%s' already exists"),
6002 aName.c_str());
6003
6004 ctrl.createObject();
6005
6006 /* get a new instance number for the storage controller */
6007 ULONG ulInstance = 0;
6008 bool fBootable = true;
6009 for (StorageControllerList::const_iterator
6010 it = mStorageControllers->begin();
6011 it != mStorageControllers->end();
6012 ++it)
6013 {
6014 if ((*it)->i_getStorageBus() == aConnectionType)
6015 {
6016 ULONG ulCurInst = (*it)->i_getInstance();
6017
6018 if (ulCurInst >= ulInstance)
6019 ulInstance = ulCurInst + 1;
6020
6021 /* Only one controller of each type can be marked as bootable. */
6022 if ((*it)->i_getBootable())
6023 fBootable = false;
6024 }
6025 }
6026
6027 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6028 if (FAILED(rc)) return rc;
6029
6030 i_setModified(IsModified_Storage);
6031 mStorageControllers.backup();
6032 mStorageControllers->push_back(ctrl);
6033
6034 ctrl.queryInterfaceTo(aController.asOutParam());
6035
6036 /* inform the direct session if any */
6037 alock.release();
6038 i_onStorageControllerChange();
6039
6040 return S_OK;
6041}
6042
6043HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6044 ComPtr<IStorageController> &aStorageController)
6045{
6046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6047
6048 ComObjPtr<StorageController> ctrl;
6049
6050 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6051 if (SUCCEEDED(rc))
6052 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6053
6054 return rc;
6055}
6056
6057HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6058 ULONG aInstance,
6059 ComPtr<IStorageController> &aStorageController)
6060{
6061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6062
6063 for (StorageControllerList::const_iterator
6064 it = mStorageControllers->begin();
6065 it != mStorageControllers->end();
6066 ++it)
6067 {
6068 if ( (*it)->i_getStorageBus() == aConnectionType
6069 && (*it)->i_getInstance() == aInstance)
6070 {
6071 (*it).queryInterfaceTo(aStorageController.asOutParam());
6072 return S_OK;
6073 }
6074 }
6075
6076 return setError(VBOX_E_OBJECT_NOT_FOUND,
6077 tr("Could not find a storage controller with instance number '%lu'"),
6078 aInstance);
6079}
6080
6081HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6082{
6083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6084
6085 HRESULT rc = i_checkStateDependency(MutableStateDep);
6086 if (FAILED(rc)) return rc;
6087
6088 ComObjPtr<StorageController> ctrl;
6089
6090 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6091 if (SUCCEEDED(rc))
6092 {
6093 /* Ensure that only one controller of each type is marked as bootable. */
6094 if (aBootable == TRUE)
6095 {
6096 for (StorageControllerList::const_iterator
6097 it = mStorageControllers->begin();
6098 it != mStorageControllers->end();
6099 ++it)
6100 {
6101 ComObjPtr<StorageController> aCtrl = (*it);
6102
6103 if ( (aCtrl->i_getName() != aName)
6104 && aCtrl->i_getBootable() == TRUE
6105 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6106 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6107 {
6108 aCtrl->i_setBootable(FALSE);
6109 break;
6110 }
6111 }
6112 }
6113
6114 if (SUCCEEDED(rc))
6115 {
6116 ctrl->i_setBootable(aBootable);
6117 i_setModified(IsModified_Storage);
6118 }
6119 }
6120
6121 if (SUCCEEDED(rc))
6122 {
6123 /* inform the direct session if any */
6124 alock.release();
6125 i_onStorageControllerChange();
6126 }
6127
6128 return rc;
6129}
6130
6131HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6132{
6133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 HRESULT rc = i_checkStateDependency(MutableStateDep);
6136 if (FAILED(rc)) return rc;
6137
6138 ComObjPtr<StorageController> ctrl;
6139 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6140 if (FAILED(rc)) return rc;
6141
6142 {
6143 /* find all attached devices to the appropriate storage controller and detach them all */
6144 // make a temporary list because detachDevice invalidates iterators into
6145 // mMediumAttachments
6146 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6147
6148 for (MediumAttachmentList::const_iterator
6149 it = llAttachments2.begin();
6150 it != llAttachments2.end();
6151 ++it)
6152 {
6153 MediumAttachment *pAttachTemp = *it;
6154
6155 AutoCaller localAutoCaller(pAttachTemp);
6156 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6157
6158 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6159
6160 if (pAttachTemp->i_getControllerName() == aName)
6161 {
6162 rc = i_detachDevice(pAttachTemp, alock, NULL);
6163 if (FAILED(rc)) return rc;
6164 }
6165 }
6166 }
6167
6168 /* We can remove it now. */
6169 i_setModified(IsModified_Storage);
6170 mStorageControllers.backup();
6171
6172 ctrl->i_unshare();
6173
6174 mStorageControllers->remove(ctrl);
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onStorageControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6184 ComPtr<IUSBController> &aController)
6185{
6186 if ( (aType <= USBControllerType_Null)
6187 || (aType >= USBControllerType_Last))
6188 return setError(E_INVALIDARG,
6189 tr("Invalid USB controller type: %d"),
6190 aType);
6191
6192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6193
6194 HRESULT rc = i_checkStateDependency(MutableStateDep);
6195 if (FAILED(rc)) return rc;
6196
6197 /* try to find one with the same type first. */
6198 ComObjPtr<USBController> ctrl;
6199
6200 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6201 if (SUCCEEDED(rc))
6202 return setError(VBOX_E_OBJECT_IN_USE,
6203 tr("USB controller named '%s' already exists"),
6204 aName.c_str());
6205
6206 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6207 ULONG maxInstances;
6208 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6209 if (FAILED(rc))
6210 return rc;
6211
6212 ULONG cInstances = i_getUSBControllerCountByType(aType);
6213 if (cInstances >= maxInstances)
6214 return setError(E_INVALIDARG,
6215 tr("Too many USB controllers of this type"));
6216
6217 ctrl.createObject();
6218
6219 rc = ctrl->init(this, aName, aType);
6220 if (FAILED(rc)) return rc;
6221
6222 i_setModified(IsModified_USB);
6223 mUSBControllers.backup();
6224 mUSBControllers->push_back(ctrl);
6225
6226 ctrl.queryInterfaceTo(aController.asOutParam());
6227
6228 /* inform the direct session if any */
6229 alock.release();
6230 i_onUSBControllerChange();
6231
6232 return S_OK;
6233}
6234
6235HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6236{
6237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6238
6239 ComObjPtr<USBController> ctrl;
6240
6241 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6242 if (SUCCEEDED(rc))
6243 ctrl.queryInterfaceTo(aController.asOutParam());
6244
6245 return rc;
6246}
6247
6248HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6249 ULONG *aControllers)
6250{
6251 if ( (aType <= USBControllerType_Null)
6252 || (aType >= USBControllerType_Last))
6253 return setError(E_INVALIDARG,
6254 tr("Invalid USB controller type: %d"),
6255 aType);
6256
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 ComObjPtr<USBController> ctrl;
6260
6261 *aControllers = i_getUSBControllerCountByType(aType);
6262
6263 return S_OK;
6264}
6265
6266HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6267{
6268
6269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6270
6271 HRESULT rc = i_checkStateDependency(MutableStateDep);
6272 if (FAILED(rc)) return rc;
6273
6274 ComObjPtr<USBController> ctrl;
6275 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6276 if (FAILED(rc)) return rc;
6277
6278 i_setModified(IsModified_USB);
6279 mUSBControllers.backup();
6280
6281 ctrl->i_unshare();
6282
6283 mUSBControllers->remove(ctrl);
6284
6285 /* inform the direct session if any */
6286 alock.release();
6287 i_onUSBControllerChange();
6288
6289 return S_OK;
6290}
6291
6292HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6293 ULONG *aOriginX,
6294 ULONG *aOriginY,
6295 ULONG *aWidth,
6296 ULONG *aHeight,
6297 BOOL *aEnabled)
6298{
6299 uint32_t u32OriginX= 0;
6300 uint32_t u32OriginY= 0;
6301 uint32_t u32Width = 0;
6302 uint32_t u32Height = 0;
6303 uint16_t u16Flags = 0;
6304
6305 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6306 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6307 if (RT_FAILURE(vrc))
6308 {
6309#ifdef RT_OS_WINDOWS
6310 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6311 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6312 * So just assign fEnable to TRUE again.
6313 * The right fix would be to change GUI API wrappers to make sure that parameters
6314 * are changed only if API succeeds.
6315 */
6316 *aEnabled = TRUE;
6317#endif
6318 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6319 tr("Saved guest size is not available (%Rrc)"),
6320 vrc);
6321 }
6322
6323 *aOriginX = u32OriginX;
6324 *aOriginY = u32OriginY;
6325 *aWidth = u32Width;
6326 *aHeight = u32Height;
6327 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6328
6329 return S_OK;
6330}
6331
6332HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6333 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6334{
6335 if (aScreenId != 0)
6336 return E_NOTIMPL;
6337
6338 if ( aBitmapFormat != BitmapFormat_BGR0
6339 && aBitmapFormat != BitmapFormat_BGRA
6340 && aBitmapFormat != BitmapFormat_RGBA
6341 && aBitmapFormat != BitmapFormat_PNG)
6342 return setError(E_NOTIMPL,
6343 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6344
6345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6346
6347 uint8_t *pu8Data = NULL;
6348 uint32_t cbData = 0;
6349 uint32_t u32Width = 0;
6350 uint32_t u32Height = 0;
6351
6352 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6353
6354 if (RT_FAILURE(vrc))
6355 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6356 tr("Saved thumbnail data is not available (%Rrc)"),
6357 vrc);
6358
6359 HRESULT hr = S_OK;
6360
6361 *aWidth = u32Width;
6362 *aHeight = u32Height;
6363
6364 if (cbData > 0)
6365 {
6366 /* Convert pixels to the format expected by the API caller. */
6367 if (aBitmapFormat == BitmapFormat_BGR0)
6368 {
6369 /* [0] B, [1] G, [2] R, [3] 0. */
6370 aData.resize(cbData);
6371 memcpy(&aData.front(), pu8Data, cbData);
6372 }
6373 else if (aBitmapFormat == BitmapFormat_BGRA)
6374 {
6375 /* [0] B, [1] G, [2] R, [3] A. */
6376 aData.resize(cbData);
6377 for (uint32_t i = 0; i < cbData; i += 4)
6378 {
6379 aData[i] = pu8Data[i];
6380 aData[i + 1] = pu8Data[i + 1];
6381 aData[i + 2] = pu8Data[i + 2];
6382 aData[i + 3] = 0xff;
6383 }
6384 }
6385 else if (aBitmapFormat == BitmapFormat_RGBA)
6386 {
6387 /* [0] R, [1] G, [2] B, [3] A. */
6388 aData.resize(cbData);
6389 for (uint32_t i = 0; i < cbData; i += 4)
6390 {
6391 aData[i] = pu8Data[i + 2];
6392 aData[i + 1] = pu8Data[i + 1];
6393 aData[i + 2] = pu8Data[i];
6394 aData[i + 3] = 0xff;
6395 }
6396 }
6397 else if (aBitmapFormat == BitmapFormat_PNG)
6398 {
6399 uint8_t *pu8PNG = NULL;
6400 uint32_t cbPNG = 0;
6401 uint32_t cxPNG = 0;
6402 uint32_t cyPNG = 0;
6403
6404 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6405
6406 if (RT_SUCCESS(vrc))
6407 {
6408 aData.resize(cbPNG);
6409 if (cbPNG)
6410 memcpy(&aData.front(), pu8PNG, cbPNG);
6411 }
6412 else
6413 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6414 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6415 vrc);
6416
6417 RTMemFree(pu8PNG);
6418 }
6419 }
6420
6421 freeSavedDisplayScreenshot(pu8Data);
6422
6423 return hr;
6424}
6425
6426HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6427 ULONG *aWidth,
6428 ULONG *aHeight,
6429 std::vector<BitmapFormat_T> &aBitmapFormats)
6430{
6431 if (aScreenId != 0)
6432 return E_NOTIMPL;
6433
6434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6435
6436 uint8_t *pu8Data = NULL;
6437 uint32_t cbData = 0;
6438 uint32_t u32Width = 0;
6439 uint32_t u32Height = 0;
6440
6441 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6442
6443 if (RT_FAILURE(vrc))
6444 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6445 tr("Saved screenshot data is not available (%Rrc)"),
6446 vrc);
6447
6448 *aWidth = u32Width;
6449 *aHeight = u32Height;
6450 aBitmapFormats.resize(1);
6451 aBitmapFormats[0] = BitmapFormat_PNG;
6452
6453 freeSavedDisplayScreenshot(pu8Data);
6454
6455 return S_OK;
6456}
6457
6458HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6459 BitmapFormat_T aBitmapFormat,
6460 ULONG *aWidth,
6461 ULONG *aHeight,
6462 std::vector<BYTE> &aData)
6463{
6464 if (aScreenId != 0)
6465 return E_NOTIMPL;
6466
6467 if (aBitmapFormat != BitmapFormat_PNG)
6468 return E_NOTIMPL;
6469
6470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6471
6472 uint8_t *pu8Data = NULL;
6473 uint32_t cbData = 0;
6474 uint32_t u32Width = 0;
6475 uint32_t u32Height = 0;
6476
6477 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6478
6479 if (RT_FAILURE(vrc))
6480 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6481 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6482 vrc);
6483
6484 *aWidth = u32Width;
6485 *aHeight = u32Height;
6486
6487 aData.resize(cbData);
6488 if (cbData)
6489 memcpy(&aData.front(), pu8Data, cbData);
6490
6491 freeSavedDisplayScreenshot(pu8Data);
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::hotPlugCPU(ULONG aCpu)
6497{
6498 HRESULT rc = S_OK;
6499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6500
6501 if (!mHWData->mCPUHotPlugEnabled)
6502 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6503
6504 if (aCpu >= mHWData->mCPUCount)
6505 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6506
6507 if (mHWData->mCPUAttached[aCpu])
6508 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6509
6510 alock.release();
6511 rc = i_onCPUChange(aCpu, false);
6512 alock.acquire();
6513 if (FAILED(rc)) return rc;
6514
6515 i_setModified(IsModified_MachineData);
6516 mHWData.backup();
6517 mHWData->mCPUAttached[aCpu] = true;
6518
6519 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6520 if (Global::IsOnline(mData->mMachineState))
6521 i_saveSettings(NULL);
6522
6523 return S_OK;
6524}
6525
6526HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6527{
6528 HRESULT rc = S_OK;
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 if (!mHWData->mCPUHotPlugEnabled)
6533 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6534
6535 if (aCpu >= SchemaDefs::MaxCPUCount)
6536 return setError(E_INVALIDARG,
6537 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6538 SchemaDefs::MaxCPUCount);
6539
6540 if (!mHWData->mCPUAttached[aCpu])
6541 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6542
6543 /* CPU 0 can't be detached */
6544 if (aCpu == 0)
6545 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6546
6547 alock.release();
6548 rc = i_onCPUChange(aCpu, true);
6549 alock.acquire();
6550 if (FAILED(rc)) return rc;
6551
6552 i_setModified(IsModified_MachineData);
6553 mHWData.backup();
6554 mHWData->mCPUAttached[aCpu] = false;
6555
6556 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6557 if (Global::IsOnline(mData->mMachineState))
6558 i_saveSettings(NULL);
6559
6560 return S_OK;
6561}
6562
6563HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6564{
6565 *aAttached = false;
6566
6567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6568
6569 /* If hotplug is enabled the CPU is always enabled. */
6570 if (!mHWData->mCPUHotPlugEnabled)
6571 {
6572 if (aCpu < mHWData->mCPUCount)
6573 *aAttached = true;
6574 }
6575 else
6576 {
6577 if (aCpu < SchemaDefs::MaxCPUCount)
6578 *aAttached = mHWData->mCPUAttached[aCpu];
6579 }
6580
6581 return S_OK;
6582}
6583
6584HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6585{
6586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 Utf8Str log = i_getLogFilename(aIdx);
6589 if (!RTFileExists(log.c_str()))
6590 log.setNull();
6591 aFilename = log;
6592
6593 return S_OK;
6594}
6595
6596HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6597{
6598 if (aSize < 0)
6599 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6600
6601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6602
6603 HRESULT rc = S_OK;
6604 Utf8Str log = i_getLogFilename(aIdx);
6605
6606 /* do not unnecessarily hold the lock while doing something which does
6607 * not need the lock and potentially takes a long time. */
6608 alock.release();
6609
6610 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6611 * keeps the SOAP reply size under 1M for the webservice (we're using
6612 * base64 encoded strings for binary data for years now, avoiding the
6613 * expansion of each byte array element to approx. 25 bytes of XML. */
6614 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6615 aData.resize(cbData);
6616
6617 RTFILE LogFile;
6618 int vrc = RTFileOpen(&LogFile, log.c_str(),
6619 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6620 if (RT_SUCCESS(vrc))
6621 {
6622 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6623 if (RT_SUCCESS(vrc))
6624 aData.resize(cbData);
6625 else
6626 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6627 tr("Could not read log file '%s' (%Rrc)"),
6628 log.c_str(), vrc);
6629 RTFileClose(LogFile);
6630 }
6631 else
6632 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6633 tr("Could not open log file '%s' (%Rrc)"),
6634 log.c_str(), vrc);
6635
6636 if (FAILED(rc))
6637 aData.resize(0);
6638
6639 return rc;
6640}
6641
6642
6643/**
6644 * Currently this method doesn't attach device to the running VM,
6645 * just makes sure it's plugged on next VM start.
6646 */
6647HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6648{
6649 // lock scope
6650 {
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 HRESULT rc = i_checkStateDependency(MutableStateDep);
6654 if (FAILED(rc)) return rc;
6655
6656 ChipsetType_T aChipset = ChipsetType_PIIX3;
6657 COMGETTER(ChipsetType)(&aChipset);
6658
6659 if (aChipset != ChipsetType_ICH9)
6660 {
6661 return setError(E_INVALIDARG,
6662 tr("Host PCI attachment only supported with ICH9 chipset"));
6663 }
6664
6665 // check if device with this host PCI address already attached
6666 for (HWData::PCIDeviceAssignmentList::const_iterator
6667 it = mHWData->mPCIDeviceAssignments.begin();
6668 it != mHWData->mPCIDeviceAssignments.end();
6669 ++it)
6670 {
6671 LONG iHostAddress = -1;
6672 ComPtr<PCIDeviceAttachment> pAttach;
6673 pAttach = *it;
6674 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6675 if (iHostAddress == aHostAddress)
6676 return setError(E_INVALIDARG,
6677 tr("Device with host PCI address already attached to this VM"));
6678 }
6679
6680 ComObjPtr<PCIDeviceAttachment> pda;
6681 char name[32];
6682
6683 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6684 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6685 pda.createObject();
6686 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6687 i_setModified(IsModified_MachineData);
6688 mHWData.backup();
6689 mHWData->mPCIDeviceAssignments.push_back(pda);
6690 }
6691
6692 return S_OK;
6693}
6694
6695/**
6696 * Currently this method doesn't detach device from the running VM,
6697 * just makes sure it's not plugged on next VM start.
6698 */
6699HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6700{
6701 ComObjPtr<PCIDeviceAttachment> pAttach;
6702 bool fRemoved = false;
6703 HRESULT rc;
6704
6705 // lock scope
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 rc = i_checkStateDependency(MutableStateDep);
6710 if (FAILED(rc)) return rc;
6711
6712 for (HWData::PCIDeviceAssignmentList::const_iterator
6713 it = mHWData->mPCIDeviceAssignments.begin();
6714 it != mHWData->mPCIDeviceAssignments.end();
6715 ++it)
6716 {
6717 LONG iHostAddress = -1;
6718 pAttach = *it;
6719 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6720 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6721 {
6722 i_setModified(IsModified_MachineData);
6723 mHWData.backup();
6724 mHWData->mPCIDeviceAssignments.remove(pAttach);
6725 fRemoved = true;
6726 break;
6727 }
6728 }
6729 }
6730
6731
6732 /* Fire event outside of the lock */
6733 if (fRemoved)
6734 {
6735 Assert(!pAttach.isNull());
6736 ComPtr<IEventSource> es;
6737 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6738 Assert(SUCCEEDED(rc));
6739 Bstr mid;
6740 rc = this->COMGETTER(Id)(mid.asOutParam());
6741 Assert(SUCCEEDED(rc));
6742 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6743 }
6744
6745 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6746 tr("No host PCI device %08x attached"),
6747 aHostAddress
6748 );
6749}
6750
6751HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6756 size_t i = 0;
6757 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6758 it = mHWData->mPCIDeviceAssignments.begin();
6759 it != mHWData->mPCIDeviceAssignments.end();
6760 ++it, ++i)
6761 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6767{
6768 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6778
6779 return S_OK;
6780}
6781
6782HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6783{
6784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6785 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6786 if (SUCCEEDED(hrc))
6787 {
6788 hrc = mHWData.backupEx();
6789 if (SUCCEEDED(hrc))
6790 {
6791 i_setModified(IsModified_MachineData);
6792 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6793 }
6794 }
6795 return hrc;
6796}
6797
6798HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6799{
6800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6802 return S_OK;
6803}
6804
6805HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6806{
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6815 if (SUCCEEDED(hrc))
6816 i_setModified(IsModified_MachineData);
6817 }
6818 }
6819 return hrc;
6820}
6821
6822HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6823{
6824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6825
6826 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6832{
6833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6834 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6835 if (SUCCEEDED(hrc))
6836 {
6837 hrc = mHWData.backupEx();
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6842 }
6843 }
6844 return hrc;
6845}
6846
6847HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6848{
6849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6850
6851 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6857{
6858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6861 if ( SUCCEEDED(hrc)
6862 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6863 {
6864 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6865 int vrc;
6866
6867 if (aAutostartEnabled)
6868 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6869 else
6870 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6879 }
6880 }
6881 else if (vrc == VERR_NOT_SUPPORTED)
6882 hrc = setError(VBOX_E_NOT_SUPPORTED,
6883 tr("The VM autostart feature is not supported on this platform"));
6884 else if (vrc == VERR_PATH_NOT_FOUND)
6885 hrc = setError(E_FAIL,
6886 tr("The path to the autostart database is not set"));
6887 else
6888 hrc = setError(E_UNEXPECTED,
6889 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6890 aAutostartEnabled ? "Adding" : "Removing",
6891 mUserData->s.strName.c_str(), vrc);
6892 }
6893 return hrc;
6894}
6895
6896HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6897{
6898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6899
6900 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6901
6902 return S_OK;
6903}
6904
6905HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6906{
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6909 if (SUCCEEDED(hrc))
6910 {
6911 hrc = mHWData.backupEx();
6912 if (SUCCEEDED(hrc))
6913 {
6914 i_setModified(IsModified_MachineData);
6915 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6916 }
6917 }
6918 return hrc;
6919}
6920
6921HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6922{
6923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6924
6925 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6926
6927 return S_OK;
6928}
6929
6930HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6931{
6932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6933 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6934 if ( SUCCEEDED(hrc)
6935 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6936 {
6937 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6938 int vrc;
6939
6940 if (aAutostopType != AutostopType_Disabled)
6941 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6942 else
6943 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6944
6945 if (RT_SUCCESS(vrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 i_setModified(IsModified_MachineData);
6951 mHWData->mAutostart.enmAutostopType = aAutostopType;
6952 }
6953 }
6954 else if (vrc == VERR_NOT_SUPPORTED)
6955 hrc = setError(VBOX_E_NOT_SUPPORTED,
6956 tr("The VM autostop feature is not supported on this platform"));
6957 else if (vrc == VERR_PATH_NOT_FOUND)
6958 hrc = setError(E_FAIL,
6959 tr("The path to the autostart database is not set"));
6960 else
6961 hrc = setError(E_UNEXPECTED,
6962 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6963 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6964 mUserData->s.strName.c_str(), vrc);
6965 }
6966 return hrc;
6967}
6968
6969HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6970{
6971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 aDefaultFrontend = mHWData->mDefaultFrontend;
6974
6975 return S_OK;
6976}
6977
6978HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6979{
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6982 if (SUCCEEDED(hrc))
6983 {
6984 hrc = mHWData.backupEx();
6985 if (SUCCEEDED(hrc))
6986 {
6987 i_setModified(IsModified_MachineData);
6988 mHWData->mDefaultFrontend = aDefaultFrontend;
6989 }
6990 }
6991 return hrc;
6992}
6993
6994HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6995{
6996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 size_t cbIcon = mUserData->s.ovIcon.size();
6998 aIcon.resize(cbIcon);
6999 if (cbIcon)
7000 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7001 return S_OK;
7002}
7003
7004HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7005{
7006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7007 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7008 if (SUCCEEDED(hrc))
7009 {
7010 i_setModified(IsModified_MachineData);
7011 mUserData.backup();
7012 size_t cbIcon = aIcon.size();
7013 mUserData->s.ovIcon.resize(cbIcon);
7014 if (cbIcon)
7015 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7016 }
7017 return hrc;
7018}
7019
7020HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7021{
7022#ifdef VBOX_WITH_USB
7023 *aUSBProxyAvailable = true;
7024#else
7025 *aUSBProxyAvailable = false;
7026#endif
7027 return S_OK;
7028}
7029
7030HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7031{
7032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7033
7034 aVMProcessPriority = mUserData->s.strVMPriority;
7035
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7040{
7041 RT_NOREF(aVMProcessPriority);
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7044 if (SUCCEEDED(hrc))
7045 {
7046 /** @todo r=klaus: currently this is marked as not implemented, as
7047 * the code for setting the priority of the process is not there
7048 * (neither when starting the VM nor at runtime). */
7049 ReturnComNotImplemented();
7050#if 0
7051 hrc = mUserData.backupEx();
7052 if (SUCCEEDED(hrc))
7053 {
7054 i_setModified(IsModified_MachineData);
7055 mUserData->s.strVMPriority = aVMProcessPriority;
7056 }
7057#endif
7058 }
7059 return hrc;
7060}
7061
7062HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7063 ComPtr<IProgress> &aProgress)
7064{
7065 ComObjPtr<Progress> pP;
7066 Progress *ppP = pP;
7067 IProgress *iP = static_cast<IProgress *>(ppP);
7068 IProgress **pProgress = &iP;
7069
7070 IMachine *pTarget = aTarget;
7071
7072 /* Convert the options. */
7073 RTCList<CloneOptions_T> optList;
7074 if (aOptions.size())
7075 for (size_t i = 0; i < aOptions.size(); ++i)
7076 optList.append(aOptions[i]);
7077
7078 if (optList.contains(CloneOptions_Link))
7079 {
7080 if (!i_isSnapshotMachine())
7081 return setError(E_INVALIDARG,
7082 tr("Linked clone can only be created from a snapshot"));
7083 if (aMode != CloneMode_MachineState)
7084 return setError(E_INVALIDARG,
7085 tr("Linked clone can only be created for a single machine state"));
7086 }
7087 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7088
7089 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7090
7091 HRESULT rc = pWorker->start(pProgress);
7092
7093 pP = static_cast<Progress *>(*pProgress);
7094 pP.queryInterfaceTo(aProgress.asOutParam());
7095
7096 return rc;
7097
7098}
7099
7100HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7101 const com::Utf8Str &aType,
7102 ComPtr<IProgress> &aProgress)
7103{
7104 LogFlowThisFuncEnter();
7105
7106 ComObjPtr<Progress> progress;
7107
7108 progress.createObject();
7109
7110 HRESULT rc = S_OK;
7111 Utf8Str targetPath = aTargetPath;
7112 Utf8Str type = aType;
7113
7114 /* Initialize our worker task */
7115 MachineMoveVM* task = NULL;
7116 try
7117 {
7118 task = new MachineMoveVM(this, targetPath, type, progress);
7119 }
7120 catch(...)
7121 {
7122 delete task;
7123 return rc;
7124 }
7125
7126 /*
7127 * task pointer will be owned by the ThreadTask class.
7128 * There is no need to call operator "delete" in the end.
7129 */
7130 rc = task->init();
7131 if (SUCCEEDED(rc))
7132 {
7133 rc = task->createThread();
7134 if (FAILED(rc))
7135 {
7136 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7137 }
7138
7139 /* Return progress to the caller */
7140 progress.queryInterfaceTo(aProgress.asOutParam());
7141 }
7142
7143 LogFlowThisFuncLeave();
7144 return rc;
7145
7146}
7147
7148HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7149{
7150 NOREF(aProgress);
7151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7152
7153 // This check should always fail.
7154 HRESULT rc = i_checkStateDependency(MutableStateDep);
7155 if (FAILED(rc)) return rc;
7156
7157 AssertFailedReturn(E_NOTIMPL);
7158}
7159
7160HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7161{
7162 NOREF(aSavedStateFile);
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 // This check should always fail.
7166 HRESULT rc = i_checkStateDependency(MutableStateDep);
7167 if (FAILED(rc)) return rc;
7168
7169 AssertFailedReturn(E_NOTIMPL);
7170}
7171
7172HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7173{
7174 NOREF(aFRemoveFile);
7175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 // This check should always fail.
7178 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7179 if (FAILED(rc)) return rc;
7180
7181 AssertFailedReturn(E_NOTIMPL);
7182}
7183
7184// public methods for internal purposes
7185/////////////////////////////////////////////////////////////////////////////
7186
7187/**
7188 * Adds the given IsModified_* flag to the dirty flags of the machine.
7189 * This must be called either during i_loadSettings or under the machine write lock.
7190 * @param fl Flag
7191 * @param fAllowStateModification If state modifications are allowed.
7192 */
7193void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7194{
7195 mData->flModifications |= fl;
7196 if (fAllowStateModification && i_isStateModificationAllowed())
7197 mData->mCurrentStateModified = true;
7198}
7199
7200/**
7201 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7202 * care of the write locking.
7203 *
7204 * @param fModification The flag to add.
7205 * @param fAllowStateModification If state modifications are allowed.
7206 */
7207void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7208{
7209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7210 i_setModified(fModification, fAllowStateModification);
7211}
7212
7213/**
7214 * Saves the registry entry of this machine to the given configuration node.
7215 *
7216 * @param data Machine registry data.
7217 *
7218 * @note locks this object for reading.
7219 */
7220HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7221{
7222 AutoLimitedCaller autoCaller(this);
7223 AssertComRCReturnRC(autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 data.uuid = mData->mUuid;
7228 data.strSettingsFile = mData->m_strConfigFile;
7229
7230 return S_OK;
7231}
7232
7233/**
7234 * Calculates the absolute path of the given path taking the directory of the
7235 * machine settings file as the current directory.
7236 *
7237 * @param strPath Path to calculate the absolute path for.
7238 * @param aResult Where to put the result (used only on success, can be the
7239 * same Utf8Str instance as passed in @a aPath).
7240 * @return IPRT result.
7241 *
7242 * @note Locks this object for reading.
7243 */
7244int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7245{
7246 AutoCaller autoCaller(this);
7247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7248
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7252
7253 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7254
7255 strSettingsDir.stripFilename();
7256 char folder[RTPATH_MAX];
7257 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7258 if (RT_SUCCESS(vrc))
7259 aResult = folder;
7260
7261 return vrc;
7262}
7263
7264/**
7265 * Copies strSource to strTarget, making it relative to the machine folder
7266 * if it is a subdirectory thereof, or simply copying it otherwise.
7267 *
7268 * @param strSource Path to evaluate and copy.
7269 * @param strTarget Buffer to receive target path.
7270 *
7271 * @note Locks this object for reading.
7272 */
7273void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7274 Utf8Str &strTarget)
7275{
7276 AutoCaller autoCaller(this);
7277 AssertComRCReturn(autoCaller.rc(), (void)0);
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7282 // use strTarget as a temporary buffer to hold the machine settings dir
7283 strTarget = mData->m_strConfigFileFull;
7284 strTarget.stripFilename();
7285 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7286 {
7287 // is relative: then append what's left
7288 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7289 // for empty paths (only possible for subdirs) use "." to avoid
7290 // triggering default settings for not present config attributes.
7291 if (strTarget.isEmpty())
7292 strTarget = ".";
7293 }
7294 else
7295 // is not relative: then overwrite
7296 strTarget = strSource;
7297}
7298
7299/**
7300 * Returns the full path to the machine's log folder in the
7301 * \a aLogFolder argument.
7302 */
7303void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7304{
7305 AutoCaller autoCaller(this);
7306 AssertComRCReturnVoid(autoCaller.rc());
7307
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 char szTmp[RTPATH_MAX];
7311 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7312 if (RT_SUCCESS(vrc))
7313 {
7314 if (szTmp[0] && !mUserData.isNull())
7315 {
7316 char szTmp2[RTPATH_MAX];
7317 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7318 if (RT_SUCCESS(vrc))
7319 aLogFolder = Utf8StrFmt("%s%c%s",
7320 szTmp2,
7321 RTPATH_DELIMITER,
7322 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7323 }
7324 else
7325 vrc = VERR_PATH_IS_RELATIVE;
7326 }
7327
7328 if (RT_FAILURE(vrc))
7329 {
7330 // fallback if VBOX_USER_LOGHOME is not set or invalid
7331 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7332 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7333 aLogFolder.append(RTPATH_DELIMITER);
7334 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7335 }
7336}
7337
7338/**
7339 * Returns the full path to the machine's log file for an given index.
7340 */
7341Utf8Str Machine::i_getLogFilename(ULONG idx)
7342{
7343 Utf8Str logFolder;
7344 getLogFolder(logFolder);
7345 Assert(logFolder.length());
7346
7347 Utf8Str log;
7348 if (idx == 0)
7349 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7350#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7351 else if (idx == 1)
7352 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7353 else
7354 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7355#else
7356 else
7357 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7358#endif
7359 return log;
7360}
7361
7362/**
7363 * Returns the full path to the machine's hardened log file.
7364 */
7365Utf8Str Machine::i_getHardeningLogFilename(void)
7366{
7367 Utf8Str strFilename;
7368 getLogFolder(strFilename);
7369 Assert(strFilename.length());
7370 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7371 return strFilename;
7372}
7373
7374
7375/**
7376 * Composes a unique saved state filename based on the current system time. The filename is
7377 * granular to the second so this will work so long as no more than one snapshot is taken on
7378 * a machine per second.
7379 *
7380 * Before version 4.1, we used this formula for saved state files:
7381 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7382 * which no longer works because saved state files can now be shared between the saved state of the
7383 * "saved" machine and an online snapshot, and the following would cause problems:
7384 * 1) save machine
7385 * 2) create online snapshot from that machine state --> reusing saved state file
7386 * 3) save machine again --> filename would be reused, breaking the online snapshot
7387 *
7388 * So instead we now use a timestamp.
7389 *
7390 * @param strStateFilePath
7391 */
7392
7393void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7394{
7395 AutoCaller autoCaller(this);
7396 AssertComRCReturnVoid(autoCaller.rc());
7397
7398 {
7399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7401 }
7402
7403 RTTIMESPEC ts;
7404 RTTimeNow(&ts);
7405 RTTIME time;
7406 RTTimeExplode(&time, &ts);
7407
7408 strStateFilePath += RTPATH_DELIMITER;
7409 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7410 time.i32Year, time.u8Month, time.u8MonthDay,
7411 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7412}
7413
7414/**
7415 * Returns whether at least one USB controller is present for the VM.
7416 */
7417bool Machine::i_isUSBControllerPresent()
7418{
7419 AutoCaller autoCaller(this);
7420 AssertComRCReturn(autoCaller.rc(), false);
7421
7422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7423
7424 return (mUSBControllers->size() > 0);
7425}
7426
7427/**
7428 * @note Locks this object for writing, calls the client process
7429 * (inside the lock).
7430 */
7431HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7432 const Utf8Str &strFrontend,
7433 const Utf8Str &strEnvironment,
7434 ProgressProxy *aProgress)
7435{
7436 LogFlowThisFuncEnter();
7437
7438 AssertReturn(aControl, E_FAIL);
7439 AssertReturn(aProgress, E_FAIL);
7440 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7441
7442 AutoCaller autoCaller(this);
7443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7444
7445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7446
7447 if (!mData->mRegistered)
7448 return setError(E_UNEXPECTED,
7449 tr("The machine '%s' is not registered"),
7450 mUserData->s.strName.c_str());
7451
7452 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7453
7454 /* The process started when launching a VM with separate UI/VM processes is always
7455 * the UI process, i.e. needs special handling as it won't claim the session. */
7456 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7457
7458 if (fSeparate)
7459 {
7460 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7461 return setError(VBOX_E_INVALID_OBJECT_STATE,
7462 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7463 mUserData->s.strName.c_str());
7464 }
7465 else
7466 {
7467 if ( mData->mSession.mState == SessionState_Locked
7468 || mData->mSession.mState == SessionState_Spawning
7469 || mData->mSession.mState == SessionState_Unlocking)
7470 return setError(VBOX_E_INVALID_OBJECT_STATE,
7471 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7472 mUserData->s.strName.c_str());
7473
7474 /* may not be busy */
7475 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7476 }
7477
7478 /* get the path to the executable */
7479 char szPath[RTPATH_MAX];
7480 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7481 size_t cchBufLeft = strlen(szPath);
7482 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7483 szPath[cchBufLeft] = 0;
7484 char *pszNamePart = szPath + cchBufLeft;
7485 cchBufLeft = sizeof(szPath) - cchBufLeft;
7486
7487 int vrc = VINF_SUCCESS;
7488 RTPROCESS pid = NIL_RTPROCESS;
7489
7490 RTENV env = RTENV_DEFAULT;
7491
7492 if (!strEnvironment.isEmpty())
7493 {
7494 char *newEnvStr = NULL;
7495
7496 do
7497 {
7498 /* clone the current environment */
7499 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7500 AssertRCBreakStmt(vrc2, vrc = vrc2);
7501
7502 newEnvStr = RTStrDup(strEnvironment.c_str());
7503 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7504
7505 /* put new variables to the environment
7506 * (ignore empty variable names here since RTEnv API
7507 * intentionally doesn't do that) */
7508 char *var = newEnvStr;
7509 for (char *p = newEnvStr; *p; ++p)
7510 {
7511 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7512 {
7513 *p = '\0';
7514 if (*var)
7515 {
7516 char *val = strchr(var, '=');
7517 if (val)
7518 {
7519 *val++ = '\0';
7520 vrc2 = RTEnvSetEx(env, var, val);
7521 }
7522 else
7523 vrc2 = RTEnvUnsetEx(env, var);
7524 if (RT_FAILURE(vrc2))
7525 break;
7526 }
7527 var = p + 1;
7528 }
7529 }
7530 if (RT_SUCCESS(vrc2) && *var)
7531 vrc2 = RTEnvPutEx(env, var);
7532
7533 AssertRCBreakStmt(vrc2, vrc = vrc2);
7534 }
7535 while (0);
7536
7537 if (newEnvStr != NULL)
7538 RTStrFree(newEnvStr);
7539 }
7540
7541 /* Hardening logging */
7542#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7543 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7544 {
7545 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7546 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7547 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7548 {
7549 Utf8Str strStartupLogDir = strHardeningLogFile;
7550 strStartupLogDir.stripFilename();
7551 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7552 file without stripping the file. */
7553 }
7554 strSupHardeningLogArg.append(strHardeningLogFile);
7555
7556 /* Remove legacy log filename to avoid confusion. */
7557 Utf8Str strOldStartupLogFile;
7558 getLogFolder(strOldStartupLogFile);
7559 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7560 RTFileDelete(strOldStartupLogFile.c_str());
7561 }
7562 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7563#else
7564 const char *pszSupHardeningLogArg = NULL;
7565#endif
7566
7567 Utf8Str strCanonicalName;
7568
7569#ifdef VBOX_WITH_QTGUI
7570 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7571 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7572 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7573 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7574 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7575 {
7576 strCanonicalName = "GUI/Qt";
7577# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7578 /* Modify the base path so that we don't need to use ".." below. */
7579 RTPathStripTrailingSlash(szPath);
7580 RTPathStripFilename(szPath);
7581 cchBufLeft = strlen(szPath);
7582 pszNamePart = szPath + cchBufLeft;
7583 cchBufLeft = sizeof(szPath) - cchBufLeft;
7584
7585# define OSX_APP_NAME "VirtualBoxVM"
7586# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7587
7588 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7589 if ( strAppOverride.contains(".")
7590 || strAppOverride.contains("/")
7591 || strAppOverride.contains("\\")
7592 || strAppOverride.contains(":"))
7593 strAppOverride.setNull();
7594 Utf8Str strAppPath;
7595 if (!strAppOverride.isEmpty())
7596 {
7597 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7598 Utf8Str strFullPath(szPath);
7599 strFullPath.append(strAppPath);
7600 /* there is a race, but people using this deserve the failure */
7601 if (!RTFileExists(strFullPath.c_str()))
7602 strAppOverride.setNull();
7603 }
7604 if (strAppOverride.isEmpty())
7605 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7606 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7607 strcpy(pszNamePart, strAppPath.c_str());
7608# else
7609# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7610 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7611# else
7612 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7613# endif
7614 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7615 strcpy(pszNamePart, s_szVirtualBox_exe);
7616# endif
7617
7618 Utf8Str idStr = mData->mUuid.toString();
7619 const char *apszArgs[] =
7620 {
7621 szPath,
7622 "--comment", mUserData->s.strName.c_str(),
7623 "--startvm", idStr.c_str(),
7624 "--no-startvm-errormsgbox",
7625 NULL, /* For "--separate". */
7626 NULL, /* For "--sup-startup-log". */
7627 NULL
7628 };
7629 unsigned iArg = 6;
7630 if (fSeparate)
7631 apszArgs[iArg++] = "--separate";
7632 apszArgs[iArg++] = pszSupHardeningLogArg;
7633
7634 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7635 }
7636#else /* !VBOX_WITH_QTGUI */
7637 if (0)
7638 ;
7639#endif /* VBOX_WITH_QTGUI */
7640
7641 else
7642
7643#ifdef VBOX_WITH_VBOXSDL
7644 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7645 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7646 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7647 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7648 {
7649 strCanonicalName = "GUI/SDL";
7650 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7651 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7652 strcpy(pszNamePart, s_szVBoxSDL_exe);
7653
7654 Utf8Str idStr = mData->mUuid.toString();
7655 const char *apszArgs[] =
7656 {
7657 szPath,
7658 "--comment", mUserData->s.strName.c_str(),
7659 "--startvm", idStr.c_str(),
7660 NULL, /* For "--separate". */
7661 NULL, /* For "--sup-startup-log". */
7662 NULL
7663 };
7664 unsigned iArg = 5;
7665 if (fSeparate)
7666 apszArgs[iArg++] = "--separate";
7667 apszArgs[iArg++] = pszSupHardeningLogArg;
7668
7669 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7670 }
7671#else /* !VBOX_WITH_VBOXSDL */
7672 if (0)
7673 ;
7674#endif /* !VBOX_WITH_VBOXSDL */
7675
7676 else
7677
7678#ifdef VBOX_WITH_HEADLESS
7679 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7680 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7681 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7682 )
7683 {
7684 strCanonicalName = "headless";
7685 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7686 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7687 * and a VM works even if the server has not been installed.
7688 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7689 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7690 * differently in 4.0 and 3.x.
7691 */
7692 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7693 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7694 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7695
7696 Utf8Str idStr = mData->mUuid.toString();
7697 const char *apszArgs[] =
7698 {
7699 szPath,
7700 "--comment", mUserData->s.strName.c_str(),
7701 "--startvm", idStr.c_str(),
7702 "--vrde", "config",
7703 NULL, /* For "--capture". */
7704 NULL, /* For "--sup-startup-log". */
7705 NULL
7706 };
7707 unsigned iArg = 7;
7708 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7709 apszArgs[iArg++] = "--capture";
7710 apszArgs[iArg++] = pszSupHardeningLogArg;
7711
7712# ifdef RT_OS_WINDOWS
7713 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7714# else
7715 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7716# endif
7717 }
7718#else /* !VBOX_WITH_HEADLESS */
7719 if (0)
7720 ;
7721#endif /* !VBOX_WITH_HEADLESS */
7722 else
7723 {
7724 RTEnvDestroy(env);
7725 return setError(E_INVALIDARG,
7726 tr("Invalid frontend name: '%s'"),
7727 strFrontend.c_str());
7728 }
7729
7730 RTEnvDestroy(env);
7731
7732 if (RT_FAILURE(vrc))
7733 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7734 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7735 mUserData->s.strName.c_str(), vrc);
7736
7737 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7738
7739 if (!fSeparate)
7740 {
7741 /*
7742 * Note that we don't release the lock here before calling the client,
7743 * because it doesn't need to call us back if called with a NULL argument.
7744 * Releasing the lock here is dangerous because we didn't prepare the
7745 * launch data yet, but the client we've just started may happen to be
7746 * too fast and call LockMachine() that will fail (because of PID, etc.),
7747 * so that the Machine will never get out of the Spawning session state.
7748 */
7749
7750 /* inform the session that it will be a remote one */
7751 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7752#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7753 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7754#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7755 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7756#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7757 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7758
7759 if (FAILED(rc))
7760 {
7761 /* restore the session state */
7762 mData->mSession.mState = SessionState_Unlocked;
7763 alock.release();
7764 mParent->i_addProcessToReap(pid);
7765 /* The failure may occur w/o any error info (from RPC), so provide one */
7766 return setError(VBOX_E_VM_ERROR,
7767 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7768 }
7769
7770 /* attach launch data to the machine */
7771 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7772 mData->mSession.mRemoteControls.push_back(aControl);
7773 mData->mSession.mProgress = aProgress;
7774 mData->mSession.mPID = pid;
7775 mData->mSession.mState = SessionState_Spawning;
7776 Assert(strCanonicalName.isNotEmpty());
7777 mData->mSession.mName = strCanonicalName;
7778 }
7779 else
7780 {
7781 /* For separate UI process we declare the launch as completed instantly, as the
7782 * actual headless VM start may or may not come. No point in remembering anything
7783 * yet, as what matters for us is when the headless VM gets started. */
7784 aProgress->i_notifyComplete(S_OK);
7785 }
7786
7787 alock.release();
7788 mParent->i_addProcessToReap(pid);
7789
7790 LogFlowThisFuncLeave();
7791 return S_OK;
7792}
7793
7794/**
7795 * Returns @c true if the given session machine instance has an open direct
7796 * session (and optionally also for direct sessions which are closing) and
7797 * returns the session control machine instance if so.
7798 *
7799 * Note that when the method returns @c false, the arguments remain unchanged.
7800 *
7801 * @param aMachine Session machine object.
7802 * @param aControl Direct session control object (optional).
7803 * @param aRequireVM If true then only allow VM sessions.
7804 * @param aAllowClosing If true then additionally a session which is currently
7805 * being closed will also be allowed.
7806 *
7807 * @note locks this object for reading.
7808 */
7809bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7810 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7811 bool aRequireVM /*= false*/,
7812 bool aAllowClosing /*= false*/)
7813{
7814 AutoLimitedCaller autoCaller(this);
7815 AssertComRCReturn(autoCaller.rc(), false);
7816
7817 /* just return false for inaccessible machines */
7818 if (getObjectState().getState() != ObjectState::Ready)
7819 return false;
7820
7821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7822
7823 if ( ( mData->mSession.mState == SessionState_Locked
7824 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7825 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7826 )
7827 {
7828 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7829
7830 aMachine = mData->mSession.mMachine;
7831
7832 if (aControl != NULL)
7833 *aControl = mData->mSession.mDirectControl;
7834
7835 return true;
7836 }
7837
7838 return false;
7839}
7840
7841/**
7842 * Returns @c true if the given machine has an spawning direct session.
7843 *
7844 * @note locks this object for reading.
7845 */
7846bool Machine::i_isSessionSpawning()
7847{
7848 AutoLimitedCaller autoCaller(this);
7849 AssertComRCReturn(autoCaller.rc(), false);
7850
7851 /* just return false for inaccessible machines */
7852 if (getObjectState().getState() != ObjectState::Ready)
7853 return false;
7854
7855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 if (mData->mSession.mState == SessionState_Spawning)
7858 return true;
7859
7860 return false;
7861}
7862
7863/**
7864 * Called from the client watcher thread to check for unexpected client process
7865 * death during Session_Spawning state (e.g. before it successfully opened a
7866 * direct session).
7867 *
7868 * On Win32 and on OS/2, this method is called only when we've got the
7869 * direct client's process termination notification, so it always returns @c
7870 * true.
7871 *
7872 * On other platforms, this method returns @c true if the client process is
7873 * terminated and @c false if it's still alive.
7874 *
7875 * @note Locks this object for writing.
7876 */
7877bool Machine::i_checkForSpawnFailure()
7878{
7879 AutoCaller autoCaller(this);
7880 if (!autoCaller.isOk())
7881 {
7882 /* nothing to do */
7883 LogFlowThisFunc(("Already uninitialized!\n"));
7884 return true;
7885 }
7886
7887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7888
7889 if (mData->mSession.mState != SessionState_Spawning)
7890 {
7891 /* nothing to do */
7892 LogFlowThisFunc(("Not spawning any more!\n"));
7893 return true;
7894 }
7895
7896 HRESULT rc = S_OK;
7897
7898 /* PID not yet initialized, skip check. */
7899 if (mData->mSession.mPID == NIL_RTPROCESS)
7900 return false;
7901
7902 RTPROCSTATUS status;
7903 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7904
7905 if (vrc != VERR_PROCESS_RUNNING)
7906 {
7907 Utf8Str strExtraInfo;
7908
7909#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7910 /* If the startup logfile exists and is of non-zero length, tell the
7911 user to look there for more details to encourage them to attach it
7912 when reporting startup issues. */
7913 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7914 uint64_t cbStartupLogFile = 0;
7915 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7916 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7917 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7918#endif
7919
7920 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7921 rc = setError(E_FAIL,
7922 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7923 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7924 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7925 rc = setError(E_FAIL,
7926 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7927 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7928 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7929 rc = setError(E_FAIL,
7930 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7931 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7932 else
7933 rc = setErrorBoth(E_FAIL, vrc,
7934 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7935 i_getName().c_str(), vrc, strExtraInfo.c_str());
7936 }
7937
7938 if (FAILED(rc))
7939 {
7940 /* Close the remote session, remove the remote control from the list
7941 * and reset session state to Closed (@note keep the code in sync with
7942 * the relevant part in LockMachine()). */
7943
7944 Assert(mData->mSession.mRemoteControls.size() == 1);
7945 if (mData->mSession.mRemoteControls.size() == 1)
7946 {
7947 ErrorInfoKeeper eik;
7948 mData->mSession.mRemoteControls.front()->Uninitialize();
7949 }
7950
7951 mData->mSession.mRemoteControls.clear();
7952 mData->mSession.mState = SessionState_Unlocked;
7953
7954 /* finalize the progress after setting the state */
7955 if (!mData->mSession.mProgress.isNull())
7956 {
7957 mData->mSession.mProgress->notifyComplete(rc);
7958 mData->mSession.mProgress.setNull();
7959 }
7960
7961 mData->mSession.mPID = NIL_RTPROCESS;
7962
7963 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7964 return true;
7965 }
7966
7967 return false;
7968}
7969
7970/**
7971 * Checks whether the machine can be registered. If so, commits and saves
7972 * all settings.
7973 *
7974 * @note Must be called from mParent's write lock. Locks this object and
7975 * children for writing.
7976 */
7977HRESULT Machine::i_prepareRegister()
7978{
7979 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7980
7981 AutoLimitedCaller autoCaller(this);
7982 AssertComRCReturnRC(autoCaller.rc());
7983
7984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7985
7986 /* wait for state dependents to drop to zero */
7987 i_ensureNoStateDependencies();
7988
7989 if (!mData->mAccessible)
7990 return setError(VBOX_E_INVALID_OBJECT_STATE,
7991 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7992 mUserData->s.strName.c_str(),
7993 mData->mUuid.toString().c_str());
7994
7995 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7996
7997 if (mData->mRegistered)
7998 return setError(VBOX_E_INVALID_OBJECT_STATE,
7999 tr("The machine '%s' with UUID {%s} is already registered"),
8000 mUserData->s.strName.c_str(),
8001 mData->mUuid.toString().c_str());
8002
8003 HRESULT rc = S_OK;
8004
8005 // Ensure the settings are saved. If we are going to be registered and
8006 // no config file exists yet, create it by calling i_saveSettings() too.
8007 if ( (mData->flModifications)
8008 || (!mData->pMachineConfigFile->fileExists())
8009 )
8010 {
8011 rc = i_saveSettings(NULL);
8012 // no need to check whether VirtualBox.xml needs saving too since
8013 // we can't have a machine XML file rename pending
8014 if (FAILED(rc)) return rc;
8015 }
8016
8017 /* more config checking goes here */
8018
8019 if (SUCCEEDED(rc))
8020 {
8021 /* we may have had implicit modifications we want to fix on success */
8022 i_commit();
8023
8024 mData->mRegistered = true;
8025 }
8026 else
8027 {
8028 /* we may have had implicit modifications we want to cancel on failure*/
8029 i_rollback(false /* aNotify */);
8030 }
8031
8032 return rc;
8033}
8034
8035/**
8036 * Increases the number of objects dependent on the machine state or on the
8037 * registered state. Guarantees that these two states will not change at least
8038 * until #i_releaseStateDependency() is called.
8039 *
8040 * Depending on the @a aDepType value, additional state checks may be made.
8041 * These checks will set extended error info on failure. See
8042 * #i_checkStateDependency() for more info.
8043 *
8044 * If this method returns a failure, the dependency is not added and the caller
8045 * is not allowed to rely on any particular machine state or registration state
8046 * value and may return the failed result code to the upper level.
8047 *
8048 * @param aDepType Dependency type to add.
8049 * @param aState Current machine state (NULL if not interested).
8050 * @param aRegistered Current registered state (NULL if not interested).
8051 *
8052 * @note Locks this object for writing.
8053 */
8054HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8055 MachineState_T *aState /* = NULL */,
8056 BOOL *aRegistered /* = NULL */)
8057{
8058 AutoCaller autoCaller(this);
8059 AssertComRCReturnRC(autoCaller.rc());
8060
8061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8062
8063 HRESULT rc = i_checkStateDependency(aDepType);
8064 if (FAILED(rc)) return rc;
8065
8066 {
8067 if (mData->mMachineStateChangePending != 0)
8068 {
8069 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8070 * drop to zero so don't add more. It may make sense to wait a bit
8071 * and retry before reporting an error (since the pending state
8072 * transition should be really quick) but let's just assert for
8073 * now to see if it ever happens on practice. */
8074
8075 AssertFailed();
8076
8077 return setError(E_ACCESSDENIED,
8078 tr("Machine state change is in progress. Please retry the operation later."));
8079 }
8080
8081 ++mData->mMachineStateDeps;
8082 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8083 }
8084
8085 if (aState)
8086 *aState = mData->mMachineState;
8087 if (aRegistered)
8088 *aRegistered = mData->mRegistered;
8089
8090 return S_OK;
8091}
8092
8093/**
8094 * Decreases the number of objects dependent on the machine state.
8095 * Must always complete the #i_addStateDependency() call after the state
8096 * dependency is no more necessary.
8097 */
8098void Machine::i_releaseStateDependency()
8099{
8100 AutoCaller autoCaller(this);
8101 AssertComRCReturnVoid(autoCaller.rc());
8102
8103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8104
8105 /* releaseStateDependency() w/o addStateDependency()? */
8106 AssertReturnVoid(mData->mMachineStateDeps != 0);
8107 -- mData->mMachineStateDeps;
8108
8109 if (mData->mMachineStateDeps == 0)
8110 {
8111 /* inform i_ensureNoStateDependencies() that there are no more deps */
8112 if (mData->mMachineStateChangePending != 0)
8113 {
8114 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8115 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8116 }
8117 }
8118}
8119
8120Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8121{
8122 /* start with nothing found */
8123 Utf8Str strResult("");
8124
8125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8126
8127 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8128 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8129 // found:
8130 strResult = it->second; // source is a Utf8Str
8131
8132 return strResult;
8133}
8134
8135// protected methods
8136/////////////////////////////////////////////////////////////////////////////
8137
8138/**
8139 * Performs machine state checks based on the @a aDepType value. If a check
8140 * fails, this method will set extended error info, otherwise it will return
8141 * S_OK. It is supposed, that on failure, the caller will immediately return
8142 * the return value of this method to the upper level.
8143 *
8144 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8145 *
8146 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8147 * current state of this machine object allows to change settings of the
8148 * machine (i.e. the machine is not registered, or registered but not running
8149 * and not saved). It is useful to call this method from Machine setters
8150 * before performing any change.
8151 *
8152 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8153 * as for MutableStateDep except that if the machine is saved, S_OK is also
8154 * returned. This is useful in setters which allow changing machine
8155 * properties when it is in the saved state.
8156 *
8157 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8158 * if the current state of this machine object allows to change runtime
8159 * changeable settings of the machine (i.e. the machine is not registered, or
8160 * registered but either running or not running and not saved). It is useful
8161 * to call this method from Machine setters before performing any changes to
8162 * runtime changeable settings.
8163 *
8164 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8165 * the same as for MutableOrRunningStateDep except that if the machine is
8166 * saved, S_OK is also returned. This is useful in setters which allow
8167 * changing runtime and saved state changeable machine properties.
8168 *
8169 * @param aDepType Dependency type to check.
8170 *
8171 * @note Non Machine based classes should use #i_addStateDependency() and
8172 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8173 * template.
8174 *
8175 * @note This method must be called from under this object's read or write
8176 * lock.
8177 */
8178HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8179{
8180 switch (aDepType)
8181 {
8182 case AnyStateDep:
8183 {
8184 break;
8185 }
8186 case MutableStateDep:
8187 {
8188 if ( mData->mRegistered
8189 && ( !i_isSessionMachine()
8190 || ( mData->mMachineState != MachineState_Aborted
8191 && mData->mMachineState != MachineState_Teleported
8192 && mData->mMachineState != MachineState_PoweredOff
8193 )
8194 )
8195 )
8196 return setError(VBOX_E_INVALID_VM_STATE,
8197 tr("The machine is not mutable (state is %s)"),
8198 Global::stringifyMachineState(mData->mMachineState));
8199 break;
8200 }
8201 case MutableOrSavedStateDep:
8202 {
8203 if ( mData->mRegistered
8204 && ( !i_isSessionMachine()
8205 || ( mData->mMachineState != MachineState_Aborted
8206 && mData->mMachineState != MachineState_Teleported
8207 && mData->mMachineState != MachineState_Saved
8208 && mData->mMachineState != MachineState_PoweredOff
8209 )
8210 )
8211 )
8212 return setError(VBOX_E_INVALID_VM_STATE,
8213 tr("The machine is not mutable or saved (state is %s)"),
8214 Global::stringifyMachineState(mData->mMachineState));
8215 break;
8216 }
8217 case MutableOrRunningStateDep:
8218 {
8219 if ( mData->mRegistered
8220 && ( !i_isSessionMachine()
8221 || ( mData->mMachineState != MachineState_Aborted
8222 && mData->mMachineState != MachineState_Teleported
8223 && mData->mMachineState != MachineState_PoweredOff
8224 && !Global::IsOnline(mData->mMachineState)
8225 )
8226 )
8227 )
8228 return setError(VBOX_E_INVALID_VM_STATE,
8229 tr("The machine is not mutable or running (state is %s)"),
8230 Global::stringifyMachineState(mData->mMachineState));
8231 break;
8232 }
8233 case MutableOrSavedOrRunningStateDep:
8234 {
8235 if ( mData->mRegistered
8236 && ( !i_isSessionMachine()
8237 || ( mData->mMachineState != MachineState_Aborted
8238 && mData->mMachineState != MachineState_Teleported
8239 && mData->mMachineState != MachineState_Saved
8240 && mData->mMachineState != MachineState_PoweredOff
8241 && !Global::IsOnline(mData->mMachineState)
8242 )
8243 )
8244 )
8245 return setError(VBOX_E_INVALID_VM_STATE,
8246 tr("The machine is not mutable, saved or running (state is %s)"),
8247 Global::stringifyMachineState(mData->mMachineState));
8248 break;
8249 }
8250 }
8251
8252 return S_OK;
8253}
8254
8255/**
8256 * Helper to initialize all associated child objects and allocate data
8257 * structures.
8258 *
8259 * This method must be called as a part of the object's initialization procedure
8260 * (usually done in the #init() method).
8261 *
8262 * @note Must be called only from #init() or from #i_registeredInit().
8263 */
8264HRESULT Machine::initDataAndChildObjects()
8265{
8266 AutoCaller autoCaller(this);
8267 AssertComRCReturnRC(autoCaller.rc());
8268 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8269 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8270
8271 AssertReturn(!mData->mAccessible, E_FAIL);
8272
8273 /* allocate data structures */
8274 mSSData.allocate();
8275 mUserData.allocate();
8276 mHWData.allocate();
8277 mMediumAttachments.allocate();
8278 mStorageControllers.allocate();
8279 mUSBControllers.allocate();
8280
8281 /* initialize mOSTypeId */
8282 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8283
8284/** @todo r=bird: init() methods never fails, right? Why don't we make them
8285 * return void then! */
8286
8287 /* create associated BIOS settings object */
8288 unconst(mBIOSSettings).createObject();
8289 mBIOSSettings->init(this);
8290
8291 /* create associated record settings object */
8292 unconst(mRecordingSettings).createObject();
8293 mRecordingSettings->init(this);
8294
8295 /* create an associated VRDE object (default is disabled) */
8296 unconst(mVRDEServer).createObject();
8297 mVRDEServer->init(this);
8298
8299 /* create associated serial port objects */
8300 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8301 {
8302 unconst(mSerialPorts[slot]).createObject();
8303 mSerialPorts[slot]->init(this, slot);
8304 }
8305
8306 /* create associated parallel port objects */
8307 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8308 {
8309 unconst(mParallelPorts[slot]).createObject();
8310 mParallelPorts[slot]->init(this, slot);
8311 }
8312
8313 /* create the audio adapter object (always present, default is disabled) */
8314 unconst(mAudioAdapter).createObject();
8315 mAudioAdapter->init(this);
8316
8317 /* create the USB device filters object (always present) */
8318 unconst(mUSBDeviceFilters).createObject();
8319 mUSBDeviceFilters->init(this);
8320
8321 /* create associated network adapter objects */
8322 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8323 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8324 {
8325 unconst(mNetworkAdapters[slot]).createObject();
8326 mNetworkAdapters[slot]->init(this, slot);
8327 }
8328
8329 /* create the bandwidth control */
8330 unconst(mBandwidthControl).createObject();
8331 mBandwidthControl->init(this);
8332
8333 return S_OK;
8334}
8335
8336/**
8337 * Helper to uninitialize all associated child objects and to free all data
8338 * structures.
8339 *
8340 * This method must be called as a part of the object's uninitialization
8341 * procedure (usually done in the #uninit() method).
8342 *
8343 * @note Must be called only from #uninit() or from #i_registeredInit().
8344 */
8345void Machine::uninitDataAndChildObjects()
8346{
8347 AutoCaller autoCaller(this);
8348 AssertComRCReturnVoid(autoCaller.rc());
8349 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8350 || getObjectState().getState() == ObjectState::Limited);
8351
8352 /* tell all our other child objects we've been uninitialized */
8353 if (mBandwidthControl)
8354 {
8355 mBandwidthControl->uninit();
8356 unconst(mBandwidthControl).setNull();
8357 }
8358
8359 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8360 {
8361 if (mNetworkAdapters[slot])
8362 {
8363 mNetworkAdapters[slot]->uninit();
8364 unconst(mNetworkAdapters[slot]).setNull();
8365 }
8366 }
8367
8368 if (mUSBDeviceFilters)
8369 {
8370 mUSBDeviceFilters->uninit();
8371 unconst(mUSBDeviceFilters).setNull();
8372 }
8373
8374 if (mAudioAdapter)
8375 {
8376 mAudioAdapter->uninit();
8377 unconst(mAudioAdapter).setNull();
8378 }
8379
8380 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8381 {
8382 if (mParallelPorts[slot])
8383 {
8384 mParallelPorts[slot]->uninit();
8385 unconst(mParallelPorts[slot]).setNull();
8386 }
8387 }
8388
8389 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8390 {
8391 if (mSerialPorts[slot])
8392 {
8393 mSerialPorts[slot]->uninit();
8394 unconst(mSerialPorts[slot]).setNull();
8395 }
8396 }
8397
8398 if (mVRDEServer)
8399 {
8400 mVRDEServer->uninit();
8401 unconst(mVRDEServer).setNull();
8402 }
8403
8404 if (mBIOSSettings)
8405 {
8406 mBIOSSettings->uninit();
8407 unconst(mBIOSSettings).setNull();
8408 }
8409
8410 if (mRecordingSettings)
8411 {
8412 mRecordingSettings->uninit();
8413 unconst(mRecordingSettings).setNull();
8414 }
8415
8416 /* Deassociate media (only when a real Machine or a SnapshotMachine
8417 * instance is uninitialized; SessionMachine instances refer to real
8418 * Machine media). This is necessary for a clean re-initialization of
8419 * the VM after successfully re-checking the accessibility state. Note
8420 * that in case of normal Machine or SnapshotMachine uninitialization (as
8421 * a result of unregistering or deleting the snapshot), outdated media
8422 * attachments will already be uninitialized and deleted, so this
8423 * code will not affect them. */
8424 if ( !mMediumAttachments.isNull()
8425 && !i_isSessionMachine()
8426 )
8427 {
8428 for (MediumAttachmentList::const_iterator
8429 it = mMediumAttachments->begin();
8430 it != mMediumAttachments->end();
8431 ++it)
8432 {
8433 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8434 if (pMedium.isNull())
8435 continue;
8436 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8437 AssertComRC(rc);
8438 }
8439 }
8440
8441 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8442 {
8443 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8444 if (mData->mFirstSnapshot)
8445 {
8446 // snapshots tree is protected by machine write lock; strictly
8447 // this isn't necessary here since we're deleting the entire
8448 // machine, but otherwise we assert in Snapshot::uninit()
8449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8450 mData->mFirstSnapshot->uninit();
8451 mData->mFirstSnapshot.setNull();
8452 }
8453
8454 mData->mCurrentSnapshot.setNull();
8455 }
8456
8457 /* free data structures (the essential mData structure is not freed here
8458 * since it may be still in use) */
8459 mMediumAttachments.free();
8460 mStorageControllers.free();
8461 mUSBControllers.free();
8462 mHWData.free();
8463 mUserData.free();
8464 mSSData.free();
8465}
8466
8467/**
8468 * Returns a pointer to the Machine object for this machine that acts like a
8469 * parent for complex machine data objects such as shared folders, etc.
8470 *
8471 * For primary Machine objects and for SnapshotMachine objects, returns this
8472 * object's pointer itself. For SessionMachine objects, returns the peer
8473 * (primary) machine pointer.
8474 */
8475Machine *Machine::i_getMachine()
8476{
8477 if (i_isSessionMachine())
8478 return (Machine*)mPeer;
8479 return this;
8480}
8481
8482/**
8483 * Makes sure that there are no machine state dependents. If necessary, waits
8484 * for the number of dependents to drop to zero.
8485 *
8486 * Make sure this method is called from under this object's write lock to
8487 * guarantee that no new dependents may be added when this method returns
8488 * control to the caller.
8489 *
8490 * @note Locks this object for writing. The lock will be released while waiting
8491 * (if necessary).
8492 *
8493 * @warning To be used only in methods that change the machine state!
8494 */
8495void Machine::i_ensureNoStateDependencies()
8496{
8497 AssertReturnVoid(isWriteLockOnCurrentThread());
8498
8499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8500
8501 /* Wait for all state dependents if necessary */
8502 if (mData->mMachineStateDeps != 0)
8503 {
8504 /* lazy semaphore creation */
8505 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8506 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8507
8508 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8509 mData->mMachineStateDeps));
8510
8511 ++mData->mMachineStateChangePending;
8512
8513 /* reset the semaphore before waiting, the last dependent will signal
8514 * it */
8515 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8516
8517 alock.release();
8518
8519 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8520
8521 alock.acquire();
8522
8523 -- mData->mMachineStateChangePending;
8524 }
8525}
8526
8527/**
8528 * Changes the machine state and informs callbacks.
8529 *
8530 * This method is not intended to fail so it either returns S_OK or asserts (and
8531 * returns a failure).
8532 *
8533 * @note Locks this object for writing.
8534 */
8535HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8536{
8537 LogFlowThisFuncEnter();
8538 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8539 Assert(aMachineState != MachineState_Null);
8540
8541 AutoCaller autoCaller(this);
8542 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8543
8544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8545
8546 /* wait for state dependents to drop to zero */
8547 i_ensureNoStateDependencies();
8548
8549 MachineState_T const enmOldState = mData->mMachineState;
8550 if (enmOldState != aMachineState)
8551 {
8552 mData->mMachineState = aMachineState;
8553 RTTimeNow(&mData->mLastStateChange);
8554
8555#ifdef VBOX_WITH_DTRACE_R3_MAIN
8556 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8557#endif
8558 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8559 }
8560
8561 LogFlowThisFuncLeave();
8562 return S_OK;
8563}
8564
8565/**
8566 * Searches for a shared folder with the given logical name
8567 * in the collection of shared folders.
8568 *
8569 * @param aName logical name of the shared folder
8570 * @param aSharedFolder where to return the found object
8571 * @param aSetError whether to set the error info if the folder is
8572 * not found
8573 * @return
8574 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8575 *
8576 * @note
8577 * must be called from under the object's lock!
8578 */
8579HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8580 ComObjPtr<SharedFolder> &aSharedFolder,
8581 bool aSetError /* = false */)
8582{
8583 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8584 for (HWData::SharedFolderList::const_iterator
8585 it = mHWData->mSharedFolders.begin();
8586 it != mHWData->mSharedFolders.end();
8587 ++it)
8588 {
8589 SharedFolder *pSF = *it;
8590 AutoCaller autoCaller(pSF);
8591 if (pSF->i_getName() == aName)
8592 {
8593 aSharedFolder = pSF;
8594 rc = S_OK;
8595 break;
8596 }
8597 }
8598
8599 if (aSetError && FAILED(rc))
8600 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8601
8602 return rc;
8603}
8604
8605/**
8606 * Initializes all machine instance data from the given settings structures
8607 * from XML. The exception is the machine UUID which needs special handling
8608 * depending on the caller's use case, so the caller needs to set that herself.
8609 *
8610 * This gets called in several contexts during machine initialization:
8611 *
8612 * -- When machine XML exists on disk already and needs to be loaded into memory,
8613 * for example, from #i_registeredInit() to load all registered machines on
8614 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8615 * attached to the machine should be part of some media registry already.
8616 *
8617 * -- During OVF import, when a machine config has been constructed from an
8618 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8619 * ensure that the media listed as attachments in the config (which have
8620 * been imported from the OVF) receive the correct registry ID.
8621 *
8622 * -- During VM cloning.
8623 *
8624 * @param config Machine settings from XML.
8625 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8626 * for each attached medium in the config.
8627 * @return
8628 */
8629HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8630 const Guid *puuidRegistry)
8631{
8632 // copy name, description, OS type, teleporter, UTC etc.
8633 mUserData->s = config.machineUserData;
8634
8635 // look up the object by Id to check it is valid
8636 ComObjPtr<GuestOSType> pGuestOSType;
8637 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8638 if (!pGuestOSType.isNull())
8639 mUserData->s.strOsType = pGuestOSType->i_id();
8640
8641 // stateFile (optional)
8642 if (config.strStateFile.isEmpty())
8643 mSSData->strStateFilePath.setNull();
8644 else
8645 {
8646 Utf8Str stateFilePathFull(config.strStateFile);
8647 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8648 if (RT_FAILURE(vrc))
8649 return setErrorBoth(E_FAIL, vrc,
8650 tr("Invalid saved state file path '%s' (%Rrc)"),
8651 config.strStateFile.c_str(),
8652 vrc);
8653 mSSData->strStateFilePath = stateFilePathFull;
8654 }
8655
8656 // snapshot folder needs special processing so set it again
8657 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8658 if (FAILED(rc)) return rc;
8659
8660 /* Copy the extra data items (config may or may not be the same as
8661 * mData->pMachineConfigFile) if necessary. When loading the XML files
8662 * from disk they are the same, but not for OVF import. */
8663 if (mData->pMachineConfigFile != &config)
8664 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8665
8666 /* currentStateModified (optional, default is true) */
8667 mData->mCurrentStateModified = config.fCurrentStateModified;
8668
8669 mData->mLastStateChange = config.timeLastStateChange;
8670
8671 /*
8672 * note: all mUserData members must be assigned prior this point because
8673 * we need to commit changes in order to let mUserData be shared by all
8674 * snapshot machine instances.
8675 */
8676 mUserData.commitCopy();
8677
8678 // machine registry, if present (must be loaded before snapshots)
8679 if (config.canHaveOwnMediaRegistry())
8680 {
8681 // determine machine folder
8682 Utf8Str strMachineFolder = i_getSettingsFileFull();
8683 strMachineFolder.stripFilename();
8684 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8685 config.mediaRegistry,
8686 strMachineFolder);
8687 if (FAILED(rc)) return rc;
8688 }
8689
8690 /* Snapshot node (optional) */
8691 size_t cRootSnapshots;
8692 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8693 {
8694 // there must be only one root snapshot
8695 Assert(cRootSnapshots == 1);
8696
8697 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8698
8699 rc = i_loadSnapshot(snap,
8700 config.uuidCurrentSnapshot,
8701 NULL); // no parent == first snapshot
8702 if (FAILED(rc)) return rc;
8703 }
8704
8705 // hardware data
8706 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8707 if (FAILED(rc)) return rc;
8708
8709 /*
8710 * NOTE: the assignment below must be the last thing to do,
8711 * otherwise it will be not possible to change the settings
8712 * somewhere in the code above because all setters will be
8713 * blocked by i_checkStateDependency(MutableStateDep).
8714 */
8715
8716 /* set the machine state to Aborted or Saved when appropriate */
8717 if (config.fAborted)
8718 {
8719 mSSData->strStateFilePath.setNull();
8720
8721 /* no need to use i_setMachineState() during init() */
8722 mData->mMachineState = MachineState_Aborted;
8723 }
8724 else if (!mSSData->strStateFilePath.isEmpty())
8725 {
8726 /* no need to use i_setMachineState() during init() */
8727 mData->mMachineState = MachineState_Saved;
8728 }
8729
8730 // after loading settings, we are no longer different from the XML on disk
8731 mData->flModifications = 0;
8732
8733 return S_OK;
8734}
8735
8736/**
8737 * Recursively loads all snapshots starting from the given.
8738 *
8739 * @param data snapshot settings.
8740 * @param aCurSnapshotId Current snapshot ID from the settings file.
8741 * @param aParentSnapshot Parent snapshot.
8742 */
8743HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8744 const Guid &aCurSnapshotId,
8745 Snapshot *aParentSnapshot)
8746{
8747 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8748 AssertReturn(!i_isSessionMachine(), E_FAIL);
8749
8750 HRESULT rc = S_OK;
8751
8752 Utf8Str strStateFile;
8753 if (!data.strStateFile.isEmpty())
8754 {
8755 /* optional */
8756 strStateFile = data.strStateFile;
8757 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8758 if (RT_FAILURE(vrc))
8759 return setErrorBoth(E_FAIL, vrc,
8760 tr("Invalid saved state file path '%s' (%Rrc)"),
8761 strStateFile.c_str(),
8762 vrc);
8763 }
8764
8765 /* create a snapshot machine object */
8766 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8767 pSnapshotMachine.createObject();
8768 rc = pSnapshotMachine->initFromSettings(this,
8769 data.hardware,
8770 &data.debugging,
8771 &data.autostart,
8772 data.uuid.ref(),
8773 strStateFile);
8774 if (FAILED(rc)) return rc;
8775
8776 /* create a snapshot object */
8777 ComObjPtr<Snapshot> pSnapshot;
8778 pSnapshot.createObject();
8779 /* initialize the snapshot */
8780 rc = pSnapshot->init(mParent, // VirtualBox object
8781 data.uuid,
8782 data.strName,
8783 data.strDescription,
8784 data.timestamp,
8785 pSnapshotMachine,
8786 aParentSnapshot);
8787 if (FAILED(rc)) return rc;
8788
8789 /* memorize the first snapshot if necessary */
8790 if (!mData->mFirstSnapshot)
8791 mData->mFirstSnapshot = pSnapshot;
8792
8793 /* memorize the current snapshot when appropriate */
8794 if ( !mData->mCurrentSnapshot
8795 && pSnapshot->i_getId() == aCurSnapshotId
8796 )
8797 mData->mCurrentSnapshot = pSnapshot;
8798
8799 // now create the children
8800 for (settings::SnapshotsList::const_iterator
8801 it = data.llChildSnapshots.begin();
8802 it != data.llChildSnapshots.end();
8803 ++it)
8804 {
8805 const settings::Snapshot &childData = *it;
8806 // recurse
8807 rc = i_loadSnapshot(childData,
8808 aCurSnapshotId,
8809 pSnapshot); // parent = the one we created above
8810 if (FAILED(rc)) return rc;
8811 }
8812
8813 return rc;
8814}
8815
8816/**
8817 * Loads settings into mHWData.
8818 *
8819 * @param puuidRegistry Registry ID.
8820 * @param puuidSnapshot Snapshot ID
8821 * @param data Reference to the hardware settings.
8822 * @param pDbg Pointer to the debugging settings.
8823 * @param pAutostart Pointer to the autostart settings.
8824 */
8825HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8826 const Guid *puuidSnapshot,
8827 const settings::Hardware &data,
8828 const settings::Debugging *pDbg,
8829 const settings::Autostart *pAutostart)
8830{
8831 AssertReturn(!i_isSessionMachine(), E_FAIL);
8832
8833 HRESULT rc = S_OK;
8834
8835 try
8836 {
8837 ComObjPtr<GuestOSType> pGuestOSType;
8838 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8839
8840 /* The hardware version attribute (optional). */
8841 mHWData->mHWVersion = data.strVersion;
8842 mHWData->mHardwareUUID = data.uuid;
8843
8844 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8845 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8846 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8847 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8848 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8849 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8850 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8851 mHWData->mPAEEnabled = data.fPAE;
8852 mHWData->mLongMode = data.enmLongMode;
8853 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8854 mHWData->mAPIC = data.fAPIC;
8855 mHWData->mX2APIC = data.fX2APIC;
8856 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8857 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8858 mHWData->mSpecCtrl = data.fSpecCtrl;
8859 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8860 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8861 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8862 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8863 mHWData->mCPUCount = data.cCPUs;
8864 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8865 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8866 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8867 mHWData->mCpuProfile = data.strCpuProfile;
8868
8869 // cpu
8870 if (mHWData->mCPUHotPlugEnabled)
8871 {
8872 for (settings::CpuList::const_iterator
8873 it = data.llCpus.begin();
8874 it != data.llCpus.end();
8875 ++it)
8876 {
8877 const settings::Cpu &cpu = *it;
8878
8879 mHWData->mCPUAttached[cpu.ulId] = true;
8880 }
8881 }
8882
8883 // cpuid leafs
8884 for (settings::CpuIdLeafsList::const_iterator
8885 it = data.llCpuIdLeafs.begin();
8886 it != data.llCpuIdLeafs.end();
8887 ++it)
8888 {
8889 const settings::CpuIdLeaf &rLeaf= *it;
8890 if ( rLeaf.idx < UINT32_C(0x20)
8891 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8892 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8893 mHWData->mCpuIdLeafList.push_back(rLeaf);
8894 /* else: just ignore */
8895 }
8896
8897 mHWData->mMemorySize = data.ulMemorySizeMB;
8898 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8899
8900 // boot order
8901 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8902 {
8903 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8904 if (it == data.mapBootOrder.end())
8905 mHWData->mBootOrder[i] = DeviceType_Null;
8906 else
8907 mHWData->mBootOrder[i] = it->second;
8908 }
8909
8910 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8911 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8912 mHWData->mMonitorCount = data.cMonitors;
8913 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8914 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8915 mHWData->mFirmwareType = data.firmwareType;
8916 mHWData->mPointingHIDType = data.pointingHIDType;
8917 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8918 mHWData->mChipsetType = data.chipsetType;
8919 mHWData->mParavirtProvider = data.paravirtProvider;
8920 mHWData->mParavirtDebug = data.strParavirtDebug;
8921 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8922 mHWData->mHPETEnabled = data.fHPETEnabled;
8923
8924 /* VRDEServer */
8925 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8926 if (FAILED(rc)) return rc;
8927
8928 /* BIOS */
8929 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 /* Recording settings */
8933 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8934 if (FAILED(rc)) return rc;
8935
8936 // Bandwidth control (must come before network adapters)
8937 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8938 if (FAILED(rc)) return rc;
8939
8940 /* USB controllers */
8941 for (settings::USBControllerList::const_iterator
8942 it = data.usbSettings.llUSBControllers.begin();
8943 it != data.usbSettings.llUSBControllers.end();
8944 ++it)
8945 {
8946 const settings::USBController &settingsCtrl = *it;
8947 ComObjPtr<USBController> newCtrl;
8948
8949 newCtrl.createObject();
8950 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8951 mUSBControllers->push_back(newCtrl);
8952 }
8953
8954 /* USB device filters */
8955 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8956 if (FAILED(rc)) return rc;
8957
8958 // network adapters (establish array size first and apply defaults, to
8959 // ensure reading the same settings as we saved, since the list skips
8960 // adapters having defaults)
8961 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8962 size_t oldCount = mNetworkAdapters.size();
8963 if (newCount > oldCount)
8964 {
8965 mNetworkAdapters.resize(newCount);
8966 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8967 {
8968 unconst(mNetworkAdapters[slot]).createObject();
8969 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8970 }
8971 }
8972 else if (newCount < oldCount)
8973 mNetworkAdapters.resize(newCount);
8974 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8975 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8976 for (settings::NetworkAdaptersList::const_iterator
8977 it = data.llNetworkAdapters.begin();
8978 it != data.llNetworkAdapters.end();
8979 ++it)
8980 {
8981 const settings::NetworkAdapter &nic = *it;
8982
8983 /* slot uniqueness is guaranteed by XML Schema */
8984 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8985 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8986 if (FAILED(rc)) return rc;
8987 }
8988
8989 // serial ports (establish defaults first, to ensure reading the same
8990 // settings as we saved, since the list skips ports having defaults)
8991 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8992 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8993 for (settings::SerialPortsList::const_iterator
8994 it = data.llSerialPorts.begin();
8995 it != data.llSerialPorts.end();
8996 ++it)
8997 {
8998 const settings::SerialPort &s = *it;
8999
9000 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9001 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9002 if (FAILED(rc)) return rc;
9003 }
9004
9005 // parallel ports (establish defaults first, to ensure reading the same
9006 // settings as we saved, since the list skips ports having defaults)
9007 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9008 mParallelPorts[i]->i_applyDefaults();
9009 for (settings::ParallelPortsList::const_iterator
9010 it = data.llParallelPorts.begin();
9011 it != data.llParallelPorts.end();
9012 ++it)
9013 {
9014 const settings::ParallelPort &p = *it;
9015
9016 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9017 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9018 if (FAILED(rc)) return rc;
9019 }
9020
9021 /* AudioAdapter */
9022 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9023 if (FAILED(rc)) return rc;
9024
9025 /* storage controllers */
9026 rc = i_loadStorageControllers(data.storage,
9027 puuidRegistry,
9028 puuidSnapshot);
9029 if (FAILED(rc)) return rc;
9030
9031 /* Shared folders */
9032 for (settings::SharedFoldersList::const_iterator
9033 it = data.llSharedFolders.begin();
9034 it != data.llSharedFolders.end();
9035 ++it)
9036 {
9037 const settings::SharedFolder &sf = *it;
9038
9039 ComObjPtr<SharedFolder> sharedFolder;
9040 /* Check for double entries. Not allowed! */
9041 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9042 if (SUCCEEDED(rc))
9043 return setError(VBOX_E_OBJECT_IN_USE,
9044 tr("Shared folder named '%s' already exists"),
9045 sf.strName.c_str());
9046
9047 /* Create the new shared folder. Don't break on error. This will be
9048 * reported when the machine starts. */
9049 sharedFolder.createObject();
9050 rc = sharedFolder->init(i_getMachine(),
9051 sf.strName,
9052 sf.strHostPath,
9053 RT_BOOL(sf.fWritable),
9054 RT_BOOL(sf.fAutoMount),
9055 sf.strAutoMountPoint,
9056 false /* fFailOnError */);
9057 if (FAILED(rc)) return rc;
9058 mHWData->mSharedFolders.push_back(sharedFolder);
9059 }
9060
9061 // Clipboard
9062 mHWData->mClipboardMode = data.clipboardMode;
9063
9064 // drag'n'drop
9065 mHWData->mDnDMode = data.dndMode;
9066
9067 // guest settings
9068 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9069
9070 // IO settings
9071 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9072 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9073
9074 // Host PCI devices
9075 for (settings::HostPCIDeviceAttachmentList::const_iterator
9076 it = data.pciAttachments.begin();
9077 it != data.pciAttachments.end();
9078 ++it)
9079 {
9080 const settings::HostPCIDeviceAttachment &hpda = *it;
9081 ComObjPtr<PCIDeviceAttachment> pda;
9082
9083 pda.createObject();
9084 pda->i_loadSettings(this, hpda);
9085 mHWData->mPCIDeviceAssignments.push_back(pda);
9086 }
9087
9088 /*
9089 * (The following isn't really real hardware, but it lives in HWData
9090 * for reasons of convenience.)
9091 */
9092
9093#ifdef VBOX_WITH_GUEST_PROPS
9094 /* Guest properties (optional) */
9095
9096 /* Only load transient guest properties for configs which have saved
9097 * state, because there shouldn't be any for powered off VMs. The same
9098 * logic applies for snapshots, as offline snapshots shouldn't have
9099 * any such properties. They confuse the code in various places.
9100 * Note: can't rely on the machine state, as it isn't set yet. */
9101 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9102 /* apologies for the hacky unconst() usage, but this needs hacking
9103 * actually inconsistent settings into consistency, otherwise there
9104 * will be some corner cases where the inconsistency survives
9105 * surprisingly long without getting fixed, especially for snapshots
9106 * as there are no config changes. */
9107 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9108 for (settings::GuestPropertiesList::iterator
9109 it = llGuestProperties.begin();
9110 it != llGuestProperties.end();
9111 /*nothing*/)
9112 {
9113 const settings::GuestProperty &prop = *it;
9114 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9115 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9116 if ( fSkipTransientGuestProperties
9117 && ( fFlags & GUEST_PROP_F_TRANSIENT
9118 || fFlags & GUEST_PROP_F_TRANSRESET))
9119 {
9120 it = llGuestProperties.erase(it);
9121 continue;
9122 }
9123 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9124 mHWData->mGuestProperties[prop.strName] = property;
9125 ++it;
9126 }
9127#endif /* VBOX_WITH_GUEST_PROPS defined */
9128
9129 rc = i_loadDebugging(pDbg);
9130 if (FAILED(rc))
9131 return rc;
9132
9133 mHWData->mAutostart = *pAutostart;
9134
9135 /* default frontend */
9136 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9137 }
9138 catch (std::bad_alloc &)
9139 {
9140 return E_OUTOFMEMORY;
9141 }
9142
9143 AssertComRC(rc);
9144 return rc;
9145}
9146
9147/**
9148 * Called from i_loadHardware() to load the debugging settings of the
9149 * machine.
9150 *
9151 * @param pDbg Pointer to the settings.
9152 */
9153HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9154{
9155 mHWData->mDebugging = *pDbg;
9156 /* no more processing currently required, this will probably change. */
9157 return S_OK;
9158}
9159
9160/**
9161 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9162 *
9163 * @param data storage settings.
9164 * @param puuidRegistry media registry ID to set media to or NULL;
9165 * see Machine::i_loadMachineDataFromSettings()
9166 * @param puuidSnapshot snapshot ID
9167 * @return
9168 */
9169HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9170 const Guid *puuidRegistry,
9171 const Guid *puuidSnapshot)
9172{
9173 AssertReturn(!i_isSessionMachine(), E_FAIL);
9174
9175 HRESULT rc = S_OK;
9176
9177 for (settings::StorageControllersList::const_iterator
9178 it = data.llStorageControllers.begin();
9179 it != data.llStorageControllers.end();
9180 ++it)
9181 {
9182 const settings::StorageController &ctlData = *it;
9183
9184 ComObjPtr<StorageController> pCtl;
9185 /* Try to find one with the name first. */
9186 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9187 if (SUCCEEDED(rc))
9188 return setError(VBOX_E_OBJECT_IN_USE,
9189 tr("Storage controller named '%s' already exists"),
9190 ctlData.strName.c_str());
9191
9192 pCtl.createObject();
9193 rc = pCtl->init(this,
9194 ctlData.strName,
9195 ctlData.storageBus,
9196 ctlData.ulInstance,
9197 ctlData.fBootable);
9198 if (FAILED(rc)) return rc;
9199
9200 mStorageControllers->push_back(pCtl);
9201
9202 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9203 if (FAILED(rc)) return rc;
9204
9205 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9206 if (FAILED(rc)) return rc;
9207
9208 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9209 if (FAILED(rc)) return rc;
9210
9211 /* Load the attached devices now. */
9212 rc = i_loadStorageDevices(pCtl,
9213 ctlData,
9214 puuidRegistry,
9215 puuidSnapshot);
9216 if (FAILED(rc)) return rc;
9217 }
9218
9219 return S_OK;
9220}
9221
9222/**
9223 * Called from i_loadStorageControllers for a controller's devices.
9224 *
9225 * @param aStorageController
9226 * @param data
9227 * @param puuidRegistry media registry ID to set media to or NULL; see
9228 * Machine::i_loadMachineDataFromSettings()
9229 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9230 * @return
9231 */
9232HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9233 const settings::StorageController &data,
9234 const Guid *puuidRegistry,
9235 const Guid *puuidSnapshot)
9236{
9237 HRESULT rc = S_OK;
9238
9239 /* paranoia: detect duplicate attachments */
9240 for (settings::AttachedDevicesList::const_iterator
9241 it = data.llAttachedDevices.begin();
9242 it != data.llAttachedDevices.end();
9243 ++it)
9244 {
9245 const settings::AttachedDevice &ad = *it;
9246
9247 for (settings::AttachedDevicesList::const_iterator it2 = it;
9248 it2 != data.llAttachedDevices.end();
9249 ++it2)
9250 {
9251 if (it == it2)
9252 continue;
9253
9254 const settings::AttachedDevice &ad2 = *it2;
9255
9256 if ( ad.lPort == ad2.lPort
9257 && ad.lDevice == ad2.lDevice)
9258 {
9259 return setError(E_FAIL,
9260 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9261 aStorageController->i_getName().c_str(),
9262 ad.lPort,
9263 ad.lDevice,
9264 mUserData->s.strName.c_str());
9265 }
9266 }
9267 }
9268
9269 for (settings::AttachedDevicesList::const_iterator
9270 it = data.llAttachedDevices.begin();
9271 it != data.llAttachedDevices.end();
9272 ++it)
9273 {
9274 const settings::AttachedDevice &dev = *it;
9275 ComObjPtr<Medium> medium;
9276
9277 switch (dev.deviceType)
9278 {
9279 case DeviceType_Floppy:
9280 case DeviceType_DVD:
9281 if (dev.strHostDriveSrc.isNotEmpty())
9282 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9283 false /* fRefresh */, medium);
9284 else
9285 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9286 dev.uuid,
9287 false /* fRefresh */,
9288 false /* aSetError */,
9289 medium);
9290 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9291 // This is not an error. The host drive or UUID might have vanished, so just go
9292 // ahead without this removeable medium attachment
9293 rc = S_OK;
9294 break;
9295
9296 case DeviceType_HardDisk:
9297 {
9298 /* find a hard disk by UUID */
9299 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9300 if (FAILED(rc))
9301 {
9302 if (i_isSnapshotMachine())
9303 {
9304 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9305 // so the user knows that the bad disk is in a snapshot somewhere
9306 com::ErrorInfo info;
9307 return setError(E_FAIL,
9308 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9309 puuidSnapshot->raw(),
9310 info.getText().raw());
9311 }
9312 else
9313 return rc;
9314 }
9315
9316 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9317
9318 if (medium->i_getType() == MediumType_Immutable)
9319 {
9320 if (i_isSnapshotMachine())
9321 return setError(E_FAIL,
9322 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9323 "of the virtual machine '%s' ('%s')"),
9324 medium->i_getLocationFull().c_str(),
9325 dev.uuid.raw(),
9326 puuidSnapshot->raw(),
9327 mUserData->s.strName.c_str(),
9328 mData->m_strConfigFileFull.c_str());
9329
9330 return setError(E_FAIL,
9331 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9332 medium->i_getLocationFull().c_str(),
9333 dev.uuid.raw(),
9334 mUserData->s.strName.c_str(),
9335 mData->m_strConfigFileFull.c_str());
9336 }
9337
9338 if (medium->i_getType() == MediumType_MultiAttach)
9339 {
9340 if (i_isSnapshotMachine())
9341 return setError(E_FAIL,
9342 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9343 "of the virtual machine '%s' ('%s')"),
9344 medium->i_getLocationFull().c_str(),
9345 dev.uuid.raw(),
9346 puuidSnapshot->raw(),
9347 mUserData->s.strName.c_str(),
9348 mData->m_strConfigFileFull.c_str());
9349
9350 return setError(E_FAIL,
9351 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9352 medium->i_getLocationFull().c_str(),
9353 dev.uuid.raw(),
9354 mUserData->s.strName.c_str(),
9355 mData->m_strConfigFileFull.c_str());
9356 }
9357
9358 if ( !i_isSnapshotMachine()
9359 && medium->i_getChildren().size() != 0
9360 )
9361 return setError(E_FAIL,
9362 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9363 "because it has %d differencing child hard disks"),
9364 medium->i_getLocationFull().c_str(),
9365 dev.uuid.raw(),
9366 mUserData->s.strName.c_str(),
9367 mData->m_strConfigFileFull.c_str(),
9368 medium->i_getChildren().size());
9369
9370 if (i_findAttachment(*mMediumAttachments.data(),
9371 medium))
9372 return setError(E_FAIL,
9373 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9374 medium->i_getLocationFull().c_str(),
9375 dev.uuid.raw(),
9376 mUserData->s.strName.c_str(),
9377 mData->m_strConfigFileFull.c_str());
9378
9379 break;
9380 }
9381
9382 default:
9383 return setError(E_FAIL,
9384 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9385 medium->i_getLocationFull().c_str(),
9386 mUserData->s.strName.c_str(),
9387 mData->m_strConfigFileFull.c_str());
9388 }
9389
9390 if (FAILED(rc))
9391 break;
9392
9393 /* Bandwidth groups are loaded at this point. */
9394 ComObjPtr<BandwidthGroup> pBwGroup;
9395
9396 if (!dev.strBwGroup.isEmpty())
9397 {
9398 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9399 if (FAILED(rc))
9400 return setError(E_FAIL,
9401 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9402 medium->i_getLocationFull().c_str(),
9403 dev.strBwGroup.c_str(),
9404 mUserData->s.strName.c_str(),
9405 mData->m_strConfigFileFull.c_str());
9406 pBwGroup->i_reference();
9407 }
9408
9409 const Utf8Str controllerName = aStorageController->i_getName();
9410 ComObjPtr<MediumAttachment> pAttachment;
9411 pAttachment.createObject();
9412 rc = pAttachment->init(this,
9413 medium,
9414 controllerName,
9415 dev.lPort,
9416 dev.lDevice,
9417 dev.deviceType,
9418 false,
9419 dev.fPassThrough,
9420 dev.fTempEject,
9421 dev.fNonRotational,
9422 dev.fDiscard,
9423 dev.fHotPluggable,
9424 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9425 if (FAILED(rc)) break;
9426
9427 /* associate the medium with this machine and snapshot */
9428 if (!medium.isNull())
9429 {
9430 AutoCaller medCaller(medium);
9431 if (FAILED(medCaller.rc())) return medCaller.rc();
9432 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9433
9434 if (i_isSnapshotMachine())
9435 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9436 else
9437 rc = medium->i_addBackReference(mData->mUuid);
9438 /* If the medium->addBackReference fails it sets an appropriate
9439 * error message, so no need to do any guesswork here. */
9440
9441 if (puuidRegistry)
9442 // caller wants registry ID to be set on all attached media (OVF import case)
9443 medium->i_addRegistry(*puuidRegistry);
9444 }
9445
9446 if (FAILED(rc))
9447 break;
9448
9449 /* back up mMediumAttachments to let registeredInit() properly rollback
9450 * on failure (= limited accessibility) */
9451 i_setModified(IsModified_Storage);
9452 mMediumAttachments.backup();
9453 mMediumAttachments->push_back(pAttachment);
9454 }
9455
9456 return rc;
9457}
9458
9459/**
9460 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9461 *
9462 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9463 * @param aSnapshot where to return the found snapshot
9464 * @param aSetError true to set extended error info on failure
9465 */
9466HRESULT Machine::i_findSnapshotById(const Guid &aId,
9467 ComObjPtr<Snapshot> &aSnapshot,
9468 bool aSetError /* = false */)
9469{
9470 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9471
9472 if (!mData->mFirstSnapshot)
9473 {
9474 if (aSetError)
9475 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9476 return E_FAIL;
9477 }
9478
9479 if (aId.isZero())
9480 aSnapshot = mData->mFirstSnapshot;
9481 else
9482 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9483
9484 if (!aSnapshot)
9485 {
9486 if (aSetError)
9487 return setError(E_FAIL,
9488 tr("Could not find a snapshot with UUID {%s}"),
9489 aId.toString().c_str());
9490 return E_FAIL;
9491 }
9492
9493 return S_OK;
9494}
9495
9496/**
9497 * Returns the snapshot with the given name or fails of no such snapshot.
9498 *
9499 * @param strName snapshot name to find
9500 * @param aSnapshot where to return the found snapshot
9501 * @param aSetError true to set extended error info on failure
9502 */
9503HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9504 ComObjPtr<Snapshot> &aSnapshot,
9505 bool aSetError /* = false */)
9506{
9507 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9508
9509 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9510
9511 if (!mData->mFirstSnapshot)
9512 {
9513 if (aSetError)
9514 return setError(VBOX_E_OBJECT_NOT_FOUND,
9515 tr("This machine does not have any snapshots"));
9516 return VBOX_E_OBJECT_NOT_FOUND;
9517 }
9518
9519 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9520
9521 if (!aSnapshot)
9522 {
9523 if (aSetError)
9524 return setError(VBOX_E_OBJECT_NOT_FOUND,
9525 tr("Could not find a snapshot named '%s'"), strName.c_str());
9526 return VBOX_E_OBJECT_NOT_FOUND;
9527 }
9528
9529 return S_OK;
9530}
9531
9532/**
9533 * Returns a storage controller object with the given name.
9534 *
9535 * @param aName storage controller name to find
9536 * @param aStorageController where to return the found storage controller
9537 * @param aSetError true to set extended error info on failure
9538 */
9539HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9540 ComObjPtr<StorageController> &aStorageController,
9541 bool aSetError /* = false */)
9542{
9543 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9544
9545 for (StorageControllerList::const_iterator
9546 it = mStorageControllers->begin();
9547 it != mStorageControllers->end();
9548 ++it)
9549 {
9550 if ((*it)->i_getName() == aName)
9551 {
9552 aStorageController = (*it);
9553 return S_OK;
9554 }
9555 }
9556
9557 if (aSetError)
9558 return setError(VBOX_E_OBJECT_NOT_FOUND,
9559 tr("Could not find a storage controller named '%s'"),
9560 aName.c_str());
9561 return VBOX_E_OBJECT_NOT_FOUND;
9562}
9563
9564/**
9565 * Returns a USB controller object with the given name.
9566 *
9567 * @param aName USB controller name to find
9568 * @param aUSBController where to return the found USB controller
9569 * @param aSetError true to set extended error info on failure
9570 */
9571HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9572 ComObjPtr<USBController> &aUSBController,
9573 bool aSetError /* = false */)
9574{
9575 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9576
9577 for (USBControllerList::const_iterator
9578 it = mUSBControllers->begin();
9579 it != mUSBControllers->end();
9580 ++it)
9581 {
9582 if ((*it)->i_getName() == aName)
9583 {
9584 aUSBController = (*it);
9585 return S_OK;
9586 }
9587 }
9588
9589 if (aSetError)
9590 return setError(VBOX_E_OBJECT_NOT_FOUND,
9591 tr("Could not find a storage controller named '%s'"),
9592 aName.c_str());
9593 return VBOX_E_OBJECT_NOT_FOUND;
9594}
9595
9596/**
9597 * Returns the number of USB controller instance of the given type.
9598 *
9599 * @param enmType USB controller type.
9600 */
9601ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9602{
9603 ULONG cCtrls = 0;
9604
9605 for (USBControllerList::const_iterator
9606 it = mUSBControllers->begin();
9607 it != mUSBControllers->end();
9608 ++it)
9609 {
9610 if ((*it)->i_getControllerType() == enmType)
9611 cCtrls++;
9612 }
9613
9614 return cCtrls;
9615}
9616
9617HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9618 MediumAttachmentList &atts)
9619{
9620 AutoCaller autoCaller(this);
9621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9622
9623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9624
9625 for (MediumAttachmentList::const_iterator
9626 it = mMediumAttachments->begin();
9627 it != mMediumAttachments->end();
9628 ++it)
9629 {
9630 const ComObjPtr<MediumAttachment> &pAtt = *it;
9631 // should never happen, but deal with NULL pointers in the list.
9632 AssertContinue(!pAtt.isNull());
9633
9634 // getControllerName() needs caller+read lock
9635 AutoCaller autoAttCaller(pAtt);
9636 if (FAILED(autoAttCaller.rc()))
9637 {
9638 atts.clear();
9639 return autoAttCaller.rc();
9640 }
9641 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9642
9643 if (pAtt->i_getControllerName() == aName)
9644 atts.push_back(pAtt);
9645 }
9646
9647 return S_OK;
9648}
9649
9650
9651/**
9652 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9653 * file if the machine name was changed and about creating a new settings file
9654 * if this is a new machine.
9655 *
9656 * @note Must be never called directly but only from #saveSettings().
9657 */
9658HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9659{
9660 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9661
9662 HRESULT rc = S_OK;
9663
9664 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9665
9666 /// @todo need to handle primary group change, too
9667
9668 /* attempt to rename the settings file if machine name is changed */
9669 if ( mUserData->s.fNameSync
9670 && mUserData.isBackedUp()
9671 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9672 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9673 )
9674 {
9675 bool dirRenamed = false;
9676 bool fileRenamed = false;
9677
9678 Utf8Str configFile, newConfigFile;
9679 Utf8Str configFilePrev, newConfigFilePrev;
9680 Utf8Str configDir, newConfigDir;
9681
9682 do
9683 {
9684 int vrc = VINF_SUCCESS;
9685
9686 Utf8Str name = mUserData.backedUpData()->s.strName;
9687 Utf8Str newName = mUserData->s.strName;
9688 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9689 if (group == "/")
9690 group.setNull();
9691 Utf8Str newGroup = mUserData->s.llGroups.front();
9692 if (newGroup == "/")
9693 newGroup.setNull();
9694
9695 configFile = mData->m_strConfigFileFull;
9696
9697 /* first, rename the directory if it matches the group and machine name */
9698 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9699 group.c_str(), RTPATH_DELIMITER, name.c_str());
9700 /** @todo hack, make somehow use of ComposeMachineFilename */
9701 if (mUserData->s.fDirectoryIncludesUUID)
9702 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9703 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9704 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9705 /** @todo hack, make somehow use of ComposeMachineFilename */
9706 if (mUserData->s.fDirectoryIncludesUUID)
9707 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9708 configDir = configFile;
9709 configDir.stripFilename();
9710 newConfigDir = configDir;
9711 if ( configDir.length() >= groupPlusName.length()
9712 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9713 groupPlusName.c_str()))
9714 {
9715 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9716 Utf8Str newConfigBaseDir(newConfigDir);
9717 newConfigDir.append(newGroupPlusName);
9718 /* consistency: use \ if appropriate on the platform */
9719 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9720 /* new dir and old dir cannot be equal here because of 'if'
9721 * above and because name != newName */
9722 Assert(configDir != newConfigDir);
9723 if (!fSettingsFileIsNew)
9724 {
9725 /* perform real rename only if the machine is not new */
9726 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9727 if ( vrc == VERR_FILE_NOT_FOUND
9728 || vrc == VERR_PATH_NOT_FOUND)
9729 {
9730 /* create the parent directory, then retry renaming */
9731 Utf8Str parent(newConfigDir);
9732 parent.stripFilename();
9733 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9734 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9735 }
9736 if (RT_FAILURE(vrc))
9737 {
9738 rc = setErrorBoth(E_FAIL, vrc,
9739 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9740 configDir.c_str(),
9741 newConfigDir.c_str(),
9742 vrc);
9743 break;
9744 }
9745 /* delete subdirectories which are no longer needed */
9746 Utf8Str dir(configDir);
9747 dir.stripFilename();
9748 while (dir != newConfigBaseDir && dir != ".")
9749 {
9750 vrc = RTDirRemove(dir.c_str());
9751 if (RT_FAILURE(vrc))
9752 break;
9753 dir.stripFilename();
9754 }
9755 dirRenamed = true;
9756 }
9757 }
9758
9759 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9760 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9761
9762 /* then try to rename the settings file itself */
9763 if (newConfigFile != configFile)
9764 {
9765 /* get the path to old settings file in renamed directory */
9766 configFile = Utf8StrFmt("%s%c%s",
9767 newConfigDir.c_str(),
9768 RTPATH_DELIMITER,
9769 RTPathFilename(configFile.c_str()));
9770 if (!fSettingsFileIsNew)
9771 {
9772 /* perform real rename only if the machine is not new */
9773 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9774 if (RT_FAILURE(vrc))
9775 {
9776 rc = setErrorBoth(E_FAIL, vrc,
9777 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9778 configFile.c_str(),
9779 newConfigFile.c_str(),
9780 vrc);
9781 break;
9782 }
9783 fileRenamed = true;
9784 configFilePrev = configFile;
9785 configFilePrev += "-prev";
9786 newConfigFilePrev = newConfigFile;
9787 newConfigFilePrev += "-prev";
9788 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9789 }
9790 }
9791
9792 // update m_strConfigFileFull amd mConfigFile
9793 mData->m_strConfigFileFull = newConfigFile;
9794 // compute the relative path too
9795 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9796
9797 // store the old and new so that VirtualBox::i_saveSettings() can update
9798 // the media registry
9799 if ( mData->mRegistered
9800 && (configDir != newConfigDir || configFile != newConfigFile))
9801 {
9802 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9803
9804 if (pfNeedsGlobalSaveSettings)
9805 *pfNeedsGlobalSaveSettings = true;
9806 }
9807
9808 // in the saved state file path, replace the old directory with the new directory
9809 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9810 {
9811 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9812 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9813 }
9814
9815 // and do the same thing for the saved state file paths of all the online snapshots
9816 if (mData->mFirstSnapshot)
9817 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9818 newConfigDir.c_str());
9819 }
9820 while (0);
9821
9822 if (FAILED(rc))
9823 {
9824 /* silently try to rename everything back */
9825 if (fileRenamed)
9826 {
9827 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9828 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9829 }
9830 if (dirRenamed)
9831 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9832 }
9833
9834 if (FAILED(rc)) return rc;
9835 }
9836
9837 if (fSettingsFileIsNew)
9838 {
9839 /* create a virgin config file */
9840 int vrc = VINF_SUCCESS;
9841
9842 /* ensure the settings directory exists */
9843 Utf8Str path(mData->m_strConfigFileFull);
9844 path.stripFilename();
9845 if (!RTDirExists(path.c_str()))
9846 {
9847 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9848 if (RT_FAILURE(vrc))
9849 {
9850 return setErrorBoth(E_FAIL, vrc,
9851 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9852 path.c_str(),
9853 vrc);
9854 }
9855 }
9856
9857 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9858 path = Utf8Str(mData->m_strConfigFileFull);
9859 RTFILE f = NIL_RTFILE;
9860 vrc = RTFileOpen(&f, path.c_str(),
9861 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9862 if (RT_FAILURE(vrc))
9863 return setErrorBoth(E_FAIL, vrc,
9864 tr("Could not create the settings file '%s' (%Rrc)"),
9865 path.c_str(),
9866 vrc);
9867 RTFileClose(f);
9868 }
9869
9870 return rc;
9871}
9872
9873/**
9874 * Saves and commits machine data, user data and hardware data.
9875 *
9876 * Note that on failure, the data remains uncommitted.
9877 *
9878 * @a aFlags may combine the following flags:
9879 *
9880 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9881 * Used when saving settings after an operation that makes them 100%
9882 * correspond to the settings from the current snapshot.
9883 * - SaveS_Force: settings will be saved without doing a deep compare of the
9884 * settings structures. This is used when this is called because snapshots
9885 * have changed to avoid the overhead of the deep compare.
9886 *
9887 * @note Must be called from under this object's write lock. Locks children for
9888 * writing.
9889 *
9890 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9891 * initialized to false and that will be set to true by this function if
9892 * the caller must invoke VirtualBox::i_saveSettings() because the global
9893 * settings have changed. This will happen if a machine rename has been
9894 * saved and the global machine and media registries will therefore need
9895 * updating.
9896 * @param aFlags Flags.
9897 */
9898HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9899 int aFlags /*= 0*/)
9900{
9901 LogFlowThisFuncEnter();
9902
9903 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9904
9905 /* make sure child objects are unable to modify the settings while we are
9906 * saving them */
9907 i_ensureNoStateDependencies();
9908
9909 AssertReturn(!i_isSnapshotMachine(),
9910 E_FAIL);
9911
9912 HRESULT rc = S_OK;
9913 bool fNeedsWrite = false;
9914
9915 /* First, prepare to save settings. It will care about renaming the
9916 * settings directory and file if the machine name was changed and about
9917 * creating a new settings file if this is a new machine. */
9918 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9919 if (FAILED(rc)) return rc;
9920
9921 // keep a pointer to the current settings structures
9922 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9923 settings::MachineConfigFile *pNewConfig = NULL;
9924
9925 try
9926 {
9927 // make a fresh one to have everyone write stuff into
9928 pNewConfig = new settings::MachineConfigFile(NULL);
9929 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9930
9931 // now go and copy all the settings data from COM to the settings structures
9932 // (this calls i_saveSettings() on all the COM objects in the machine)
9933 i_copyMachineDataToSettings(*pNewConfig);
9934
9935 if (aFlags & SaveS_ResetCurStateModified)
9936 {
9937 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9938 mData->mCurrentStateModified = FALSE;
9939 fNeedsWrite = true; // always, no need to compare
9940 }
9941 else if (aFlags & SaveS_Force)
9942 {
9943 fNeedsWrite = true; // always, no need to compare
9944 }
9945 else
9946 {
9947 if (!mData->mCurrentStateModified)
9948 {
9949 // do a deep compare of the settings that we just saved with the settings
9950 // previously stored in the config file; this invokes MachineConfigFile::operator==
9951 // which does a deep compare of all the settings, which is expensive but less expensive
9952 // than writing out XML in vain
9953 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9954
9955 // could still be modified if any settings changed
9956 mData->mCurrentStateModified = fAnySettingsChanged;
9957
9958 fNeedsWrite = fAnySettingsChanged;
9959 }
9960 else
9961 fNeedsWrite = true;
9962 }
9963
9964 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9965
9966 if (fNeedsWrite)
9967 // now spit it all out!
9968 pNewConfig->write(mData->m_strConfigFileFull);
9969
9970 mData->pMachineConfigFile = pNewConfig;
9971 delete pOldConfig;
9972 i_commit();
9973
9974 // after saving settings, we are no longer different from the XML on disk
9975 mData->flModifications = 0;
9976 }
9977 catch (HRESULT err)
9978 {
9979 // we assume that error info is set by the thrower
9980 rc = err;
9981
9982 // restore old config
9983 delete pNewConfig;
9984 mData->pMachineConfigFile = pOldConfig;
9985 }
9986 catch (...)
9987 {
9988 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9989 }
9990
9991 if (fNeedsWrite)
9992 {
9993 /* Fire the data change event, even on failure (since we've already
9994 * committed all data). This is done only for SessionMachines because
9995 * mutable Machine instances are always not registered (i.e. private
9996 * to the client process that creates them) and thus don't need to
9997 * inform callbacks. */
9998 if (i_isSessionMachine())
9999 mParent->i_onMachineDataChange(mData->mUuid);
10000 }
10001
10002 LogFlowThisFunc(("rc=%08X\n", rc));
10003 LogFlowThisFuncLeave();
10004 return rc;
10005}
10006
10007/**
10008 * Implementation for saving the machine settings into the given
10009 * settings::MachineConfigFile instance. This copies machine extradata
10010 * from the previous machine config file in the instance data, if any.
10011 *
10012 * This gets called from two locations:
10013 *
10014 * -- Machine::i_saveSettings(), during the regular XML writing;
10015 *
10016 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10017 * exported to OVF and we write the VirtualBox proprietary XML
10018 * into a <vbox:Machine> tag.
10019 *
10020 * This routine fills all the fields in there, including snapshots, *except*
10021 * for the following:
10022 *
10023 * -- fCurrentStateModified. There is some special logic associated with that.
10024 *
10025 * The caller can then call MachineConfigFile::write() or do something else
10026 * with it.
10027 *
10028 * Caller must hold the machine lock!
10029 *
10030 * This throws XML errors and HRESULT, so the caller must have a catch block!
10031 */
10032void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10033{
10034 // deep copy extradata, being extra careful with self assignment (the STL
10035 // map assignment on Mac OS X clang based Xcode isn't checking)
10036 if (&config != mData->pMachineConfigFile)
10037 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10038
10039 config.uuid = mData->mUuid;
10040
10041 // copy name, description, OS type, teleport, UTC etc.
10042 config.machineUserData = mUserData->s;
10043
10044 if ( mData->mMachineState == MachineState_Saved
10045 || mData->mMachineState == MachineState_Restoring
10046 // when doing certain snapshot operations we may or may not have
10047 // a saved state in the current state, so keep everything as is
10048 || ( ( mData->mMachineState == MachineState_Snapshotting
10049 || mData->mMachineState == MachineState_DeletingSnapshot
10050 || mData->mMachineState == MachineState_RestoringSnapshot)
10051 && (!mSSData->strStateFilePath.isEmpty())
10052 )
10053 )
10054 {
10055 Assert(!mSSData->strStateFilePath.isEmpty());
10056 /* try to make the file name relative to the settings file dir */
10057 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10058 }
10059 else
10060 {
10061 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10062 config.strStateFile.setNull();
10063 }
10064
10065 if (mData->mCurrentSnapshot)
10066 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10067 else
10068 config.uuidCurrentSnapshot.clear();
10069
10070 config.timeLastStateChange = mData->mLastStateChange;
10071 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10072 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10073
10074 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10075 if (FAILED(rc)) throw rc;
10076
10077 // save machine's media registry if this is VirtualBox 4.0 or later
10078 if (config.canHaveOwnMediaRegistry())
10079 {
10080 // determine machine folder
10081 Utf8Str strMachineFolder = i_getSettingsFileFull();
10082 strMachineFolder.stripFilename();
10083 mParent->i_saveMediaRegistry(config.mediaRegistry,
10084 i_getId(), // only media with registry ID == machine UUID
10085 strMachineFolder);
10086 // this throws HRESULT
10087 }
10088
10089 // save snapshots
10090 rc = i_saveAllSnapshots(config);
10091 if (FAILED(rc)) throw rc;
10092}
10093
10094/**
10095 * Saves all snapshots of the machine into the given machine config file. Called
10096 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10097 * @param config
10098 * @return
10099 */
10100HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10101{
10102 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10103
10104 HRESULT rc = S_OK;
10105
10106 try
10107 {
10108 config.llFirstSnapshot.clear();
10109
10110 if (mData->mFirstSnapshot)
10111 {
10112 // the settings use a list for "the first snapshot"
10113 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10114
10115 // get reference to the snapshot on the list and work on that
10116 // element straight in the list to avoid excessive copying later
10117 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10118 if (FAILED(rc)) throw rc;
10119 }
10120
10121// if (mType == IsSessionMachine)
10122// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10123
10124 }
10125 catch (HRESULT err)
10126 {
10127 /* we assume that error info is set by the thrower */
10128 rc = err;
10129 }
10130 catch (...)
10131 {
10132 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10133 }
10134
10135 return rc;
10136}
10137
10138/**
10139 * Saves the VM hardware configuration. It is assumed that the
10140 * given node is empty.
10141 *
10142 * @param data Reference to the settings object for the hardware config.
10143 * @param pDbg Pointer to the settings object for the debugging config
10144 * which happens to live in mHWData.
10145 * @param pAutostart Pointer to the settings object for the autostart config
10146 * which happens to live in mHWData.
10147 */
10148HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10149 settings::Autostart *pAutostart)
10150{
10151 HRESULT rc = S_OK;
10152
10153 try
10154 {
10155 /* The hardware version attribute (optional).
10156 Automatically upgrade from 1 to current default hardware version
10157 when there is no saved state. (ugly!) */
10158 if ( mHWData->mHWVersion == "1"
10159 && mSSData->strStateFilePath.isEmpty()
10160 )
10161 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10162
10163 data.strVersion = mHWData->mHWVersion;
10164 data.uuid = mHWData->mHardwareUUID;
10165
10166 // CPU
10167 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10168 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10169 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10170 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10171 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10172 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10173 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10174 data.fPAE = !!mHWData->mPAEEnabled;
10175 data.enmLongMode = mHWData->mLongMode;
10176 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10177 data.fAPIC = !!mHWData->mAPIC;
10178 data.fX2APIC = !!mHWData->mX2APIC;
10179 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10180 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10181 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10182 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10183 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10184 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10185 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10186 data.cCPUs = mHWData->mCPUCount;
10187 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10188 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10189 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10190 data.strCpuProfile = mHWData->mCpuProfile;
10191
10192 data.llCpus.clear();
10193 if (data.fCpuHotPlug)
10194 {
10195 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10196 {
10197 if (mHWData->mCPUAttached[idx])
10198 {
10199 settings::Cpu cpu;
10200 cpu.ulId = idx;
10201 data.llCpus.push_back(cpu);
10202 }
10203 }
10204 }
10205
10206 /* Standard and Extended CPUID leafs. */
10207 data.llCpuIdLeafs.clear();
10208 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10209
10210 // memory
10211 data.ulMemorySizeMB = mHWData->mMemorySize;
10212 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10213
10214 // firmware
10215 data.firmwareType = mHWData->mFirmwareType;
10216
10217 // HID
10218 data.pointingHIDType = mHWData->mPointingHIDType;
10219 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10220
10221 // chipset
10222 data.chipsetType = mHWData->mChipsetType;
10223
10224 // paravirt
10225 data.paravirtProvider = mHWData->mParavirtProvider;
10226 data.strParavirtDebug = mHWData->mParavirtDebug;
10227
10228 // emulated USB card reader
10229 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10230
10231 // HPET
10232 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10233
10234 // boot order
10235 data.mapBootOrder.clear();
10236 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10237 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10238
10239 // display
10240 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10241 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10242 data.cMonitors = mHWData->mMonitorCount;
10243 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10244 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10245
10246 /* VRDEServer settings (optional) */
10247 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* BIOS settings (required) */
10251 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10252 if (FAILED(rc)) throw rc;
10253
10254 /* Recording settings (required) */
10255 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10256 if (FAILED(rc)) throw rc;
10257
10258 /* USB Controller (required) */
10259 data.usbSettings.llUSBControllers.clear();
10260 for (USBControllerList::const_iterator
10261 it = mUSBControllers->begin();
10262 it != mUSBControllers->end();
10263 ++it)
10264 {
10265 ComObjPtr<USBController> ctrl = *it;
10266 settings::USBController settingsCtrl;
10267
10268 settingsCtrl.strName = ctrl->i_getName();
10269 settingsCtrl.enmType = ctrl->i_getControllerType();
10270
10271 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10272 }
10273
10274 /* USB device filters (required) */
10275 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10276 if (FAILED(rc)) throw rc;
10277
10278 /* Network adapters (required) */
10279 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10280 data.llNetworkAdapters.clear();
10281 /* Write out only the nominal number of network adapters for this
10282 * chipset type. Since Machine::commit() hasn't been called there
10283 * may be extra NIC settings in the vector. */
10284 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10285 {
10286 settings::NetworkAdapter nic;
10287 nic.ulSlot = (uint32_t)slot;
10288 /* paranoia check... must not be NULL, but must not crash either. */
10289 if (mNetworkAdapters[slot])
10290 {
10291 if (mNetworkAdapters[slot]->i_hasDefaults())
10292 continue;
10293
10294 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10295 if (FAILED(rc)) throw rc;
10296
10297 data.llNetworkAdapters.push_back(nic);
10298 }
10299 }
10300
10301 /* Serial ports */
10302 data.llSerialPorts.clear();
10303 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10304 {
10305 if (mSerialPorts[slot]->i_hasDefaults())
10306 continue;
10307
10308 settings::SerialPort s;
10309 s.ulSlot = slot;
10310 rc = mSerialPorts[slot]->i_saveSettings(s);
10311 if (FAILED(rc)) return rc;
10312
10313 data.llSerialPorts.push_back(s);
10314 }
10315
10316 /* Parallel ports */
10317 data.llParallelPorts.clear();
10318 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10319 {
10320 if (mParallelPorts[slot]->i_hasDefaults())
10321 continue;
10322
10323 settings::ParallelPort p;
10324 p.ulSlot = slot;
10325 rc = mParallelPorts[slot]->i_saveSettings(p);
10326 if (FAILED(rc)) return rc;
10327
10328 data.llParallelPorts.push_back(p);
10329 }
10330
10331 /* Audio adapter */
10332 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10333 if (FAILED(rc)) return rc;
10334
10335 rc = i_saveStorageControllers(data.storage);
10336 if (FAILED(rc)) return rc;
10337
10338 /* Shared folders */
10339 data.llSharedFolders.clear();
10340 for (HWData::SharedFolderList::const_iterator
10341 it = mHWData->mSharedFolders.begin();
10342 it != mHWData->mSharedFolders.end();
10343 ++it)
10344 {
10345 SharedFolder *pSF = *it;
10346 AutoCaller sfCaller(pSF);
10347 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10348 settings::SharedFolder sf;
10349 sf.strName = pSF->i_getName();
10350 sf.strHostPath = pSF->i_getHostPath();
10351 sf.fWritable = !!pSF->i_isWritable();
10352 sf.fAutoMount = !!pSF->i_isAutoMounted();
10353 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10354
10355 data.llSharedFolders.push_back(sf);
10356 }
10357
10358 // clipboard
10359 data.clipboardMode = mHWData->mClipboardMode;
10360
10361 // drag'n'drop
10362 data.dndMode = mHWData->mDnDMode;
10363
10364 /* Guest */
10365 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10366
10367 // IO settings
10368 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10369 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10370
10371 /* BandwidthControl (required) */
10372 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10373 if (FAILED(rc)) throw rc;
10374
10375 /* Host PCI devices */
10376 data.pciAttachments.clear();
10377 for (HWData::PCIDeviceAssignmentList::const_iterator
10378 it = mHWData->mPCIDeviceAssignments.begin();
10379 it != mHWData->mPCIDeviceAssignments.end();
10380 ++it)
10381 {
10382 ComObjPtr<PCIDeviceAttachment> pda = *it;
10383 settings::HostPCIDeviceAttachment hpda;
10384
10385 rc = pda->i_saveSettings(hpda);
10386 if (FAILED(rc)) throw rc;
10387
10388 data.pciAttachments.push_back(hpda);
10389 }
10390
10391 // guest properties
10392 data.llGuestProperties.clear();
10393#ifdef VBOX_WITH_GUEST_PROPS
10394 for (HWData::GuestPropertyMap::const_iterator
10395 it = mHWData->mGuestProperties.begin();
10396 it != mHWData->mGuestProperties.end();
10397 ++it)
10398 {
10399 HWData::GuestProperty property = it->second;
10400
10401 /* Remove transient guest properties at shutdown unless we
10402 * are saving state. Note that restoring snapshot intentionally
10403 * keeps them, they will be removed if appropriate once the final
10404 * machine state is set (as crashes etc. need to work). */
10405 if ( ( mData->mMachineState == MachineState_PoweredOff
10406 || mData->mMachineState == MachineState_Aborted
10407 || mData->mMachineState == MachineState_Teleported)
10408 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10409 continue;
10410 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10411 prop.strName = it->first;
10412 prop.strValue = property.strValue;
10413 prop.timestamp = property.mTimestamp;
10414 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10415 GuestPropWriteFlags(property.mFlags, szFlags);
10416 prop.strFlags = szFlags;
10417
10418 data.llGuestProperties.push_back(prop);
10419 }
10420
10421 /* I presume this doesn't require a backup(). */
10422 mData->mGuestPropertiesModified = FALSE;
10423#endif /* VBOX_WITH_GUEST_PROPS defined */
10424
10425 *pDbg = mHWData->mDebugging;
10426 *pAutostart = mHWData->mAutostart;
10427
10428 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10429 }
10430 catch (std::bad_alloc &)
10431 {
10432 return E_OUTOFMEMORY;
10433 }
10434
10435 AssertComRC(rc);
10436 return rc;
10437}
10438
10439/**
10440 * Saves the storage controller configuration.
10441 *
10442 * @param data storage settings.
10443 */
10444HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10445{
10446 data.llStorageControllers.clear();
10447
10448 for (StorageControllerList::const_iterator
10449 it = mStorageControllers->begin();
10450 it != mStorageControllers->end();
10451 ++it)
10452 {
10453 HRESULT rc;
10454 ComObjPtr<StorageController> pCtl = *it;
10455
10456 settings::StorageController ctl;
10457 ctl.strName = pCtl->i_getName();
10458 ctl.controllerType = pCtl->i_getControllerType();
10459 ctl.storageBus = pCtl->i_getStorageBus();
10460 ctl.ulInstance = pCtl->i_getInstance();
10461 ctl.fBootable = pCtl->i_getBootable();
10462
10463 /* Save the port count. */
10464 ULONG portCount;
10465 rc = pCtl->COMGETTER(PortCount)(&portCount);
10466 ComAssertComRCRet(rc, rc);
10467 ctl.ulPortCount = portCount;
10468
10469 /* Save fUseHostIOCache */
10470 BOOL fUseHostIOCache;
10471 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10472 ComAssertComRCRet(rc, rc);
10473 ctl.fUseHostIOCache = !!fUseHostIOCache;
10474
10475 /* save the devices now. */
10476 rc = i_saveStorageDevices(pCtl, ctl);
10477 ComAssertComRCRet(rc, rc);
10478
10479 data.llStorageControllers.push_back(ctl);
10480 }
10481
10482 return S_OK;
10483}
10484
10485/**
10486 * Saves the hard disk configuration.
10487 */
10488HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10489 settings::StorageController &data)
10490{
10491 MediumAttachmentList atts;
10492
10493 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10494 if (FAILED(rc)) return rc;
10495
10496 data.llAttachedDevices.clear();
10497 for (MediumAttachmentList::const_iterator
10498 it = atts.begin();
10499 it != atts.end();
10500 ++it)
10501 {
10502 settings::AttachedDevice dev;
10503 IMediumAttachment *iA = *it;
10504 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10505 Medium *pMedium = pAttach->i_getMedium();
10506
10507 dev.deviceType = pAttach->i_getType();
10508 dev.lPort = pAttach->i_getPort();
10509 dev.lDevice = pAttach->i_getDevice();
10510 dev.fPassThrough = pAttach->i_getPassthrough();
10511 dev.fHotPluggable = pAttach->i_getHotPluggable();
10512 if (pMedium)
10513 {
10514 if (pMedium->i_isHostDrive())
10515 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10516 else
10517 dev.uuid = pMedium->i_getId();
10518 dev.fTempEject = pAttach->i_getTempEject();
10519 dev.fNonRotational = pAttach->i_getNonRotational();
10520 dev.fDiscard = pAttach->i_getDiscard();
10521 }
10522
10523 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10524
10525 data.llAttachedDevices.push_back(dev);
10526 }
10527
10528 return S_OK;
10529}
10530
10531/**
10532 * Saves machine state settings as defined by aFlags
10533 * (SaveSTS_* values).
10534 *
10535 * @param aFlags Combination of SaveSTS_* flags.
10536 *
10537 * @note Locks objects for writing.
10538 */
10539HRESULT Machine::i_saveStateSettings(int aFlags)
10540{
10541 if (aFlags == 0)
10542 return S_OK;
10543
10544 AutoCaller autoCaller(this);
10545 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10546
10547 /* This object's write lock is also necessary to serialize file access
10548 * (prevent concurrent reads and writes) */
10549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10550
10551 HRESULT rc = S_OK;
10552
10553 Assert(mData->pMachineConfigFile);
10554
10555 try
10556 {
10557 if (aFlags & SaveSTS_CurStateModified)
10558 mData->pMachineConfigFile->fCurrentStateModified = true;
10559
10560 if (aFlags & SaveSTS_StateFilePath)
10561 {
10562 if (!mSSData->strStateFilePath.isEmpty())
10563 /* try to make the file name relative to the settings file dir */
10564 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10565 else
10566 mData->pMachineConfigFile->strStateFile.setNull();
10567 }
10568
10569 if (aFlags & SaveSTS_StateTimeStamp)
10570 {
10571 Assert( mData->mMachineState != MachineState_Aborted
10572 || mSSData->strStateFilePath.isEmpty());
10573
10574 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10575
10576 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10577/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10578 }
10579
10580 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10581 }
10582 catch (...)
10583 {
10584 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10585 }
10586
10587 return rc;
10588}
10589
10590/**
10591 * Ensures that the given medium is added to a media registry. If this machine
10592 * was created with 4.0 or later, then the machine registry is used. Otherwise
10593 * the global VirtualBox media registry is used.
10594 *
10595 * Caller must NOT hold machine lock, media tree or any medium locks!
10596 *
10597 * @param pMedium
10598 */
10599void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10600{
10601 /* Paranoia checks: do not hold machine or media tree locks. */
10602 AssertReturnVoid(!isWriteLockOnCurrentThread());
10603 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10604
10605 ComObjPtr<Medium> pBase;
10606 {
10607 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10608 pBase = pMedium->i_getBase();
10609 }
10610
10611 /* Paranoia checks: do not hold medium locks. */
10612 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10613 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10614
10615 // decide which medium registry to use now that the medium is attached:
10616 Guid uuid;
10617 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10618 if (fCanHaveOwnMediaRegistry)
10619 // machine XML is VirtualBox 4.0 or higher:
10620 uuid = i_getId(); // machine UUID
10621 else
10622 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10623
10624 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10625 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10626 if (pMedium->i_addRegistry(uuid))
10627 mParent->i_markRegistryModified(uuid);
10628
10629 /* For more complex hard disk structures it can happen that the base
10630 * medium isn't yet associated with any medium registry. Do that now. */
10631 if (pMedium != pBase)
10632 {
10633 /* Tree lock needed by Medium::addRegistry when recursing. */
10634 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10635 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10636 {
10637 treeLock.release();
10638 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10639 treeLock.acquire();
10640 }
10641 if (pBase->i_addRegistryRecursive(uuid))
10642 {
10643 treeLock.release();
10644 mParent->i_markRegistryModified(uuid);
10645 }
10646 }
10647}
10648
10649/**
10650 * Creates differencing hard disks for all normal hard disks attached to this
10651 * machine and a new set of attachments to refer to created disks.
10652 *
10653 * Used when taking a snapshot or when deleting the current state. Gets called
10654 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10655 *
10656 * This method assumes that mMediumAttachments contains the original hard disk
10657 * attachments it needs to create diffs for. On success, these attachments will
10658 * be replaced with the created diffs.
10659 *
10660 * Attachments with non-normal hard disks are left as is.
10661 *
10662 * If @a aOnline is @c false then the original hard disks that require implicit
10663 * diffs will be locked for reading. Otherwise it is assumed that they are
10664 * already locked for writing (when the VM was started). Note that in the latter
10665 * case it is responsibility of the caller to lock the newly created diffs for
10666 * writing if this method succeeds.
10667 *
10668 * @param aProgress Progress object to run (must contain at least as
10669 * many operations left as the number of hard disks
10670 * attached).
10671 * @param aWeight Weight of this operation.
10672 * @param aOnline Whether the VM was online prior to this operation.
10673 *
10674 * @note The progress object is not marked as completed, neither on success nor
10675 * on failure. This is a responsibility of the caller.
10676 *
10677 * @note Locks this object and the media tree for writing.
10678 */
10679HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10680 ULONG aWeight,
10681 bool aOnline)
10682{
10683 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10684
10685 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10686 AssertReturn(!!pProgressControl, E_INVALIDARG);
10687
10688 AutoCaller autoCaller(this);
10689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10690
10691 AutoMultiWriteLock2 alock(this->lockHandle(),
10692 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10693
10694 /* must be in a protective state because we release the lock below */
10695 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10696 || mData->mMachineState == MachineState_OnlineSnapshotting
10697 || mData->mMachineState == MachineState_LiveSnapshotting
10698 || mData->mMachineState == MachineState_RestoringSnapshot
10699 || mData->mMachineState == MachineState_DeletingSnapshot
10700 , E_FAIL);
10701
10702 HRESULT rc = S_OK;
10703
10704 // use appropriate locked media map (online or offline)
10705 MediumLockListMap lockedMediaOffline;
10706 MediumLockListMap *lockedMediaMap;
10707 if (aOnline)
10708 lockedMediaMap = &mData->mSession.mLockedMedia;
10709 else
10710 lockedMediaMap = &lockedMediaOffline;
10711
10712 try
10713 {
10714 if (!aOnline)
10715 {
10716 /* lock all attached hard disks early to detect "in use"
10717 * situations before creating actual diffs */
10718 for (MediumAttachmentList::const_iterator
10719 it = mMediumAttachments->begin();
10720 it != mMediumAttachments->end();
10721 ++it)
10722 {
10723 MediumAttachment *pAtt = *it;
10724 if (pAtt->i_getType() == DeviceType_HardDisk)
10725 {
10726 Medium *pMedium = pAtt->i_getMedium();
10727 Assert(pMedium);
10728
10729 MediumLockList *pMediumLockList(new MediumLockList());
10730 alock.release();
10731 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10732 NULL /* pToLockWrite */,
10733 false /* fMediumLockWriteAll */,
10734 NULL,
10735 *pMediumLockList);
10736 alock.acquire();
10737 if (FAILED(rc))
10738 {
10739 delete pMediumLockList;
10740 throw rc;
10741 }
10742 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10743 if (FAILED(rc))
10744 {
10745 throw setError(rc,
10746 tr("Collecting locking information for all attached media failed"));
10747 }
10748 }
10749 }
10750
10751 /* Now lock all media. If this fails, nothing is locked. */
10752 alock.release();
10753 rc = lockedMediaMap->Lock();
10754 alock.acquire();
10755 if (FAILED(rc))
10756 {
10757 throw setError(rc,
10758 tr("Locking of attached media failed"));
10759 }
10760 }
10761
10762 /* remember the current list (note that we don't use backup() since
10763 * mMediumAttachments may be already backed up) */
10764 MediumAttachmentList atts = *mMediumAttachments.data();
10765
10766 /* start from scratch */
10767 mMediumAttachments->clear();
10768
10769 /* go through remembered attachments and create diffs for normal hard
10770 * disks and attach them */
10771 for (MediumAttachmentList::const_iterator
10772 it = atts.begin();
10773 it != atts.end();
10774 ++it)
10775 {
10776 MediumAttachment *pAtt = *it;
10777
10778 DeviceType_T devType = pAtt->i_getType();
10779 Medium *pMedium = pAtt->i_getMedium();
10780
10781 if ( devType != DeviceType_HardDisk
10782 || pMedium == NULL
10783 || pMedium->i_getType() != MediumType_Normal)
10784 {
10785 /* copy the attachment as is */
10786
10787 /** @todo the progress object created in SessionMachine::TakeSnaphot
10788 * only expects operations for hard disks. Later other
10789 * device types need to show up in the progress as well. */
10790 if (devType == DeviceType_HardDisk)
10791 {
10792 if (pMedium == NULL)
10793 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10794 aWeight); // weight
10795 else
10796 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10797 pMedium->i_getBase()->i_getName().c_str()).raw(),
10798 aWeight); // weight
10799 }
10800
10801 mMediumAttachments->push_back(pAtt);
10802 continue;
10803 }
10804
10805 /* need a diff */
10806 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10807 pMedium->i_getBase()->i_getName().c_str()).raw(),
10808 aWeight); // weight
10809
10810 Utf8Str strFullSnapshotFolder;
10811 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10812
10813 ComObjPtr<Medium> diff;
10814 diff.createObject();
10815 // store the diff in the same registry as the parent
10816 // (this cannot fail here because we can't create implicit diffs for
10817 // unregistered images)
10818 Guid uuidRegistryParent;
10819 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10820 Assert(fInRegistry); NOREF(fInRegistry);
10821 rc = diff->init(mParent,
10822 pMedium->i_getPreferredDiffFormat(),
10823 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10824 uuidRegistryParent,
10825 DeviceType_HardDisk);
10826 if (FAILED(rc)) throw rc;
10827
10828 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10829 * the push_back? Looks like we're going to release medium with the
10830 * wrong kind of lock (general issue with if we fail anywhere at all)
10831 * and an orphaned VDI in the snapshots folder. */
10832
10833 /* update the appropriate lock list */
10834 MediumLockList *pMediumLockList;
10835 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10836 AssertComRCThrowRC(rc);
10837 if (aOnline)
10838 {
10839 alock.release();
10840 /* The currently attached medium will be read-only, change
10841 * the lock type to read. */
10842 rc = pMediumLockList->Update(pMedium, false);
10843 alock.acquire();
10844 AssertComRCThrowRC(rc);
10845 }
10846
10847 /* release the locks before the potentially lengthy operation */
10848 alock.release();
10849 rc = pMedium->i_createDiffStorage(diff,
10850 pMedium->i_getPreferredDiffVariant(),
10851 pMediumLockList,
10852 NULL /* aProgress */,
10853 true /* aWait */,
10854 false /* aNotify */);
10855 alock.acquire();
10856 if (FAILED(rc)) throw rc;
10857
10858 /* actual lock list update is done in Machine::i_commitMedia */
10859
10860 rc = diff->i_addBackReference(mData->mUuid);
10861 AssertComRCThrowRC(rc);
10862
10863 /* add a new attachment */
10864 ComObjPtr<MediumAttachment> attachment;
10865 attachment.createObject();
10866 rc = attachment->init(this,
10867 diff,
10868 pAtt->i_getControllerName(),
10869 pAtt->i_getPort(),
10870 pAtt->i_getDevice(),
10871 DeviceType_HardDisk,
10872 true /* aImplicit */,
10873 false /* aPassthrough */,
10874 false /* aTempEject */,
10875 pAtt->i_getNonRotational(),
10876 pAtt->i_getDiscard(),
10877 pAtt->i_getHotPluggable(),
10878 pAtt->i_getBandwidthGroup());
10879 if (FAILED(rc)) throw rc;
10880
10881 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10882 AssertComRCThrowRC(rc);
10883 mMediumAttachments->push_back(attachment);
10884 }
10885 }
10886 catch (HRESULT aRC) { rc = aRC; }
10887
10888 /* unlock all hard disks we locked when there is no VM */
10889 if (!aOnline)
10890 {
10891 ErrorInfoKeeper eik;
10892
10893 HRESULT rc1 = lockedMediaMap->Clear();
10894 AssertComRC(rc1);
10895 }
10896
10897 return rc;
10898}
10899
10900/**
10901 * Deletes implicit differencing hard disks created either by
10902 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10903 * mMediumAttachments.
10904 *
10905 * Note that to delete hard disks created by #attachDevice() this method is
10906 * called from #i_rollbackMedia() when the changes are rolled back.
10907 *
10908 * @note Locks this object and the media tree for writing.
10909 */
10910HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10911{
10912 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10913
10914 AutoCaller autoCaller(this);
10915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10916
10917 AutoMultiWriteLock2 alock(this->lockHandle(),
10918 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10919
10920 /* We absolutely must have backed up state. */
10921 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10922
10923 /* Check if there are any implicitly created diff images. */
10924 bool fImplicitDiffs = false;
10925 for (MediumAttachmentList::const_iterator
10926 it = mMediumAttachments->begin();
10927 it != mMediumAttachments->end();
10928 ++it)
10929 {
10930 const ComObjPtr<MediumAttachment> &pAtt = *it;
10931 if (pAtt->i_isImplicit())
10932 {
10933 fImplicitDiffs = true;
10934 break;
10935 }
10936 }
10937 /* If there is nothing to do, leave early. This saves lots of image locking
10938 * effort. It also avoids a MachineStateChanged event without real reason.
10939 * This is important e.g. when loading a VM config, because there should be
10940 * no events. Otherwise API clients can become thoroughly confused for
10941 * inaccessible VMs (the code for loading VM configs uses this method for
10942 * cleanup if the config makes no sense), as they take such events as an
10943 * indication that the VM is alive, and they would force the VM config to
10944 * be reread, leading to an endless loop. */
10945 if (!fImplicitDiffs)
10946 return S_OK;
10947
10948 HRESULT rc = S_OK;
10949 MachineState_T oldState = mData->mMachineState;
10950
10951 /* will release the lock before the potentially lengthy operation,
10952 * so protect with the special state (unless already protected) */
10953 if ( oldState != MachineState_Snapshotting
10954 && oldState != MachineState_OnlineSnapshotting
10955 && oldState != MachineState_LiveSnapshotting
10956 && oldState != MachineState_RestoringSnapshot
10957 && oldState != MachineState_DeletingSnapshot
10958 && oldState != MachineState_DeletingSnapshotOnline
10959 && oldState != MachineState_DeletingSnapshotPaused
10960 )
10961 i_setMachineState(MachineState_SettingUp);
10962
10963 // use appropriate locked media map (online or offline)
10964 MediumLockListMap lockedMediaOffline;
10965 MediumLockListMap *lockedMediaMap;
10966 if (aOnline)
10967 lockedMediaMap = &mData->mSession.mLockedMedia;
10968 else
10969 lockedMediaMap = &lockedMediaOffline;
10970
10971 try
10972 {
10973 if (!aOnline)
10974 {
10975 /* lock all attached hard disks early to detect "in use"
10976 * situations before deleting actual diffs */
10977 for (MediumAttachmentList::const_iterator
10978 it = mMediumAttachments->begin();
10979 it != mMediumAttachments->end();
10980 ++it)
10981 {
10982 MediumAttachment *pAtt = *it;
10983 if (pAtt->i_getType() == DeviceType_HardDisk)
10984 {
10985 Medium *pMedium = pAtt->i_getMedium();
10986 Assert(pMedium);
10987
10988 MediumLockList *pMediumLockList(new MediumLockList());
10989 alock.release();
10990 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10991 NULL /* pToLockWrite */,
10992 false /* fMediumLockWriteAll */,
10993 NULL,
10994 *pMediumLockList);
10995 alock.acquire();
10996
10997 if (FAILED(rc))
10998 {
10999 delete pMediumLockList;
11000 throw rc;
11001 }
11002
11003 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11004 if (FAILED(rc))
11005 throw rc;
11006 }
11007 }
11008
11009 if (FAILED(rc))
11010 throw rc;
11011 } // end of offline
11012
11013 /* Lock lists are now up to date and include implicitly created media */
11014
11015 /* Go through remembered attachments and delete all implicitly created
11016 * diffs and fix up the attachment information */
11017 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11018 MediumAttachmentList implicitAtts;
11019 for (MediumAttachmentList::const_iterator
11020 it = mMediumAttachments->begin();
11021 it != mMediumAttachments->end();
11022 ++it)
11023 {
11024 ComObjPtr<MediumAttachment> pAtt = *it;
11025 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11026 if (pMedium.isNull())
11027 continue;
11028
11029 // Implicit attachments go on the list for deletion and back references are removed.
11030 if (pAtt->i_isImplicit())
11031 {
11032 /* Deassociate and mark for deletion */
11033 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11034 rc = pMedium->i_removeBackReference(mData->mUuid);
11035 if (FAILED(rc))
11036 throw rc;
11037 implicitAtts.push_back(pAtt);
11038 continue;
11039 }
11040
11041 /* Was this medium attached before? */
11042 if (!i_findAttachment(oldAtts, pMedium))
11043 {
11044 /* no: de-associate */
11045 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11046 rc = pMedium->i_removeBackReference(mData->mUuid);
11047 if (FAILED(rc))
11048 throw rc;
11049 continue;
11050 }
11051 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11052 }
11053
11054 /* If there are implicit attachments to delete, throw away the lock
11055 * map contents (which will unlock all media) since the medium
11056 * attachments will be rolled back. Below we need to completely
11057 * recreate the lock map anyway since it is infinitely complex to
11058 * do this incrementally (would need reconstructing each attachment
11059 * change, which would be extremely hairy). */
11060 if (implicitAtts.size() != 0)
11061 {
11062 ErrorInfoKeeper eik;
11063
11064 HRESULT rc1 = lockedMediaMap->Clear();
11065 AssertComRC(rc1);
11066 }
11067
11068 /* rollback hard disk changes */
11069 mMediumAttachments.rollback();
11070
11071 MultiResult mrc(S_OK);
11072
11073 // Delete unused implicit diffs.
11074 if (implicitAtts.size() != 0)
11075 {
11076 alock.release();
11077
11078 for (MediumAttachmentList::const_iterator
11079 it = implicitAtts.begin();
11080 it != implicitAtts.end();
11081 ++it)
11082 {
11083 // Remove medium associated with this attachment.
11084 ComObjPtr<MediumAttachment> pAtt = *it;
11085 Assert(pAtt);
11086 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11087 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11088 Assert(pMedium);
11089
11090 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11091 // continue on delete failure, just collect error messages
11092 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11093 pMedium->i_getLocationFull().c_str() ));
11094 mrc = rc;
11095 }
11096 // Clear the list of deleted implicit attachments now, while not
11097 // holding the lock, as it will ultimately trigger Medium::uninit()
11098 // calls which assume that the media tree lock isn't held.
11099 implicitAtts.clear();
11100
11101 alock.acquire();
11102
11103 /* if there is a VM recreate media lock map as mentioned above,
11104 * otherwise it is a waste of time and we leave things unlocked */
11105 if (aOnline)
11106 {
11107 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11108 /* must never be NULL, but better safe than sorry */
11109 if (!pMachine.isNull())
11110 {
11111 alock.release();
11112 rc = mData->mSession.mMachine->i_lockMedia();
11113 alock.acquire();
11114 if (FAILED(rc))
11115 throw rc;
11116 }
11117 }
11118 }
11119 }
11120 catch (HRESULT aRC) {rc = aRC;}
11121
11122 if (mData->mMachineState == MachineState_SettingUp)
11123 i_setMachineState(oldState);
11124
11125 /* unlock all hard disks we locked when there is no VM */
11126 if (!aOnline)
11127 {
11128 ErrorInfoKeeper eik;
11129
11130 HRESULT rc1 = lockedMediaMap->Clear();
11131 AssertComRC(rc1);
11132 }
11133
11134 return rc;
11135}
11136
11137
11138/**
11139 * Looks through the given list of media attachments for one with the given parameters
11140 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11141 * can be searched as well if needed.
11142 *
11143 * @param ll
11144 * @param aControllerName
11145 * @param aControllerPort
11146 * @param aDevice
11147 * @return
11148 */
11149MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11150 const Utf8Str &aControllerName,
11151 LONG aControllerPort,
11152 LONG aDevice)
11153{
11154 for (MediumAttachmentList::const_iterator
11155 it = ll.begin();
11156 it != ll.end();
11157 ++it)
11158 {
11159 MediumAttachment *pAttach = *it;
11160 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11161 return pAttach;
11162 }
11163
11164 return NULL;
11165}
11166
11167/**
11168 * Looks through the given list of media attachments for one with the given parameters
11169 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11170 * can be searched as well if needed.
11171 *
11172 * @param ll
11173 * @param pMedium
11174 * @return
11175 */
11176MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11177 ComObjPtr<Medium> pMedium)
11178{
11179 for (MediumAttachmentList::const_iterator
11180 it = ll.begin();
11181 it != ll.end();
11182 ++it)
11183 {
11184 MediumAttachment *pAttach = *it;
11185 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11186 if (pMediumThis == pMedium)
11187 return pAttach;
11188 }
11189
11190 return NULL;
11191}
11192
11193/**
11194 * Looks through the given list of media attachments for one with the given parameters
11195 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11196 * can be searched as well if needed.
11197 *
11198 * @param ll
11199 * @param id
11200 * @return
11201 */
11202MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11203 Guid &id)
11204{
11205 for (MediumAttachmentList::const_iterator
11206 it = ll.begin();
11207 it != ll.end();
11208 ++it)
11209 {
11210 MediumAttachment *pAttach = *it;
11211 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11212 if (pMediumThis->i_getId() == id)
11213 return pAttach;
11214 }
11215
11216 return NULL;
11217}
11218
11219/**
11220 * Main implementation for Machine::DetachDevice. This also gets called
11221 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11222 *
11223 * @param pAttach Medium attachment to detach.
11224 * @param writeLock Machine write lock which the caller must have locked once.
11225 * This may be released temporarily in here.
11226 * @param pSnapshot If NULL, then the detachment is for the current machine.
11227 * Otherwise this is for a SnapshotMachine, and this must be
11228 * its snapshot.
11229 * @return
11230 */
11231HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11232 AutoWriteLock &writeLock,
11233 Snapshot *pSnapshot)
11234{
11235 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11236 DeviceType_T mediumType = pAttach->i_getType();
11237
11238 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11239
11240 if (pAttach->i_isImplicit())
11241 {
11242 /* attempt to implicitly delete the implicitly created diff */
11243
11244 /// @todo move the implicit flag from MediumAttachment to Medium
11245 /// and forbid any hard disk operation when it is implicit. Or maybe
11246 /// a special media state for it to make it even more simple.
11247
11248 Assert(mMediumAttachments.isBackedUp());
11249
11250 /* will release the lock before the potentially lengthy operation, so
11251 * protect with the special state */
11252 MachineState_T oldState = mData->mMachineState;
11253 i_setMachineState(MachineState_SettingUp);
11254
11255 writeLock.release();
11256
11257 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11258 true /*aWait*/,
11259 false /*aNotify*/);
11260
11261 writeLock.acquire();
11262
11263 i_setMachineState(oldState);
11264
11265 if (FAILED(rc)) return rc;
11266 }
11267
11268 i_setModified(IsModified_Storage);
11269 mMediumAttachments.backup();
11270 mMediumAttachments->remove(pAttach);
11271
11272 if (!oldmedium.isNull())
11273 {
11274 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11275 if (pSnapshot)
11276 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11277 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11278 else if (mediumType != DeviceType_HardDisk)
11279 oldmedium->i_removeBackReference(mData->mUuid);
11280 }
11281
11282 return S_OK;
11283}
11284
11285/**
11286 * Goes thru all media of the given list and
11287 *
11288 * 1) calls i_detachDevice() on each of them for this machine and
11289 * 2) adds all Medium objects found in the process to the given list,
11290 * depending on cleanupMode.
11291 *
11292 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11293 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11294 * media to the list.
11295 *
11296 * This gets called from Machine::Unregister, both for the actual Machine and
11297 * the SnapshotMachine objects that might be found in the snapshots.
11298 *
11299 * Requires caller and locking. The machine lock must be passed in because it
11300 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11301 *
11302 * @param writeLock Machine lock from top-level caller; this gets passed to
11303 * i_detachDevice.
11304 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11305 * object if called for a SnapshotMachine.
11306 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11307 * added to llMedia; if Full, then all media get added;
11308 * otherwise no media get added.
11309 * @param llMedia Caller's list to receive Medium objects which got detached so
11310 * caller can close() them, depending on cleanupMode.
11311 * @return
11312 */
11313HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11314 Snapshot *pSnapshot,
11315 CleanupMode_T cleanupMode,
11316 MediaList &llMedia)
11317{
11318 Assert(isWriteLockOnCurrentThread());
11319
11320 HRESULT rc;
11321
11322 // make a temporary list because i_detachDevice invalidates iterators into
11323 // mMediumAttachments
11324 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11325
11326 for (MediumAttachmentList::iterator
11327 it = llAttachments2.begin();
11328 it != llAttachments2.end();
11329 ++it)
11330 {
11331 ComObjPtr<MediumAttachment> &pAttach = *it;
11332 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11333
11334 if (!pMedium.isNull())
11335 {
11336 AutoCaller mac(pMedium);
11337 if (FAILED(mac.rc())) return mac.rc();
11338 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11339 DeviceType_T devType = pMedium->i_getDeviceType();
11340 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11341 && devType == DeviceType_HardDisk)
11342 || (cleanupMode == CleanupMode_Full)
11343 )
11344 {
11345 llMedia.push_back(pMedium);
11346 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11347 /* Not allowed to keep this lock as below we need the parent
11348 * medium lock, and the lock order is parent to child. */
11349 lock.release();
11350 /*
11351 * Search for medias which are not attached to any machine, but
11352 * in the chain to an attached disk. Mediums are only consided
11353 * if they are:
11354 * - have only one child
11355 * - no references to any machines
11356 * - are of normal medium type
11357 */
11358 while (!pParent.isNull())
11359 {
11360 AutoCaller mac1(pParent);
11361 if (FAILED(mac1.rc())) return mac1.rc();
11362 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11363 if (pParent->i_getChildren().size() == 1)
11364 {
11365 if ( pParent->i_getMachineBackRefCount() == 0
11366 && pParent->i_getType() == MediumType_Normal
11367 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11368 llMedia.push_back(pParent);
11369 }
11370 else
11371 break;
11372 pParent = pParent->i_getParent();
11373 }
11374 }
11375 }
11376
11377 // real machine: then we need to use the proper method
11378 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11379
11380 if (FAILED(rc))
11381 return rc;
11382 }
11383
11384 return S_OK;
11385}
11386
11387/**
11388 * Perform deferred hard disk detachments.
11389 *
11390 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11391 * changed (not backed up).
11392 *
11393 * If @a aOnline is @c true then this method will also unlock the old hard
11394 * disks for which the new implicit diffs were created and will lock these new
11395 * diffs for writing.
11396 *
11397 * @param aOnline Whether the VM was online prior to this operation.
11398 *
11399 * @note Locks this object for writing!
11400 */
11401void Machine::i_commitMedia(bool aOnline /*= false*/)
11402{
11403 AutoCaller autoCaller(this);
11404 AssertComRCReturnVoid(autoCaller.rc());
11405
11406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11407
11408 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11409
11410 HRESULT rc = S_OK;
11411
11412 /* no attach/detach operations -- nothing to do */
11413 if (!mMediumAttachments.isBackedUp())
11414 return;
11415
11416 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11417 bool fMediaNeedsLocking = false;
11418
11419 /* enumerate new attachments */
11420 for (MediumAttachmentList::const_iterator
11421 it = mMediumAttachments->begin();
11422 it != mMediumAttachments->end();
11423 ++it)
11424 {
11425 MediumAttachment *pAttach = *it;
11426
11427 pAttach->i_commit();
11428
11429 Medium *pMedium = pAttach->i_getMedium();
11430 bool fImplicit = pAttach->i_isImplicit();
11431
11432 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11433 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11434 fImplicit));
11435
11436 /** @todo convert all this Machine-based voodoo to MediumAttachment
11437 * based commit logic. */
11438 if (fImplicit)
11439 {
11440 /* convert implicit attachment to normal */
11441 pAttach->i_setImplicit(false);
11442
11443 if ( aOnline
11444 && pMedium
11445 && pAttach->i_getType() == DeviceType_HardDisk
11446 )
11447 {
11448 /* update the appropriate lock list */
11449 MediumLockList *pMediumLockList;
11450 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11451 AssertComRC(rc);
11452 if (pMediumLockList)
11453 {
11454 /* unlock if there's a need to change the locking */
11455 if (!fMediaNeedsLocking)
11456 {
11457 rc = mData->mSession.mLockedMedia.Unlock();
11458 AssertComRC(rc);
11459 fMediaNeedsLocking = true;
11460 }
11461 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11462 AssertComRC(rc);
11463 rc = pMediumLockList->Append(pMedium, true);
11464 AssertComRC(rc);
11465 }
11466 }
11467
11468 continue;
11469 }
11470
11471 if (pMedium)
11472 {
11473 /* was this medium attached before? */
11474 for (MediumAttachmentList::iterator
11475 oldIt = oldAtts.begin();
11476 oldIt != oldAtts.end();
11477 ++oldIt)
11478 {
11479 MediumAttachment *pOldAttach = *oldIt;
11480 if (pOldAttach->i_getMedium() == pMedium)
11481 {
11482 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11483
11484 /* yes: remove from old to avoid de-association */
11485 oldAtts.erase(oldIt);
11486 break;
11487 }
11488 }
11489 }
11490 }
11491
11492 /* enumerate remaining old attachments and de-associate from the
11493 * current machine state */
11494 for (MediumAttachmentList::const_iterator
11495 it = oldAtts.begin();
11496 it != oldAtts.end();
11497 ++it)
11498 {
11499 MediumAttachment *pAttach = *it;
11500 Medium *pMedium = pAttach->i_getMedium();
11501
11502 /* Detach only hard disks, since DVD/floppy media is detached
11503 * instantly in MountMedium. */
11504 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11505 {
11506 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11507
11508 /* now de-associate from the current machine state */
11509 rc = pMedium->i_removeBackReference(mData->mUuid);
11510 AssertComRC(rc);
11511
11512 if (aOnline)
11513 {
11514 /* unlock since medium is not used anymore */
11515 MediumLockList *pMediumLockList;
11516 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11517 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11518 {
11519 /* this happens for online snapshots, there the attachment
11520 * is changing, but only to a diff image created under
11521 * the old one, so there is no separate lock list */
11522 Assert(!pMediumLockList);
11523 }
11524 else
11525 {
11526 AssertComRC(rc);
11527 if (pMediumLockList)
11528 {
11529 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11530 AssertComRC(rc);
11531 }
11532 }
11533 }
11534 }
11535 }
11536
11537 /* take media locks again so that the locking state is consistent */
11538 if (fMediaNeedsLocking)
11539 {
11540 Assert(aOnline);
11541 rc = mData->mSession.mLockedMedia.Lock();
11542 AssertComRC(rc);
11543 }
11544
11545 /* commit the hard disk changes */
11546 mMediumAttachments.commit();
11547
11548 if (i_isSessionMachine())
11549 {
11550 /*
11551 * Update the parent machine to point to the new owner.
11552 * This is necessary because the stored parent will point to the
11553 * session machine otherwise and cause crashes or errors later
11554 * when the session machine gets invalid.
11555 */
11556 /** @todo Change the MediumAttachment class to behave like any other
11557 * class in this regard by creating peer MediumAttachment
11558 * objects for session machines and share the data with the peer
11559 * machine.
11560 */
11561 for (MediumAttachmentList::const_iterator
11562 it = mMediumAttachments->begin();
11563 it != mMediumAttachments->end();
11564 ++it)
11565 (*it)->i_updateParentMachine(mPeer);
11566
11567 /* attach new data to the primary machine and reshare it */
11568 mPeer->mMediumAttachments.attach(mMediumAttachments);
11569 }
11570
11571 return;
11572}
11573
11574/**
11575 * Perform deferred deletion of implicitly created diffs.
11576 *
11577 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11578 * changed (not backed up).
11579 *
11580 * @note Locks this object for writing!
11581 */
11582void Machine::i_rollbackMedia()
11583{
11584 AutoCaller autoCaller(this);
11585 AssertComRCReturnVoid(autoCaller.rc());
11586
11587 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11588 LogFlowThisFunc(("Entering rollbackMedia\n"));
11589
11590 HRESULT rc = S_OK;
11591
11592 /* no attach/detach operations -- nothing to do */
11593 if (!mMediumAttachments.isBackedUp())
11594 return;
11595
11596 /* enumerate new attachments */
11597 for (MediumAttachmentList::const_iterator
11598 it = mMediumAttachments->begin();
11599 it != mMediumAttachments->end();
11600 ++it)
11601 {
11602 MediumAttachment *pAttach = *it;
11603 /* Fix up the backrefs for DVD/floppy media. */
11604 if (pAttach->i_getType() != DeviceType_HardDisk)
11605 {
11606 Medium *pMedium = pAttach->i_getMedium();
11607 if (pMedium)
11608 {
11609 rc = pMedium->i_removeBackReference(mData->mUuid);
11610 AssertComRC(rc);
11611 }
11612 }
11613
11614 (*it)->i_rollback();
11615
11616 pAttach = *it;
11617 /* Fix up the backrefs for DVD/floppy media. */
11618 if (pAttach->i_getType() != DeviceType_HardDisk)
11619 {
11620 Medium *pMedium = pAttach->i_getMedium();
11621 if (pMedium)
11622 {
11623 rc = pMedium->i_addBackReference(mData->mUuid);
11624 AssertComRC(rc);
11625 }
11626 }
11627 }
11628
11629 /** @todo convert all this Machine-based voodoo to MediumAttachment
11630 * based rollback logic. */
11631 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11632
11633 return;
11634}
11635
11636/**
11637 * Returns true if the settings file is located in the directory named exactly
11638 * as the machine; this means, among other things, that the machine directory
11639 * should be auto-renamed.
11640 *
11641 * @param aSettingsDir if not NULL, the full machine settings file directory
11642 * name will be assigned there.
11643 *
11644 * @note Doesn't lock anything.
11645 * @note Not thread safe (must be called from this object's lock).
11646 */
11647bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11648{
11649 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11650 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11651 if (aSettingsDir)
11652 *aSettingsDir = strMachineDirName;
11653 strMachineDirName.stripPath(); // vmname
11654 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11655 strConfigFileOnly.stripPath() // vmname.vbox
11656 .stripSuffix(); // vmname
11657 /** @todo hack, make somehow use of ComposeMachineFilename */
11658 if (mUserData->s.fDirectoryIncludesUUID)
11659 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11660
11661 AssertReturn(!strMachineDirName.isEmpty(), false);
11662 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11663
11664 return strMachineDirName == strConfigFileOnly;
11665}
11666
11667/**
11668 * Discards all changes to machine settings.
11669 *
11670 * @param aNotify Whether to notify the direct session about changes or not.
11671 *
11672 * @note Locks objects for writing!
11673 */
11674void Machine::i_rollback(bool aNotify)
11675{
11676 AutoCaller autoCaller(this);
11677 AssertComRCReturn(autoCaller.rc(), (void)0);
11678
11679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11680
11681 if (!mStorageControllers.isNull())
11682 {
11683 if (mStorageControllers.isBackedUp())
11684 {
11685 /* unitialize all new devices (absent in the backed up list). */
11686 StorageControllerList *backedList = mStorageControllers.backedUpData();
11687 for (StorageControllerList::const_iterator
11688 it = mStorageControllers->begin();
11689 it != mStorageControllers->end();
11690 ++it)
11691 {
11692 if ( std::find(backedList->begin(), backedList->end(), *it)
11693 == backedList->end()
11694 )
11695 {
11696 (*it)->uninit();
11697 }
11698 }
11699
11700 /* restore the list */
11701 mStorageControllers.rollback();
11702 }
11703
11704 /* rollback any changes to devices after restoring the list */
11705 if (mData->flModifications & IsModified_Storage)
11706 {
11707 for (StorageControllerList::const_iterator
11708 it = mStorageControllers->begin();
11709 it != mStorageControllers->end();
11710 ++it)
11711 {
11712 (*it)->i_rollback();
11713 }
11714 }
11715 }
11716
11717 if (!mUSBControllers.isNull())
11718 {
11719 if (mUSBControllers.isBackedUp())
11720 {
11721 /* unitialize all new devices (absent in the backed up list). */
11722 USBControllerList *backedList = mUSBControllers.backedUpData();
11723 for (USBControllerList::const_iterator
11724 it = mUSBControllers->begin();
11725 it != mUSBControllers->end();
11726 ++it)
11727 {
11728 if ( std::find(backedList->begin(), backedList->end(), *it)
11729 == backedList->end()
11730 )
11731 {
11732 (*it)->uninit();
11733 }
11734 }
11735
11736 /* restore the list */
11737 mUSBControllers.rollback();
11738 }
11739
11740 /* rollback any changes to devices after restoring the list */
11741 if (mData->flModifications & IsModified_USB)
11742 {
11743 for (USBControllerList::const_iterator
11744 it = mUSBControllers->begin();
11745 it != mUSBControllers->end();
11746 ++it)
11747 {
11748 (*it)->i_rollback();
11749 }
11750 }
11751 }
11752
11753 mUserData.rollback();
11754
11755 mHWData.rollback();
11756
11757 if (mData->flModifications & IsModified_Storage)
11758 i_rollbackMedia();
11759
11760 if (mBIOSSettings)
11761 mBIOSSettings->i_rollback();
11762
11763 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11764 mRecordingSettings->i_rollback();
11765
11766 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11767 mVRDEServer->i_rollback();
11768
11769 if (mAudioAdapter)
11770 mAudioAdapter->i_rollback();
11771
11772 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11773 mUSBDeviceFilters->i_rollback();
11774
11775 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11776 mBandwidthControl->i_rollback();
11777
11778 if (!mHWData.isNull())
11779 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11780 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11781 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11782 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11783
11784 if (mData->flModifications & IsModified_NetworkAdapters)
11785 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11786 if ( mNetworkAdapters[slot]
11787 && mNetworkAdapters[slot]->i_isModified())
11788 {
11789 mNetworkAdapters[slot]->i_rollback();
11790 networkAdapters[slot] = mNetworkAdapters[slot];
11791 }
11792
11793 if (mData->flModifications & IsModified_SerialPorts)
11794 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11795 if ( mSerialPorts[slot]
11796 && mSerialPorts[slot]->i_isModified())
11797 {
11798 mSerialPorts[slot]->i_rollback();
11799 serialPorts[slot] = mSerialPorts[slot];
11800 }
11801
11802 if (mData->flModifications & IsModified_ParallelPorts)
11803 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11804 if ( mParallelPorts[slot]
11805 && mParallelPorts[slot]->i_isModified())
11806 {
11807 mParallelPorts[slot]->i_rollback();
11808 parallelPorts[slot] = mParallelPorts[slot];
11809 }
11810
11811 if (aNotify)
11812 {
11813 /* inform the direct session about changes */
11814
11815 ComObjPtr<Machine> that = this;
11816 uint32_t flModifications = mData->flModifications;
11817 alock.release();
11818
11819 if (flModifications & IsModified_SharedFolders)
11820 that->i_onSharedFolderChange();
11821
11822 if (flModifications & IsModified_VRDEServer)
11823 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11824 if (flModifications & IsModified_USB)
11825 that->i_onUSBControllerChange();
11826
11827 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11828 if (networkAdapters[slot])
11829 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11830 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11831 if (serialPorts[slot])
11832 that->i_onSerialPortChange(serialPorts[slot]);
11833 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11834 if (parallelPorts[slot])
11835 that->i_onParallelPortChange(parallelPorts[slot]);
11836
11837 if (flModifications & IsModified_Storage)
11838 that->i_onStorageControllerChange();
11839
11840#if 0
11841 if (flModifications & IsModified_BandwidthControl)
11842 that->onBandwidthControlChange();
11843#endif
11844 }
11845}
11846
11847/**
11848 * Commits all the changes to machine settings.
11849 *
11850 * Note that this operation is supposed to never fail.
11851 *
11852 * @note Locks this object and children for writing.
11853 */
11854void Machine::i_commit()
11855{
11856 AutoCaller autoCaller(this);
11857 AssertComRCReturnVoid(autoCaller.rc());
11858
11859 AutoCaller peerCaller(mPeer);
11860 AssertComRCReturnVoid(peerCaller.rc());
11861
11862 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11863
11864 /*
11865 * use safe commit to ensure Snapshot machines (that share mUserData)
11866 * will still refer to a valid memory location
11867 */
11868 mUserData.commitCopy();
11869
11870 mHWData.commit();
11871
11872 if (mMediumAttachments.isBackedUp())
11873 i_commitMedia(Global::IsOnline(mData->mMachineState));
11874
11875 mBIOSSettings->i_commit();
11876 mRecordingSettings->i_commit();
11877 mVRDEServer->i_commit();
11878 mAudioAdapter->i_commit();
11879 mUSBDeviceFilters->i_commit();
11880 mBandwidthControl->i_commit();
11881
11882 /* Since mNetworkAdapters is a list which might have been changed (resized)
11883 * without using the Backupable<> template we need to handle the copying
11884 * of the list entries manually, including the creation of peers for the
11885 * new objects. */
11886 bool commitNetworkAdapters = false;
11887 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11888 if (mPeer)
11889 {
11890 /* commit everything, even the ones which will go away */
11891 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11892 mNetworkAdapters[slot]->i_commit();
11893 /* copy over the new entries, creating a peer and uninit the original */
11894 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11895 for (size_t slot = 0; slot < newSize; slot++)
11896 {
11897 /* look if this adapter has a peer device */
11898 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11899 if (!peer)
11900 {
11901 /* no peer means the adapter is a newly created one;
11902 * create a peer owning data this data share it with */
11903 peer.createObject();
11904 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11905 }
11906 mPeer->mNetworkAdapters[slot] = peer;
11907 }
11908 /* uninit any no longer needed network adapters */
11909 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11910 mNetworkAdapters[slot]->uninit();
11911 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11912 {
11913 if (mPeer->mNetworkAdapters[slot])
11914 mPeer->mNetworkAdapters[slot]->uninit();
11915 }
11916 /* Keep the original network adapter count until this point, so that
11917 * discarding a chipset type change will not lose settings. */
11918 mNetworkAdapters.resize(newSize);
11919 mPeer->mNetworkAdapters.resize(newSize);
11920 }
11921 else
11922 {
11923 /* we have no peer (our parent is the newly created machine);
11924 * just commit changes to the network adapters */
11925 commitNetworkAdapters = true;
11926 }
11927 if (commitNetworkAdapters)
11928 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11929 mNetworkAdapters[slot]->i_commit();
11930
11931 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11932 mSerialPorts[slot]->i_commit();
11933 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11934 mParallelPorts[slot]->i_commit();
11935
11936 bool commitStorageControllers = false;
11937
11938 if (mStorageControllers.isBackedUp())
11939 {
11940 mStorageControllers.commit();
11941
11942 if (mPeer)
11943 {
11944 /* Commit all changes to new controllers (this will reshare data with
11945 * peers for those who have peers) */
11946 StorageControllerList *newList = new StorageControllerList();
11947 for (StorageControllerList::const_iterator
11948 it = mStorageControllers->begin();
11949 it != mStorageControllers->end();
11950 ++it)
11951 {
11952 (*it)->i_commit();
11953
11954 /* look if this controller has a peer device */
11955 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11956 if (!peer)
11957 {
11958 /* no peer means the device is a newly created one;
11959 * create a peer owning data this device share it with */
11960 peer.createObject();
11961 peer->init(mPeer, *it, true /* aReshare */);
11962 }
11963 else
11964 {
11965 /* remove peer from the old list */
11966 mPeer->mStorageControllers->remove(peer);
11967 }
11968 /* and add it to the new list */
11969 newList->push_back(peer);
11970 }
11971
11972 /* uninit old peer's controllers that are left */
11973 for (StorageControllerList::const_iterator
11974 it = mPeer->mStorageControllers->begin();
11975 it != mPeer->mStorageControllers->end();
11976 ++it)
11977 {
11978 (*it)->uninit();
11979 }
11980
11981 /* attach new list of controllers to our peer */
11982 mPeer->mStorageControllers.attach(newList);
11983 }
11984 else
11985 {
11986 /* we have no peer (our parent is the newly created machine);
11987 * just commit changes to devices */
11988 commitStorageControllers = true;
11989 }
11990 }
11991 else
11992 {
11993 /* the list of controllers itself is not changed,
11994 * just commit changes to controllers themselves */
11995 commitStorageControllers = true;
11996 }
11997
11998 if (commitStorageControllers)
11999 {
12000 for (StorageControllerList::const_iterator
12001 it = mStorageControllers->begin();
12002 it != mStorageControllers->end();
12003 ++it)
12004 {
12005 (*it)->i_commit();
12006 }
12007 }
12008
12009 bool commitUSBControllers = false;
12010
12011 if (mUSBControllers.isBackedUp())
12012 {
12013 mUSBControllers.commit();
12014
12015 if (mPeer)
12016 {
12017 /* Commit all changes to new controllers (this will reshare data with
12018 * peers for those who have peers) */
12019 USBControllerList *newList = new USBControllerList();
12020 for (USBControllerList::const_iterator
12021 it = mUSBControllers->begin();
12022 it != mUSBControllers->end();
12023 ++it)
12024 {
12025 (*it)->i_commit();
12026
12027 /* look if this controller has a peer device */
12028 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12029 if (!peer)
12030 {
12031 /* no peer means the device is a newly created one;
12032 * create a peer owning data this device share it with */
12033 peer.createObject();
12034 peer->init(mPeer, *it, true /* aReshare */);
12035 }
12036 else
12037 {
12038 /* remove peer from the old list */
12039 mPeer->mUSBControllers->remove(peer);
12040 }
12041 /* and add it to the new list */
12042 newList->push_back(peer);
12043 }
12044
12045 /* uninit old peer's controllers that are left */
12046 for (USBControllerList::const_iterator
12047 it = mPeer->mUSBControllers->begin();
12048 it != mPeer->mUSBControllers->end();
12049 ++it)
12050 {
12051 (*it)->uninit();
12052 }
12053
12054 /* attach new list of controllers to our peer */
12055 mPeer->mUSBControllers.attach(newList);
12056 }
12057 else
12058 {
12059 /* we have no peer (our parent is the newly created machine);
12060 * just commit changes to devices */
12061 commitUSBControllers = true;
12062 }
12063 }
12064 else
12065 {
12066 /* the list of controllers itself is not changed,
12067 * just commit changes to controllers themselves */
12068 commitUSBControllers = true;
12069 }
12070
12071 if (commitUSBControllers)
12072 {
12073 for (USBControllerList::const_iterator
12074 it = mUSBControllers->begin();
12075 it != mUSBControllers->end();
12076 ++it)
12077 {
12078 (*it)->i_commit();
12079 }
12080 }
12081
12082 if (i_isSessionMachine())
12083 {
12084 /* attach new data to the primary machine and reshare it */
12085 mPeer->mUserData.attach(mUserData);
12086 mPeer->mHWData.attach(mHWData);
12087 /* mmMediumAttachments is reshared by fixupMedia */
12088 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12089 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12090 }
12091}
12092
12093/**
12094 * Copies all the hardware data from the given machine.
12095 *
12096 * Currently, only called when the VM is being restored from a snapshot. In
12097 * particular, this implies that the VM is not running during this method's
12098 * call.
12099 *
12100 * @note This method must be called from under this object's lock.
12101 *
12102 * @note This method doesn't call #i_commit(), so all data remains backed up and
12103 * unsaved.
12104 */
12105void Machine::i_copyFrom(Machine *aThat)
12106{
12107 AssertReturnVoid(!i_isSnapshotMachine());
12108 AssertReturnVoid(aThat->i_isSnapshotMachine());
12109
12110 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12111
12112 mHWData.assignCopy(aThat->mHWData);
12113
12114 // create copies of all shared folders (mHWData after attaching a copy
12115 // contains just references to original objects)
12116 for (HWData::SharedFolderList::iterator
12117 it = mHWData->mSharedFolders.begin();
12118 it != mHWData->mSharedFolders.end();
12119 ++it)
12120 {
12121 ComObjPtr<SharedFolder> folder;
12122 folder.createObject();
12123 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12124 AssertComRC(rc);
12125 *it = folder;
12126 }
12127
12128 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12129 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12130 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12131 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12132 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12133 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12134
12135 /* create private copies of all controllers */
12136 mStorageControllers.backup();
12137 mStorageControllers->clear();
12138 for (StorageControllerList::const_iterator
12139 it = aThat->mStorageControllers->begin();
12140 it != aThat->mStorageControllers->end();
12141 ++it)
12142 {
12143 ComObjPtr<StorageController> ctrl;
12144 ctrl.createObject();
12145 ctrl->initCopy(this, *it);
12146 mStorageControllers->push_back(ctrl);
12147 }
12148
12149 /* create private copies of all USB controllers */
12150 mUSBControllers.backup();
12151 mUSBControllers->clear();
12152 for (USBControllerList::const_iterator
12153 it = aThat->mUSBControllers->begin();
12154 it != aThat->mUSBControllers->end();
12155 ++it)
12156 {
12157 ComObjPtr<USBController> ctrl;
12158 ctrl.createObject();
12159 ctrl->initCopy(this, *it);
12160 mUSBControllers->push_back(ctrl);
12161 }
12162
12163 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12164 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12165 {
12166 if (mNetworkAdapters[slot].isNotNull())
12167 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12168 else
12169 {
12170 unconst(mNetworkAdapters[slot]).createObject();
12171 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12172 }
12173 }
12174 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12175 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12176 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12177 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12178}
12179
12180/**
12181 * Returns whether the given storage controller is hotplug capable.
12182 *
12183 * @returns true if the controller supports hotplugging
12184 * false otherwise.
12185 * @param enmCtrlType The controller type to check for.
12186 */
12187bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12188{
12189 ComPtr<ISystemProperties> systemProperties;
12190 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12191 if (FAILED(rc))
12192 return false;
12193
12194 BOOL aHotplugCapable = FALSE;
12195 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12196
12197 return RT_BOOL(aHotplugCapable);
12198}
12199
12200#ifdef VBOX_WITH_RESOURCE_USAGE_API
12201
12202void Machine::i_getDiskList(MediaList &list)
12203{
12204 for (MediumAttachmentList::const_iterator
12205 it = mMediumAttachments->begin();
12206 it != mMediumAttachments->end();
12207 ++it)
12208 {
12209 MediumAttachment *pAttach = *it;
12210 /* just in case */
12211 AssertContinue(pAttach);
12212
12213 AutoCaller localAutoCallerA(pAttach);
12214 if (FAILED(localAutoCallerA.rc())) continue;
12215
12216 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12217
12218 if (pAttach->i_getType() == DeviceType_HardDisk)
12219 list.push_back(pAttach->i_getMedium());
12220 }
12221}
12222
12223void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12224{
12225 AssertReturnVoid(isWriteLockOnCurrentThread());
12226 AssertPtrReturnVoid(aCollector);
12227
12228 pm::CollectorHAL *hal = aCollector->getHAL();
12229 /* Create sub metrics */
12230 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12231 "Percentage of processor time spent in user mode by the VM process.");
12232 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12233 "Percentage of processor time spent in kernel mode by the VM process.");
12234 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12235 "Size of resident portion of VM process in memory.");
12236 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12237 "Actual size of all VM disks combined.");
12238 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12239 "Network receive rate.");
12240 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12241 "Network transmit rate.");
12242 /* Create and register base metrics */
12243 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12244 cpuLoadUser, cpuLoadKernel);
12245 aCollector->registerBaseMetric(cpuLoad);
12246 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12247 ramUsageUsed);
12248 aCollector->registerBaseMetric(ramUsage);
12249 MediaList disks;
12250 i_getDiskList(disks);
12251 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12252 diskUsageUsed);
12253 aCollector->registerBaseMetric(diskUsage);
12254
12255 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12256 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12257 new pm::AggregateAvg()));
12258 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12259 new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12261 new pm::AggregateMax()));
12262 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12263 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12264 new pm::AggregateAvg()));
12265 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12266 new pm::AggregateMin()));
12267 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12268 new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12271 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12272 new pm::AggregateAvg()));
12273 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12274 new pm::AggregateMin()));
12275 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12276 new pm::AggregateMax()));
12277
12278 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12279 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12280 new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12282 new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12284 new pm::AggregateMax()));
12285
12286
12287 /* Guest metrics collector */
12288 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12289 aCollector->registerGuest(mCollectorGuest);
12290 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12291
12292 /* Create sub metrics */
12293 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12294 "Percentage of processor time spent in user mode as seen by the guest.");
12295 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12296 "Percentage of processor time spent in kernel mode as seen by the guest.");
12297 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12298 "Percentage of processor time spent idling as seen by the guest.");
12299
12300 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12301 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12302 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12303 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12304 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12305 pm::SubMetric *guestMemCache = new pm::SubMetric(
12306 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12307
12308 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12309 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12310
12311 /* Create and register base metrics */
12312 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12313 machineNetRx, machineNetTx);
12314 aCollector->registerBaseMetric(machineNetRate);
12315
12316 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12317 guestLoadUser, guestLoadKernel, guestLoadIdle);
12318 aCollector->registerBaseMetric(guestCpuLoad);
12319
12320 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12321 guestMemTotal, guestMemFree,
12322 guestMemBalloon, guestMemShared,
12323 guestMemCache, guestPagedTotal);
12324 aCollector->registerBaseMetric(guestCpuMem);
12325
12326 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12327 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12328 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12329 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12330
12331 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12334 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12335
12336 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12337 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12340
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12345
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12349 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12350
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12355
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12360
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12365
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12370
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12375
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12380}
12381
12382void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12383{
12384 AssertReturnVoid(isWriteLockOnCurrentThread());
12385
12386 if (aCollector)
12387 {
12388 aCollector->unregisterMetricsFor(aMachine);
12389 aCollector->unregisterBaseMetricsFor(aMachine);
12390 }
12391}
12392
12393#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12394
12395
12396////////////////////////////////////////////////////////////////////////////////
12397
12398DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12399
12400HRESULT SessionMachine::FinalConstruct()
12401{
12402 LogFlowThisFunc(("\n"));
12403
12404 mClientToken = NULL;
12405
12406 return BaseFinalConstruct();
12407}
12408
12409void SessionMachine::FinalRelease()
12410{
12411 LogFlowThisFunc(("\n"));
12412
12413 Assert(!mClientToken);
12414 /* paranoia, should not hang around any more */
12415 if (mClientToken)
12416 {
12417 delete mClientToken;
12418 mClientToken = NULL;
12419 }
12420
12421 uninit(Uninit::Unexpected);
12422
12423 BaseFinalRelease();
12424}
12425
12426/**
12427 * @note Must be called only by Machine::LockMachine() from its own write lock.
12428 */
12429HRESULT SessionMachine::init(Machine *aMachine)
12430{
12431 LogFlowThisFuncEnter();
12432 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12433
12434 AssertReturn(aMachine, E_INVALIDARG);
12435
12436 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12437
12438 /* Enclose the state transition NotReady->InInit->Ready */
12439 AutoInitSpan autoInitSpan(this);
12440 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12441
12442 HRESULT rc = S_OK;
12443
12444 RT_ZERO(mAuthLibCtx);
12445
12446 /* create the machine client token */
12447 try
12448 {
12449 mClientToken = new ClientToken(aMachine, this);
12450 if (!mClientToken->isReady())
12451 {
12452 delete mClientToken;
12453 mClientToken = NULL;
12454 rc = E_FAIL;
12455 }
12456 }
12457 catch (std::bad_alloc &)
12458 {
12459 rc = E_OUTOFMEMORY;
12460 }
12461 if (FAILED(rc))
12462 return rc;
12463
12464 /* memorize the peer Machine */
12465 unconst(mPeer) = aMachine;
12466 /* share the parent pointer */
12467 unconst(mParent) = aMachine->mParent;
12468
12469 /* take the pointers to data to share */
12470 mData.share(aMachine->mData);
12471 mSSData.share(aMachine->mSSData);
12472
12473 mUserData.share(aMachine->mUserData);
12474 mHWData.share(aMachine->mHWData);
12475 mMediumAttachments.share(aMachine->mMediumAttachments);
12476
12477 mStorageControllers.allocate();
12478 for (StorageControllerList::const_iterator
12479 it = aMachine->mStorageControllers->begin();
12480 it != aMachine->mStorageControllers->end();
12481 ++it)
12482 {
12483 ComObjPtr<StorageController> ctl;
12484 ctl.createObject();
12485 ctl->init(this, *it);
12486 mStorageControllers->push_back(ctl);
12487 }
12488
12489 mUSBControllers.allocate();
12490 for (USBControllerList::const_iterator
12491 it = aMachine->mUSBControllers->begin();
12492 it != aMachine->mUSBControllers->end();
12493 ++it)
12494 {
12495 ComObjPtr<USBController> ctl;
12496 ctl.createObject();
12497 ctl->init(this, *it);
12498 mUSBControllers->push_back(ctl);
12499 }
12500
12501 unconst(mBIOSSettings).createObject();
12502 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12503 unconst(mRecordingSettings).createObject();
12504 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12505 /* create another VRDEServer object that will be mutable */
12506 unconst(mVRDEServer).createObject();
12507 mVRDEServer->init(this, aMachine->mVRDEServer);
12508 /* create another audio adapter object that will be mutable */
12509 unconst(mAudioAdapter).createObject();
12510 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12511 /* create a list of serial ports that will be mutable */
12512 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12513 {
12514 unconst(mSerialPorts[slot]).createObject();
12515 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12516 }
12517 /* create a list of parallel ports that will be mutable */
12518 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12519 {
12520 unconst(mParallelPorts[slot]).createObject();
12521 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12522 }
12523
12524 /* create another USB device filters object that will be mutable */
12525 unconst(mUSBDeviceFilters).createObject();
12526 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12527
12528 /* create a list of network adapters that will be mutable */
12529 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12530 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12531 {
12532 unconst(mNetworkAdapters[slot]).createObject();
12533 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12534 }
12535
12536 /* create another bandwidth control object that will be mutable */
12537 unconst(mBandwidthControl).createObject();
12538 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12539
12540 /* default is to delete saved state on Saved -> PoweredOff transition */
12541 mRemoveSavedState = true;
12542
12543 /* Confirm a successful initialization when it's the case */
12544 autoInitSpan.setSucceeded();
12545
12546 miNATNetworksStarted = 0;
12547
12548 LogFlowThisFuncLeave();
12549 return rc;
12550}
12551
12552/**
12553 * Uninitializes this session object. If the reason is other than
12554 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12555 * or the client watcher code.
12556 *
12557 * @param aReason uninitialization reason
12558 *
12559 * @note Locks mParent + this object for writing.
12560 */
12561void SessionMachine::uninit(Uninit::Reason aReason)
12562{
12563 LogFlowThisFuncEnter();
12564 LogFlowThisFunc(("reason=%d\n", aReason));
12565
12566 /*
12567 * Strongly reference ourselves to prevent this object deletion after
12568 * mData->mSession.mMachine.setNull() below (which can release the last
12569 * reference and call the destructor). Important: this must be done before
12570 * accessing any members (and before AutoUninitSpan that does it as well).
12571 * This self reference will be released as the very last step on return.
12572 */
12573 ComObjPtr<SessionMachine> selfRef;
12574 if (aReason != Uninit::Unexpected)
12575 selfRef = this;
12576
12577 /* Enclose the state transition Ready->InUninit->NotReady */
12578 AutoUninitSpan autoUninitSpan(this);
12579 if (autoUninitSpan.uninitDone())
12580 {
12581 LogFlowThisFunc(("Already uninitialized\n"));
12582 LogFlowThisFuncLeave();
12583 return;
12584 }
12585
12586 if (autoUninitSpan.initFailed())
12587 {
12588 /* We've been called by init() because it's failed. It's not really
12589 * necessary (nor it's safe) to perform the regular uninit sequence
12590 * below, the following is enough.
12591 */
12592 LogFlowThisFunc(("Initialization failed.\n"));
12593 /* destroy the machine client token */
12594 if (mClientToken)
12595 {
12596 delete mClientToken;
12597 mClientToken = NULL;
12598 }
12599 uninitDataAndChildObjects();
12600 mData.free();
12601 unconst(mParent) = NULL;
12602 unconst(mPeer) = NULL;
12603 LogFlowThisFuncLeave();
12604 return;
12605 }
12606
12607 MachineState_T lastState;
12608 {
12609 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12610 lastState = mData->mMachineState;
12611 }
12612 NOREF(lastState);
12613
12614#ifdef VBOX_WITH_USB
12615 // release all captured USB devices, but do this before requesting the locks below
12616 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12617 {
12618 /* Console::captureUSBDevices() is called in the VM process only after
12619 * setting the machine state to Starting or Restoring.
12620 * Console::detachAllUSBDevices() will be called upon successful
12621 * termination. So, we need to release USB devices only if there was
12622 * an abnormal termination of a running VM.
12623 *
12624 * This is identical to SessionMachine::DetachAllUSBDevices except
12625 * for the aAbnormal argument. */
12626 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12627 AssertComRC(rc);
12628 NOREF(rc);
12629
12630 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12631 if (service)
12632 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12633 }
12634#endif /* VBOX_WITH_USB */
12635
12636 // we need to lock this object in uninit() because the lock is shared
12637 // with mPeer (as well as data we modify below). mParent lock is needed
12638 // by several calls to it.
12639 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12640
12641#ifdef VBOX_WITH_RESOURCE_USAGE_API
12642 /*
12643 * It is safe to call Machine::i_unregisterMetrics() here because
12644 * PerformanceCollector::samplerCallback no longer accesses guest methods
12645 * holding the lock.
12646 */
12647 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12648 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12649 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12650 if (mCollectorGuest)
12651 {
12652 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12653 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12654 mCollectorGuest = NULL;
12655 }
12656#endif
12657
12658 if (aReason == Uninit::Abnormal)
12659 {
12660 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12661
12662 /* reset the state to Aborted */
12663 if (mData->mMachineState != MachineState_Aborted)
12664 i_setMachineState(MachineState_Aborted);
12665 }
12666
12667 // any machine settings modified?
12668 if (mData->flModifications)
12669 {
12670 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12671 i_rollback(false /* aNotify */);
12672 }
12673
12674 mData->mSession.mPID = NIL_RTPROCESS;
12675
12676 if (aReason == Uninit::Unexpected)
12677 {
12678 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12679 * client watcher thread to update the set of machines that have open
12680 * sessions. */
12681 mParent->i_updateClientWatcher();
12682 }
12683
12684 /* uninitialize all remote controls */
12685 if (mData->mSession.mRemoteControls.size())
12686 {
12687 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12688 mData->mSession.mRemoteControls.size()));
12689
12690 /* Always restart a the beginning, since the iterator is invalidated
12691 * by using erase(). */
12692 for (Data::Session::RemoteControlList::iterator
12693 it = mData->mSession.mRemoteControls.begin();
12694 it != mData->mSession.mRemoteControls.end();
12695 it = mData->mSession.mRemoteControls.begin())
12696 {
12697 ComPtr<IInternalSessionControl> pControl = *it;
12698 mData->mSession.mRemoteControls.erase(it);
12699 multilock.release();
12700 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12701 HRESULT rc = pControl->Uninitialize();
12702 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12703 if (FAILED(rc))
12704 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12705 multilock.acquire();
12706 }
12707 mData->mSession.mRemoteControls.clear();
12708 }
12709
12710 /* Remove all references to the NAT network service. The service will stop
12711 * if all references (also from other VMs) are removed. */
12712 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12713 {
12714 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12715 {
12716 BOOL enabled;
12717 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12718 if ( FAILED(hrc)
12719 || !enabled)
12720 continue;
12721
12722 NetworkAttachmentType_T type;
12723 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12724 if ( SUCCEEDED(hrc)
12725 && type == NetworkAttachmentType_NATNetwork)
12726 {
12727 Bstr name;
12728 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12729 if (SUCCEEDED(hrc))
12730 {
12731 multilock.release();
12732 Utf8Str strName(name);
12733 LogRel(("VM '%s' stops using NAT network '%s'\n",
12734 mUserData->s.strName.c_str(), strName.c_str()));
12735 mParent->i_natNetworkRefDec(strName);
12736 multilock.acquire();
12737 }
12738 }
12739 }
12740 }
12741
12742 /*
12743 * An expected uninitialization can come only from #i_checkForDeath().
12744 * Otherwise it means that something's gone really wrong (for example,
12745 * the Session implementation has released the VirtualBox reference
12746 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12747 * etc). However, it's also possible, that the client releases the IPC
12748 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12749 * but the VirtualBox release event comes first to the server process.
12750 * This case is practically possible, so we should not assert on an
12751 * unexpected uninit, just log a warning.
12752 */
12753
12754 if (aReason == Uninit::Unexpected)
12755 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12756
12757 if (aReason != Uninit::Normal)
12758 {
12759 mData->mSession.mDirectControl.setNull();
12760 }
12761 else
12762 {
12763 /* this must be null here (see #OnSessionEnd()) */
12764 Assert(mData->mSession.mDirectControl.isNull());
12765 Assert(mData->mSession.mState == SessionState_Unlocking);
12766 Assert(!mData->mSession.mProgress.isNull());
12767 }
12768 if (mData->mSession.mProgress)
12769 {
12770 if (aReason == Uninit::Normal)
12771 mData->mSession.mProgress->i_notifyComplete(S_OK);
12772 else
12773 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12774 COM_IIDOF(ISession),
12775 getComponentName(),
12776 tr("The VM session was aborted"));
12777 mData->mSession.mProgress.setNull();
12778 }
12779
12780 if (mConsoleTaskData.mProgress)
12781 {
12782 Assert(aReason == Uninit::Abnormal);
12783 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12784 COM_IIDOF(ISession),
12785 getComponentName(),
12786 tr("The VM session was aborted"));
12787 mConsoleTaskData.mProgress.setNull();
12788 }
12789
12790 /* remove the association between the peer machine and this session machine */
12791 Assert( (SessionMachine*)mData->mSession.mMachine == this
12792 || aReason == Uninit::Unexpected);
12793
12794 /* reset the rest of session data */
12795 mData->mSession.mLockType = LockType_Null;
12796 mData->mSession.mMachine.setNull();
12797 mData->mSession.mState = SessionState_Unlocked;
12798 mData->mSession.mName.setNull();
12799
12800 /* destroy the machine client token before leaving the exclusive lock */
12801 if (mClientToken)
12802 {
12803 delete mClientToken;
12804 mClientToken = NULL;
12805 }
12806
12807 /* fire an event */
12808 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12809
12810 uninitDataAndChildObjects();
12811
12812 /* free the essential data structure last */
12813 mData.free();
12814
12815 /* release the exclusive lock before setting the below two to NULL */
12816 multilock.release();
12817
12818 unconst(mParent) = NULL;
12819 unconst(mPeer) = NULL;
12820
12821 AuthLibUnload(&mAuthLibCtx);
12822
12823 LogFlowThisFuncLeave();
12824}
12825
12826// util::Lockable interface
12827////////////////////////////////////////////////////////////////////////////////
12828
12829/**
12830 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12831 * with the primary Machine instance (mPeer).
12832 */
12833RWLockHandle *SessionMachine::lockHandle() const
12834{
12835 AssertReturn(mPeer != NULL, NULL);
12836 return mPeer->lockHandle();
12837}
12838
12839// IInternalMachineControl methods
12840////////////////////////////////////////////////////////////////////////////////
12841
12842/**
12843 * Passes collected guest statistics to performance collector object
12844 */
12845HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12846 ULONG aCpuKernel, ULONG aCpuIdle,
12847 ULONG aMemTotal, ULONG aMemFree,
12848 ULONG aMemBalloon, ULONG aMemShared,
12849 ULONG aMemCache, ULONG aPageTotal,
12850 ULONG aAllocVMM, ULONG aFreeVMM,
12851 ULONG aBalloonedVMM, ULONG aSharedVMM,
12852 ULONG aVmNetRx, ULONG aVmNetTx)
12853{
12854#ifdef VBOX_WITH_RESOURCE_USAGE_API
12855 if (mCollectorGuest)
12856 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12857 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12858 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12859 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12860
12861 return S_OK;
12862#else
12863 NOREF(aValidStats);
12864 NOREF(aCpuUser);
12865 NOREF(aCpuKernel);
12866 NOREF(aCpuIdle);
12867 NOREF(aMemTotal);
12868 NOREF(aMemFree);
12869 NOREF(aMemBalloon);
12870 NOREF(aMemShared);
12871 NOREF(aMemCache);
12872 NOREF(aPageTotal);
12873 NOREF(aAllocVMM);
12874 NOREF(aFreeVMM);
12875 NOREF(aBalloonedVMM);
12876 NOREF(aSharedVMM);
12877 NOREF(aVmNetRx);
12878 NOREF(aVmNetTx);
12879 return E_NOTIMPL;
12880#endif
12881}
12882
12883////////////////////////////////////////////////////////////////////////////////
12884//
12885// SessionMachine task records
12886//
12887////////////////////////////////////////////////////////////////////////////////
12888
12889/**
12890 * Task record for saving the machine state.
12891 */
12892class SessionMachine::SaveStateTask
12893 : public Machine::Task
12894{
12895public:
12896 SaveStateTask(SessionMachine *m,
12897 Progress *p,
12898 const Utf8Str &t,
12899 Reason_T enmReason,
12900 const Utf8Str &strStateFilePath)
12901 : Task(m, p, t),
12902 m_enmReason(enmReason),
12903 m_strStateFilePath(strStateFilePath)
12904 {}
12905
12906private:
12907 void handler()
12908 {
12909 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12910 }
12911
12912 Reason_T m_enmReason;
12913 Utf8Str m_strStateFilePath;
12914
12915 friend class SessionMachine;
12916};
12917
12918/**
12919 * Task thread implementation for SessionMachine::SaveState(), called from
12920 * SessionMachine::taskHandler().
12921 *
12922 * @note Locks this object for writing.
12923 *
12924 * @param task
12925 * @return
12926 */
12927void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12928{
12929 LogFlowThisFuncEnter();
12930
12931 AutoCaller autoCaller(this);
12932 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12933 if (FAILED(autoCaller.rc()))
12934 {
12935 /* we might have been uninitialized because the session was accidentally
12936 * closed by the client, so don't assert */
12937 HRESULT rc = setError(E_FAIL,
12938 tr("The session has been accidentally closed"));
12939 task.m_pProgress->i_notifyComplete(rc);
12940 LogFlowThisFuncLeave();
12941 return;
12942 }
12943
12944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12945
12946 HRESULT rc = S_OK;
12947
12948 try
12949 {
12950 ComPtr<IInternalSessionControl> directControl;
12951 if (mData->mSession.mLockType == LockType_VM)
12952 directControl = mData->mSession.mDirectControl;
12953 if (directControl.isNull())
12954 throw setError(VBOX_E_INVALID_VM_STATE,
12955 tr("Trying to save state without a running VM"));
12956 alock.release();
12957 BOOL fSuspendedBySave;
12958 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12959 Assert(!fSuspendedBySave);
12960 alock.acquire();
12961
12962 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12963 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12964 throw E_FAIL);
12965
12966 if (SUCCEEDED(rc))
12967 {
12968 mSSData->strStateFilePath = task.m_strStateFilePath;
12969
12970 /* save all VM settings */
12971 rc = i_saveSettings(NULL);
12972 // no need to check whether VirtualBox.xml needs saving also since
12973 // we can't have a name change pending at this point
12974 }
12975 else
12976 {
12977 // On failure, set the state to the state we had at the beginning.
12978 i_setMachineState(task.m_machineStateBackup);
12979 i_updateMachineStateOnClient();
12980
12981 // Delete the saved state file (might have been already created).
12982 // No need to check whether this is shared with a snapshot here
12983 // because we certainly created a fresh saved state file here.
12984 RTFileDelete(task.m_strStateFilePath.c_str());
12985 }
12986 }
12987 catch (HRESULT aRC) { rc = aRC; }
12988
12989 task.m_pProgress->i_notifyComplete(rc);
12990
12991 LogFlowThisFuncLeave();
12992}
12993
12994/**
12995 * @note Locks this object for writing.
12996 */
12997HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12998{
12999 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13000}
13001
13002HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13003{
13004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13005
13006 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13007 if (FAILED(rc)) return rc;
13008
13009 if ( mData->mMachineState != MachineState_Running
13010 && mData->mMachineState != MachineState_Paused
13011 )
13012 return setError(VBOX_E_INVALID_VM_STATE,
13013 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13014 Global::stringifyMachineState(mData->mMachineState));
13015
13016 ComObjPtr<Progress> pProgress;
13017 pProgress.createObject();
13018 rc = pProgress->init(i_getVirtualBox(),
13019 static_cast<IMachine *>(this) /* aInitiator */,
13020 tr("Saving the execution state of the virtual machine"),
13021 FALSE /* aCancelable */);
13022 if (FAILED(rc))
13023 return rc;
13024
13025 Utf8Str strStateFilePath;
13026 i_composeSavedStateFilename(strStateFilePath);
13027
13028 /* create and start the task on a separate thread (note that it will not
13029 * start working until we release alock) */
13030 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13031 rc = pTask->createThread();
13032 if (FAILED(rc))
13033 return rc;
13034
13035 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13036 i_setMachineState(MachineState_Saving);
13037 i_updateMachineStateOnClient();
13038
13039 pProgress.queryInterfaceTo(aProgress.asOutParam());
13040
13041 return S_OK;
13042}
13043
13044/**
13045 * @note Locks this object for writing.
13046 */
13047HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13048{
13049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13050
13051 HRESULT rc = i_checkStateDependency(MutableStateDep);
13052 if (FAILED(rc)) return rc;
13053
13054 if ( mData->mMachineState != MachineState_PoweredOff
13055 && mData->mMachineState != MachineState_Teleported
13056 && mData->mMachineState != MachineState_Aborted
13057 )
13058 return setError(VBOX_E_INVALID_VM_STATE,
13059 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13060 Global::stringifyMachineState(mData->mMachineState));
13061
13062 com::Utf8Str stateFilePathFull;
13063 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13064 if (RT_FAILURE(vrc))
13065 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13066 tr("Invalid saved state file path '%s' (%Rrc)"),
13067 aSavedStateFile.c_str(),
13068 vrc);
13069
13070 mSSData->strStateFilePath = stateFilePathFull;
13071
13072 /* The below i_setMachineState() will detect the state transition and will
13073 * update the settings file */
13074
13075 return i_setMachineState(MachineState_Saved);
13076}
13077
13078/**
13079 * @note Locks this object for writing.
13080 */
13081HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13082{
13083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13084
13085 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13086 if (FAILED(rc)) return rc;
13087
13088 if (mData->mMachineState != MachineState_Saved)
13089 return setError(VBOX_E_INVALID_VM_STATE,
13090 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13091 Global::stringifyMachineState(mData->mMachineState));
13092
13093 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13094
13095 /*
13096 * Saved -> PoweredOff transition will be detected in the SessionMachine
13097 * and properly handled.
13098 */
13099 rc = i_setMachineState(MachineState_PoweredOff);
13100 return rc;
13101}
13102
13103
13104/**
13105 * @note Locks the same as #i_setMachineState() does.
13106 */
13107HRESULT SessionMachine::updateState(MachineState_T aState)
13108{
13109 return i_setMachineState(aState);
13110}
13111
13112/**
13113 * @note Locks this object for writing.
13114 */
13115HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13116{
13117 IProgress *pProgress(aProgress);
13118
13119 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13120
13121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13122
13123 if (mData->mSession.mState != SessionState_Locked)
13124 return VBOX_E_INVALID_OBJECT_STATE;
13125
13126 if (!mData->mSession.mProgress.isNull())
13127 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13128
13129 /* If we didn't reference the NAT network service yet, add a reference to
13130 * force a start */
13131 if (miNATNetworksStarted < 1)
13132 {
13133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13134 {
13135 BOOL enabled;
13136 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13137 if ( FAILED(hrc)
13138 || !enabled)
13139 continue;
13140
13141 NetworkAttachmentType_T type;
13142 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13143 if ( SUCCEEDED(hrc)
13144 && type == NetworkAttachmentType_NATNetwork)
13145 {
13146 Bstr name;
13147 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13148 if (SUCCEEDED(hrc))
13149 {
13150 Utf8Str strName(name);
13151 LogRel(("VM '%s' starts using NAT network '%s'\n",
13152 mUserData->s.strName.c_str(), strName.c_str()));
13153 mPeer->lockHandle()->unlockWrite();
13154 mParent->i_natNetworkRefInc(strName);
13155#ifdef RT_LOCK_STRICT
13156 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13157#else
13158 mPeer->lockHandle()->lockWrite();
13159#endif
13160 }
13161 }
13162 }
13163 miNATNetworksStarted++;
13164 }
13165
13166 LogFlowThisFunc(("returns S_OK.\n"));
13167 return S_OK;
13168}
13169
13170/**
13171 * @note Locks this object for writing.
13172 */
13173HRESULT SessionMachine::endPowerUp(LONG aResult)
13174{
13175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13176
13177 if (mData->mSession.mState != SessionState_Locked)
13178 return VBOX_E_INVALID_OBJECT_STATE;
13179
13180 /* Finalize the LaunchVMProcess progress object. */
13181 if (mData->mSession.mProgress)
13182 {
13183 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13184 mData->mSession.mProgress.setNull();
13185 }
13186
13187 if (SUCCEEDED((HRESULT)aResult))
13188 {
13189#ifdef VBOX_WITH_RESOURCE_USAGE_API
13190 /* The VM has been powered up successfully, so it makes sense
13191 * now to offer the performance metrics for a running machine
13192 * object. Doing it earlier wouldn't be safe. */
13193 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13194 mData->mSession.mPID);
13195#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13196 }
13197
13198 return S_OK;
13199}
13200
13201/**
13202 * @note Locks this object for writing.
13203 */
13204HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13205{
13206 LogFlowThisFuncEnter();
13207
13208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13209
13210 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13211 E_FAIL);
13212
13213 /* create a progress object to track operation completion */
13214 ComObjPtr<Progress> pProgress;
13215 pProgress.createObject();
13216 pProgress->init(i_getVirtualBox(),
13217 static_cast<IMachine *>(this) /* aInitiator */,
13218 tr("Stopping the virtual machine"),
13219 FALSE /* aCancelable */);
13220
13221 /* fill in the console task data */
13222 mConsoleTaskData.mLastState = mData->mMachineState;
13223 mConsoleTaskData.mProgress = pProgress;
13224
13225 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13226 i_setMachineState(MachineState_Stopping);
13227
13228 pProgress.queryInterfaceTo(aProgress.asOutParam());
13229
13230 return S_OK;
13231}
13232
13233/**
13234 * @note Locks this object for writing.
13235 */
13236HRESULT SessionMachine::endPoweringDown(LONG aResult,
13237 const com::Utf8Str &aErrMsg)
13238{
13239 LogFlowThisFuncEnter();
13240
13241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13242
13243 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13244 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13245 && mConsoleTaskData.mLastState != MachineState_Null,
13246 E_FAIL);
13247
13248 /*
13249 * On failure, set the state to the state we had when BeginPoweringDown()
13250 * was called (this is expected by Console::PowerDown() and the associated
13251 * task). On success the VM process already changed the state to
13252 * MachineState_PoweredOff, so no need to do anything.
13253 */
13254 if (FAILED(aResult))
13255 i_setMachineState(mConsoleTaskData.mLastState);
13256
13257 /* notify the progress object about operation completion */
13258 Assert(mConsoleTaskData.mProgress);
13259 if (SUCCEEDED(aResult))
13260 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13261 else
13262 {
13263 if (aErrMsg.length())
13264 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13265 COM_IIDOF(ISession),
13266 getComponentName(),
13267 aErrMsg.c_str());
13268 else
13269 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13270 }
13271
13272 /* clear out the temporary saved state data */
13273 mConsoleTaskData.mLastState = MachineState_Null;
13274 mConsoleTaskData.mProgress.setNull();
13275
13276 LogFlowThisFuncLeave();
13277 return S_OK;
13278}
13279
13280
13281/**
13282 * Goes through the USB filters of the given machine to see if the given
13283 * device matches any filter or not.
13284 *
13285 * @note Locks the same as USBController::hasMatchingFilter() does.
13286 */
13287HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13288 BOOL *aMatched,
13289 ULONG *aMaskedInterfaces)
13290{
13291 LogFlowThisFunc(("\n"));
13292
13293#ifdef VBOX_WITH_USB
13294 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13295#else
13296 NOREF(aDevice);
13297 NOREF(aMaskedInterfaces);
13298 *aMatched = FALSE;
13299#endif
13300
13301 return S_OK;
13302}
13303
13304/**
13305 * @note Locks the same as Host::captureUSBDevice() does.
13306 */
13307HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13308{
13309 LogFlowThisFunc(("\n"));
13310
13311#ifdef VBOX_WITH_USB
13312 /* if captureDeviceForVM() fails, it must have set extended error info */
13313 clearError();
13314 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13315 if (FAILED(rc)) return rc;
13316
13317 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13318 AssertReturn(service, E_FAIL);
13319 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13320#else
13321 NOREF(aId);
13322 return E_NOTIMPL;
13323#endif
13324}
13325
13326/**
13327 * @note Locks the same as Host::detachUSBDevice() does.
13328 */
13329HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13330 BOOL aDone)
13331{
13332 LogFlowThisFunc(("\n"));
13333
13334#ifdef VBOX_WITH_USB
13335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13336 AssertReturn(service, E_FAIL);
13337 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13338#else
13339 NOREF(aId);
13340 NOREF(aDone);
13341 return E_NOTIMPL;
13342#endif
13343}
13344
13345/**
13346 * Inserts all machine filters to the USB proxy service and then calls
13347 * Host::autoCaptureUSBDevices().
13348 *
13349 * Called by Console from the VM process upon VM startup.
13350 *
13351 * @note Locks what called methods lock.
13352 */
13353HRESULT SessionMachine::autoCaptureUSBDevices()
13354{
13355 LogFlowThisFunc(("\n"));
13356
13357#ifdef VBOX_WITH_USB
13358 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13359 AssertComRC(rc);
13360 NOREF(rc);
13361
13362 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13363 AssertReturn(service, E_FAIL);
13364 return service->autoCaptureDevicesForVM(this);
13365#else
13366 return S_OK;
13367#endif
13368}
13369
13370/**
13371 * Removes all machine filters from the USB proxy service and then calls
13372 * Host::detachAllUSBDevices().
13373 *
13374 * Called by Console from the VM process upon normal VM termination or by
13375 * SessionMachine::uninit() upon abnormal VM termination (from under the
13376 * Machine/SessionMachine lock).
13377 *
13378 * @note Locks what called methods lock.
13379 */
13380HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384#ifdef VBOX_WITH_USB
13385 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13386 AssertComRC(rc);
13387 NOREF(rc);
13388
13389 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13390 AssertReturn(service, E_FAIL);
13391 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13392#else
13393 NOREF(aDone);
13394 return S_OK;
13395#endif
13396}
13397
13398/**
13399 * @note Locks this object for writing.
13400 */
13401HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13402 ComPtr<IProgress> &aProgress)
13403{
13404 LogFlowThisFuncEnter();
13405
13406 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13407 /*
13408 * We don't assert below because it might happen that a non-direct session
13409 * informs us it is closed right after we've been uninitialized -- it's ok.
13410 */
13411
13412 /* get IInternalSessionControl interface */
13413 ComPtr<IInternalSessionControl> control(aSession);
13414
13415 ComAssertRet(!control.isNull(), E_INVALIDARG);
13416
13417 /* Creating a Progress object requires the VirtualBox lock, and
13418 * thus locking it here is required by the lock order rules. */
13419 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13420
13421 if (control == mData->mSession.mDirectControl)
13422 {
13423 /* The direct session is being normally closed by the client process
13424 * ----------------------------------------------------------------- */
13425
13426 /* go to the closing state (essential for all open*Session() calls and
13427 * for #i_checkForDeath()) */
13428 Assert(mData->mSession.mState == SessionState_Locked);
13429 mData->mSession.mState = SessionState_Unlocking;
13430
13431 /* set direct control to NULL to release the remote instance */
13432 mData->mSession.mDirectControl.setNull();
13433 LogFlowThisFunc(("Direct control is set to NULL\n"));
13434
13435 if (mData->mSession.mProgress)
13436 {
13437 /* finalize the progress, someone might wait if a frontend
13438 * closes the session before powering on the VM. */
13439 mData->mSession.mProgress->notifyComplete(E_FAIL,
13440 COM_IIDOF(ISession),
13441 getComponentName(),
13442 tr("The VM session was closed before any attempt to power it on"));
13443 mData->mSession.mProgress.setNull();
13444 }
13445
13446 /* Create the progress object the client will use to wait until
13447 * #i_checkForDeath() is called to uninitialize this session object after
13448 * it releases the IPC semaphore.
13449 * Note! Because we're "reusing" mProgress here, this must be a proxy
13450 * object just like for LaunchVMProcess. */
13451 Assert(mData->mSession.mProgress.isNull());
13452 ComObjPtr<ProgressProxy> progress;
13453 progress.createObject();
13454 ComPtr<IUnknown> pPeer(mPeer);
13455 progress->init(mParent, pPeer,
13456 Bstr(tr("Closing session")).raw(),
13457 FALSE /* aCancelable */);
13458 progress.queryInterfaceTo(aProgress.asOutParam());
13459 mData->mSession.mProgress = progress;
13460 }
13461 else
13462 {
13463 /* the remote session is being normally closed */
13464 bool found = false;
13465 for (Data::Session::RemoteControlList::iterator
13466 it = mData->mSession.mRemoteControls.begin();
13467 it != mData->mSession.mRemoteControls.end();
13468 ++it)
13469 {
13470 if (control == *it)
13471 {
13472 found = true;
13473 // This MUST be erase(it), not remove(*it) as the latter
13474 // triggers a very nasty use after free due to the place where
13475 // the value "lives".
13476 mData->mSession.mRemoteControls.erase(it);
13477 break;
13478 }
13479 }
13480 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13481 E_INVALIDARG);
13482 }
13483
13484 /* signal the client watcher thread, because the client is going away */
13485 mParent->i_updateClientWatcher();
13486
13487 LogFlowThisFuncLeave();
13488 return S_OK;
13489}
13490
13491HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13492 std::vector<com::Utf8Str> &aValues,
13493 std::vector<LONG64> &aTimestamps,
13494 std::vector<com::Utf8Str> &aFlags)
13495{
13496 LogFlowThisFunc(("\n"));
13497
13498#ifdef VBOX_WITH_GUEST_PROPS
13499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13500
13501 size_t cEntries = mHWData->mGuestProperties.size();
13502 aNames.resize(cEntries);
13503 aValues.resize(cEntries);
13504 aTimestamps.resize(cEntries);
13505 aFlags.resize(cEntries);
13506
13507 size_t i = 0;
13508 for (HWData::GuestPropertyMap::const_iterator
13509 it = mHWData->mGuestProperties.begin();
13510 it != mHWData->mGuestProperties.end();
13511 ++it, ++i)
13512 {
13513 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13514 aNames[i] = it->first;
13515 aValues[i] = it->second.strValue;
13516 aTimestamps[i] = it->second.mTimestamp;
13517
13518 /* If it is NULL, keep it NULL. */
13519 if (it->second.mFlags)
13520 {
13521 GuestPropWriteFlags(it->second.mFlags, szFlags);
13522 aFlags[i] = szFlags;
13523 }
13524 else
13525 aFlags[i] = "";
13526 }
13527 return S_OK;
13528#else
13529 ReturnComNotImplemented();
13530#endif
13531}
13532
13533HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13534 const com::Utf8Str &aValue,
13535 LONG64 aTimestamp,
13536 const com::Utf8Str &aFlags)
13537{
13538 LogFlowThisFunc(("\n"));
13539
13540#ifdef VBOX_WITH_GUEST_PROPS
13541 try
13542 {
13543 /*
13544 * Convert input up front.
13545 */
13546 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13547 if (aFlags.length())
13548 {
13549 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13550 AssertRCReturn(vrc, E_INVALIDARG);
13551 }
13552
13553 /*
13554 * Now grab the object lock, validate the state and do the update.
13555 */
13556
13557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13558
13559 if (!Global::IsOnline(mData->mMachineState))
13560 {
13561 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13562 VBOX_E_INVALID_VM_STATE);
13563 }
13564
13565 i_setModified(IsModified_MachineData);
13566 mHWData.backup();
13567
13568 bool fDelete = !aValue.length();
13569 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13570 if (it != mHWData->mGuestProperties.end())
13571 {
13572 if (!fDelete)
13573 {
13574 it->second.strValue = aValue;
13575 it->second.mTimestamp = aTimestamp;
13576 it->second.mFlags = fFlags;
13577 }
13578 else
13579 mHWData->mGuestProperties.erase(it);
13580
13581 mData->mGuestPropertiesModified = TRUE;
13582 }
13583 else if (!fDelete)
13584 {
13585 HWData::GuestProperty prop;
13586 prop.strValue = aValue;
13587 prop.mTimestamp = aTimestamp;
13588 prop.mFlags = fFlags;
13589
13590 mHWData->mGuestProperties[aName] = prop;
13591 mData->mGuestPropertiesModified = TRUE;
13592 }
13593
13594 alock.release();
13595
13596 mParent->i_onGuestPropertyChange(mData->mUuid,
13597 Bstr(aName).raw(),
13598 Bstr(aValue).raw(),
13599 Bstr(aFlags).raw());
13600 }
13601 catch (...)
13602 {
13603 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13604 }
13605 return S_OK;
13606#else
13607 ReturnComNotImplemented();
13608#endif
13609}
13610
13611
13612HRESULT SessionMachine::lockMedia()
13613{
13614 AutoMultiWriteLock2 alock(this->lockHandle(),
13615 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13616
13617 AssertReturn( mData->mMachineState == MachineState_Starting
13618 || mData->mMachineState == MachineState_Restoring
13619 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13620
13621 clearError();
13622 alock.release();
13623 return i_lockMedia();
13624}
13625
13626HRESULT SessionMachine::unlockMedia()
13627{
13628 HRESULT hrc = i_unlockMedia();
13629 return hrc;
13630}
13631
13632HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13633 ComPtr<IMediumAttachment> &aNewAttachment)
13634{
13635 // request the host lock first, since might be calling Host methods for getting host drives;
13636 // next, protect the media tree all the while we're in here, as well as our member variables
13637 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13638 this->lockHandle(),
13639 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13640
13641 IMediumAttachment *iAttach = aAttachment;
13642 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13643
13644 Utf8Str ctrlName;
13645 LONG lPort;
13646 LONG lDevice;
13647 bool fTempEject;
13648 {
13649 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13650
13651 /* Need to query the details first, as the IMediumAttachment reference
13652 * might be to the original settings, which we are going to change. */
13653 ctrlName = pAttach->i_getControllerName();
13654 lPort = pAttach->i_getPort();
13655 lDevice = pAttach->i_getDevice();
13656 fTempEject = pAttach->i_getTempEject();
13657 }
13658
13659 if (!fTempEject)
13660 {
13661 /* Remember previously mounted medium. The medium before taking the
13662 * backup is not necessarily the same thing. */
13663 ComObjPtr<Medium> oldmedium;
13664 oldmedium = pAttach->i_getMedium();
13665
13666 i_setModified(IsModified_Storage);
13667 mMediumAttachments.backup();
13668
13669 // The backup operation makes the pAttach reference point to the
13670 // old settings. Re-get the correct reference.
13671 pAttach = i_findAttachment(*mMediumAttachments.data(),
13672 ctrlName,
13673 lPort,
13674 lDevice);
13675
13676 {
13677 AutoCaller autoAttachCaller(this);
13678 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13679
13680 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13681 if (!oldmedium.isNull())
13682 oldmedium->i_removeBackReference(mData->mUuid);
13683
13684 pAttach->i_updateMedium(NULL);
13685 pAttach->i_updateEjected();
13686 }
13687
13688 i_setModified(IsModified_Storage);
13689 }
13690 else
13691 {
13692 {
13693 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13694 pAttach->i_updateEjected();
13695 }
13696 }
13697
13698 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13699
13700 return S_OK;
13701}
13702
13703HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13704 com::Utf8Str &aResult)
13705{
13706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13707
13708 HRESULT hr = S_OK;
13709
13710 if (!mAuthLibCtx.hAuthLibrary)
13711 {
13712 /* Load the external authentication library. */
13713 Bstr authLibrary;
13714 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13715
13716 Utf8Str filename = authLibrary;
13717
13718 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13719 if (RT_FAILURE(vrc))
13720 hr = setErrorBoth(E_FAIL, vrc,
13721 tr("Could not load the external authentication library '%s' (%Rrc)"),
13722 filename.c_str(), vrc);
13723 }
13724
13725 /* The auth library might need the machine lock. */
13726 alock.release();
13727
13728 if (FAILED(hr))
13729 return hr;
13730
13731 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13732 {
13733 enum VRDEAuthParams
13734 {
13735 parmUuid = 1,
13736 parmGuestJudgement,
13737 parmUser,
13738 parmPassword,
13739 parmDomain,
13740 parmClientId
13741 };
13742
13743 AuthResult result = AuthResultAccessDenied;
13744
13745 Guid uuid(aAuthParams[parmUuid]);
13746 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13747 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13748
13749 result = AuthLibAuthenticate(&mAuthLibCtx,
13750 uuid.raw(), guestJudgement,
13751 aAuthParams[parmUser].c_str(),
13752 aAuthParams[parmPassword].c_str(),
13753 aAuthParams[parmDomain].c_str(),
13754 u32ClientId);
13755
13756 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13757 size_t cbPassword = aAuthParams[parmPassword].length();
13758 if (cbPassword)
13759 {
13760 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13761 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13762 }
13763
13764 if (result == AuthResultAccessGranted)
13765 aResult = "granted";
13766 else
13767 aResult = "denied";
13768
13769 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13770 aAuthParams[parmUser].c_str(), aResult.c_str()));
13771 }
13772 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13773 {
13774 enum VRDEAuthDisconnectParams
13775 {
13776 parmUuid = 1,
13777 parmClientId
13778 };
13779
13780 Guid uuid(aAuthParams[parmUuid]);
13781 uint32_t u32ClientId = 0;
13782 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13783 }
13784 else
13785 {
13786 hr = E_INVALIDARG;
13787 }
13788
13789 return hr;
13790}
13791
13792// public methods only for internal purposes
13793/////////////////////////////////////////////////////////////////////////////
13794
13795#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13796/**
13797 * Called from the client watcher thread to check for expected or unexpected
13798 * death of the client process that has a direct session to this machine.
13799 *
13800 * On Win32 and on OS/2, this method is called only when we've got the
13801 * mutex (i.e. the client has either died or terminated normally) so it always
13802 * returns @c true (the client is terminated, the session machine is
13803 * uninitialized).
13804 *
13805 * On other platforms, the method returns @c true if the client process has
13806 * terminated normally or abnormally and the session machine was uninitialized,
13807 * and @c false if the client process is still alive.
13808 *
13809 * @note Locks this object for writing.
13810 */
13811bool SessionMachine::i_checkForDeath()
13812{
13813 Uninit::Reason reason;
13814 bool terminated = false;
13815
13816 /* Enclose autoCaller with a block because calling uninit() from under it
13817 * will deadlock. */
13818 {
13819 AutoCaller autoCaller(this);
13820 if (!autoCaller.isOk())
13821 {
13822 /* return true if not ready, to cause the client watcher to exclude
13823 * the corresponding session from watching */
13824 LogFlowThisFunc(("Already uninitialized!\n"));
13825 return true;
13826 }
13827
13828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13829
13830 /* Determine the reason of death: if the session state is Closing here,
13831 * everything is fine. Otherwise it means that the client did not call
13832 * OnSessionEnd() before it released the IPC semaphore. This may happen
13833 * either because the client process has abnormally terminated, or
13834 * because it simply forgot to call ISession::Close() before exiting. We
13835 * threat the latter also as an abnormal termination (see
13836 * Session::uninit() for details). */
13837 reason = mData->mSession.mState == SessionState_Unlocking ?
13838 Uninit::Normal :
13839 Uninit::Abnormal;
13840
13841 if (mClientToken)
13842 terminated = mClientToken->release();
13843 } /* AutoCaller block */
13844
13845 if (terminated)
13846 uninit(reason);
13847
13848 return terminated;
13849}
13850
13851void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 strTokenId.setNull();
13856
13857 AutoCaller autoCaller(this);
13858 AssertComRCReturnVoid(autoCaller.rc());
13859
13860 Assert(mClientToken);
13861 if (mClientToken)
13862 mClientToken->getId(strTokenId);
13863}
13864#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13865IToken *SessionMachine::i_getToken()
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), NULL);
13871
13872 Assert(mClientToken);
13873 if (mClientToken)
13874 return mClientToken->getToken();
13875 else
13876 return NULL;
13877}
13878#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13879
13880Machine::ClientToken *SessionMachine::i_getClientToken()
13881{
13882 LogFlowThisFunc(("\n"));
13883
13884 AutoCaller autoCaller(this);
13885 AssertComRCReturn(autoCaller.rc(), NULL);
13886
13887 return mClientToken;
13888}
13889
13890
13891/**
13892 * @note Locks this object for reading.
13893 */
13894HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 if (mData->mSession.mLockType == LockType_VM)
13905 directControl = mData->mSession.mDirectControl;
13906 }
13907
13908 /* ignore notifications sent after #OnSessionEnd() is called */
13909 if (!directControl)
13910 return S_OK;
13911
13912 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13913}
13914
13915/**
13916 * @note Locks this object for reading.
13917 */
13918HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13919 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13920 IN_BSTR aGuestIp, LONG aGuestPort)
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 AutoCaller autoCaller(this);
13925 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13926
13927 ComPtr<IInternalSessionControl> directControl;
13928 {
13929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13930 if (mData->mSession.mLockType == LockType_VM)
13931 directControl = mData->mSession.mDirectControl;
13932 }
13933
13934 /* ignore notifications sent after #OnSessionEnd() is called */
13935 if (!directControl)
13936 return S_OK;
13937 /*
13938 * instead acting like callback we ask IVirtualBox deliver corresponding event
13939 */
13940
13941 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13942 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13943 return S_OK;
13944}
13945
13946/**
13947 * @note Locks this object for reading.
13948 */
13949HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13950{
13951 LogFlowThisFunc(("\n"));
13952
13953 AutoCaller autoCaller(this);
13954 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13955
13956 ComPtr<IInternalSessionControl> directControl;
13957 {
13958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13959 if (mData->mSession.mLockType == LockType_VM)
13960 directControl = mData->mSession.mDirectControl;
13961 }
13962
13963 /* ignore notifications sent after #OnSessionEnd() is called */
13964 if (!directControl)
13965 return S_OK;
13966
13967 return directControl->OnAudioAdapterChange(audioAdapter);
13968}
13969
13970/**
13971 * @note Locks this object for reading.
13972 */
13973HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13974{
13975 LogFlowThisFunc(("\n"));
13976
13977 AutoCaller autoCaller(this);
13978 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13979
13980 ComPtr<IInternalSessionControl> directControl;
13981 {
13982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13983 if (mData->mSession.mLockType == LockType_VM)
13984 directControl = mData->mSession.mDirectControl;
13985 }
13986
13987 /* ignore notifications sent after #OnSessionEnd() is called */
13988 if (!directControl)
13989 return S_OK;
13990
13991 return directControl->OnSerialPortChange(serialPort);
13992}
13993
13994/**
13995 * @note Locks this object for reading.
13996 */
13997HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13998{
13999 LogFlowThisFunc(("\n"));
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14003
14004 ComPtr<IInternalSessionControl> directControl;
14005 {
14006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14007 if (mData->mSession.mLockType == LockType_VM)
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 /* ignore notifications sent after #OnSessionEnd() is called */
14012 if (!directControl)
14013 return S_OK;
14014
14015 return directControl->OnParallelPortChange(parallelPort);
14016}
14017
14018/**
14019 * @note Locks this object for reading.
14020 */
14021HRESULT SessionMachine::i_onStorageControllerChange()
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 if (mData->mSession.mLockType == LockType_VM)
14032 directControl = mData->mSession.mDirectControl;
14033 }
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnStorageControllerChange();
14040}
14041
14042/**
14043 * @note Locks this object for reading.
14044 */
14045HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 mParent->i_onMediumChanged(aAttachment);
14060
14061 /* ignore notifications sent after #OnSessionEnd() is called */
14062 if (!directControl)
14063 return S_OK;
14064
14065 return directControl->OnMediumChange(aAttachment, aForce);
14066}
14067
14068/**
14069 * @note Locks this object for reading.
14070 */
14071HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14072{
14073 LogFlowThisFunc(("\n"));
14074
14075 AutoCaller autoCaller(this);
14076 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14077
14078 ComPtr<IInternalSessionControl> directControl;
14079 {
14080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14081 if (mData->mSession.mLockType == LockType_VM)
14082 directControl = mData->mSession.mDirectControl;
14083 }
14084
14085 /* ignore notifications sent after #OnSessionEnd() is called */
14086 if (!directControl)
14087 return S_OK;
14088
14089 return directControl->OnCPUChange(aCPU, aRemove);
14090}
14091
14092HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14093{
14094 LogFlowThisFunc(("\n"));
14095
14096 AutoCaller autoCaller(this);
14097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14098
14099 ComPtr<IInternalSessionControl> directControl;
14100 {
14101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14102 if (mData->mSession.mLockType == LockType_VM)
14103 directControl = mData->mSession.mDirectControl;
14104 }
14105
14106 /* ignore notifications sent after #OnSessionEnd() is called */
14107 if (!directControl)
14108 return S_OK;
14109
14110 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14111}
14112
14113/**
14114 * @note Locks this object for reading.
14115 */
14116HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14117{
14118 LogFlowThisFunc(("\n"));
14119
14120 AutoCaller autoCaller(this);
14121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14122
14123 ComPtr<IInternalSessionControl> directControl;
14124 {
14125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14126 if (mData->mSession.mLockType == LockType_VM)
14127 directControl = mData->mSession.mDirectControl;
14128 }
14129
14130 /* ignore notifications sent after #OnSessionEnd() is called */
14131 if (!directControl)
14132 return S_OK;
14133
14134 return directControl->OnVRDEServerChange(aRestart);
14135}
14136
14137/**
14138 * @note Locks this object for reading.
14139 */
14140HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14141{
14142 LogFlowThisFunc(("\n"));
14143
14144 AutoCaller autoCaller(this);
14145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14146
14147 ComPtr<IInternalSessionControl> directControl;
14148 {
14149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14150 if (mData->mSession.mLockType == LockType_VM)
14151 directControl = mData->mSession.mDirectControl;
14152 }
14153
14154 /* ignore notifications sent after #OnSessionEnd() is called */
14155 if (!directControl)
14156 return S_OK;
14157
14158 return directControl->OnRecordingChange(aEnable);
14159}
14160
14161/**
14162 * @note Locks this object for reading.
14163 */
14164HRESULT SessionMachine::i_onUSBControllerChange()
14165{
14166 LogFlowThisFunc(("\n"));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 if (mData->mSession.mLockType == LockType_VM)
14175 directControl = mData->mSession.mDirectControl;
14176 }
14177
14178 /* ignore notifications sent after #OnSessionEnd() is called */
14179 if (!directControl)
14180 return S_OK;
14181
14182 return directControl->OnUSBControllerChange();
14183}
14184
14185/**
14186 * @note Locks this object for reading.
14187 */
14188HRESULT SessionMachine::i_onSharedFolderChange()
14189{
14190 LogFlowThisFunc(("\n"));
14191
14192 AutoCaller autoCaller(this);
14193 AssertComRCReturnRC(autoCaller.rc());
14194
14195 ComPtr<IInternalSessionControl> directControl;
14196 {
14197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14198 if (mData->mSession.mLockType == LockType_VM)
14199 directControl = mData->mSession.mDirectControl;
14200 }
14201
14202 /* ignore notifications sent after #OnSessionEnd() is called */
14203 if (!directControl)
14204 return S_OK;
14205
14206 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14207}
14208
14209/**
14210 * @note Locks this object for reading.
14211 */
14212HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14213{
14214 LogFlowThisFunc(("\n"));
14215
14216 AutoCaller autoCaller(this);
14217 AssertComRCReturnRC(autoCaller.rc());
14218
14219 ComPtr<IInternalSessionControl> directControl;
14220 {
14221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14222 if (mData->mSession.mLockType == LockType_VM)
14223 directControl = mData->mSession.mDirectControl;
14224 }
14225
14226 /* ignore notifications sent after #OnSessionEnd() is called */
14227 if (!directControl)
14228 return S_OK;
14229
14230 return directControl->OnClipboardModeChange(aClipboardMode);
14231}
14232
14233/**
14234 * @note Locks this object for reading.
14235 */
14236HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14237{
14238 LogFlowThisFunc(("\n"));
14239
14240 AutoCaller autoCaller(this);
14241 AssertComRCReturnRC(autoCaller.rc());
14242
14243 ComPtr<IInternalSessionControl> directControl;
14244 {
14245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14246 if (mData->mSession.mLockType == LockType_VM)
14247 directControl = mData->mSession.mDirectControl;
14248 }
14249
14250 /* ignore notifications sent after #OnSessionEnd() is called */
14251 if (!directControl)
14252 return S_OK;
14253
14254 return directControl->OnDnDModeChange(aDnDMode);
14255}
14256
14257/**
14258 * @note Locks this object for reading.
14259 */
14260HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14261{
14262 LogFlowThisFunc(("\n"));
14263
14264 AutoCaller autoCaller(this);
14265 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14266
14267 ComPtr<IInternalSessionControl> directControl;
14268 {
14269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14270 if (mData->mSession.mLockType == LockType_VM)
14271 directControl = mData->mSession.mDirectControl;
14272 }
14273
14274 /* ignore notifications sent after #OnSessionEnd() is called */
14275 if (!directControl)
14276 return S_OK;
14277
14278 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14279}
14280
14281/**
14282 * @note Locks this object for reading.
14283 */
14284HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14285{
14286 LogFlowThisFunc(("\n"));
14287
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14290
14291 ComPtr<IInternalSessionControl> directControl;
14292 {
14293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14294 if (mData->mSession.mLockType == LockType_VM)
14295 directControl = mData->mSession.mDirectControl;
14296 }
14297
14298 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14305}
14306
14307/**
14308 * Returns @c true if this machine's USB controller reports it has a matching
14309 * filter for the given USB device and @c false otherwise.
14310 *
14311 * @note locks this object for reading.
14312 */
14313bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14314{
14315 AutoCaller autoCaller(this);
14316 /* silently return if not ready -- this method may be called after the
14317 * direct machine session has been called */
14318 if (!autoCaller.isOk())
14319 return false;
14320
14321#ifdef VBOX_WITH_USB
14322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14323
14324 switch (mData->mMachineState)
14325 {
14326 case MachineState_Starting:
14327 case MachineState_Restoring:
14328 case MachineState_TeleportingIn:
14329 case MachineState_Paused:
14330 case MachineState_Running:
14331 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14332 * elsewhere... */
14333 alock.release();
14334 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14335 default: break;
14336 }
14337#else
14338 NOREF(aDevice);
14339 NOREF(aMaskedIfs);
14340#endif
14341 return false;
14342}
14343
14344/**
14345 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14346 */
14347HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14348 IVirtualBoxErrorInfo *aError,
14349 ULONG aMaskedIfs,
14350 const com::Utf8Str &aCaptureFilename)
14351{
14352 LogFlowThisFunc(("\n"));
14353
14354 AutoCaller autoCaller(this);
14355
14356 /* This notification may happen after the machine object has been
14357 * uninitialized (the session was closed), so don't assert. */
14358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14359
14360 ComPtr<IInternalSessionControl> directControl;
14361 {
14362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14363 if (mData->mSession.mLockType == LockType_VM)
14364 directControl = mData->mSession.mDirectControl;
14365 }
14366
14367 /* fail on notifications sent after #OnSessionEnd() is called, it is
14368 * expected by the caller */
14369 if (!directControl)
14370 return E_FAIL;
14371
14372 /* No locks should be held at this point. */
14373 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14374 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14375
14376 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14377}
14378
14379/**
14380 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14381 */
14382HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14383 IVirtualBoxErrorInfo *aError)
14384{
14385 LogFlowThisFunc(("\n"));
14386
14387 AutoCaller autoCaller(this);
14388
14389 /* This notification may happen after the machine object has been
14390 * uninitialized (the session was closed), so don't assert. */
14391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14392
14393 ComPtr<IInternalSessionControl> directControl;
14394 {
14395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14396 if (mData->mSession.mLockType == LockType_VM)
14397 directControl = mData->mSession.mDirectControl;
14398 }
14399
14400 /* fail on notifications sent after #OnSessionEnd() is called, it is
14401 * expected by the caller */
14402 if (!directControl)
14403 return E_FAIL;
14404
14405 /* No locks should be held at this point. */
14406 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14407 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14408
14409 return directControl->OnUSBDeviceDetach(aId, aError);
14410}
14411
14412// protected methods
14413/////////////////////////////////////////////////////////////////////////////
14414
14415/**
14416 * Deletes the given file if it is no longer in use by either the current machine state
14417 * (if the machine is "saved") or any of the machine's snapshots.
14418 *
14419 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14420 * but is different for each SnapshotMachine. When calling this, the order of calling this
14421 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14422 * is therefore critical. I know, it's all rather messy.
14423 *
14424 * @param strStateFile
14425 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14426 * the test for whether the saved state file is in use.
14427 */
14428void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14429 Snapshot *pSnapshotToIgnore)
14430{
14431 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14432 if ( (strStateFile.isNotEmpty())
14433 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14434 )
14435 // ... and it must also not be shared with other snapshots
14436 if ( !mData->mFirstSnapshot
14437 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14438 // this checks the SnapshotMachine's state file paths
14439 )
14440 RTFileDelete(strStateFile.c_str());
14441}
14442
14443/**
14444 * Locks the attached media.
14445 *
14446 * All attached hard disks are locked for writing and DVD/floppy are locked for
14447 * reading. Parents of attached hard disks (if any) are locked for reading.
14448 *
14449 * This method also performs accessibility check of all media it locks: if some
14450 * media is inaccessible, the method will return a failure and a bunch of
14451 * extended error info objects per each inaccessible medium.
14452 *
14453 * Note that this method is atomic: if it returns a success, all media are
14454 * locked as described above; on failure no media is locked at all (all
14455 * succeeded individual locks will be undone).
14456 *
14457 * The caller is responsible for doing the necessary state sanity checks.
14458 *
14459 * The locks made by this method must be undone by calling #unlockMedia() when
14460 * no more needed.
14461 */
14462HRESULT SessionMachine::i_lockMedia()
14463{
14464 AutoCaller autoCaller(this);
14465 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14466
14467 AutoMultiWriteLock2 alock(this->lockHandle(),
14468 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14469
14470 /* bail out if trying to lock things with already set up locking */
14471 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14472
14473 MultiResult mrc(S_OK);
14474
14475 /* Collect locking information for all medium objects attached to the VM. */
14476 for (MediumAttachmentList::const_iterator
14477 it = mMediumAttachments->begin();
14478 it != mMediumAttachments->end();
14479 ++it)
14480 {
14481 MediumAttachment *pAtt = *it;
14482 DeviceType_T devType = pAtt->i_getType();
14483 Medium *pMedium = pAtt->i_getMedium();
14484
14485 MediumLockList *pMediumLockList(new MediumLockList());
14486 // There can be attachments without a medium (floppy/dvd), and thus
14487 // it's impossible to create a medium lock list. It still makes sense
14488 // to have the empty medium lock list in the map in case a medium is
14489 // attached later.
14490 if (pMedium != NULL)
14491 {
14492 MediumType_T mediumType = pMedium->i_getType();
14493 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14494 || mediumType == MediumType_Shareable;
14495 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14496
14497 alock.release();
14498 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14499 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14500 false /* fMediumLockWriteAll */,
14501 NULL,
14502 *pMediumLockList);
14503 alock.acquire();
14504 if (FAILED(mrc))
14505 {
14506 delete pMediumLockList;
14507 mData->mSession.mLockedMedia.Clear();
14508 break;
14509 }
14510 }
14511
14512 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14513 if (FAILED(rc))
14514 {
14515 mData->mSession.mLockedMedia.Clear();
14516 mrc = setError(rc,
14517 tr("Collecting locking information for all attached media failed"));
14518 break;
14519 }
14520 }
14521
14522 if (SUCCEEDED(mrc))
14523 {
14524 /* Now lock all media. If this fails, nothing is locked. */
14525 alock.release();
14526 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14527 alock.acquire();
14528 if (FAILED(rc))
14529 {
14530 mrc = setError(rc,
14531 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14532 }
14533 }
14534
14535 return mrc;
14536}
14537
14538/**
14539 * Undoes the locks made by by #lockMedia().
14540 */
14541HRESULT SessionMachine::i_unlockMedia()
14542{
14543 AutoCaller autoCaller(this);
14544 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14545
14546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14547
14548 /* we may be holding important error info on the current thread;
14549 * preserve it */
14550 ErrorInfoKeeper eik;
14551
14552 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14553 AssertComRC(rc);
14554 return rc;
14555}
14556
14557/**
14558 * Helper to change the machine state (reimplementation).
14559 *
14560 * @note Locks this object for writing.
14561 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14562 * it can cause crashes in random places due to unexpectedly committing
14563 * the current settings. The caller is responsible for that. The call
14564 * to saveStateSettings is fine, because this method does not commit.
14565 */
14566HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14567{
14568 LogFlowThisFuncEnter();
14569 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14570
14571 AutoCaller autoCaller(this);
14572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14573
14574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14575
14576 MachineState_T oldMachineState = mData->mMachineState;
14577
14578 AssertMsgReturn(oldMachineState != aMachineState,
14579 ("oldMachineState=%s, aMachineState=%s\n",
14580 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14581 E_FAIL);
14582
14583 HRESULT rc = S_OK;
14584
14585 int stsFlags = 0;
14586 bool deleteSavedState = false;
14587
14588 /* detect some state transitions */
14589
14590 if ( ( oldMachineState == MachineState_Saved
14591 && aMachineState == MachineState_Restoring)
14592 || ( ( oldMachineState == MachineState_PoweredOff
14593 || oldMachineState == MachineState_Teleported
14594 || oldMachineState == MachineState_Aborted
14595 )
14596 && ( aMachineState == MachineState_TeleportingIn
14597 || aMachineState == MachineState_Starting
14598 )
14599 )
14600 )
14601 {
14602 /* The EMT thread is about to start */
14603
14604 /* Nothing to do here for now... */
14605
14606 /// @todo NEWMEDIA don't let mDVDDrive and other children
14607 /// change anything when in the Starting/Restoring state
14608 }
14609 else if ( ( oldMachineState == MachineState_Running
14610 || oldMachineState == MachineState_Paused
14611 || oldMachineState == MachineState_Teleporting
14612 || oldMachineState == MachineState_OnlineSnapshotting
14613 || oldMachineState == MachineState_LiveSnapshotting
14614 || oldMachineState == MachineState_Stuck
14615 || oldMachineState == MachineState_Starting
14616 || oldMachineState == MachineState_Stopping
14617 || oldMachineState == MachineState_Saving
14618 || oldMachineState == MachineState_Restoring
14619 || oldMachineState == MachineState_TeleportingPausedVM
14620 || oldMachineState == MachineState_TeleportingIn
14621 )
14622 && ( aMachineState == MachineState_PoweredOff
14623 || aMachineState == MachineState_Saved
14624 || aMachineState == MachineState_Teleported
14625 || aMachineState == MachineState_Aborted
14626 )
14627 )
14628 {
14629 /* The EMT thread has just stopped, unlock attached media. Note that as
14630 * opposed to locking that is done from Console, we do unlocking here
14631 * because the VM process may have aborted before having a chance to
14632 * properly unlock all media it locked. */
14633
14634 unlockMedia();
14635 }
14636
14637 if (oldMachineState == MachineState_Restoring)
14638 {
14639 if (aMachineState != MachineState_Saved)
14640 {
14641 /*
14642 * delete the saved state file once the machine has finished
14643 * restoring from it (note that Console sets the state from
14644 * Restoring to Saved if the VM couldn't restore successfully,
14645 * to give the user an ability to fix an error and retry --
14646 * we keep the saved state file in this case)
14647 */
14648 deleteSavedState = true;
14649 }
14650 }
14651 else if ( oldMachineState == MachineState_Saved
14652 && ( aMachineState == MachineState_PoweredOff
14653 || aMachineState == MachineState_Aborted
14654 || aMachineState == MachineState_Teleported
14655 )
14656 )
14657 {
14658 /*
14659 * delete the saved state after SessionMachine::ForgetSavedState() is called
14660 * or if the VM process (owning a direct VM session) crashed while the
14661 * VM was Saved
14662 */
14663
14664 /// @todo (dmik)
14665 // Not sure that deleting the saved state file just because of the
14666 // client death before it attempted to restore the VM is a good
14667 // thing. But when it crashes we need to go to the Aborted state
14668 // which cannot have the saved state file associated... The only
14669 // way to fix this is to make the Aborted condition not a VM state
14670 // but a bool flag: i.e., when a crash occurs, set it to true and
14671 // change the state to PoweredOff or Saved depending on the
14672 // saved state presence.
14673
14674 deleteSavedState = true;
14675 mData->mCurrentStateModified = TRUE;
14676 stsFlags |= SaveSTS_CurStateModified;
14677 }
14678
14679 if ( aMachineState == MachineState_Starting
14680 || aMachineState == MachineState_Restoring
14681 || aMachineState == MachineState_TeleportingIn
14682 )
14683 {
14684 /* set the current state modified flag to indicate that the current
14685 * state is no more identical to the state in the
14686 * current snapshot */
14687 if (!mData->mCurrentSnapshot.isNull())
14688 {
14689 mData->mCurrentStateModified = TRUE;
14690 stsFlags |= SaveSTS_CurStateModified;
14691 }
14692 }
14693
14694 if (deleteSavedState)
14695 {
14696 if (mRemoveSavedState)
14697 {
14698 Assert(!mSSData->strStateFilePath.isEmpty());
14699
14700 // it is safe to delete the saved state file if ...
14701 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14702 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14703 // ... none of the snapshots share the saved state file
14704 )
14705 RTFileDelete(mSSData->strStateFilePath.c_str());
14706 }
14707
14708 mSSData->strStateFilePath.setNull();
14709 stsFlags |= SaveSTS_StateFilePath;
14710 }
14711
14712 /* redirect to the underlying peer machine */
14713 mPeer->i_setMachineState(aMachineState);
14714
14715 if ( oldMachineState != MachineState_RestoringSnapshot
14716 && ( aMachineState == MachineState_PoweredOff
14717 || aMachineState == MachineState_Teleported
14718 || aMachineState == MachineState_Aborted
14719 || aMachineState == MachineState_Saved))
14720 {
14721 /* the machine has stopped execution
14722 * (or the saved state file was adopted) */
14723 stsFlags |= SaveSTS_StateTimeStamp;
14724 }
14725
14726 if ( ( oldMachineState == MachineState_PoweredOff
14727 || oldMachineState == MachineState_Aborted
14728 || oldMachineState == MachineState_Teleported
14729 )
14730 && aMachineState == MachineState_Saved)
14731 {
14732 /* the saved state file was adopted */
14733 Assert(!mSSData->strStateFilePath.isEmpty());
14734 stsFlags |= SaveSTS_StateFilePath;
14735 }
14736
14737#ifdef VBOX_WITH_GUEST_PROPS
14738 if ( aMachineState == MachineState_PoweredOff
14739 || aMachineState == MachineState_Aborted
14740 || aMachineState == MachineState_Teleported)
14741 {
14742 /* Make sure any transient guest properties get removed from the
14743 * property store on shutdown. */
14744 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14745
14746 /* remove it from the settings representation */
14747 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14748 for (settings::GuestPropertiesList::iterator
14749 it = llGuestProperties.begin();
14750 it != llGuestProperties.end();
14751 /*nothing*/)
14752 {
14753 const settings::GuestProperty &prop = *it;
14754 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14755 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14756 {
14757 it = llGuestProperties.erase(it);
14758 fNeedsSaving = true;
14759 }
14760 else
14761 {
14762 ++it;
14763 }
14764 }
14765
14766 /* Additionally remove it from the HWData representation. Required to
14767 * keep everything in sync, as this is what the API keeps using. */
14768 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14769 for (HWData::GuestPropertyMap::iterator
14770 it = llHWGuestProperties.begin();
14771 it != llHWGuestProperties.end();
14772 /*nothing*/)
14773 {
14774 uint32_t fFlags = it->second.mFlags;
14775 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14776 {
14777 /* iterator where we need to continue after the erase call
14778 * (C++03 is a fact still, and it doesn't return the iterator
14779 * which would allow continuing) */
14780 HWData::GuestPropertyMap::iterator it2 = it;
14781 ++it2;
14782 llHWGuestProperties.erase(it);
14783 it = it2;
14784 fNeedsSaving = true;
14785 }
14786 else
14787 {
14788 ++it;
14789 }
14790 }
14791
14792 if (fNeedsSaving)
14793 {
14794 mData->mCurrentStateModified = TRUE;
14795 stsFlags |= SaveSTS_CurStateModified;
14796 }
14797 }
14798#endif /* VBOX_WITH_GUEST_PROPS */
14799
14800 rc = i_saveStateSettings(stsFlags);
14801
14802 if ( ( oldMachineState != MachineState_PoweredOff
14803 && oldMachineState != MachineState_Aborted
14804 && oldMachineState != MachineState_Teleported
14805 )
14806 && ( aMachineState == MachineState_PoweredOff
14807 || aMachineState == MachineState_Aborted
14808 || aMachineState == MachineState_Teleported
14809 )
14810 )
14811 {
14812 /* we've been shut down for any reason */
14813 /* no special action so far */
14814 }
14815
14816 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14817 LogFlowThisFuncLeave();
14818 return rc;
14819}
14820
14821/**
14822 * Sends the current machine state value to the VM process.
14823 *
14824 * @note Locks this object for reading, then calls a client process.
14825 */
14826HRESULT SessionMachine::i_updateMachineStateOnClient()
14827{
14828 AutoCaller autoCaller(this);
14829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14830
14831 ComPtr<IInternalSessionControl> directControl;
14832 {
14833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14834 AssertReturn(!!mData, E_FAIL);
14835 if (mData->mSession.mLockType == LockType_VM)
14836 directControl = mData->mSession.mDirectControl;
14837
14838 /* directControl may be already set to NULL here in #OnSessionEnd()
14839 * called too early by the direct session process while there is still
14840 * some operation (like deleting the snapshot) in progress. The client
14841 * process in this case is waiting inside Session::close() for the
14842 * "end session" process object to complete, while #uninit() called by
14843 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14844 * operation to complete. For now, we accept this inconsistent behavior
14845 * and simply do nothing here. */
14846
14847 if (mData->mSession.mState == SessionState_Unlocking)
14848 return S_OK;
14849 }
14850
14851 /* ignore notifications sent after #OnSessionEnd() is called */
14852 if (!directControl)
14853 return S_OK;
14854
14855 return directControl->UpdateMachineState(mData->mMachineState);
14856}
14857
14858
14859/*static*/
14860HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14861{
14862 va_list args;
14863 va_start(args, pcszMsg);
14864 HRESULT rc = setErrorInternal(aResultCode,
14865 getStaticClassIID(),
14866 getStaticComponentName(),
14867 Utf8Str(pcszMsg, args),
14868 false /* aWarning */,
14869 true /* aLogIt */);
14870 va_end(args);
14871 return rc;
14872}
14873
14874
14875HRESULT Machine::updateState(MachineState_T aState)
14876{
14877 NOREF(aState);
14878 ReturnComNotImplemented();
14879}
14880
14881HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14882{
14883 NOREF(aProgress);
14884 ReturnComNotImplemented();
14885}
14886
14887HRESULT Machine::endPowerUp(LONG aResult)
14888{
14889 NOREF(aResult);
14890 ReturnComNotImplemented();
14891}
14892
14893HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14894{
14895 NOREF(aProgress);
14896 ReturnComNotImplemented();
14897}
14898
14899HRESULT Machine::endPoweringDown(LONG aResult,
14900 const com::Utf8Str &aErrMsg)
14901{
14902 NOREF(aResult);
14903 NOREF(aErrMsg);
14904 ReturnComNotImplemented();
14905}
14906
14907HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14908 BOOL *aMatched,
14909 ULONG *aMaskedInterfaces)
14910{
14911 NOREF(aDevice);
14912 NOREF(aMatched);
14913 NOREF(aMaskedInterfaces);
14914 ReturnComNotImplemented();
14915
14916}
14917
14918HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14919{
14920 NOREF(aId); NOREF(aCaptureFilename);
14921 ReturnComNotImplemented();
14922}
14923
14924HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14925 BOOL aDone)
14926{
14927 NOREF(aId);
14928 NOREF(aDone);
14929 ReturnComNotImplemented();
14930}
14931
14932HRESULT Machine::autoCaptureUSBDevices()
14933{
14934 ReturnComNotImplemented();
14935}
14936
14937HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14938{
14939 NOREF(aDone);
14940 ReturnComNotImplemented();
14941}
14942
14943HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14944 ComPtr<IProgress> &aProgress)
14945{
14946 NOREF(aSession);
14947 NOREF(aProgress);
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::finishOnlineMergeMedium()
14952{
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14957 std::vector<com::Utf8Str> &aValues,
14958 std::vector<LONG64> &aTimestamps,
14959 std::vector<com::Utf8Str> &aFlags)
14960{
14961 NOREF(aNames);
14962 NOREF(aValues);
14963 NOREF(aTimestamps);
14964 NOREF(aFlags);
14965 ReturnComNotImplemented();
14966}
14967
14968HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14969 const com::Utf8Str &aValue,
14970 LONG64 aTimestamp,
14971 const com::Utf8Str &aFlags)
14972{
14973 NOREF(aName);
14974 NOREF(aValue);
14975 NOREF(aTimestamp);
14976 NOREF(aFlags);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::lockMedia()
14981{
14982 ReturnComNotImplemented();
14983}
14984
14985HRESULT Machine::unlockMedia()
14986{
14987 ReturnComNotImplemented();
14988}
14989
14990HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14991 ComPtr<IMediumAttachment> &aNewAttachment)
14992{
14993 NOREF(aAttachment);
14994 NOREF(aNewAttachment);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14999 ULONG aCpuUser,
15000 ULONG aCpuKernel,
15001 ULONG aCpuIdle,
15002 ULONG aMemTotal,
15003 ULONG aMemFree,
15004 ULONG aMemBalloon,
15005 ULONG aMemShared,
15006 ULONG aMemCache,
15007 ULONG aPagedTotal,
15008 ULONG aMemAllocTotal,
15009 ULONG aMemFreeTotal,
15010 ULONG aMemBalloonTotal,
15011 ULONG aMemSharedTotal,
15012 ULONG aVmNetRx,
15013 ULONG aVmNetTx)
15014{
15015 NOREF(aValidStats);
15016 NOREF(aCpuUser);
15017 NOREF(aCpuKernel);
15018 NOREF(aCpuIdle);
15019 NOREF(aMemTotal);
15020 NOREF(aMemFree);
15021 NOREF(aMemBalloon);
15022 NOREF(aMemShared);
15023 NOREF(aMemCache);
15024 NOREF(aPagedTotal);
15025 NOREF(aMemAllocTotal);
15026 NOREF(aMemFreeTotal);
15027 NOREF(aMemBalloonTotal);
15028 NOREF(aMemSharedTotal);
15029 NOREF(aVmNetRx);
15030 NOREF(aVmNetTx);
15031 ReturnComNotImplemented();
15032}
15033
15034HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15035 com::Utf8Str &aResult)
15036{
15037 NOREF(aAuthParams);
15038 NOREF(aResult);
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15043{
15044 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15045
15046 AutoCaller autoCaller(this);
15047 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15048
15049 HRESULT rc = S_OK;
15050
15051 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15052 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15053 rc = getUSBDeviceFilters(usbDeviceFilters);
15054 if (FAILED(rc)) return rc;
15055
15056 NOREF(aFlags);
15057 com::Utf8Str osTypeId;
15058 ComObjPtr<GuestOSType> osType = NULL;
15059
15060 /* Get the guest os type as a string from the VB. */
15061 rc = getOSTypeId(osTypeId);
15062 if (FAILED(rc)) return rc;
15063
15064 /* Get the os type obj that coresponds, can be used to get
15065 * the defaults for this guest OS. */
15066 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15067 if (FAILED(rc)) return rc;
15068
15069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15070
15071 /* Let the OS type select 64-bit ness. */
15072 mHWData->mLongMode = osType->i_is64Bit()
15073 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15074
15075 /* Apply network adapters defaults */
15076 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15077 mNetworkAdapters[slot]->i_applyDefaults(osType);
15078
15079 /* Apply serial port defaults */
15080 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15081 mSerialPorts[slot]->i_applyDefaults(osType);
15082
15083 /* Apply parallel port defaults - not OS dependent*/
15084 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15085 mParallelPorts[slot]->i_applyDefaults();
15086
15087
15088 /* Let the OS type enable the X2APIC */
15089 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15090
15091 /* This one covers IOAPICEnabled. */
15092 mBIOSSettings->i_applyDefaults(osType);
15093
15094 /* Initialize default record settings. */
15095 mRecordingSettings->i_applyDefaults();
15096
15097 /* Initialize default BIOS settings here */
15098 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15099 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15100
15101 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15102 if (FAILED(rc)) return rc;
15103
15104 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15105 if (FAILED(rc)) return rc;
15106
15107 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15108 if (FAILED(rc)) return rc;
15109
15110 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15111 if (FAILED(rc)) return rc;
15112
15113 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15114 if (FAILED(rc)) return rc;
15115
15116 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15117 if (FAILED(rc)) return rc;
15118
15119 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15120 if (FAILED(rc)) return rc;
15121
15122 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15123 if (FAILED(rc)) return rc;
15124
15125 BOOL mRTCUseUTC;
15126 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15127 if (FAILED(rc)) return rc;
15128
15129 setRTCUseUTC(mRTCUseUTC);
15130 if (FAILED(rc)) return rc;
15131
15132 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15133 if (FAILED(rc)) return rc;
15134
15135 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15136 if (FAILED(rc)) return rc;
15137
15138 /* Audio stuff. */
15139 AudioCodecType_T audioCodec;
15140 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15141 if (FAILED(rc)) return rc;
15142
15143 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15144 if (FAILED(rc)) return rc;
15145
15146 AudioControllerType_T audioController;
15147 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15148 if (FAILED(rc)) return rc;
15149
15150 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15151 if (FAILED(rc)) return rc;
15152
15153 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15154 if (FAILED(rc)) return rc;
15155
15156 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15157 if (FAILED(rc)) return rc;
15158
15159 /* Storage Controllers */
15160 StorageControllerType_T hdStorageControllerType;
15161 StorageBus_T hdStorageBusType;
15162 StorageControllerType_T dvdStorageControllerType;
15163 StorageBus_T dvdStorageBusType;
15164 BOOL recommendedFloppy;
15165 ComPtr<IStorageController> floppyController;
15166 ComPtr<IStorageController> hdController;
15167 ComPtr<IStorageController> dvdController;
15168 Utf8Str strFloppyName, strDVDName, strHDName;
15169
15170 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15171 strFloppyName = Bstr("Floppy 1").raw();
15172 strDVDName = Bstr("DVD 1").raw();
15173 strHDName = Bstr("HDD 1").raw();
15174
15175 /* Floppy recommended? add one. */
15176 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15177 if (FAILED(rc)) return rc;
15178 if (recommendedFloppy)
15179 {
15180 rc = addStorageController(strFloppyName,
15181 StorageBus_Floppy,
15182 floppyController);
15183 if (FAILED(rc)) return rc;
15184 }
15185
15186 /* Setup one DVD storage controller. */
15187 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = addStorageController(strDVDName,
15194 dvdStorageBusType,
15195 dvdController);
15196 if (FAILED(rc)) return rc;
15197
15198 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15199 if (FAILED(rc)) return rc;
15200
15201 /* Setup one HDD storage controller. */
15202 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15203 if (FAILED(rc)) return rc;
15204
15205 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15206 if (FAILED(rc)) return rc;
15207
15208 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15209 {
15210 rc = addStorageController(strHDName,
15211 hdStorageBusType,
15212 hdController);
15213 if (FAILED(rc)) return rc;
15214
15215 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15216 if (FAILED(rc)) return rc;
15217 }
15218 else
15219 {
15220 /* The HD controller is the same as DVD: */
15221 hdController = dvdController;
15222 strHDName = Bstr("DVD 1").raw();
15223 }
15224
15225 /* Limit the AHCI port count if it's used because windows has trouble with
15226 * too many ports and other guest (OS X in particular) may take extra long
15227 * boot: */
15228
15229 // pParent = static_cast<Medium*>(aP)
15230 IStorageController *temp = hdController;
15231 ComObjPtr<StorageController> storageController;
15232 storageController = static_cast<StorageController *>(temp);
15233
15234 // tempHDController = aHDController;
15235 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15236 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15237 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15238 storageController->COMSETTER(PortCount)(1);
15239
15240 /* USB stuff */
15241
15242 bool ohciEnabled = false;
15243
15244 ComPtr<IUSBController> usbController;
15245 BOOL recommendedUSB3;
15246 BOOL recommendedUSB;
15247 BOOL usbProxyAvailable;
15248
15249 getUSBProxyAvailable(&usbProxyAvailable);
15250 if (FAILED(rc)) return rc;
15251
15252 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15253 if (FAILED(rc)) return rc;
15254 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15255 if (FAILED(rc)) return rc;
15256
15257 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15258 {
15259#ifdef VBOX_WITH_EXTPACK
15260 /* USB 3.0 is only available if the proper ExtPack is installed. */
15261 ExtPackManager *aManager = mParent->i_getExtPackManager();
15262 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15263 {
15264 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15265 if (FAILED(rc)) return rc;
15266
15267 /* xHci includes OHCI */
15268 ohciEnabled = true;
15269 }
15270#endif
15271 }
15272 if ( !ohciEnabled
15273 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15274 {
15275 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15276 if (FAILED(rc)) return rc;
15277 ohciEnabled = true;
15278
15279#ifdef VBOX_WITH_EXTPACK
15280 /* USB 2.0 is only available if the proper ExtPack is installed.
15281 * Note. Configuring EHCI here and providing messages about
15282 * the missing extpack isn't exactly clean, but it is a
15283 * necessary evil to patch over legacy compatability issues
15284 * introduced by the new distribution model. */
15285 ExtPackManager *manager = mParent->i_getExtPackManager();
15286 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15287 {
15288 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15289 if (FAILED(rc)) return rc;
15290 }
15291#endif
15292 }
15293
15294 /* Set recommended human interface device types: */
15295 BOOL recommendedUSBHID;
15296 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15297 if (FAILED(rc)) return rc;
15298
15299 if (recommendedUSBHID)
15300 {
15301 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15302 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15303 if (!ohciEnabled && !usbDeviceFilters.isNull())
15304 {
15305 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15306 if (FAILED(rc)) return rc;
15307 }
15308 }
15309
15310 BOOL recommendedUSBTablet;
15311 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15312 if (FAILED(rc)) return rc;
15313
15314 if (recommendedUSBTablet)
15315 {
15316 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15317 if (!ohciEnabled && !usbDeviceFilters.isNull())
15318 {
15319 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15320 if (FAILED(rc)) return rc;
15321 }
15322 }
15323 return S_OK;
15324}
15325
15326/* This isn't handled entirely by the wrapper generator yet. */
15327#ifdef VBOX_WITH_XPCOM
15328NS_DECL_CLASSINFO(SessionMachine)
15329NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15330
15331NS_DECL_CLASSINFO(SnapshotMachine)
15332NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15333#endif
Note: See TracBrowser for help on using the repository browser.

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