VirtualBox

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

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

Recording/Main: Settings locking fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 531.9 KB
Line 
1/* $Id: MachineImpl.cpp 78072 2019-04-10 10:04:06Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 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 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 aRecordingSettings = mRecordingSettings;
1952
1953 return S_OK;
1954}
1955
1956HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1957{
1958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1959
1960 switch (aProperty)
1961 {
1962 case CPUPropertyType_PAE:
1963 *aValue = mHWData->mPAEEnabled;
1964 break;
1965
1966 case CPUPropertyType_LongMode:
1967 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1968 *aValue = TRUE;
1969 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1970 *aValue = FALSE;
1971#if HC_ARCH_BITS == 64
1972 else
1973 *aValue = TRUE;
1974#else
1975 else
1976 {
1977 *aValue = FALSE;
1978
1979 ComObjPtr<GuestOSType> pGuestOSType;
1980 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1981 pGuestOSType);
1982 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1983 {
1984 if (pGuestOSType->i_is64Bit())
1985 {
1986 ComObjPtr<Host> pHost = mParent->i_host();
1987 alock.release();
1988
1989 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1990 if (FAILED(hrc2))
1991 *aValue = FALSE;
1992 }
1993 }
1994 }
1995#endif
1996 break;
1997
1998 case CPUPropertyType_TripleFaultReset:
1999 *aValue = mHWData->mTripleFaultReset;
2000 break;
2001
2002 case CPUPropertyType_APIC:
2003 *aValue = mHWData->mAPIC;
2004 break;
2005
2006 case CPUPropertyType_X2APIC:
2007 *aValue = mHWData->mX2APIC;
2008 break;
2009
2010 case CPUPropertyType_IBPBOnVMExit:
2011 *aValue = mHWData->mIBPBOnVMExit;
2012 break;
2013
2014 case CPUPropertyType_IBPBOnVMEntry:
2015 *aValue = mHWData->mIBPBOnVMEntry;
2016 break;
2017
2018 case CPUPropertyType_SpecCtrl:
2019 *aValue = mHWData->mSpecCtrl;
2020 break;
2021
2022 case CPUPropertyType_SpecCtrlByHost:
2023 *aValue = mHWData->mSpecCtrlByHost;
2024 break;
2025
2026 case CPUPropertyType_HWVirt:
2027 *aValue = mHWData->mNestedHWVirt;
2028 break;
2029
2030 case CPUPropertyType_L1DFlushOnEMTScheduling:
2031 *aValue = mHWData->mL1DFlushOnSched;
2032 break;
2033
2034 case CPUPropertyType_L1DFlushOnVMEntry:
2035 *aValue = mHWData->mL1DFlushOnVMEntry;
2036 break;
2037
2038 default:
2039 return E_INVALIDARG;
2040 }
2041 return S_OK;
2042}
2043
2044HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2045{
2046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 HRESULT rc = i_checkStateDependency(MutableStateDep);
2049 if (FAILED(rc)) return rc;
2050
2051 switch (aProperty)
2052 {
2053 case CPUPropertyType_PAE:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mPAEEnabled = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_LongMode:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2063 break;
2064
2065 case CPUPropertyType_TripleFaultReset:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mTripleFaultReset = !!aValue;
2069 break;
2070
2071 case CPUPropertyType_APIC:
2072 if (mHWData->mX2APIC)
2073 aValue = TRUE;
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mAPIC = !!aValue;
2077 break;
2078
2079 case CPUPropertyType_X2APIC:
2080 i_setModified(IsModified_MachineData);
2081 mHWData.backup();
2082 mHWData->mX2APIC = !!aValue;
2083 if (aValue)
2084 mHWData->mAPIC = !!aValue;
2085 break;
2086
2087 case CPUPropertyType_IBPBOnVMExit:
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mIBPBOnVMExit = !!aValue;
2091 break;
2092
2093 case CPUPropertyType_IBPBOnVMEntry:
2094 i_setModified(IsModified_MachineData);
2095 mHWData.backup();
2096 mHWData->mIBPBOnVMEntry = !!aValue;
2097 break;
2098
2099 case CPUPropertyType_SpecCtrl:
2100 i_setModified(IsModified_MachineData);
2101 mHWData.backup();
2102 mHWData->mSpecCtrl = !!aValue;
2103 break;
2104
2105 case CPUPropertyType_SpecCtrlByHost:
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108 mHWData->mSpecCtrlByHost = !!aValue;
2109 break;
2110
2111 case CPUPropertyType_HWVirt:
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mNestedHWVirt = !!aValue;
2115 break;
2116
2117 case CPUPropertyType_L1DFlushOnEMTScheduling:
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mL1DFlushOnSched = !!aValue;
2121 break;
2122
2123 case CPUPropertyType_L1DFlushOnVMEntry:
2124 i_setModified(IsModified_MachineData);
2125 mHWData.backup();
2126 mHWData->mL1DFlushOnVMEntry = !!aValue;
2127 break;
2128
2129 default:
2130 return E_INVALIDARG;
2131 }
2132 return S_OK;
2133}
2134
2135HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2136 ULONG *aValEcx, ULONG *aValEdx)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2140 {
2141 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2142 it != mHWData->mCpuIdLeafList.end();
2143 ++it)
2144 {
2145 if (aOrdinal == 0)
2146 {
2147 const settings::CpuIdLeaf &rLeaf= *it;
2148 *aIdx = rLeaf.idx;
2149 *aSubIdx = rLeaf.idxSub;
2150 *aValEax = rLeaf.uEax;
2151 *aValEbx = rLeaf.uEbx;
2152 *aValEcx = rLeaf.uEcx;
2153 *aValEdx = rLeaf.uEdx;
2154 return S_OK;
2155 }
2156 aOrdinal--;
2157 }
2158 }
2159 return E_INVALIDARG;
2160}
2161
2162HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2163{
2164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2165
2166 /*
2167 * Search the list.
2168 */
2169 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2170 {
2171 const settings::CpuIdLeaf &rLeaf= *it;
2172 if ( rLeaf.idx == aIdx
2173 && ( aSubIdx == UINT32_MAX
2174 || rLeaf.idxSub == aSubIdx) )
2175 {
2176 *aValEax = rLeaf.uEax;
2177 *aValEbx = rLeaf.uEbx;
2178 *aValEcx = rLeaf.uEcx;
2179 *aValEdx = rLeaf.uEdx;
2180 return S_OK;
2181 }
2182 }
2183
2184 return E_INVALIDARG;
2185}
2186
2187
2188HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2189{
2190 /*
2191 * Validate input before taking locks and checking state.
2192 */
2193 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2194 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2195 if ( aIdx >= UINT32_C(0x20)
2196 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2197 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2198 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2199
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201 HRESULT rc = i_checkStateDependency(MutableStateDep);
2202 if (FAILED(rc)) return rc;
2203
2204 /*
2205 * Impose a maximum number of leaves.
2206 */
2207 if (mHWData->mCpuIdLeafList.size() > 256)
2208 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2209
2210 /*
2211 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2212 */
2213 i_setModified(IsModified_MachineData);
2214 mHWData.backup();
2215
2216 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2217 {
2218 settings::CpuIdLeaf &rLeaf= *it;
2219 if ( rLeaf.idx == aIdx
2220 && ( aSubIdx == UINT32_MAX
2221 || rLeaf.idxSub == aSubIdx) )
2222 it = mHWData->mCpuIdLeafList.erase(it);
2223 else
2224 ++it;
2225 }
2226
2227 settings::CpuIdLeaf NewLeaf;
2228 NewLeaf.idx = aIdx;
2229 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2230 NewLeaf.uEax = aValEax;
2231 NewLeaf.uEbx = aValEbx;
2232 NewLeaf.uEcx = aValEcx;
2233 NewLeaf.uEdx = aValEdx;
2234 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2235 return S_OK;
2236}
2237
2238HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2239{
2240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 HRESULT rc = i_checkStateDependency(MutableStateDep);
2243 if (FAILED(rc)) return rc;
2244
2245 /*
2246 * Do the removal.
2247 */
2248 bool fModified = mHWData.isBackedUp();
2249 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2250 {
2251 settings::CpuIdLeaf &rLeaf= *it;
2252 if ( rLeaf.idx == aIdx
2253 && ( aSubIdx == UINT32_MAX
2254 || rLeaf.idxSub == aSubIdx) )
2255 {
2256 if (!fModified)
2257 {
2258 fModified = true;
2259 i_setModified(IsModified_MachineData);
2260 mHWData.backup();
2261 // Start from the beginning, since mHWData.backup() creates
2262 // a new list, causing iterator mixup. This makes sure that
2263 // the settings are not unnecessarily marked as modified,
2264 // at the price of extra list walking.
2265 it = mHWData->mCpuIdLeafList.begin();
2266 }
2267 else
2268 it = mHWData->mCpuIdLeafList.erase(it);
2269 }
2270 else
2271 ++it;
2272 }
2273
2274 return S_OK;
2275}
2276
2277HRESULT Machine::removeAllCPUIDLeaves()
2278{
2279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2280
2281 HRESULT rc = i_checkStateDependency(MutableStateDep);
2282 if (FAILED(rc)) return rc;
2283
2284 if (mHWData->mCpuIdLeafList.size() > 0)
2285 {
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288
2289 mHWData->mCpuIdLeafList.clear();
2290 }
2291
2292 return S_OK;
2293}
2294HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2295{
2296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 switch(aProperty)
2299 {
2300 case HWVirtExPropertyType_Enabled:
2301 *aValue = mHWData->mHWVirtExEnabled;
2302 break;
2303
2304 case HWVirtExPropertyType_VPID:
2305 *aValue = mHWData->mHWVirtExVPIDEnabled;
2306 break;
2307
2308 case HWVirtExPropertyType_NestedPaging:
2309 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2310 break;
2311
2312 case HWVirtExPropertyType_UnrestrictedExecution:
2313 *aValue = mHWData->mHWVirtExUXEnabled;
2314 break;
2315
2316 case HWVirtExPropertyType_LargePages:
2317 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2318#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2319 *aValue = FALSE;
2320#endif
2321 break;
2322
2323 case HWVirtExPropertyType_Force:
2324 *aValue = mHWData->mHWVirtExForceEnabled;
2325 break;
2326
2327 case HWVirtExPropertyType_UseNativeApi:
2328 *aValue = mHWData->mHWVirtExUseNativeApi;
2329 break;
2330
2331 default:
2332 return E_INVALIDARG;
2333 }
2334 return S_OK;
2335}
2336
2337HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2338{
2339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2340
2341 HRESULT rc = i_checkStateDependency(MutableStateDep);
2342 if (FAILED(rc)) return rc;
2343
2344 switch (aProperty)
2345 {
2346 case HWVirtExPropertyType_Enabled:
2347 i_setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExEnabled = !!aValue;
2350 break;
2351
2352 case HWVirtExPropertyType_VPID:
2353 i_setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2356 break;
2357
2358 case HWVirtExPropertyType_NestedPaging:
2359 i_setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2362 break;
2363
2364 case HWVirtExPropertyType_UnrestrictedExecution:
2365 i_setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mHWVirtExUXEnabled = !!aValue;
2368 break;
2369
2370 case HWVirtExPropertyType_LargePages:
2371 i_setModified(IsModified_MachineData);
2372 mHWData.backup();
2373 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2374 break;
2375
2376 case HWVirtExPropertyType_Force:
2377 i_setModified(IsModified_MachineData);
2378 mHWData.backup();
2379 mHWData->mHWVirtExForceEnabled = !!aValue;
2380 break;
2381
2382 case HWVirtExPropertyType_UseNativeApi:
2383 i_setModified(IsModified_MachineData);
2384 mHWData.backup();
2385 mHWData->mHWVirtExUseNativeApi = !!aValue;
2386 break;
2387
2388 default:
2389 return E_INVALIDARG;
2390 }
2391
2392 return S_OK;
2393}
2394
2395HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2396{
2397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2400
2401 return S_OK;
2402}
2403
2404HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2405{
2406 /** @todo (r=dmik):
2407 * 1. Allow to change the name of the snapshot folder containing snapshots
2408 * 2. Rename the folder on disk instead of just changing the property
2409 * value (to be smart and not to leave garbage). Note that it cannot be
2410 * done here because the change may be rolled back. Thus, the right
2411 * place is #saveSettings().
2412 */
2413
2414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2415
2416 HRESULT rc = i_checkStateDependency(MutableStateDep);
2417 if (FAILED(rc)) return rc;
2418
2419 if (!mData->mCurrentSnapshot.isNull())
2420 return setError(E_FAIL,
2421 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2422
2423 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2424
2425 if (strSnapshotFolder.isEmpty())
2426 strSnapshotFolder = "Snapshots";
2427 int vrc = i_calculateFullPath(strSnapshotFolder,
2428 strSnapshotFolder);
2429 if (RT_FAILURE(vrc))
2430 return setErrorBoth(E_FAIL, vrc,
2431 tr("Invalid snapshot folder '%s' (%Rrc)"),
2432 strSnapshotFolder.c_str(), vrc);
2433
2434 i_setModified(IsModified_MachineData);
2435 mUserData.backup();
2436
2437 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2438
2439 return S_OK;
2440}
2441
2442HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2443{
2444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2445
2446 aMediumAttachments.resize(mMediumAttachments->size());
2447 size_t i = 0;
2448 for (MediumAttachmentList::const_iterator
2449 it = mMediumAttachments->begin();
2450 it != mMediumAttachments->end();
2451 ++it, ++i)
2452 aMediumAttachments[i] = *it;
2453
2454 return S_OK;
2455}
2456
2457HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2458{
2459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 Assert(!!mVRDEServer);
2462
2463 aVRDEServer = mVRDEServer;
2464
2465 return S_OK;
2466}
2467
2468HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2469{
2470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2471
2472 aAudioAdapter = mAudioAdapter;
2473
2474 return S_OK;
2475}
2476
2477HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2478{
2479#ifdef VBOX_WITH_VUSB
2480 clearError();
2481 MultiResult rc(S_OK);
2482
2483# ifdef VBOX_WITH_USB
2484 rc = mParent->i_host()->i_checkUSBProxyService();
2485 if (FAILED(rc)) return rc;
2486# endif
2487
2488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 aUSBControllers.resize(mUSBControllers->size());
2491 size_t i = 0;
2492 for (USBControllerList::const_iterator
2493 it = mUSBControllers->begin();
2494 it != mUSBControllers->end();
2495 ++it, ++i)
2496 aUSBControllers[i] = *it;
2497
2498 return S_OK;
2499#else
2500 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2501 * extended error info to indicate that USB is simply not available
2502 * (w/o treating it as a failure), for example, as in OSE */
2503 NOREF(aUSBControllers);
2504 ReturnComNotImplemented();
2505#endif /* VBOX_WITH_VUSB */
2506}
2507
2508HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2509{
2510#ifdef VBOX_WITH_VUSB
2511 clearError();
2512 MultiResult rc(S_OK);
2513
2514# ifdef VBOX_WITH_USB
2515 rc = mParent->i_host()->i_checkUSBProxyService();
2516 if (FAILED(rc)) return rc;
2517# endif
2518
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 aUSBDeviceFilters = mUSBDeviceFilters;
2522 return rc;
2523#else
2524 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2525 * extended error info to indicate that USB is simply not available
2526 * (w/o treating it as a failure), for example, as in OSE */
2527 NOREF(aUSBDeviceFilters);
2528 ReturnComNotImplemented();
2529#endif /* VBOX_WITH_VUSB */
2530}
2531
2532HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 aSettingsFilePath = mData->m_strConfigFileFull;
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2542{
2543 RT_NOREF(aSettingsFilePath);
2544 ReturnComNotImplemented();
2545}
2546
2547HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2548{
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2552 if (FAILED(rc)) return rc;
2553
2554 if (!mData->pMachineConfigFile->fileExists())
2555 // this is a new machine, and no config file exists yet:
2556 *aSettingsModified = TRUE;
2557 else
2558 *aSettingsModified = (mData->flModifications != 0);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 *aSessionState = mData->mSession.mState;
2568
2569 return S_OK;
2570}
2571
2572HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2573{
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576 aSessionName = mData->mSession.mName;
2577
2578 return S_OK;
2579}
2580
2581HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2582{
2583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2584
2585 *aSessionPID = mData->mSession.mPID;
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getState(MachineState_T *aState)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 *aState = mData->mMachineState;
2595 Assert(mData->mMachineState != MachineState_Null);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2610{
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 aStateFilePath = mSSData->strStateFilePath;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 i_getLogFolder(aLogFolder);
2623
2624 return S_OK;
2625}
2626
2627HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2628{
2629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2630
2631 aCurrentSnapshot = mData->mCurrentSnapshot;
2632
2633 return S_OK;
2634}
2635
2636HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2637{
2638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2639
2640 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2641 ? 0
2642 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 /* Note: for machines with no snapshots, we always return FALSE
2652 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2653 * reasons :) */
2654
2655 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2656 ? FALSE
2657 : mData->mCurrentStateModified;
2658
2659 return S_OK;
2660}
2661
2662HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2663{
2664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2665
2666 aSharedFolders.resize(mHWData->mSharedFolders.size());
2667 size_t i = 0;
2668 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2669 it = mHWData->mSharedFolders.begin();
2670 it != mHWData->mSharedFolders.end();
2671 ++it, ++i)
2672 aSharedFolders[i] = *it;
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2678{
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 *aClipboardMode = mHWData->mClipboardMode;
2682
2683 return S_OK;
2684}
2685
2686HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2687{
2688 HRESULT rc = S_OK;
2689
2690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 alock.release();
2693 rc = i_onClipboardModeChange(aClipboardMode);
2694 alock.acquire();
2695 if (FAILED(rc)) return rc;
2696
2697 i_setModified(IsModified_MachineData);
2698 mHWData.backup();
2699 mHWData->mClipboardMode = aClipboardMode;
2700
2701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2702 if (Global::IsOnline(mData->mMachineState))
2703 i_saveSettings(NULL);
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 *aDnDMode = mHWData->mDnDMode;
2713
2714 return S_OK;
2715}
2716
2717HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2718{
2719 HRESULT rc = S_OK;
2720
2721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 alock.release();
2724 rc = i_onDnDModeChange(aDnDMode);
2725
2726 alock.acquire();
2727 if (FAILED(rc)) return rc;
2728
2729 i_setModified(IsModified_MachineData);
2730 mHWData.backup();
2731 mHWData->mDnDMode = aDnDMode;
2732
2733 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2734 if (Global::IsOnline(mData->mMachineState))
2735 i_saveSettings(NULL);
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 aStorageControllers.resize(mStorageControllers->size());
2745 size_t i = 0;
2746 for (StorageControllerList::const_iterator
2747 it = mStorageControllers->begin();
2748 it != mStorageControllers->end();
2749 ++it, ++i)
2750 aStorageControllers[i] = *it;
2751
2752 return S_OK;
2753}
2754
2755HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2756{
2757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 *aEnabled = mUserData->s.fTeleporterEnabled;
2760
2761 return S_OK;
2762}
2763
2764HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2765{
2766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 /* Only allow it to be set to true when PoweredOff or Aborted.
2769 (Clearing it is always permitted.) */
2770 if ( aTeleporterEnabled
2771 && mData->mRegistered
2772 && ( !i_isSessionMachine()
2773 || ( mData->mMachineState != MachineState_PoweredOff
2774 && mData->mMachineState != MachineState_Teleported
2775 && mData->mMachineState != MachineState_Aborted
2776 )
2777 )
2778 )
2779 return setError(VBOX_E_INVALID_VM_STATE,
2780 tr("The machine is not powered off (state is %s)"),
2781 Global::stringifyMachineState(mData->mMachineState));
2782
2783 i_setModified(IsModified_MachineData);
2784 mUserData.backup();
2785 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2791{
2792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2800{
2801 if (aTeleporterPort >= _64K)
2802 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2803
2804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2807 if (FAILED(rc)) return rc;
2808
2809 i_setModified(IsModified_MachineData);
2810 mUserData.backup();
2811 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2826{
2827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2830 if (FAILED(rc)) return rc;
2831
2832 i_setModified(IsModified_MachineData);
2833 mUserData.backup();
2834 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2848{
2849 /*
2850 * Hash the password first.
2851 */
2852 com::Utf8Str aT = aTeleporterPassword;
2853
2854 if (!aT.isEmpty())
2855 {
2856 if (VBoxIsPasswordHashed(&aT))
2857 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2858 VBoxHashPassword(&aT);
2859 }
2860
2861 /*
2862 * Do the update.
2863 */
2864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2865 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2866 if (SUCCEEDED(hrc))
2867 {
2868 i_setModified(IsModified_MachineData);
2869 mUserData.backup();
2870 mUserData->s.strTeleporterPassword = aT;
2871 }
2872
2873 return hrc;
2874}
2875
2876HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2877{
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2881 return S_OK;
2882}
2883
2884HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2885{
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 /** @todo deal with running state change. */
2889 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2890 if (FAILED(rc)) return rc;
2891
2892 i_setModified(IsModified_MachineData);
2893 mUserData.backup();
2894 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2895 return S_OK;
2896}
2897
2898HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2907{
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 /** @todo deal with running state change. */
2911 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mUserData.backup();
2916 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2917 return S_OK;
2918}
2919
2920HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2925 return S_OK;
2926}
2927
2928HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2929{
2930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2931
2932 /** @todo deal with running state change. */
2933 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2934 if (FAILED(rc)) return rc;
2935
2936 i_setModified(IsModified_MachineData);
2937 mUserData.backup();
2938 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2943{
2944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2952{
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 /** @todo deal with running state change. */
2956 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2957 if (FAILED(rc)) return rc;
2958
2959 i_setModified(IsModified_MachineData);
2960 mUserData.backup();
2961 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2962
2963 return S_OK;
2964}
2965
2966HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2967{
2968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2971 return S_OK;
2972}
2973
2974HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2975{
2976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 /** @todo deal with running state change. */
2979 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2980 if (FAILED(rc)) return rc;
2981
2982 i_setModified(IsModified_MachineData);
2983 mUserData.backup();
2984 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2985 return S_OK;
2986}
2987
2988HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2989{
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2993
2994 return S_OK;
2995}
2996
2997HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2998{
2999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 /* Only allow it to be set to true when PoweredOff or Aborted.
3002 (Clearing it is always permitted.) */
3003 if ( aRTCUseUTC
3004 && mData->mRegistered
3005 && ( !i_isSessionMachine()
3006 || ( mData->mMachineState != MachineState_PoweredOff
3007 && mData->mMachineState != MachineState_Teleported
3008 && mData->mMachineState != MachineState_Aborted
3009 )
3010 )
3011 )
3012 return setError(VBOX_E_INVALID_VM_STATE,
3013 tr("The machine is not powered off (state is %s)"),
3014 Global::stringifyMachineState(mData->mMachineState));
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3024{
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3028
3029 return S_OK;
3030}
3031
3032HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3033{
3034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 HRESULT rc = i_checkStateDependency(MutableStateDep);
3037 if (FAILED(rc)) return rc;
3038
3039 i_setModified(IsModified_MachineData);
3040 mHWData.backup();
3041 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3042
3043 return S_OK;
3044}
3045
3046HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3047{
3048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3049
3050 *aIOCacheSize = mHWData->mIOCacheSize;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3056{
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 HRESULT rc = i_checkStateDependency(MutableStateDep);
3060 if (FAILED(rc)) return rc;
3061
3062 i_setModified(IsModified_MachineData);
3063 mHWData.backup();
3064 mHWData->mIOCacheSize = aIOCacheSize;
3065
3066 return S_OK;
3067}
3068
3069
3070/**
3071 * @note Locks objects!
3072 */
3073HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3074 LockType_T aLockType)
3075{
3076 /* check the session state */
3077 SessionState_T state;
3078 HRESULT rc = aSession->COMGETTER(State)(&state);
3079 if (FAILED(rc)) return rc;
3080
3081 if (state != SessionState_Unlocked)
3082 return setError(VBOX_E_INVALID_OBJECT_STATE,
3083 tr("The given session is busy"));
3084
3085 // get the client's IInternalSessionControl interface
3086 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3087 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3088 E_INVALIDARG);
3089
3090 // session name (only used in some code paths)
3091 Utf8Str strSessionName;
3092
3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 if (!mData->mRegistered)
3096 return setError(E_UNEXPECTED,
3097 tr("The machine '%s' is not registered"),
3098 mUserData->s.strName.c_str());
3099
3100 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3101
3102 SessionState_T oldState = mData->mSession.mState;
3103 /* Hack: in case the session is closing and there is a progress object
3104 * which allows waiting for the session to be closed, take the opportunity
3105 * and do a limited wait (max. 1 second). This helps a lot when the system
3106 * is busy and thus session closing can take a little while. */
3107 if ( mData->mSession.mState == SessionState_Unlocking
3108 && mData->mSession.mProgress)
3109 {
3110 alock.release();
3111 mData->mSession.mProgress->WaitForCompletion(1000);
3112 alock.acquire();
3113 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3114 }
3115
3116 // try again now
3117 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3118 // (i.e. session machine exists)
3119 && (aLockType == LockType_Shared) // caller wants a shared link to the
3120 // existing session that holds the write lock:
3121 )
3122 {
3123 // OK, share the session... we are now dealing with three processes:
3124 // 1) VBoxSVC (where this code runs);
3125 // 2) process C: the caller's client process (who wants a shared session);
3126 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3127
3128 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3129 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3130 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3131 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3132 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3133
3134 /*
3135 * Release the lock before calling the client process. It's safe here
3136 * since the only thing to do after we get the lock again is to add
3137 * the remote control to the list (which doesn't directly influence
3138 * anything).
3139 */
3140 alock.release();
3141
3142 // get the console of the session holding the write lock (this is a remote call)
3143 ComPtr<IConsole> pConsoleW;
3144 if (mData->mSession.mLockType == LockType_VM)
3145 {
3146 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3147 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3148 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3149 if (FAILED(rc))
3150 // the failure may occur w/o any error info (from RPC), so provide one
3151 return setError(VBOX_E_VM_ERROR,
3152 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3153 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3154 }
3155
3156 // share the session machine and W's console with the caller's session
3157 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3158 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3159 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3160
3161 if (FAILED(rc))
3162 // the failure may occur w/o any error info (from RPC), so provide one
3163 return setError(VBOX_E_VM_ERROR,
3164 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3165 alock.acquire();
3166
3167 // need to revalidate the state after acquiring the lock again
3168 if (mData->mSession.mState != SessionState_Locked)
3169 {
3170 pSessionControl->Uninitialize();
3171 return setError(VBOX_E_INVALID_SESSION_STATE,
3172 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3173 mUserData->s.strName.c_str());
3174 }
3175
3176 // add the caller's session to the list
3177 mData->mSession.mRemoteControls.push_back(pSessionControl);
3178 }
3179 else if ( mData->mSession.mState == SessionState_Locked
3180 || mData->mSession.mState == SessionState_Unlocking
3181 )
3182 {
3183 // sharing not permitted, or machine still unlocking:
3184 return setError(VBOX_E_INVALID_OBJECT_STATE,
3185 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3186 mUserData->s.strName.c_str());
3187 }
3188 else
3189 {
3190 // machine is not locked: then write-lock the machine (create the session machine)
3191
3192 // must not be busy
3193 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3194
3195 // get the caller's session PID
3196 RTPROCESS pid = NIL_RTPROCESS;
3197 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3198 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3199 Assert(pid != NIL_RTPROCESS);
3200
3201 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3202
3203 if (fLaunchingVMProcess)
3204 {
3205 if (mData->mSession.mPID == NIL_RTPROCESS)
3206 {
3207 // two or more clients racing for a lock, the one which set the
3208 // session state to Spawning will win, the others will get an
3209 // error as we can't decide here if waiting a little would help
3210 // (only for shared locks this would avoid an error)
3211 return setError(VBOX_E_INVALID_OBJECT_STATE,
3212 tr("The machine '%s' already has a lock request pending"),
3213 mUserData->s.strName.c_str());
3214 }
3215
3216 // this machine is awaiting for a spawning session to be opened:
3217 // then the calling process must be the one that got started by
3218 // LaunchVMProcess()
3219
3220 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3221 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3222
3223#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3224 /* Hardened windows builds spawns three processes when a VM is
3225 launched, the 3rd one is the one that will end up here. */
3226 RTPROCESS ppid;
3227 int rc = RTProcQueryParent(pid, &ppid);
3228 if (RT_SUCCESS(rc))
3229 rc = RTProcQueryParent(ppid, &ppid);
3230 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3231 || rc == VERR_ACCESS_DENIED)
3232 {
3233 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3234 mData->mSession.mPID = pid;
3235 }
3236#endif
3237
3238 if (mData->mSession.mPID != pid)
3239 return setError(E_ACCESSDENIED,
3240 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3241 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3242 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3243 }
3244
3245 // create the mutable SessionMachine from the current machine
3246 ComObjPtr<SessionMachine> sessionMachine;
3247 sessionMachine.createObject();
3248 rc = sessionMachine->init(this);
3249 AssertComRC(rc);
3250
3251 /* NOTE: doing return from this function after this point but
3252 * before the end is forbidden since it may call SessionMachine::uninit()
3253 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3254 * lock while still holding the Machine lock in alock so that a deadlock
3255 * is possible due to the wrong lock order. */
3256
3257 if (SUCCEEDED(rc))
3258 {
3259 /*
3260 * Set the session state to Spawning to protect against subsequent
3261 * attempts to open a session and to unregister the machine after
3262 * we release the lock.
3263 */
3264 SessionState_T origState = mData->mSession.mState;
3265 mData->mSession.mState = SessionState_Spawning;
3266
3267#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3268 /* Get the client token ID to be passed to the client process */
3269 Utf8Str strTokenId;
3270 sessionMachine->i_getTokenId(strTokenId);
3271 Assert(!strTokenId.isEmpty());
3272#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3273 /* Get the client token to be passed to the client process */
3274 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3275 /* The token is now "owned" by pToken, fix refcount */
3276 if (!pToken.isNull())
3277 pToken->Release();
3278#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3279
3280 /*
3281 * Release the lock before calling the client process -- it will call
3282 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3283 * because the state is Spawning, so that LaunchVMProcess() and
3284 * LockMachine() calls will fail. This method, called before we
3285 * acquire the lock again, will fail because of the wrong PID.
3286 *
3287 * Note that mData->mSession.mRemoteControls accessed outside
3288 * the lock may not be modified when state is Spawning, so it's safe.
3289 */
3290 alock.release();
3291
3292 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3293#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3294 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3295#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3296 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3297 /* Now the token is owned by the client process. */
3298 pToken.setNull();
3299#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3300 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3301
3302 /* The failure may occur w/o any error info (from RPC), so provide one */
3303 if (FAILED(rc))
3304 setError(VBOX_E_VM_ERROR,
3305 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3306
3307 // get session name, either to remember or to compare against
3308 // the already known session name.
3309 {
3310 Bstr bstrSessionName;
3311 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3312 if (SUCCEEDED(rc2))
3313 strSessionName = bstrSessionName;
3314 }
3315
3316 if ( SUCCEEDED(rc)
3317 && fLaunchingVMProcess
3318 )
3319 {
3320 /* complete the remote session initialization */
3321
3322 /* get the console from the direct session */
3323 ComPtr<IConsole> console;
3324 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3325 ComAssertComRC(rc);
3326
3327 if (SUCCEEDED(rc) && !console)
3328 {
3329 ComAssert(!!console);
3330 rc = E_FAIL;
3331 }
3332
3333 /* assign machine & console to the remote session */
3334 if (SUCCEEDED(rc))
3335 {
3336 /*
3337 * after LaunchVMProcess(), the first and the only
3338 * entry in remoteControls is that remote session
3339 */
3340 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3341 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3342 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3343
3344 /* The failure may occur w/o any error info (from RPC), so provide one */
3345 if (FAILED(rc))
3346 setError(VBOX_E_VM_ERROR,
3347 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3348 }
3349
3350 if (FAILED(rc))
3351 pSessionControl->Uninitialize();
3352 }
3353
3354 /* acquire the lock again */
3355 alock.acquire();
3356
3357 /* Restore the session state */
3358 mData->mSession.mState = origState;
3359 }
3360
3361 // finalize spawning anyway (this is why we don't return on errors above)
3362 if (fLaunchingVMProcess)
3363 {
3364 Assert(mData->mSession.mName == strSessionName);
3365 /* Note that the progress object is finalized later */
3366 /** @todo Consider checking mData->mSession.mProgress for cancellation
3367 * around here. */
3368
3369 /* We don't reset mSession.mPID here because it is necessary for
3370 * SessionMachine::uninit() to reap the child process later. */
3371
3372 if (FAILED(rc))
3373 {
3374 /* Close the remote session, remove the remote control from the list
3375 * and reset session state to Closed (@note keep the code in sync
3376 * with the relevant part in checkForSpawnFailure()). */
3377
3378 Assert(mData->mSession.mRemoteControls.size() == 1);
3379 if (mData->mSession.mRemoteControls.size() == 1)
3380 {
3381 ErrorInfoKeeper eik;
3382 mData->mSession.mRemoteControls.front()->Uninitialize();
3383 }
3384
3385 mData->mSession.mRemoteControls.clear();
3386 mData->mSession.mState = SessionState_Unlocked;
3387 }
3388 }
3389 else
3390 {
3391 /* memorize PID of the directly opened session */
3392 if (SUCCEEDED(rc))
3393 mData->mSession.mPID = pid;
3394 }
3395
3396 if (SUCCEEDED(rc))
3397 {
3398 mData->mSession.mLockType = aLockType;
3399 /* memorize the direct session control and cache IUnknown for it */
3400 mData->mSession.mDirectControl = pSessionControl;
3401 mData->mSession.mState = SessionState_Locked;
3402 if (!fLaunchingVMProcess)
3403 mData->mSession.mName = strSessionName;
3404 /* associate the SessionMachine with this Machine */
3405 mData->mSession.mMachine = sessionMachine;
3406
3407 /* request an IUnknown pointer early from the remote party for later
3408 * identity checks (it will be internally cached within mDirectControl
3409 * at least on XPCOM) */
3410 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3411 NOREF(unk);
3412 }
3413
3414 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3415 * would break the lock order */
3416 alock.release();
3417
3418 /* uninitialize the created session machine on failure */
3419 if (FAILED(rc))
3420 sessionMachine->uninit();
3421 }
3422
3423 if (SUCCEEDED(rc))
3424 {
3425 /*
3426 * tell the client watcher thread to update the set of
3427 * machines that have open sessions
3428 */
3429 mParent->i_updateClientWatcher();
3430
3431 if (oldState != SessionState_Locked)
3432 /* fire an event */
3433 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3434 }
3435
3436 return rc;
3437}
3438
3439/**
3440 * @note Locks objects!
3441 */
3442HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3443 const com::Utf8Str &aName,
3444 const com::Utf8Str &aEnvironment,
3445 ComPtr<IProgress> &aProgress)
3446{
3447 Utf8Str strFrontend(aName);
3448 /* "emergencystop" doesn't need the session, so skip the checks/interface
3449 * retrieval. This code doesn't quite fit in here, but introducing a
3450 * special API method would be even more effort, and would require explicit
3451 * support by every API client. It's better to hide the feature a bit. */
3452 if (strFrontend != "emergencystop")
3453 CheckComArgNotNull(aSession);
3454
3455 HRESULT rc = S_OK;
3456 if (strFrontend.isEmpty())
3457 {
3458 Bstr bstrFrontend;
3459 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3460 if (FAILED(rc))
3461 return rc;
3462 strFrontend = bstrFrontend;
3463 if (strFrontend.isEmpty())
3464 {
3465 ComPtr<ISystemProperties> systemProperties;
3466 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3467 if (FAILED(rc))
3468 return rc;
3469 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3470 if (FAILED(rc))
3471 return rc;
3472 strFrontend = bstrFrontend;
3473 }
3474 /* paranoia - emergencystop is not a valid default */
3475 if (strFrontend == "emergencystop")
3476 strFrontend = Utf8Str::Empty;
3477 }
3478 /* default frontend: Qt GUI */
3479 if (strFrontend.isEmpty())
3480 strFrontend = "GUI/Qt";
3481
3482 if (strFrontend != "emergencystop")
3483 {
3484 /* check the session state */
3485 SessionState_T state;
3486 rc = aSession->COMGETTER(State)(&state);
3487 if (FAILED(rc))
3488 return rc;
3489
3490 if (state != SessionState_Unlocked)
3491 return setError(VBOX_E_INVALID_OBJECT_STATE,
3492 tr("The given session is busy"));
3493
3494 /* get the IInternalSessionControl interface */
3495 ComPtr<IInternalSessionControl> control(aSession);
3496 ComAssertMsgRet(!control.isNull(),
3497 ("No IInternalSessionControl interface"),
3498 E_INVALIDARG);
3499
3500 /* get the teleporter enable state for the progress object init. */
3501 BOOL fTeleporterEnabled;
3502 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3503 if (FAILED(rc))
3504 return rc;
3505
3506 /* create a progress object */
3507 ComObjPtr<ProgressProxy> progress;
3508 progress.createObject();
3509 rc = progress->init(mParent,
3510 static_cast<IMachine*>(this),
3511 Bstr(tr("Starting VM")).raw(),
3512 TRUE /* aCancelable */,
3513 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3514 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3515 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3516 2 /* uFirstOperationWeight */,
3517 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3518
3519 if (SUCCEEDED(rc))
3520 {
3521 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3522 if (SUCCEEDED(rc))
3523 {
3524 aProgress = progress;
3525
3526 /* signal the client watcher thread */
3527 mParent->i_updateClientWatcher();
3528
3529 /* fire an event */
3530 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3531 }
3532 }
3533 }
3534 else
3535 {
3536 /* no progress object - either instant success or failure */
3537 aProgress = NULL;
3538
3539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 if (mData->mSession.mState != SessionState_Locked)
3542 return setError(VBOX_E_INVALID_OBJECT_STATE,
3543 tr("The machine '%s' is not locked by a session"),
3544 mUserData->s.strName.c_str());
3545
3546 /* must have a VM process associated - do not kill normal API clients
3547 * with an open session */
3548 if (!Global::IsOnline(mData->mMachineState))
3549 return setError(VBOX_E_INVALID_OBJECT_STATE,
3550 tr("The machine '%s' does not have a VM process"),
3551 mUserData->s.strName.c_str());
3552
3553 /* forcibly terminate the VM process */
3554 if (mData->mSession.mPID != NIL_RTPROCESS)
3555 RTProcTerminate(mData->mSession.mPID);
3556
3557 /* signal the client watcher thread, as most likely the client has
3558 * been terminated */
3559 mParent->i_updateClientWatcher();
3560 }
3561
3562 return rc;
3563}
3564
3565HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3566{
3567 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3568 return setError(E_INVALIDARG,
3569 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3570 aPosition, SchemaDefs::MaxBootPosition);
3571
3572 if (aDevice == DeviceType_USB)
3573 return setError(E_NOTIMPL,
3574 tr("Booting from USB device is currently not supported"));
3575
3576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3577
3578 HRESULT rc = i_checkStateDependency(MutableStateDep);
3579 if (FAILED(rc)) return rc;
3580
3581 i_setModified(IsModified_MachineData);
3582 mHWData.backup();
3583 mHWData->mBootOrder[aPosition - 1] = aDevice;
3584
3585 return S_OK;
3586}
3587
3588HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3589{
3590 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3591 return setError(E_INVALIDARG,
3592 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3593 aPosition, SchemaDefs::MaxBootPosition);
3594
3595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3596
3597 *aDevice = mHWData->mBootOrder[aPosition - 1];
3598
3599 return S_OK;
3600}
3601
3602HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3603 LONG aControllerPort,
3604 LONG aDevice,
3605 DeviceType_T aType,
3606 const ComPtr<IMedium> &aMedium)
3607{
3608 IMedium *aM = aMedium;
3609 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3610 aName.c_str(), aControllerPort, aDevice, aType, aM));
3611
3612 // request the host lock first, since might be calling Host methods for getting host drives;
3613 // next, protect the media tree all the while we're in here, as well as our member variables
3614 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3615 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3616
3617 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3618 if (FAILED(rc)) return rc;
3619
3620 /// @todo NEWMEDIA implicit machine registration
3621 if (!mData->mRegistered)
3622 return setError(VBOX_E_INVALID_OBJECT_STATE,
3623 tr("Cannot attach storage devices to an unregistered machine"));
3624
3625 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3626
3627 /* Check for an existing controller. */
3628 ComObjPtr<StorageController> ctl;
3629 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3630 if (FAILED(rc)) return rc;
3631
3632 StorageControllerType_T ctrlType;
3633 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3634 if (FAILED(rc))
3635 return setError(E_FAIL,
3636 tr("Could not get type of controller '%s'"),
3637 aName.c_str());
3638
3639 bool fSilent = false;
3640 Utf8Str strReconfig;
3641
3642 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3643 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3644 if ( mData->mMachineState == MachineState_Paused
3645 && strReconfig == "1")
3646 fSilent = true;
3647
3648 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3649 bool fHotplug = false;
3650 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3651 fHotplug = true;
3652
3653 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3654 return setError(VBOX_E_INVALID_VM_STATE,
3655 tr("Controller '%s' does not support hotplugging"),
3656 aName.c_str());
3657
3658 // check that the port and device are not out of range
3659 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3660 if (FAILED(rc)) return rc;
3661
3662 /* check if the device slot is already busy */
3663 MediumAttachment *pAttachTemp;
3664 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3665 aName,
3666 aControllerPort,
3667 aDevice)))
3668 {
3669 Medium *pMedium = pAttachTemp->i_getMedium();
3670 if (pMedium)
3671 {
3672 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3673 return setError(VBOX_E_OBJECT_IN_USE,
3674 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3675 pMedium->i_getLocationFull().c_str(),
3676 aControllerPort,
3677 aDevice,
3678 aName.c_str());
3679 }
3680 else
3681 return setError(VBOX_E_OBJECT_IN_USE,
3682 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3683 aControllerPort, aDevice, aName.c_str());
3684 }
3685
3686 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3687 if (aMedium && medium.isNull())
3688 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3689
3690 AutoCaller mediumCaller(medium);
3691 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3692
3693 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3694
3695 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3696 && !medium.isNull()
3697 )
3698 return setError(VBOX_E_OBJECT_IN_USE,
3699 tr("Medium '%s' is already attached to this virtual machine"),
3700 medium->i_getLocationFull().c_str());
3701
3702 if (!medium.isNull())
3703 {
3704 MediumType_T mtype = medium->i_getType();
3705 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3706 // For DVDs it's not written to the config file, so needs no global config
3707 // version bump. For floppies it's a new attribute "type", which is ignored
3708 // by older VirtualBox version, so needs no global config version bump either.
3709 // For hard disks this type is not accepted.
3710 if (mtype == MediumType_MultiAttach)
3711 {
3712 // This type is new with VirtualBox 4.0 and therefore requires settings
3713 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3714 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3715 // two reasons: The medium type is a property of the media registry tree, which
3716 // can reside in the global config file (for pre-4.0 media); we would therefore
3717 // possibly need to bump the global config version. We don't want to do that though
3718 // because that might make downgrading to pre-4.0 impossible.
3719 // As a result, we can only use these two new types if the medium is NOT in the
3720 // global registry:
3721 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3722 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3723 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3724 )
3725 return setError(VBOX_E_INVALID_OBJECT_STATE,
3726 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3727 "to machines that were created with VirtualBox 4.0 or later"),
3728 medium->i_getLocationFull().c_str());
3729 }
3730 }
3731
3732 bool fIndirect = false;
3733 if (!medium.isNull())
3734 fIndirect = medium->i_isReadOnly();
3735 bool associate = true;
3736
3737 do
3738 {
3739 if ( aType == DeviceType_HardDisk
3740 && mMediumAttachments.isBackedUp())
3741 {
3742 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3743
3744 /* check if the medium was attached to the VM before we started
3745 * changing attachments in which case the attachment just needs to
3746 * be restored */
3747 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3748 {
3749 AssertReturn(!fIndirect, E_FAIL);
3750
3751 /* see if it's the same bus/channel/device */
3752 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3753 {
3754 /* the simplest case: restore the whole attachment
3755 * and return, nothing else to do */
3756 mMediumAttachments->push_back(pAttachTemp);
3757
3758 /* Reattach the medium to the VM. */
3759 if (fHotplug || fSilent)
3760 {
3761 mediumLock.release();
3762 treeLock.release();
3763 alock.release();
3764
3765 MediumLockList *pMediumLockList(new MediumLockList());
3766
3767 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3768 medium /* pToLockWrite */,
3769 false /* fMediumLockWriteAll */,
3770 NULL,
3771 *pMediumLockList);
3772 alock.acquire();
3773 if (FAILED(rc))
3774 delete pMediumLockList;
3775 else
3776 {
3777 mData->mSession.mLockedMedia.Unlock();
3778 alock.release();
3779 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3780 mData->mSession.mLockedMedia.Lock();
3781 alock.acquire();
3782 }
3783 alock.release();
3784
3785 if (SUCCEEDED(rc))
3786 {
3787 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3788 /* Remove lock list in case of error. */
3789 if (FAILED(rc))
3790 {
3791 mData->mSession.mLockedMedia.Unlock();
3792 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3793 mData->mSession.mLockedMedia.Lock();
3794 }
3795 }
3796 }
3797
3798 return S_OK;
3799 }
3800
3801 /* bus/channel/device differ; we need a new attachment object,
3802 * but don't try to associate it again */
3803 associate = false;
3804 break;
3805 }
3806 }
3807
3808 /* go further only if the attachment is to be indirect */
3809 if (!fIndirect)
3810 break;
3811
3812 /* perform the so called smart attachment logic for indirect
3813 * attachments. Note that smart attachment is only applicable to base
3814 * hard disks. */
3815
3816 if (medium->i_getParent().isNull())
3817 {
3818 /* first, investigate the backup copy of the current hard disk
3819 * attachments to make it possible to re-attach existing diffs to
3820 * another device slot w/o losing their contents */
3821 if (mMediumAttachments.isBackedUp())
3822 {
3823 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3824
3825 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3826 uint32_t foundLevel = 0;
3827
3828 for (MediumAttachmentList::const_iterator
3829 it = oldAtts.begin();
3830 it != oldAtts.end();
3831 ++it)
3832 {
3833 uint32_t level = 0;
3834 MediumAttachment *pAttach = *it;
3835 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3836 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3837 if (pMedium.isNull())
3838 continue;
3839
3840 if (pMedium->i_getBase(&level) == medium)
3841 {
3842 /* skip the hard disk if its currently attached (we
3843 * cannot attach the same hard disk twice) */
3844 if (i_findAttachment(*mMediumAttachments.data(),
3845 pMedium))
3846 continue;
3847
3848 /* matched device, channel and bus (i.e. attached to the
3849 * same place) will win and immediately stop the search;
3850 * otherwise the attachment that has the youngest
3851 * descendant of medium will be used
3852 */
3853 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3854 {
3855 /* the simplest case: restore the whole attachment
3856 * and return, nothing else to do */
3857 mMediumAttachments->push_back(*it);
3858
3859 /* Reattach the medium to the VM. */
3860 if (fHotplug || fSilent)
3861 {
3862 mediumLock.release();
3863 treeLock.release();
3864 alock.release();
3865
3866 MediumLockList *pMediumLockList(new MediumLockList());
3867
3868 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3869 medium /* pToLockWrite */,
3870 false /* fMediumLockWriteAll */,
3871 NULL,
3872 *pMediumLockList);
3873 alock.acquire();
3874 if (FAILED(rc))
3875 delete pMediumLockList;
3876 else
3877 {
3878 mData->mSession.mLockedMedia.Unlock();
3879 alock.release();
3880 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3881 mData->mSession.mLockedMedia.Lock();
3882 alock.acquire();
3883 }
3884 alock.release();
3885
3886 if (SUCCEEDED(rc))
3887 {
3888 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3889 /* Remove lock list in case of error. */
3890 if (FAILED(rc))
3891 {
3892 mData->mSession.mLockedMedia.Unlock();
3893 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3894 mData->mSession.mLockedMedia.Lock();
3895 }
3896 }
3897 }
3898
3899 return S_OK;
3900 }
3901 else if ( foundIt == oldAtts.end()
3902 || level > foundLevel /* prefer younger */
3903 )
3904 {
3905 foundIt = it;
3906 foundLevel = level;
3907 }
3908 }
3909 }
3910
3911 if (foundIt != oldAtts.end())
3912 {
3913 /* use the previously attached hard disk */
3914 medium = (*foundIt)->i_getMedium();
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 /* not implicit, doesn't require association with this VM */
3919 fIndirect = false;
3920 associate = false;
3921 /* go right to the MediumAttachment creation */
3922 break;
3923 }
3924 }
3925
3926 /* must give up the medium lock and medium tree lock as below we
3927 * go over snapshots, which needs a lock with higher lock order. */
3928 mediumLock.release();
3929 treeLock.release();
3930
3931 /* then, search through snapshots for the best diff in the given
3932 * hard disk's chain to base the new diff on */
3933
3934 ComObjPtr<Medium> base;
3935 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3936 while (snap)
3937 {
3938 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3939
3940 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3941
3942 MediumAttachment *pAttachFound = NULL;
3943 uint32_t foundLevel = 0;
3944
3945 for (MediumAttachmentList::const_iterator
3946 it = snapAtts.begin();
3947 it != snapAtts.end();
3948 ++it)
3949 {
3950 MediumAttachment *pAttach = *it;
3951 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3952 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3953 if (pMedium.isNull())
3954 continue;
3955
3956 uint32_t level = 0;
3957 if (pMedium->i_getBase(&level) == medium)
3958 {
3959 /* matched device, channel and bus (i.e. attached to the
3960 * same place) will win and immediately stop the search;
3961 * otherwise the attachment that has the youngest
3962 * descendant of medium will be used
3963 */
3964 if ( pAttach->i_getDevice() == aDevice
3965 && pAttach->i_getPort() == aControllerPort
3966 && pAttach->i_getControllerName() == aName
3967 )
3968 {
3969 pAttachFound = pAttach;
3970 break;
3971 }
3972 else if ( !pAttachFound
3973 || level > foundLevel /* prefer younger */
3974 )
3975 {
3976 pAttachFound = pAttach;
3977 foundLevel = level;
3978 }
3979 }
3980 }
3981
3982 if (pAttachFound)
3983 {
3984 base = pAttachFound->i_getMedium();
3985 break;
3986 }
3987
3988 snap = snap->i_getParent();
3989 }
3990
3991 /* re-lock medium tree and the medium, as we need it below */
3992 treeLock.acquire();
3993 mediumLock.acquire();
3994
3995 /* found a suitable diff, use it as a base */
3996 if (!base.isNull())
3997 {
3998 medium = base;
3999 mediumCaller.attach(medium);
4000 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4001 mediumLock.attach(medium);
4002 }
4003 }
4004
4005 Utf8Str strFullSnapshotFolder;
4006 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4007
4008 ComObjPtr<Medium> diff;
4009 diff.createObject();
4010 // store this diff in the same registry as the parent
4011 Guid uuidRegistryParent;
4012 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4013 {
4014 // parent image has no registry: this can happen if we're attaching a new immutable
4015 // image that has not yet been attached (medium then points to the base and we're
4016 // creating the diff image for the immutable, and the parent is not yet registered);
4017 // put the parent in the machine registry then
4018 mediumLock.release();
4019 treeLock.release();
4020 alock.release();
4021 i_addMediumToRegistry(medium);
4022 alock.acquire();
4023 treeLock.acquire();
4024 mediumLock.acquire();
4025 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4026 }
4027 rc = diff->init(mParent,
4028 medium->i_getPreferredDiffFormat(),
4029 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4030 uuidRegistryParent,
4031 DeviceType_HardDisk);
4032 if (FAILED(rc)) return rc;
4033
4034 /* Apply the normal locking logic to the entire chain. */
4035 MediumLockList *pMediumLockList(new MediumLockList());
4036 mediumLock.release();
4037 treeLock.release();
4038 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4039 diff /* pToLockWrite */,
4040 false /* fMediumLockWriteAll */,
4041 medium,
4042 *pMediumLockList);
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045 if (SUCCEEDED(rc))
4046 {
4047 mediumLock.release();
4048 treeLock.release();
4049 rc = pMediumLockList->Lock();
4050 treeLock.acquire();
4051 mediumLock.acquire();
4052 if (FAILED(rc))
4053 setError(rc,
4054 tr("Could not lock medium when creating diff '%s'"),
4055 diff->i_getLocationFull().c_str());
4056 else
4057 {
4058 /* will release the lock before the potentially lengthy
4059 * operation, so protect with the special state */
4060 MachineState_T oldState = mData->mMachineState;
4061 i_setMachineState(MachineState_SettingUp);
4062
4063 mediumLock.release();
4064 treeLock.release();
4065 alock.release();
4066
4067 rc = medium->i_createDiffStorage(diff,
4068 medium->i_getPreferredDiffVariant(),
4069 pMediumLockList,
4070 NULL /* aProgress */,
4071 true /* aWait */,
4072 false /* aNotify */);
4073
4074 alock.acquire();
4075 treeLock.acquire();
4076 mediumLock.acquire();
4077
4078 i_setMachineState(oldState);
4079 }
4080 }
4081
4082 /* Unlock the media and free the associated memory. */
4083 delete pMediumLockList;
4084
4085 if (FAILED(rc)) return rc;
4086
4087 /* use the created diff for the actual attachment */
4088 medium = diff;
4089 mediumCaller.attach(medium);
4090 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4091 mediumLock.attach(medium);
4092 }
4093 while (0);
4094
4095 ComObjPtr<MediumAttachment> attachment;
4096 attachment.createObject();
4097 rc = attachment->init(this,
4098 medium,
4099 aName,
4100 aControllerPort,
4101 aDevice,
4102 aType,
4103 fIndirect,
4104 false /* fPassthrough */,
4105 false /* fTempEject */,
4106 false /* fNonRotational */,
4107 false /* fDiscard */,
4108 fHotplug /* fHotPluggable */,
4109 Utf8Str::Empty);
4110 if (FAILED(rc)) return rc;
4111
4112 if (associate && !medium.isNull())
4113 {
4114 // as the last step, associate the medium to the VM
4115 rc = medium->i_addBackReference(mData->mUuid);
4116 // here we can fail because of Deleting, or being in process of creating a Diff
4117 if (FAILED(rc)) return rc;
4118
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 }
4127
4128 /* success: finally remember the attachment */
4129 i_setModified(IsModified_Storage);
4130 mMediumAttachments.backup();
4131 mMediumAttachments->push_back(attachment);
4132
4133 mediumLock.release();
4134 treeLock.release();
4135 alock.release();
4136
4137 if (fHotplug || fSilent)
4138 {
4139 if (!medium.isNull())
4140 {
4141 MediumLockList *pMediumLockList(new MediumLockList());
4142
4143 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4144 medium /* pToLockWrite */,
4145 false /* fMediumLockWriteAll */,
4146 NULL,
4147 *pMediumLockList);
4148 alock.acquire();
4149 if (FAILED(rc))
4150 delete pMediumLockList;
4151 else
4152 {
4153 mData->mSession.mLockedMedia.Unlock();
4154 alock.release();
4155 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4156 mData->mSession.mLockedMedia.Lock();
4157 alock.acquire();
4158 }
4159 alock.release();
4160 }
4161
4162 if (SUCCEEDED(rc))
4163 {
4164 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4165 /* Remove lock list in case of error. */
4166 if (FAILED(rc))
4167 {
4168 mData->mSession.mLockedMedia.Unlock();
4169 mData->mSession.mLockedMedia.Remove(attachment);
4170 mData->mSession.mLockedMedia.Lock();
4171 }
4172 }
4173 }
4174
4175 /* Save modified registries, but skip this machine as it's the caller's
4176 * job to save its settings like all other settings changes. */
4177 mParent->i_unmarkRegistryModified(i_getId());
4178 mParent->i_saveModifiedRegistries();
4179
4180 if (aM)
4181 mParent->i_onMediumConfigChanged(aM);
4182 return rc;
4183}
4184
4185HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4186 LONG aDevice)
4187{
4188 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4189 aName.c_str(), aControllerPort, aDevice));
4190
4191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4192
4193 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4194 if (FAILED(rc)) return rc;
4195
4196 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4197
4198 /* Check for an existing controller. */
4199 ComObjPtr<StorageController> ctl;
4200 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4201 if (FAILED(rc)) return rc;
4202
4203 StorageControllerType_T ctrlType;
4204 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4205 if (FAILED(rc))
4206 return setError(E_FAIL,
4207 tr("Could not get type of controller '%s'"),
4208 aName.c_str());
4209
4210 bool fSilent = false;
4211 Utf8Str strReconfig;
4212
4213 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4214 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4215 if ( mData->mMachineState == MachineState_Paused
4216 && strReconfig == "1")
4217 fSilent = true;
4218
4219 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4220 bool fHotplug = false;
4221 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4222 fHotplug = true;
4223
4224 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4225 return setError(VBOX_E_INVALID_VM_STATE,
4226 tr("Controller '%s' does not support hotplugging"),
4227 aName.c_str());
4228
4229 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4230 aName,
4231 aControllerPort,
4232 aDevice);
4233 if (!pAttach)
4234 return setError(VBOX_E_OBJECT_NOT_FOUND,
4235 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4236 aDevice, aControllerPort, aName.c_str());
4237
4238 if (fHotplug && !pAttach->i_getHotPluggable())
4239 return setError(VBOX_E_NOT_SUPPORTED,
4240 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4241 aDevice, aControllerPort, aName.c_str());
4242
4243 /*
4244 * The VM has to detach the device before we delete any implicit diffs.
4245 * If this fails we can roll back without loosing data.
4246 */
4247 if (fHotplug || fSilent)
4248 {
4249 alock.release();
4250 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4251 alock.acquire();
4252 }
4253 if (FAILED(rc)) return rc;
4254
4255 /* If we are here everything went well and we can delete the implicit now. */
4256 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4257
4258 alock.release();
4259
4260 /* Save modified registries, but skip this machine as it's the caller's
4261 * job to save its settings like all other settings changes. */
4262 mParent->i_unmarkRegistryModified(i_getId());
4263 mParent->i_saveModifiedRegistries();
4264
4265 return rc;
4266}
4267
4268HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4269 LONG aDevice, BOOL aPassthrough)
4270{
4271 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4272 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4273
4274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4275
4276 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4277 if (FAILED(rc)) return rc;
4278
4279 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4280
4281 /* Check for an existing controller. */
4282 ComObjPtr<StorageController> ctl;
4283 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4284 if (FAILED(rc)) return rc;
4285
4286 StorageControllerType_T ctrlType;
4287 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4288 if (FAILED(rc))
4289 return setError(E_FAIL,
4290 tr("Could not get type of controller '%s'"),
4291 aName.c_str());
4292
4293 bool fSilent = false;
4294 Utf8Str strReconfig;
4295
4296 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4297 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4298 if ( mData->mMachineState == MachineState_Paused
4299 && strReconfig == "1")
4300 fSilent = true;
4301
4302 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4303 bool fHotplug = false;
4304 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4305 fHotplug = true;
4306
4307 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4308 return setError(VBOX_E_INVALID_VM_STATE,
4309 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4310 aName.c_str());
4311
4312 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4313 aName,
4314 aControllerPort,
4315 aDevice);
4316 if (!pAttach)
4317 return setError(VBOX_E_OBJECT_NOT_FOUND,
4318 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4319 aDevice, aControllerPort, aName.c_str());
4320
4321
4322 i_setModified(IsModified_Storage);
4323 mMediumAttachments.backup();
4324
4325 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4326
4327 if (pAttach->i_getType() != DeviceType_DVD)
4328 return setError(E_INVALIDARG,
4329 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4330 aDevice, aControllerPort, aName.c_str());
4331 pAttach->i_updatePassthrough(!!aPassthrough);
4332
4333 attLock.release();
4334 alock.release();
4335 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4336
4337 return rc;
4338}
4339
4340HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4341 LONG aDevice, BOOL aTemporaryEject)
4342{
4343
4344 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4345 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4346
4347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4348
4349 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4350 if (FAILED(rc)) return rc;
4351
4352 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4353 aName,
4354 aControllerPort,
4355 aDevice);
4356 if (!pAttach)
4357 return setError(VBOX_E_OBJECT_NOT_FOUND,
4358 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4359 aDevice, aControllerPort, aName.c_str());
4360
4361
4362 i_setModified(IsModified_Storage);
4363 mMediumAttachments.backup();
4364
4365 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4366
4367 if (pAttach->i_getType() != DeviceType_DVD)
4368 return setError(E_INVALIDARG,
4369 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4370 aDevice, aControllerPort, aName.c_str());
4371 pAttach->i_updateTempEject(!!aTemporaryEject);
4372
4373 return S_OK;
4374}
4375
4376HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4377 LONG aDevice, BOOL aNonRotational)
4378{
4379
4380 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4381 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4382
4383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4384
4385 HRESULT rc = i_checkStateDependency(MutableStateDep);
4386 if (FAILED(rc)) return rc;
4387
4388 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4389
4390 if (Global::IsOnlineOrTransient(mData->mMachineState))
4391 return setError(VBOX_E_INVALID_VM_STATE,
4392 tr("Invalid machine state: %s"),
4393 Global::stringifyMachineState(mData->mMachineState));
4394
4395 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4396 aName,
4397 aControllerPort,
4398 aDevice);
4399 if (!pAttach)
4400 return setError(VBOX_E_OBJECT_NOT_FOUND,
4401 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4402 aDevice, aControllerPort, aName.c_str());
4403
4404
4405 i_setModified(IsModified_Storage);
4406 mMediumAttachments.backup();
4407
4408 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4409
4410 if (pAttach->i_getType() != DeviceType_HardDisk)
4411 return setError(E_INVALIDARG,
4412 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"),
4413 aDevice, aControllerPort, aName.c_str());
4414 pAttach->i_updateNonRotational(!!aNonRotational);
4415
4416 return S_OK;
4417}
4418
4419HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4420 LONG aDevice, BOOL aDiscard)
4421{
4422
4423 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4424 aName.c_str(), aControllerPort, aDevice, aDiscard));
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = i_checkStateDependency(MutableStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4432
4433 if (Global::IsOnlineOrTransient(mData->mMachineState))
4434 return setError(VBOX_E_INVALID_VM_STATE,
4435 tr("Invalid machine state: %s"),
4436 Global::stringifyMachineState(mData->mMachineState));
4437
4438 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4439 aName,
4440 aControllerPort,
4441 aDevice);
4442 if (!pAttach)
4443 return setError(VBOX_E_OBJECT_NOT_FOUND,
4444 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4445 aDevice, aControllerPort, aName.c_str());
4446
4447
4448 i_setModified(IsModified_Storage);
4449 mMediumAttachments.backup();
4450
4451 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4452
4453 if (pAttach->i_getType() != DeviceType_HardDisk)
4454 return setError(E_INVALIDARG,
4455 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"),
4456 aDevice, aControllerPort, aName.c_str());
4457 pAttach->i_updateDiscard(!!aDiscard);
4458
4459 return S_OK;
4460}
4461
4462HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4463 LONG aDevice, BOOL aHotPluggable)
4464{
4465 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4466 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4467
4468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4469
4470 HRESULT rc = i_checkStateDependency(MutableStateDep);
4471 if (FAILED(rc)) return rc;
4472
4473 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4474
4475 if (Global::IsOnlineOrTransient(mData->mMachineState))
4476 return setError(VBOX_E_INVALID_VM_STATE,
4477 tr("Invalid machine state: %s"),
4478 Global::stringifyMachineState(mData->mMachineState));
4479
4480 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4481 aName,
4482 aControllerPort,
4483 aDevice);
4484 if (!pAttach)
4485 return setError(VBOX_E_OBJECT_NOT_FOUND,
4486 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489 /* Check for an existing controller. */
4490 ComObjPtr<StorageController> ctl;
4491 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4492 if (FAILED(rc)) return rc;
4493
4494 StorageControllerType_T ctrlType;
4495 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4496 if (FAILED(rc))
4497 return setError(E_FAIL,
4498 tr("Could not get type of controller '%s'"),
4499 aName.c_str());
4500
4501 if (!i_isControllerHotplugCapable(ctrlType))
4502 return setError(VBOX_E_NOT_SUPPORTED,
4503 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4504 aName.c_str());
4505
4506 i_setModified(IsModified_Storage);
4507 mMediumAttachments.backup();
4508
4509 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4510
4511 if (pAttach->i_getType() == DeviceType_Floppy)
4512 return setError(E_INVALIDARG,
4513 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"),
4514 aDevice, aControllerPort, aName.c_str());
4515 pAttach->i_updateHotPluggable(!!aHotPluggable);
4516
4517 return S_OK;
4518}
4519
4520HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4521 LONG aDevice)
4522{
4523 int rc = S_OK;
4524 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4525 aName.c_str(), aControllerPort, aDevice));
4526
4527 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4528
4529 return rc;
4530}
4531
4532HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4533 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4534{
4535 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4536 aName.c_str(), aControllerPort, aDevice));
4537
4538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4539
4540 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4541 if (FAILED(rc)) return rc;
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4549 aName,
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557
4558 i_setModified(IsModified_Storage);
4559 mMediumAttachments.backup();
4560
4561 IBandwidthGroup *iB = aBandwidthGroup;
4562 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4563 if (aBandwidthGroup && group.isNull())
4564 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4565
4566 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4567
4568 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4569 if (strBandwidthGroupOld.isNotEmpty())
4570 {
4571 /* Get the bandwidth group object and release it - this must not fail. */
4572 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4573 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4574 Assert(SUCCEEDED(rc));
4575
4576 pBandwidthGroupOld->i_release();
4577 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4578 }
4579
4580 if (!group.isNull())
4581 {
4582 group->i_reference();
4583 pAttach->i_updateBandwidthGroup(group->i_getName());
4584 }
4585
4586 return S_OK;
4587}
4588
4589HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4590 LONG aControllerPort,
4591 LONG aDevice,
4592 DeviceType_T aType)
4593{
4594 HRESULT rc = S_OK;
4595
4596 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4597 aName.c_str(), aControllerPort, aDevice, aType));
4598
4599 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4600
4601 return rc;
4602}
4603
4604
4605HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4606 LONG aControllerPort,
4607 LONG aDevice,
4608 BOOL aForce)
4609{
4610 int rc = S_OK;
4611 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4612 aName.c_str(), aControllerPort, aForce));
4613
4614 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4615
4616 return rc;
4617}
4618
4619HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 const ComPtr<IMedium> &aMedium,
4623 BOOL aForce)
4624{
4625 int rc = S_OK;
4626 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4627 aName.c_str(), aControllerPort, aDevice, aForce));
4628
4629 // request the host lock first, since might be calling Host methods for getting host drives;
4630 // next, protect the media tree all the while we're in here, as well as our member variables
4631 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4632 this->lockHandle(),
4633 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4634
4635 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4636 aName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4642 aDevice, aControllerPort, aName.c_str());
4643
4644 /* Remember previously mounted medium. The medium before taking the
4645 * backup is not necessarily the same thing. */
4646 ComObjPtr<Medium> oldmedium;
4647 oldmedium = pAttach->i_getMedium();
4648
4649 IMedium *iM = aMedium;
4650 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4651 if (aMedium && pMedium.isNull())
4652 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4653
4654 AutoCaller mediumCaller(pMedium);
4655 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4656
4657 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4658 if (pMedium)
4659 {
4660 DeviceType_T mediumType = pAttach->i_getType();
4661 switch (mediumType)
4662 {
4663 case DeviceType_DVD:
4664 case DeviceType_Floppy:
4665 break;
4666
4667 default:
4668 return setError(VBOX_E_INVALID_OBJECT_STATE,
4669 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4670 aControllerPort,
4671 aDevice,
4672 aName.c_str());
4673 }
4674 }
4675
4676 i_setModified(IsModified_Storage);
4677 mMediumAttachments.backup();
4678
4679 {
4680 // The backup operation makes the pAttach reference point to the
4681 // old settings. Re-get the correct reference.
4682 pAttach = i_findAttachment(*mMediumAttachments.data(),
4683 aName,
4684 aControllerPort,
4685 aDevice);
4686 if (!oldmedium.isNull())
4687 oldmedium->i_removeBackReference(mData->mUuid);
4688 if (!pMedium.isNull())
4689 {
4690 pMedium->i_addBackReference(mData->mUuid);
4691
4692 mediumLock.release();
4693 multiLock.release();
4694 i_addMediumToRegistry(pMedium);
4695 multiLock.acquire();
4696 mediumLock.acquire();
4697 }
4698
4699 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4700 pAttach->i_updateMedium(pMedium);
4701 }
4702
4703 i_setModified(IsModified_Storage);
4704
4705 mediumLock.release();
4706 multiLock.release();
4707 rc = i_onMediumChange(pAttach, aForce);
4708 multiLock.acquire();
4709 mediumLock.acquire();
4710
4711 /* On error roll back this change only. */
4712 if (FAILED(rc))
4713 {
4714 if (!pMedium.isNull())
4715 pMedium->i_removeBackReference(mData->mUuid);
4716 pAttach = i_findAttachment(*mMediumAttachments.data(),
4717 aName,
4718 aControllerPort,
4719 aDevice);
4720 /* If the attachment is gone in the meantime, bail out. */
4721 if (pAttach.isNull())
4722 return rc;
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724 if (!oldmedium.isNull())
4725 oldmedium->i_addBackReference(mData->mUuid);
4726 pAttach->i_updateMedium(oldmedium);
4727 }
4728
4729 mediumLock.release();
4730 multiLock.release();
4731
4732 /* Save modified registries, but skip this machine as it's the caller's
4733 * job to save its settings like all other settings changes. */
4734 mParent->i_unmarkRegistryModified(i_getId());
4735 mParent->i_saveModifiedRegistries();
4736
4737 return rc;
4738}
4739HRESULT Machine::getMedium(const com::Utf8Str &aName,
4740 LONG aControllerPort,
4741 LONG aDevice,
4742 ComPtr<IMedium> &aMedium)
4743{
4744 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4745 aName.c_str(), aControllerPort, aDevice));
4746
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4748
4749 aMedium = NULL;
4750
4751 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4752 aName,
4753 aControllerPort,
4754 aDevice);
4755 if (pAttach.isNull())
4756 return setError(VBOX_E_OBJECT_NOT_FOUND,
4757 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4758 aDevice, aControllerPort, aName.c_str());
4759
4760 aMedium = pAttach->i_getMedium();
4761
4762 return S_OK;
4763}
4764
4765HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4766{
4767
4768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4771
4772 return S_OK;
4773}
4774
4775HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4776{
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4780
4781 return S_OK;
4782}
4783
4784HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4785{
4786 /* Do not assert if slot is out of range, just return the advertised
4787 status. testdriver/vbox.py triggers this in logVmInfo. */
4788 if (aSlot >= mNetworkAdapters.size())
4789 return setError(E_INVALIDARG,
4790 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4791 aSlot, mNetworkAdapters.size());
4792
4793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4796
4797 return S_OK;
4798}
4799
4800HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4801{
4802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4803
4804 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4805 size_t i = 0;
4806 for (settings::StringsMap::const_iterator
4807 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4808 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4809 ++it, ++i)
4810 aKeys[i] = it->first;
4811
4812 return S_OK;
4813}
4814
4815 /**
4816 * @note Locks this object for reading.
4817 */
4818HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4819 com::Utf8Str &aValue)
4820{
4821 /* start with nothing found */
4822 aValue = "";
4823
4824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4827 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4828 // found:
4829 aValue = it->second; // source is a Utf8Str
4830
4831 /* return the result to caller (may be empty) */
4832 return S_OK;
4833}
4834
4835 /**
4836 * @note Locks mParent for writing + this object for writing.
4837 */
4838HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4839{
4840 /* Because non-ASCII characters in aKey have caused problems in the settings
4841 * they are rejected unless the key should be deleted. */
4842 if (!aValue.isEmpty())
4843 {
4844 for (size_t i = 0; i < aKey.length(); ++i)
4845 {
4846 char ch = aKey[i];
4847 if (!RTLocCIsPrint(ch))
4848 return E_INVALIDARG;
4849 }
4850 }
4851
4852 Utf8Str strOldValue; // empty
4853
4854 // locking note: we only hold the read lock briefly to look up the old value,
4855 // then release it and call the onExtraCanChange callbacks. There is a small
4856 // chance of a race insofar as the callback might be called twice if two callers
4857 // change the same key at the same time, but that's a much better solution
4858 // than the deadlock we had here before. The actual changing of the extradata
4859 // is then performed under the write lock and race-free.
4860
4861 // look up the old value first; if nothing has changed then we need not do anything
4862 {
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4864
4865 // For snapshots don't even think about allowing changes, extradata
4866 // is global for a machine, so there is nothing snapshot specific.
4867 if (i_isSnapshotMachine())
4868 return setError(VBOX_E_INVALID_VM_STATE,
4869 tr("Cannot set extradata for a snapshot"));
4870
4871 // check if the right IMachine instance is used
4872 if (mData->mRegistered && !i_isSessionMachine())
4873 return setError(VBOX_E_INVALID_VM_STATE,
4874 tr("Cannot set extradata for an immutable machine"));
4875
4876 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4877 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4878 strOldValue = it->second;
4879 }
4880
4881 bool fChanged;
4882 if ((fChanged = (strOldValue != aValue)))
4883 {
4884 // ask for permission from all listeners outside the locks;
4885 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4886 // lock to copy the list of callbacks to invoke
4887 Bstr error;
4888 Bstr bstrValue(aValue);
4889
4890 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4891 {
4892 const char *sep = error.isEmpty() ? "" : ": ";
4893 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4894 return setError(E_ACCESSDENIED,
4895 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4896 aKey.c_str(),
4897 aValue.c_str(),
4898 sep,
4899 error.raw());
4900 }
4901
4902 // data is changing and change not vetoed: then write it out under the lock
4903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4904
4905 if (aValue.isEmpty())
4906 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4907 else
4908 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4909 // creates a new key if needed
4910
4911 bool fNeedsGlobalSaveSettings = false;
4912 // This saving of settings is tricky: there is no "old state" for the
4913 // extradata items at all (unlike all other settings), so the old/new
4914 // settings comparison would give a wrong result!
4915 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4916
4917 if (fNeedsGlobalSaveSettings)
4918 {
4919 // save the global settings; for that we should hold only the VirtualBox lock
4920 alock.release();
4921 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4922 mParent->i_saveSettings();
4923 }
4924 }
4925
4926 // fire notification outside the lock
4927 if (fChanged)
4928 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4929
4930 return S_OK;
4931}
4932
4933HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4934{
4935 aProgress = NULL;
4936 NOREF(aSettingsFilePath);
4937 ReturnComNotImplemented();
4938}
4939
4940HRESULT Machine::saveSettings()
4941{
4942 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4945 if (FAILED(rc)) return rc;
4946
4947 /* the settings file path may never be null */
4948 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4949
4950 /* save all VM data excluding snapshots */
4951 bool fNeedsGlobalSaveSettings = false;
4952 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4953 mlock.release();
4954
4955 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4956 {
4957 // save the global settings; for that we should hold only the VirtualBox lock
4958 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4959 rc = mParent->i_saveSettings();
4960 }
4961
4962 return rc;
4963}
4964
4965
4966HRESULT Machine::discardSettings()
4967{
4968 /*
4969 * We need to take the machine list lock here as well as the machine one
4970 * or we'll get into trouble should any media stuff require rolling back.
4971 *
4972 * Details:
4973 *
4974 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4975 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4976 * 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]
4977 * 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
4978 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4979 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4980 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4981 * 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
4982 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4983 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4984 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4985 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4986 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4987 * 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]
4988 * 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] (*)
4989 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4990 * 0:005> k
4991 * # Child-SP RetAddr Call Site
4992 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4993 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4994 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4995 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4996 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4997 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4998 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4999 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5000 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5001 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5002 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5003 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5004 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5005 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5006 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5007 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5008 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5009 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5010 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5011 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5012 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5013 *
5014 */
5015 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5017
5018 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5019 if (FAILED(rc)) return rc;
5020
5021 /*
5022 * during this rollback, the session will be notified if data has
5023 * been actually changed
5024 */
5025 i_rollback(true /* aNotify */);
5026
5027 return S_OK;
5028}
5029
5030/** @note Locks objects! */
5031HRESULT Machine::unregister(AutoCaller &autoCaller,
5032 CleanupMode_T aCleanupMode,
5033 std::vector<ComPtr<IMedium> > &aMedia)
5034{
5035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5036
5037 Guid id(i_getId());
5038
5039 if (mData->mSession.mState != SessionState_Unlocked)
5040 return setError(VBOX_E_INVALID_OBJECT_STATE,
5041 tr("Cannot unregister the machine '%s' while it is locked"),
5042 mUserData->s.strName.c_str());
5043
5044 // wait for state dependents to drop to zero
5045 i_ensureNoStateDependencies();
5046
5047 if (!mData->mAccessible)
5048 {
5049 // inaccessible maschines can only be unregistered; uninitialize ourselves
5050 // here because currently there may be no unregistered that are inaccessible
5051 // (this state combination is not supported). Note releasing the caller and
5052 // leaving the lock before calling uninit()
5053 alock.release();
5054 autoCaller.release();
5055
5056 uninit();
5057
5058 mParent->i_unregisterMachine(this, id);
5059 // calls VirtualBox::i_saveSettings()
5060
5061 return S_OK;
5062 }
5063
5064 HRESULT rc = S_OK;
5065
5066 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5067 // discard saved state
5068 if (mData->mMachineState == MachineState_Saved)
5069 {
5070 // add the saved state file to the list of files the caller should delete
5071 Assert(!mSSData->strStateFilePath.isEmpty());
5072 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5073
5074 mSSData->strStateFilePath.setNull();
5075
5076 // unconditionally set the machine state to powered off, we now
5077 // know no session has locked the machine
5078 mData->mMachineState = MachineState_PoweredOff;
5079 }
5080
5081 size_t cSnapshots = 0;
5082 if (mData->mFirstSnapshot)
5083 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5084 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5085 // fail now before we start detaching media
5086 return setError(VBOX_E_INVALID_OBJECT_STATE,
5087 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5088 mUserData->s.strName.c_str(), cSnapshots);
5089
5090 // This list collects the medium objects from all medium attachments
5091 // which we will detach from the machine and its snapshots, in a specific
5092 // order which allows for closing all media without getting "media in use"
5093 // errors, simply by going through the list from the front to the back:
5094 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5095 // and must be closed before the parent media from the snapshots, or closing the parents
5096 // will fail because they still have children);
5097 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5098 // the root ("first") snapshot of the machine.
5099 MediaList llMedia;
5100
5101 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5102 && mMediumAttachments->size()
5103 )
5104 {
5105 // we have media attachments: detach them all and add the Medium objects to our list
5106 if (aCleanupMode != CleanupMode_UnregisterOnly)
5107 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5108 else
5109 return setError(VBOX_E_INVALID_OBJECT_STATE,
5110 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5111 mUserData->s.strName.c_str(), mMediumAttachments->size());
5112 }
5113
5114 if (cSnapshots)
5115 {
5116 // add the media from the medium attachments of the snapshots to llMedia
5117 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5118 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5119 // into the children first
5120
5121 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5122 MachineState_T oldState = mData->mMachineState;
5123 mData->mMachineState = MachineState_DeletingSnapshot;
5124
5125 // make a copy of the first snapshot so the refcount does not drop to 0
5126 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5127 // because of the AutoCaller voodoo)
5128 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5129
5130 // GO!
5131 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5132
5133 mData->mMachineState = oldState;
5134 }
5135
5136 if (FAILED(rc))
5137 {
5138 i_rollbackMedia();
5139 return rc;
5140 }
5141
5142 // commit all the media changes made above
5143 i_commitMedia();
5144
5145 mData->mRegistered = false;
5146
5147 // machine lock no longer needed
5148 alock.release();
5149
5150 // return media to caller
5151 aMedia.resize(llMedia.size());
5152 size_t i = 0;
5153 for (MediaList::const_iterator
5154 it = llMedia.begin();
5155 it != llMedia.end();
5156 ++it, ++i)
5157 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5158
5159 mParent->i_unregisterMachine(this, id);
5160 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5161
5162 return S_OK;
5163}
5164
5165/**
5166 * Task record for deleting a machine config.
5167 */
5168class Machine::DeleteConfigTask
5169 : public Machine::Task
5170{
5171public:
5172 DeleteConfigTask(Machine *m,
5173 Progress *p,
5174 const Utf8Str &t,
5175 const RTCList<ComPtr<IMedium> > &llMediums,
5176 const StringsList &llFilesToDelete)
5177 : Task(m, p, t),
5178 m_llMediums(llMediums),
5179 m_llFilesToDelete(llFilesToDelete)
5180 {}
5181
5182private:
5183 void handler()
5184 {
5185 try
5186 {
5187 m_pMachine->i_deleteConfigHandler(*this);
5188 }
5189 catch (...)
5190 {
5191 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5192 }
5193 }
5194
5195 RTCList<ComPtr<IMedium> > m_llMediums;
5196 StringsList m_llFilesToDelete;
5197
5198 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5199};
5200
5201/**
5202 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5203 * SessionMachine::taskHandler().
5204 *
5205 * @note Locks this object for writing.
5206 *
5207 * @param task
5208 * @return
5209 */
5210void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5211{
5212 LogFlowThisFuncEnter();
5213
5214 AutoCaller autoCaller(this);
5215 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5216 if (FAILED(autoCaller.rc()))
5217 {
5218 /* we might have been uninitialized because the session was accidentally
5219 * closed by the client, so don't assert */
5220 HRESULT rc = setError(E_FAIL,
5221 tr("The session has been accidentally closed"));
5222 task.m_pProgress->i_notifyComplete(rc);
5223 LogFlowThisFuncLeave();
5224 return;
5225 }
5226
5227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5228
5229 HRESULT rc = S_OK;
5230
5231 try
5232 {
5233 ULONG uLogHistoryCount = 3;
5234 ComPtr<ISystemProperties> systemProperties;
5235 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5236 if (FAILED(rc)) throw rc;
5237
5238 if (!systemProperties.isNull())
5239 {
5240 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5241 if (FAILED(rc)) throw rc;
5242 }
5243
5244 MachineState_T oldState = mData->mMachineState;
5245 i_setMachineState(MachineState_SettingUp);
5246 alock.release();
5247 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5248 {
5249 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5250 {
5251 AutoCaller mac(pMedium);
5252 if (FAILED(mac.rc())) throw mac.rc();
5253 Utf8Str strLocation = pMedium->i_getLocationFull();
5254 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5255 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5256 if (FAILED(rc)) throw rc;
5257 }
5258 if (pMedium->i_isMediumFormatFile())
5259 {
5260 ComPtr<IProgress> pProgress2;
5261 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5262 if (FAILED(rc)) throw rc;
5263 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5264 if (FAILED(rc)) throw rc;
5265 }
5266
5267 /* Close the medium, deliberately without checking the return
5268 * code, and without leaving any trace in the error info, as
5269 * a failure here is a very minor issue, which shouldn't happen
5270 * as above we even managed to delete the medium. */
5271 {
5272 ErrorInfoKeeper eik;
5273 pMedium->Close();
5274 }
5275 }
5276 i_setMachineState(oldState);
5277 alock.acquire();
5278
5279 // delete the files pushed on the task list by Machine::Delete()
5280 // (this includes saved states of the machine and snapshots and
5281 // medium storage files from the IMedium list passed in, and the
5282 // machine XML file)
5283 for (StringsList::const_iterator
5284 it = task.m_llFilesToDelete.begin();
5285 it != task.m_llFilesToDelete.end();
5286 ++it)
5287 {
5288 const Utf8Str &strFile = *it;
5289 LogFunc(("Deleting file %s\n", strFile.c_str()));
5290 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5291 if (FAILED(rc)) throw rc;
5292
5293 int vrc = RTFileDelete(strFile.c_str());
5294 if (RT_FAILURE(vrc))
5295 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5296 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5297 }
5298
5299 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5300 if (FAILED(rc)) throw rc;
5301
5302 /* delete the settings only when the file actually exists */
5303 if (mData->pMachineConfigFile->fileExists())
5304 {
5305 /* Delete any backup or uncommitted XML files. Ignore failures.
5306 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5307 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5308 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5309 RTFileDelete(otherXml.c_str());
5310 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5311 RTFileDelete(otherXml.c_str());
5312
5313 /* delete the Logs folder, nothing important should be left
5314 * there (we don't check for errors because the user might have
5315 * some private files there that we don't want to delete) */
5316 Utf8Str logFolder;
5317 getLogFolder(logFolder);
5318 Assert(logFolder.length());
5319 if (RTDirExists(logFolder.c_str()))
5320 {
5321 /* Delete all VBox.log[.N] files from the Logs folder
5322 * (this must be in sync with the rotation logic in
5323 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5324 * files that may have been created by the GUI. */
5325 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5326 logFolder.c_str(), RTPATH_DELIMITER);
5327 RTFileDelete(log.c_str());
5328 log = Utf8StrFmt("%s%cVBox.png",
5329 logFolder.c_str(), RTPATH_DELIMITER);
5330 RTFileDelete(log.c_str());
5331 for (int i = uLogHistoryCount; i > 0; i--)
5332 {
5333 log = Utf8StrFmt("%s%cVBox.log.%d",
5334 logFolder.c_str(), RTPATH_DELIMITER, i);
5335 RTFileDelete(log.c_str());
5336 log = Utf8StrFmt("%s%cVBox.png.%d",
5337 logFolder.c_str(), RTPATH_DELIMITER, i);
5338 RTFileDelete(log.c_str());
5339 }
5340#if defined(RT_OS_WINDOWS)
5341 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5342 RTFileDelete(log.c_str());
5343 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345#endif
5346
5347 RTDirRemove(logFolder.c_str());
5348 }
5349
5350 /* delete the Snapshots folder, nothing important should be left
5351 * there (we don't check for errors because the user might have
5352 * some private files there that we don't want to delete) */
5353 Utf8Str strFullSnapshotFolder;
5354 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5355 Assert(!strFullSnapshotFolder.isEmpty());
5356 if (RTDirExists(strFullSnapshotFolder.c_str()))
5357 RTDirRemove(strFullSnapshotFolder.c_str());
5358
5359 // delete the directory that contains the settings file, but only
5360 // if it matches the VM name
5361 Utf8Str settingsDir;
5362 if (i_isInOwnDir(&settingsDir))
5363 RTDirRemove(settingsDir.c_str());
5364 }
5365
5366 alock.release();
5367
5368 mParent->i_saveModifiedRegistries();
5369 }
5370 catch (HRESULT aRC) { rc = aRC; }
5371
5372 task.m_pProgress->i_notifyComplete(rc);
5373
5374 LogFlowThisFuncLeave();
5375}
5376
5377HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5378{
5379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5380
5381 HRESULT rc = i_checkStateDependency(MutableStateDep);
5382 if (FAILED(rc)) return rc;
5383
5384 if (mData->mRegistered)
5385 return setError(VBOX_E_INVALID_VM_STATE,
5386 tr("Cannot delete settings of a registered machine"));
5387
5388 // collect files to delete
5389 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5390 if (mData->pMachineConfigFile->fileExists())
5391 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5392
5393 RTCList<ComPtr<IMedium> > llMediums;
5394 for (size_t i = 0; i < aMedia.size(); ++i)
5395 {
5396 IMedium *pIMedium(aMedia[i]);
5397 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5398 if (pMedium.isNull())
5399 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5400 SafeArray<BSTR> ids;
5401 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5402 if (FAILED(rc)) return rc;
5403 /* At this point the medium should not have any back references
5404 * anymore. If it has it is attached to another VM and *must* not
5405 * deleted. */
5406 if (ids.size() < 1)
5407 llMediums.append(pMedium);
5408 }
5409
5410 ComObjPtr<Progress> pProgress;
5411 pProgress.createObject();
5412 rc = pProgress->init(i_getVirtualBox(),
5413 static_cast<IMachine*>(this) /* aInitiator */,
5414 tr("Deleting files"),
5415 true /* fCancellable */,
5416 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5417 tr("Collecting file inventory"));
5418 if (FAILED(rc))
5419 return rc;
5420
5421 /* create and start the task on a separate thread (note that it will not
5422 * start working until we release alock) */
5423 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5424 rc = pTask->createThread();
5425 if (FAILED(rc))
5426 return rc;
5427
5428 pProgress.queryInterfaceTo(aProgress.asOutParam());
5429
5430 LogFlowFuncLeave();
5431
5432 return S_OK;
5433}
5434
5435HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5436{
5437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5438
5439 ComObjPtr<Snapshot> pSnapshot;
5440 HRESULT rc;
5441
5442 if (aNameOrId.isEmpty())
5443 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5444 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5445 else
5446 {
5447 Guid uuid(aNameOrId);
5448 if (uuid.isValid())
5449 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5450 else
5451 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5452 }
5453 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5454
5455 return rc;
5456}
5457
5458HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5459 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5460{
5461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5462
5463 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5464 if (FAILED(rc)) return rc;
5465
5466 ComObjPtr<SharedFolder> sharedFolder;
5467 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5468 if (SUCCEEDED(rc))
5469 return setError(VBOX_E_OBJECT_IN_USE,
5470 tr("Shared folder named '%s' already exists"),
5471 aName.c_str());
5472
5473 sharedFolder.createObject();
5474 rc = sharedFolder->init(i_getMachine(),
5475 aName,
5476 aHostPath,
5477 !!aWritable,
5478 !!aAutomount,
5479 aAutoMountPoint,
5480 true /* fFailOnError */);
5481 if (FAILED(rc)) return rc;
5482
5483 i_setModified(IsModified_SharedFolders);
5484 mHWData.backup();
5485 mHWData->mSharedFolders.push_back(sharedFolder);
5486
5487 /* inform the direct session if any */
5488 alock.release();
5489 i_onSharedFolderChange();
5490
5491 return S_OK;
5492}
5493
5494HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5495{
5496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5497
5498 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5499 if (FAILED(rc)) return rc;
5500
5501 ComObjPtr<SharedFolder> sharedFolder;
5502 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5503 if (FAILED(rc)) return rc;
5504
5505 i_setModified(IsModified_SharedFolders);
5506 mHWData.backup();
5507 mHWData->mSharedFolders.remove(sharedFolder);
5508
5509 /* inform the direct session if any */
5510 alock.release();
5511 i_onSharedFolderChange();
5512
5513 return S_OK;
5514}
5515
5516HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5517{
5518 /* start with No */
5519 *aCanShow = FALSE;
5520
5521 ComPtr<IInternalSessionControl> directControl;
5522 {
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 if (mData->mSession.mState != SessionState_Locked)
5526 return setError(VBOX_E_INVALID_VM_STATE,
5527 tr("Machine is not locked for session (session state: %s)"),
5528 Global::stringifySessionState(mData->mSession.mState));
5529
5530 if (mData->mSession.mLockType == LockType_VM)
5531 directControl = mData->mSession.mDirectControl;
5532 }
5533
5534 /* ignore calls made after #OnSessionEnd() is called */
5535 if (!directControl)
5536 return S_OK;
5537
5538 LONG64 dummy;
5539 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5540}
5541
5542HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5543{
5544 ComPtr<IInternalSessionControl> directControl;
5545 {
5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5547
5548 if (mData->mSession.mState != SessionState_Locked)
5549 return setError(E_FAIL,
5550 tr("Machine is not locked for session (session state: %s)"),
5551 Global::stringifySessionState(mData->mSession.mState));
5552
5553 if (mData->mSession.mLockType == LockType_VM)
5554 directControl = mData->mSession.mDirectControl;
5555 }
5556
5557 /* ignore calls made after #OnSessionEnd() is called */
5558 if (!directControl)
5559 return S_OK;
5560
5561 BOOL dummy;
5562 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5563}
5564
5565#ifdef VBOX_WITH_GUEST_PROPS
5566/**
5567 * Look up a guest property in VBoxSVC's internal structures.
5568 */
5569HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5570 com::Utf8Str &aValue,
5571 LONG64 *aTimestamp,
5572 com::Utf8Str &aFlags) const
5573{
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5577 if (it != mHWData->mGuestProperties.end())
5578 {
5579 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5580 aValue = it->second.strValue;
5581 *aTimestamp = it->second.mTimestamp;
5582 GuestPropWriteFlags(it->second.mFlags, szFlags);
5583 aFlags = Utf8Str(szFlags);
5584 }
5585
5586 return S_OK;
5587}
5588
5589/**
5590 * Query the VM that a guest property belongs to for the property.
5591 * @returns E_ACCESSDENIED if the VM process is not available or not
5592 * currently handling queries and the lookup should then be done in
5593 * VBoxSVC.
5594 */
5595HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags) const
5599{
5600 HRESULT rc = S_OK;
5601 Bstr bstrValue;
5602 Bstr bstrFlags;
5603
5604 ComPtr<IInternalSessionControl> directControl;
5605 {
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 /* ignore calls made after #OnSessionEnd() is called */
5612 if (!directControl)
5613 rc = E_ACCESSDENIED;
5614 else
5615 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5616 0 /* accessMode */,
5617 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5618
5619 aValue = bstrValue;
5620 aFlags = bstrFlags;
5621
5622 return rc;
5623}
5624#endif // VBOX_WITH_GUEST_PROPS
5625
5626HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5627 com::Utf8Str &aValue,
5628 LONG64 *aTimestamp,
5629 com::Utf8Str &aFlags)
5630{
5631#ifndef VBOX_WITH_GUEST_PROPS
5632 ReturnComNotImplemented();
5633#else // VBOX_WITH_GUEST_PROPS
5634
5635 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5636
5637 if (rc == E_ACCESSDENIED)
5638 /* The VM is not running or the service is not (yet) accessible */
5639 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5640 return rc;
5641#endif // VBOX_WITH_GUEST_PROPS
5642}
5643
5644HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5645{
5646 LONG64 dummyTimestamp;
5647 com::Utf8Str dummyFlags;
5648 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5649 return rc;
5650
5651}
5652HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5653{
5654 com::Utf8Str dummyFlags;
5655 com::Utf8Str dummyValue;
5656 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5657 return rc;
5658}
5659
5660#ifdef VBOX_WITH_GUEST_PROPS
5661/**
5662 * Set a guest property in VBoxSVC's internal structures.
5663 */
5664HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5665 const com::Utf8Str &aFlags, bool fDelete)
5666{
5667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5668 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5669 if (FAILED(rc)) return rc;
5670
5671 try
5672 {
5673 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5674 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5675 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5676
5677 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5678 if (it == mHWData->mGuestProperties.end())
5679 {
5680 if (!fDelete)
5681 {
5682 i_setModified(IsModified_MachineData);
5683 mHWData.backupEx();
5684
5685 RTTIMESPEC time;
5686 HWData::GuestProperty prop;
5687 prop.strValue = Bstr(aValue).raw();
5688 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5689 prop.mFlags = fFlags;
5690 mHWData->mGuestProperties[aName] = prop;
5691 }
5692 }
5693 else
5694 {
5695 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5696 {
5697 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5698 }
5699 else
5700 {
5701 i_setModified(IsModified_MachineData);
5702 mHWData.backupEx();
5703
5704 /* The backupEx() operation invalidates our iterator,
5705 * so get a new one. */
5706 it = mHWData->mGuestProperties.find(aName);
5707 Assert(it != mHWData->mGuestProperties.end());
5708
5709 if (!fDelete)
5710 {
5711 RTTIMESPEC time;
5712 it->second.strValue = aValue;
5713 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5714 it->second.mFlags = fFlags;
5715 }
5716 else
5717 mHWData->mGuestProperties.erase(it);
5718 }
5719 }
5720
5721 if (SUCCEEDED(rc))
5722 {
5723 alock.release();
5724
5725 mParent->i_onGuestPropertyChange(mData->mUuid,
5726 Bstr(aName).raw(),
5727 Bstr(aValue).raw(),
5728 Bstr(aFlags).raw());
5729 }
5730 }
5731 catch (std::bad_alloc &)
5732 {
5733 rc = E_OUTOFMEMORY;
5734 }
5735
5736 return rc;
5737}
5738
5739/**
5740 * Set a property on the VM that that property belongs to.
5741 * @returns E_ACCESSDENIED if the VM process is not available or not
5742 * currently handling queries and the setting should then be done in
5743 * VBoxSVC.
5744 */
5745HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5746 const com::Utf8Str &aFlags, bool fDelete)
5747{
5748 HRESULT rc;
5749
5750 try
5751 {
5752 ComPtr<IInternalSessionControl> directControl;
5753 {
5754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5755 if (mData->mSession.mLockType == LockType_VM)
5756 directControl = mData->mSession.mDirectControl;
5757 }
5758
5759 Bstr dummy1; /* will not be changed (setter) */
5760 Bstr dummy2; /* will not be changed (setter) */
5761 LONG64 dummy64;
5762 if (!directControl)
5763 rc = E_ACCESSDENIED;
5764 else
5765 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5766 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5767 fDelete ? 2 : 1 /* accessMode */,
5768 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5769 }
5770 catch (std::bad_alloc &)
5771 {
5772 rc = E_OUTOFMEMORY;
5773 }
5774
5775 return rc;
5776}
5777#endif // VBOX_WITH_GUEST_PROPS
5778
5779HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5780 const com::Utf8Str &aFlags)
5781{
5782#ifndef VBOX_WITH_GUEST_PROPS
5783 ReturnComNotImplemented();
5784#else // VBOX_WITH_GUEST_PROPS
5785 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5786 if (rc == E_ACCESSDENIED)
5787 /* The VM is not running or the service is not (yet) accessible */
5788 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5789 return rc;
5790#endif // VBOX_WITH_GUEST_PROPS
5791}
5792
5793HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5794{
5795 return setGuestProperty(aProperty, aValue, "");
5796}
5797
5798HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5799{
5800#ifndef VBOX_WITH_GUEST_PROPS
5801 ReturnComNotImplemented();
5802#else // VBOX_WITH_GUEST_PROPS
5803 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5804 if (rc == E_ACCESSDENIED)
5805 /* The VM is not running or the service is not (yet) accessible */
5806 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5807 return rc;
5808#endif // VBOX_WITH_GUEST_PROPS
5809}
5810
5811#ifdef VBOX_WITH_GUEST_PROPS
5812/**
5813 * Enumerate the guest properties in VBoxSVC's internal structures.
5814 */
5815HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5816 std::vector<com::Utf8Str> &aNames,
5817 std::vector<com::Utf8Str> &aValues,
5818 std::vector<LONG64> &aTimestamps,
5819 std::vector<com::Utf8Str> &aFlags)
5820{
5821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5822 Utf8Str strPatterns(aPatterns);
5823
5824 /*
5825 * Look for matching patterns and build up a list.
5826 */
5827 HWData::GuestPropertyMap propMap;
5828 for (HWData::GuestPropertyMap::const_iterator
5829 it = mHWData->mGuestProperties.begin();
5830 it != mHWData->mGuestProperties.end();
5831 ++it)
5832 {
5833 if ( strPatterns.isEmpty()
5834 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5835 RTSTR_MAX,
5836 it->first.c_str(),
5837 RTSTR_MAX,
5838 NULL)
5839 )
5840 propMap.insert(*it);
5841 }
5842
5843 alock.release();
5844
5845 /*
5846 * And build up the arrays for returning the property information.
5847 */
5848 size_t cEntries = propMap.size();
5849
5850 aNames.resize(cEntries);
5851 aValues.resize(cEntries);
5852 aTimestamps.resize(cEntries);
5853 aFlags.resize(cEntries);
5854
5855 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5856 size_t i = 0;
5857 for (HWData::GuestPropertyMap::const_iterator
5858 it = propMap.begin();
5859 it != propMap.end();
5860 ++it, ++i)
5861 {
5862 aNames[i] = it->first;
5863 aValues[i] = it->second.strValue;
5864 aTimestamps[i] = it->second.mTimestamp;
5865 GuestPropWriteFlags(it->second.mFlags, szFlags);
5866 aFlags[i] = Utf8Str(szFlags);
5867 }
5868
5869 return S_OK;
5870}
5871
5872/**
5873 * Enumerate the properties managed by a VM.
5874 * @returns E_ACCESSDENIED if the VM process is not available or not
5875 * currently handling queries and the setting should then be done in
5876 * VBoxSVC.
5877 */
5878HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5879 std::vector<com::Utf8Str> &aNames,
5880 std::vector<com::Utf8Str> &aValues,
5881 std::vector<LONG64> &aTimestamps,
5882 std::vector<com::Utf8Str> &aFlags)
5883{
5884 HRESULT rc;
5885 ComPtr<IInternalSessionControl> directControl;
5886 {
5887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5888 if (mData->mSession.mLockType == LockType_VM)
5889 directControl = mData->mSession.mDirectControl;
5890 }
5891
5892 com::SafeArray<BSTR> bNames;
5893 com::SafeArray<BSTR> bValues;
5894 com::SafeArray<LONG64> bTimestamps;
5895 com::SafeArray<BSTR> bFlags;
5896
5897 if (!directControl)
5898 rc = E_ACCESSDENIED;
5899 else
5900 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5901 ComSafeArrayAsOutParam(bNames),
5902 ComSafeArrayAsOutParam(bValues),
5903 ComSafeArrayAsOutParam(bTimestamps),
5904 ComSafeArrayAsOutParam(bFlags));
5905 size_t i;
5906 aNames.resize(bNames.size());
5907 for (i = 0; i < bNames.size(); ++i)
5908 aNames[i] = Utf8Str(bNames[i]);
5909 aValues.resize(bValues.size());
5910 for (i = 0; i < bValues.size(); ++i)
5911 aValues[i] = Utf8Str(bValues[i]);
5912 aTimestamps.resize(bTimestamps.size());
5913 for (i = 0; i < bTimestamps.size(); ++i)
5914 aTimestamps[i] = bTimestamps[i];
5915 aFlags.resize(bFlags.size());
5916 for (i = 0; i < bFlags.size(); ++i)
5917 aFlags[i] = Utf8Str(bFlags[i]);
5918
5919 return rc;
5920}
5921#endif // VBOX_WITH_GUEST_PROPS
5922HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5923 std::vector<com::Utf8Str> &aNames,
5924 std::vector<com::Utf8Str> &aValues,
5925 std::vector<LONG64> &aTimestamps,
5926 std::vector<com::Utf8Str> &aFlags)
5927{
5928#ifndef VBOX_WITH_GUEST_PROPS
5929 ReturnComNotImplemented();
5930#else // VBOX_WITH_GUEST_PROPS
5931
5932 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5933
5934 if (rc == E_ACCESSDENIED)
5935 /* The VM is not running or the service is not (yet) accessible */
5936 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5937 return rc;
5938#endif // VBOX_WITH_GUEST_PROPS
5939}
5940
5941HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5942 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5943{
5944 MediumAttachmentList atts;
5945
5946 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5947 if (FAILED(rc)) return rc;
5948
5949 aMediumAttachments.resize(atts.size());
5950 size_t i = 0;
5951 for (MediumAttachmentList::const_iterator
5952 it = atts.begin();
5953 it != atts.end();
5954 ++it, ++i)
5955 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5956
5957 return S_OK;
5958}
5959
5960HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5961 LONG aControllerPort,
5962 LONG aDevice,
5963 ComPtr<IMediumAttachment> &aAttachment)
5964{
5965 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5966 aName.c_str(), aControllerPort, aDevice));
5967
5968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5969
5970 aAttachment = NULL;
5971
5972 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5973 aName,
5974 aControllerPort,
5975 aDevice);
5976 if (pAttach.isNull())
5977 return setError(VBOX_E_OBJECT_NOT_FOUND,
5978 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5979 aDevice, aControllerPort, aName.c_str());
5980
5981 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5982
5983 return S_OK;
5984}
5985
5986
5987HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5988 StorageBus_T aConnectionType,
5989 ComPtr<IStorageController> &aController)
5990{
5991 if ( (aConnectionType <= StorageBus_Null)
5992 || (aConnectionType > StorageBus_PCIe))
5993 return setError(E_INVALIDARG,
5994 tr("Invalid connection type: %d"),
5995 aConnectionType);
5996
5997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5998
5999 HRESULT rc = i_checkStateDependency(MutableStateDep);
6000 if (FAILED(rc)) return rc;
6001
6002 /* try to find one with the name first. */
6003 ComObjPtr<StorageController> ctrl;
6004
6005 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6006 if (SUCCEEDED(rc))
6007 return setError(VBOX_E_OBJECT_IN_USE,
6008 tr("Storage controller named '%s' already exists"),
6009 aName.c_str());
6010
6011 ctrl.createObject();
6012
6013 /* get a new instance number for the storage controller */
6014 ULONG ulInstance = 0;
6015 bool fBootable = true;
6016 for (StorageControllerList::const_iterator
6017 it = mStorageControllers->begin();
6018 it != mStorageControllers->end();
6019 ++it)
6020 {
6021 if ((*it)->i_getStorageBus() == aConnectionType)
6022 {
6023 ULONG ulCurInst = (*it)->i_getInstance();
6024
6025 if (ulCurInst >= ulInstance)
6026 ulInstance = ulCurInst + 1;
6027
6028 /* Only one controller of each type can be marked as bootable. */
6029 if ((*it)->i_getBootable())
6030 fBootable = false;
6031 }
6032 }
6033
6034 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6035 if (FAILED(rc)) return rc;
6036
6037 i_setModified(IsModified_Storage);
6038 mStorageControllers.backup();
6039 mStorageControllers->push_back(ctrl);
6040
6041 ctrl.queryInterfaceTo(aController.asOutParam());
6042
6043 /* inform the direct session if any */
6044 alock.release();
6045 i_onStorageControllerChange();
6046
6047 return S_OK;
6048}
6049
6050HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6051 ComPtr<IStorageController> &aStorageController)
6052{
6053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6054
6055 ComObjPtr<StorageController> ctrl;
6056
6057 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6058 if (SUCCEEDED(rc))
6059 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6060
6061 return rc;
6062}
6063
6064HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6065 ULONG aInstance,
6066 ComPtr<IStorageController> &aStorageController)
6067{
6068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6069
6070 for (StorageControllerList::const_iterator
6071 it = mStorageControllers->begin();
6072 it != mStorageControllers->end();
6073 ++it)
6074 {
6075 if ( (*it)->i_getStorageBus() == aConnectionType
6076 && (*it)->i_getInstance() == aInstance)
6077 {
6078 (*it).queryInterfaceTo(aStorageController.asOutParam());
6079 return S_OK;
6080 }
6081 }
6082
6083 return setError(VBOX_E_OBJECT_NOT_FOUND,
6084 tr("Could not find a storage controller with instance number '%lu'"),
6085 aInstance);
6086}
6087
6088HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6089{
6090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6091
6092 HRESULT rc = i_checkStateDependency(MutableStateDep);
6093 if (FAILED(rc)) return rc;
6094
6095 ComObjPtr<StorageController> ctrl;
6096
6097 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6098 if (SUCCEEDED(rc))
6099 {
6100 /* Ensure that only one controller of each type is marked as bootable. */
6101 if (aBootable == TRUE)
6102 {
6103 for (StorageControllerList::const_iterator
6104 it = mStorageControllers->begin();
6105 it != mStorageControllers->end();
6106 ++it)
6107 {
6108 ComObjPtr<StorageController> aCtrl = (*it);
6109
6110 if ( (aCtrl->i_getName() != aName)
6111 && aCtrl->i_getBootable() == TRUE
6112 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6113 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6114 {
6115 aCtrl->i_setBootable(FALSE);
6116 break;
6117 }
6118 }
6119 }
6120
6121 if (SUCCEEDED(rc))
6122 {
6123 ctrl->i_setBootable(aBootable);
6124 i_setModified(IsModified_Storage);
6125 }
6126 }
6127
6128 if (SUCCEEDED(rc))
6129 {
6130 /* inform the direct session if any */
6131 alock.release();
6132 i_onStorageControllerChange();
6133 }
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6139{
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 ComObjPtr<StorageController> ctrl;
6146 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6147 if (FAILED(rc)) return rc;
6148
6149 {
6150 /* find all attached devices to the appropriate storage controller and detach them all */
6151 // make a temporary list because detachDevice invalidates iterators into
6152 // mMediumAttachments
6153 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6154
6155 for (MediumAttachmentList::const_iterator
6156 it = llAttachments2.begin();
6157 it != llAttachments2.end();
6158 ++it)
6159 {
6160 MediumAttachment *pAttachTemp = *it;
6161
6162 AutoCaller localAutoCaller(pAttachTemp);
6163 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6164
6165 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6166
6167 if (pAttachTemp->i_getControllerName() == aName)
6168 {
6169 rc = i_detachDevice(pAttachTemp, alock, NULL);
6170 if (FAILED(rc)) return rc;
6171 }
6172 }
6173 }
6174
6175 /* We can remove it now. */
6176 i_setModified(IsModified_Storage);
6177 mStorageControllers.backup();
6178
6179 ctrl->i_unshare();
6180
6181 mStorageControllers->remove(ctrl);
6182
6183 /* inform the direct session if any */
6184 alock.release();
6185 i_onStorageControllerChange();
6186
6187 return S_OK;
6188}
6189
6190HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6191 ComPtr<IUSBController> &aController)
6192{
6193 if ( (aType <= USBControllerType_Null)
6194 || (aType >= USBControllerType_Last))
6195 return setError(E_INVALIDARG,
6196 tr("Invalid USB controller type: %d"),
6197 aType);
6198
6199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 HRESULT rc = i_checkStateDependency(MutableStateDep);
6202 if (FAILED(rc)) return rc;
6203
6204 /* try to find one with the same type first. */
6205 ComObjPtr<USBController> ctrl;
6206
6207 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6208 if (SUCCEEDED(rc))
6209 return setError(VBOX_E_OBJECT_IN_USE,
6210 tr("USB controller named '%s' already exists"),
6211 aName.c_str());
6212
6213 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6214 ULONG maxInstances;
6215 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6216 if (FAILED(rc))
6217 return rc;
6218
6219 ULONG cInstances = i_getUSBControllerCountByType(aType);
6220 if (cInstances >= maxInstances)
6221 return setError(E_INVALIDARG,
6222 tr("Too many USB controllers of this type"));
6223
6224 ctrl.createObject();
6225
6226 rc = ctrl->init(this, aName, aType);
6227 if (FAILED(rc)) return rc;
6228
6229 i_setModified(IsModified_USB);
6230 mUSBControllers.backup();
6231 mUSBControllers->push_back(ctrl);
6232
6233 ctrl.queryInterfaceTo(aController.asOutParam());
6234
6235 /* inform the direct session if any */
6236 alock.release();
6237 i_onUSBControllerChange();
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6243{
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6249 if (SUCCEEDED(rc))
6250 ctrl.queryInterfaceTo(aController.asOutParam());
6251
6252 return rc;
6253}
6254
6255HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6256 ULONG *aControllers)
6257{
6258 if ( (aType <= USBControllerType_Null)
6259 || (aType >= USBControllerType_Last))
6260 return setError(E_INVALIDARG,
6261 tr("Invalid USB controller type: %d"),
6262 aType);
6263
6264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6265
6266 ComObjPtr<USBController> ctrl;
6267
6268 *aControllers = i_getUSBControllerCountByType(aType);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6274{
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 HRESULT rc = i_checkStateDependency(MutableStateDep);
6279 if (FAILED(rc)) return rc;
6280
6281 ComObjPtr<USBController> ctrl;
6282 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6283 if (FAILED(rc)) return rc;
6284
6285 i_setModified(IsModified_USB);
6286 mUSBControllers.backup();
6287
6288 ctrl->i_unshare();
6289
6290 mUSBControllers->remove(ctrl);
6291
6292 /* inform the direct session if any */
6293 alock.release();
6294 i_onUSBControllerChange();
6295
6296 return S_OK;
6297}
6298
6299HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6300 ULONG *aOriginX,
6301 ULONG *aOriginY,
6302 ULONG *aWidth,
6303 ULONG *aHeight,
6304 BOOL *aEnabled)
6305{
6306 uint32_t u32OriginX= 0;
6307 uint32_t u32OriginY= 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310 uint16_t u16Flags = 0;
6311
6312 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6313 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6314 if (RT_FAILURE(vrc))
6315 {
6316#ifdef RT_OS_WINDOWS
6317 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6318 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6319 * So just assign fEnable to TRUE again.
6320 * The right fix would be to change GUI API wrappers to make sure that parameters
6321 * are changed only if API succeeds.
6322 */
6323 *aEnabled = TRUE;
6324#endif
6325 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6326 tr("Saved guest size is not available (%Rrc)"),
6327 vrc);
6328 }
6329
6330 *aOriginX = u32OriginX;
6331 *aOriginY = u32OriginY;
6332 *aWidth = u32Width;
6333 *aHeight = u32Height;
6334 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6340 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6341{
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 if ( aBitmapFormat != BitmapFormat_BGR0
6346 && aBitmapFormat != BitmapFormat_BGRA
6347 && aBitmapFormat != BitmapFormat_RGBA
6348 && aBitmapFormat != BitmapFormat_PNG)
6349 return setError(E_NOTIMPL,
6350 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6351
6352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6353
6354 uint8_t *pu8Data = NULL;
6355 uint32_t cbData = 0;
6356 uint32_t u32Width = 0;
6357 uint32_t u32Height = 0;
6358
6359 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6360
6361 if (RT_FAILURE(vrc))
6362 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6363 tr("Saved thumbnail data is not available (%Rrc)"),
6364 vrc);
6365
6366 HRESULT hr = S_OK;
6367
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370
6371 if (cbData > 0)
6372 {
6373 /* Convert pixels to the format expected by the API caller. */
6374 if (aBitmapFormat == BitmapFormat_BGR0)
6375 {
6376 /* [0] B, [1] G, [2] R, [3] 0. */
6377 aData.resize(cbData);
6378 memcpy(&aData.front(), pu8Data, cbData);
6379 }
6380 else if (aBitmapFormat == BitmapFormat_BGRA)
6381 {
6382 /* [0] B, [1] G, [2] R, [3] A. */
6383 aData.resize(cbData);
6384 for (uint32_t i = 0; i < cbData; i += 4)
6385 {
6386 aData[i] = pu8Data[i];
6387 aData[i + 1] = pu8Data[i + 1];
6388 aData[i + 2] = pu8Data[i + 2];
6389 aData[i + 3] = 0xff;
6390 }
6391 }
6392 else if (aBitmapFormat == BitmapFormat_RGBA)
6393 {
6394 /* [0] R, [1] G, [2] B, [3] A. */
6395 aData.resize(cbData);
6396 for (uint32_t i = 0; i < cbData; i += 4)
6397 {
6398 aData[i] = pu8Data[i + 2];
6399 aData[i + 1] = pu8Data[i + 1];
6400 aData[i + 2] = pu8Data[i];
6401 aData[i + 3] = 0xff;
6402 }
6403 }
6404 else if (aBitmapFormat == BitmapFormat_PNG)
6405 {
6406 uint8_t *pu8PNG = NULL;
6407 uint32_t cbPNG = 0;
6408 uint32_t cxPNG = 0;
6409 uint32_t cyPNG = 0;
6410
6411 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6412
6413 if (RT_SUCCESS(vrc))
6414 {
6415 aData.resize(cbPNG);
6416 if (cbPNG)
6417 memcpy(&aData.front(), pu8PNG, cbPNG);
6418 }
6419 else
6420 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6421 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6422 vrc);
6423
6424 RTMemFree(pu8PNG);
6425 }
6426 }
6427
6428 freeSavedDisplayScreenshot(pu8Data);
6429
6430 return hr;
6431}
6432
6433HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6434 ULONG *aWidth,
6435 ULONG *aHeight,
6436 std::vector<BitmapFormat_T> &aBitmapFormats)
6437{
6438 if (aScreenId != 0)
6439 return E_NOTIMPL;
6440
6441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6442
6443 uint8_t *pu8Data = NULL;
6444 uint32_t cbData = 0;
6445 uint32_t u32Width = 0;
6446 uint32_t u32Height = 0;
6447
6448 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6449
6450 if (RT_FAILURE(vrc))
6451 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6452 tr("Saved screenshot data is not available (%Rrc)"),
6453 vrc);
6454
6455 *aWidth = u32Width;
6456 *aHeight = u32Height;
6457 aBitmapFormats.resize(1);
6458 aBitmapFormats[0] = BitmapFormat_PNG;
6459
6460 freeSavedDisplayScreenshot(pu8Data);
6461
6462 return S_OK;
6463}
6464
6465HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6466 BitmapFormat_T aBitmapFormat,
6467 ULONG *aWidth,
6468 ULONG *aHeight,
6469 std::vector<BYTE> &aData)
6470{
6471 if (aScreenId != 0)
6472 return E_NOTIMPL;
6473
6474 if (aBitmapFormat != BitmapFormat_PNG)
6475 return E_NOTIMPL;
6476
6477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6478
6479 uint8_t *pu8Data = NULL;
6480 uint32_t cbData = 0;
6481 uint32_t u32Width = 0;
6482 uint32_t u32Height = 0;
6483
6484 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6485
6486 if (RT_FAILURE(vrc))
6487 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6488 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6489 vrc);
6490
6491 *aWidth = u32Width;
6492 *aHeight = u32Height;
6493
6494 aData.resize(cbData);
6495 if (cbData)
6496 memcpy(&aData.front(), pu8Data, cbData);
6497
6498 freeSavedDisplayScreenshot(pu8Data);
6499
6500 return S_OK;
6501}
6502
6503HRESULT Machine::hotPlugCPU(ULONG aCpu)
6504{
6505 HRESULT rc = S_OK;
6506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6507
6508 if (!mHWData->mCPUHotPlugEnabled)
6509 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6510
6511 if (aCpu >= mHWData->mCPUCount)
6512 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6513
6514 if (mHWData->mCPUAttached[aCpu])
6515 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6516
6517 alock.release();
6518 rc = i_onCPUChange(aCpu, false);
6519 alock.acquire();
6520 if (FAILED(rc)) return rc;
6521
6522 i_setModified(IsModified_MachineData);
6523 mHWData.backup();
6524 mHWData->mCPUAttached[aCpu] = true;
6525
6526 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6527 if (Global::IsOnline(mData->mMachineState))
6528 i_saveSettings(NULL);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6534{
6535 HRESULT rc = S_OK;
6536
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 if (!mHWData->mCPUHotPlugEnabled)
6540 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6541
6542 if (aCpu >= SchemaDefs::MaxCPUCount)
6543 return setError(E_INVALIDARG,
6544 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6545 SchemaDefs::MaxCPUCount);
6546
6547 if (!mHWData->mCPUAttached[aCpu])
6548 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6549
6550 /* CPU 0 can't be detached */
6551 if (aCpu == 0)
6552 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6553
6554 alock.release();
6555 rc = i_onCPUChange(aCpu, true);
6556 alock.acquire();
6557 if (FAILED(rc)) return rc;
6558
6559 i_setModified(IsModified_MachineData);
6560 mHWData.backup();
6561 mHWData->mCPUAttached[aCpu] = false;
6562
6563 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6564 if (Global::IsOnline(mData->mMachineState))
6565 i_saveSettings(NULL);
6566
6567 return S_OK;
6568}
6569
6570HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6571{
6572 *aAttached = false;
6573
6574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6575
6576 /* If hotplug is enabled the CPU is always enabled. */
6577 if (!mHWData->mCPUHotPlugEnabled)
6578 {
6579 if (aCpu < mHWData->mCPUCount)
6580 *aAttached = true;
6581 }
6582 else
6583 {
6584 if (aCpu < SchemaDefs::MaxCPUCount)
6585 *aAttached = mHWData->mCPUAttached[aCpu];
6586 }
6587
6588 return S_OK;
6589}
6590
6591HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6592{
6593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 Utf8Str log = i_getLogFilename(aIdx);
6596 if (!RTFileExists(log.c_str()))
6597 log.setNull();
6598 aFilename = log;
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6604{
6605 if (aSize < 0)
6606 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6607
6608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6609
6610 HRESULT rc = S_OK;
6611 Utf8Str log = i_getLogFilename(aIdx);
6612
6613 /* do not unnecessarily hold the lock while doing something which does
6614 * not need the lock and potentially takes a long time. */
6615 alock.release();
6616
6617 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6618 * keeps the SOAP reply size under 1M for the webservice (we're using
6619 * base64 encoded strings for binary data for years now, avoiding the
6620 * expansion of each byte array element to approx. 25 bytes of XML. */
6621 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6622 aData.resize(cbData);
6623
6624 RTFILE LogFile;
6625 int vrc = RTFileOpen(&LogFile, log.c_str(),
6626 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6627 if (RT_SUCCESS(vrc))
6628 {
6629 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6630 if (RT_SUCCESS(vrc))
6631 aData.resize(cbData);
6632 else
6633 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6634 tr("Could not read log file '%s' (%Rrc)"),
6635 log.c_str(), vrc);
6636 RTFileClose(LogFile);
6637 }
6638 else
6639 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6640 tr("Could not open log file '%s' (%Rrc)"),
6641 log.c_str(), vrc);
6642
6643 if (FAILED(rc))
6644 aData.resize(0);
6645
6646 return rc;
6647}
6648
6649
6650/**
6651 * Currently this method doesn't attach device to the running VM,
6652 * just makes sure it's plugged on next VM start.
6653 */
6654HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6655{
6656 // lock scope
6657 {
6658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 HRESULT rc = i_checkStateDependency(MutableStateDep);
6661 if (FAILED(rc)) return rc;
6662
6663 ChipsetType_T aChipset = ChipsetType_PIIX3;
6664 COMGETTER(ChipsetType)(&aChipset);
6665
6666 if (aChipset != ChipsetType_ICH9)
6667 {
6668 return setError(E_INVALIDARG,
6669 tr("Host PCI attachment only supported with ICH9 chipset"));
6670 }
6671
6672 // check if device with this host PCI address already attached
6673 for (HWData::PCIDeviceAssignmentList::const_iterator
6674 it = mHWData->mPCIDeviceAssignments.begin();
6675 it != mHWData->mPCIDeviceAssignments.end();
6676 ++it)
6677 {
6678 LONG iHostAddress = -1;
6679 ComPtr<PCIDeviceAttachment> pAttach;
6680 pAttach = *it;
6681 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6682 if (iHostAddress == aHostAddress)
6683 return setError(E_INVALIDARG,
6684 tr("Device with host PCI address already attached to this VM"));
6685 }
6686
6687 ComObjPtr<PCIDeviceAttachment> pda;
6688 char name[32];
6689
6690 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6691 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6692 pda.createObject();
6693 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6694 i_setModified(IsModified_MachineData);
6695 mHWData.backup();
6696 mHWData->mPCIDeviceAssignments.push_back(pda);
6697 }
6698
6699 return S_OK;
6700}
6701
6702/**
6703 * Currently this method doesn't detach device from the running VM,
6704 * just makes sure it's not plugged on next VM start.
6705 */
6706HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6707{
6708 ComObjPtr<PCIDeviceAttachment> pAttach;
6709 bool fRemoved = false;
6710 HRESULT rc;
6711
6712 // lock scope
6713 {
6714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 rc = i_checkStateDependency(MutableStateDep);
6717 if (FAILED(rc)) return rc;
6718
6719 for (HWData::PCIDeviceAssignmentList::const_iterator
6720 it = mHWData->mPCIDeviceAssignments.begin();
6721 it != mHWData->mPCIDeviceAssignments.end();
6722 ++it)
6723 {
6724 LONG iHostAddress = -1;
6725 pAttach = *it;
6726 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6727 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6728 {
6729 i_setModified(IsModified_MachineData);
6730 mHWData.backup();
6731 mHWData->mPCIDeviceAssignments.remove(pAttach);
6732 fRemoved = true;
6733 break;
6734 }
6735 }
6736 }
6737
6738
6739 /* Fire event outside of the lock */
6740 if (fRemoved)
6741 {
6742 Assert(!pAttach.isNull());
6743 ComPtr<IEventSource> es;
6744 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6745 Assert(SUCCEEDED(rc));
6746 Bstr mid;
6747 rc = this->COMGETTER(Id)(mid.asOutParam());
6748 Assert(SUCCEEDED(rc));
6749 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6750 }
6751
6752 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6753 tr("No host PCI device %08x attached"),
6754 aHostAddress
6755 );
6756}
6757
6758HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6759{
6760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6763 size_t i = 0;
6764 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6765 it = mHWData->mPCIDeviceAssignments.begin();
6766 it != mHWData->mPCIDeviceAssignments.end();
6767 ++it, ++i)
6768 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6774{
6775 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6776
6777 return S_OK;
6778}
6779
6780HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6781{
6782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6783
6784 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6785
6786 return S_OK;
6787}
6788
6789HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6790{
6791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6792 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6793 if (SUCCEEDED(hrc))
6794 {
6795 hrc = mHWData.backupEx();
6796 if (SUCCEEDED(hrc))
6797 {
6798 i_setModified(IsModified_MachineData);
6799 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6800 }
6801 }
6802 return hrc;
6803}
6804
6805HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6806{
6807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6809 return S_OK;
6810}
6811
6812HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6813{
6814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6815 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6816 if (SUCCEEDED(hrc))
6817 {
6818 hrc = mHWData.backupEx();
6819 if (SUCCEEDED(hrc))
6820 {
6821 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6822 if (SUCCEEDED(hrc))
6823 i_setModified(IsModified_MachineData);
6824 }
6825 }
6826 return hrc;
6827}
6828
6829HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6830{
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6834
6835 return S_OK;
6836}
6837
6838HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6839{
6840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6841 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6842 if (SUCCEEDED(hrc))
6843 {
6844 hrc = mHWData.backupEx();
6845 if (SUCCEEDED(hrc))
6846 {
6847 i_setModified(IsModified_MachineData);
6848 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6849 }
6850 }
6851 return hrc;
6852}
6853
6854HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6855{
6856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6857
6858 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6859
6860 return S_OK;
6861}
6862
6863HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6864{
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6868 if ( SUCCEEDED(hrc)
6869 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6870 {
6871 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6872 int vrc;
6873
6874 if (aAutostartEnabled)
6875 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6876 else
6877 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6878
6879 if (RT_SUCCESS(vrc))
6880 {
6881 hrc = mHWData.backupEx();
6882 if (SUCCEEDED(hrc))
6883 {
6884 i_setModified(IsModified_MachineData);
6885 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6886 }
6887 }
6888 else if (vrc == VERR_NOT_SUPPORTED)
6889 hrc = setError(VBOX_E_NOT_SUPPORTED,
6890 tr("The VM autostart feature is not supported on this platform"));
6891 else if (vrc == VERR_PATH_NOT_FOUND)
6892 hrc = setError(E_FAIL,
6893 tr("The path to the autostart database is not set"));
6894 else
6895 hrc = setError(E_UNEXPECTED,
6896 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6897 aAutostartEnabled ? "Adding" : "Removing",
6898 mUserData->s.strName.c_str(), vrc);
6899 }
6900 return hrc;
6901}
6902
6903HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6908
6909 return S_OK;
6910}
6911
6912HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6913{
6914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6915 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6916 if (SUCCEEDED(hrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 i_setModified(IsModified_MachineData);
6922 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6923 }
6924 }
6925 return hrc;
6926}
6927
6928HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6929{
6930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6931
6932 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6933
6934 return S_OK;
6935}
6936
6937HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6938{
6939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6940 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6941 if ( SUCCEEDED(hrc)
6942 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6943 {
6944 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6945 int vrc;
6946
6947 if (aAutostopType != AutostopType_Disabled)
6948 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6949 else
6950 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6951
6952 if (RT_SUCCESS(vrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData->mAutostart.enmAutostopType = aAutostopType;
6959 }
6960 }
6961 else if (vrc == VERR_NOT_SUPPORTED)
6962 hrc = setError(VBOX_E_NOT_SUPPORTED,
6963 tr("The VM autostop feature is not supported on this platform"));
6964 else if (vrc == VERR_PATH_NOT_FOUND)
6965 hrc = setError(E_FAIL,
6966 tr("The path to the autostart database is not set"));
6967 else
6968 hrc = setError(E_UNEXPECTED,
6969 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6970 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6971 mUserData->s.strName.c_str(), vrc);
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 aDefaultFrontend = mHWData->mDefaultFrontend;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6989 if (SUCCEEDED(hrc))
6990 {
6991 hrc = mHWData.backupEx();
6992 if (SUCCEEDED(hrc))
6993 {
6994 i_setModified(IsModified_MachineData);
6995 mHWData->mDefaultFrontend = aDefaultFrontend;
6996 }
6997 }
6998 return hrc;
6999}
7000
7001HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7002{
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 size_t cbIcon = mUserData->s.ovIcon.size();
7005 aIcon.resize(cbIcon);
7006 if (cbIcon)
7007 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7008 return S_OK;
7009}
7010
7011HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7012{
7013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7014 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7015 if (SUCCEEDED(hrc))
7016 {
7017 i_setModified(IsModified_MachineData);
7018 mUserData.backup();
7019 size_t cbIcon = aIcon.size();
7020 mUserData->s.ovIcon.resize(cbIcon);
7021 if (cbIcon)
7022 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7023 }
7024 return hrc;
7025}
7026
7027HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7028{
7029#ifdef VBOX_WITH_USB
7030 *aUSBProxyAvailable = true;
7031#else
7032 *aUSBProxyAvailable = false;
7033#endif
7034 return S_OK;
7035}
7036
7037HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7038{
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 *aVMProcessPriority = mUserData->s.enmVMPriority;
7042
7043 return S_OK;
7044}
7045
7046HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7047{
7048 RT_NOREF(aVMProcessPriority);
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7051 if (SUCCEEDED(hrc))
7052 {
7053 hrc = mUserData.backupEx();
7054 if (SUCCEEDED(hrc))
7055 {
7056 i_setModified(IsModified_MachineData);
7057 mUserData->s.enmVMPriority = aVMProcessPriority;
7058 }
7059 }
7060 alock.release();
7061 if (SUCCEEDED(hrc))
7062 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7063 return hrc;
7064}
7065
7066HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7067 ComPtr<IProgress> &aProgress)
7068{
7069 ComObjPtr<Progress> pP;
7070 Progress *ppP = pP;
7071 IProgress *iP = static_cast<IProgress *>(ppP);
7072 IProgress **pProgress = &iP;
7073
7074 IMachine *pTarget = aTarget;
7075
7076 /* Convert the options. */
7077 RTCList<CloneOptions_T> optList;
7078 if (aOptions.size())
7079 for (size_t i = 0; i < aOptions.size(); ++i)
7080 optList.append(aOptions[i]);
7081
7082 if (optList.contains(CloneOptions_Link))
7083 {
7084 if (!i_isSnapshotMachine())
7085 return setError(E_INVALIDARG,
7086 tr("Linked clone can only be created from a snapshot"));
7087 if (aMode != CloneMode_MachineState)
7088 return setError(E_INVALIDARG,
7089 tr("Linked clone can only be created for a single machine state"));
7090 }
7091 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7092
7093 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7094
7095 HRESULT rc = pWorker->start(pProgress);
7096
7097 pP = static_cast<Progress *>(*pProgress);
7098 pP.queryInterfaceTo(aProgress.asOutParam());
7099
7100 return rc;
7101
7102}
7103
7104HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7105 const com::Utf8Str &aType,
7106 ComPtr<IProgress> &aProgress)
7107{
7108 LogFlowThisFuncEnter();
7109
7110 ComObjPtr<Progress> progress;
7111
7112 progress.createObject();
7113
7114 HRESULT rc = S_OK;
7115 Utf8Str targetPath = aTargetPath;
7116 Utf8Str type = aType;
7117
7118 /* Initialize our worker task */
7119 MachineMoveVM* task = NULL;
7120 try
7121 {
7122 task = new MachineMoveVM(this, targetPath, type, progress);
7123 }
7124 catch(...)
7125 {
7126 delete task;
7127 return rc;
7128 }
7129
7130 /*
7131 * task pointer will be owned by the ThreadTask class.
7132 * There is no need to call operator "delete" in the end.
7133 */
7134 rc = task->init();
7135 if (SUCCEEDED(rc))
7136 {
7137 rc = task->createThread();
7138 if (FAILED(rc))
7139 {
7140 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7141 }
7142
7143 /* Return progress to the caller */
7144 progress.queryInterfaceTo(aProgress.asOutParam());
7145 }
7146
7147 LogFlowThisFuncLeave();
7148 return rc;
7149
7150}
7151
7152HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7153{
7154 NOREF(aProgress);
7155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 // This check should always fail.
7158 HRESULT rc = i_checkStateDependency(MutableStateDep);
7159 if (FAILED(rc)) return rc;
7160
7161 AssertFailedReturn(E_NOTIMPL);
7162}
7163
7164HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7165{
7166 NOREF(aSavedStateFile);
7167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 // This check should always fail.
7170 HRESULT rc = i_checkStateDependency(MutableStateDep);
7171 if (FAILED(rc)) return rc;
7172
7173 AssertFailedReturn(E_NOTIMPL);
7174}
7175
7176HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7177{
7178 NOREF(aFRemoveFile);
7179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 // This check should always fail.
7182 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7183 if (FAILED(rc)) return rc;
7184
7185 AssertFailedReturn(E_NOTIMPL);
7186}
7187
7188// public methods for internal purposes
7189/////////////////////////////////////////////////////////////////////////////
7190
7191/**
7192 * Adds the given IsModified_* flag to the dirty flags of the machine.
7193 * This must be called either during i_loadSettings or under the machine write lock.
7194 * @param fl Flag
7195 * @param fAllowStateModification If state modifications are allowed.
7196 */
7197void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7198{
7199 mData->flModifications |= fl;
7200 if (fAllowStateModification && i_isStateModificationAllowed())
7201 mData->mCurrentStateModified = true;
7202}
7203
7204/**
7205 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7206 * care of the write locking.
7207 *
7208 * @param fModification The flag to add.
7209 * @param fAllowStateModification If state modifications are allowed.
7210 */
7211void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7212{
7213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7214 i_setModified(fModification, fAllowStateModification);
7215}
7216
7217/**
7218 * Saves the registry entry of this machine to the given configuration node.
7219 *
7220 * @param data Machine registry data.
7221 *
7222 * @note locks this object for reading.
7223 */
7224HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7225{
7226 AutoLimitedCaller autoCaller(this);
7227 AssertComRCReturnRC(autoCaller.rc());
7228
7229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7230
7231 data.uuid = mData->mUuid;
7232 data.strSettingsFile = mData->m_strConfigFile;
7233
7234 return S_OK;
7235}
7236
7237/**
7238 * Calculates the absolute path of the given path taking the directory of the
7239 * machine settings file as the current directory.
7240 *
7241 * @param strPath Path to calculate the absolute path for.
7242 * @param aResult Where to put the result (used only on success, can be the
7243 * same Utf8Str instance as passed in @a aPath).
7244 * @return IPRT result.
7245 *
7246 * @note Locks this object for reading.
7247 */
7248int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7249{
7250 AutoCaller autoCaller(this);
7251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7252
7253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7254
7255 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7256
7257 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7258
7259 strSettingsDir.stripFilename();
7260 char folder[RTPATH_MAX];
7261 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7262 if (RT_SUCCESS(vrc))
7263 aResult = folder;
7264
7265 return vrc;
7266}
7267
7268/**
7269 * Copies strSource to strTarget, making it relative to the machine folder
7270 * if it is a subdirectory thereof, or simply copying it otherwise.
7271 *
7272 * @param strSource Path to evaluate and copy.
7273 * @param strTarget Buffer to receive target path.
7274 *
7275 * @note Locks this object for reading.
7276 */
7277void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7278 Utf8Str &strTarget)
7279{
7280 AutoCaller autoCaller(this);
7281 AssertComRCReturn(autoCaller.rc(), (void)0);
7282
7283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7284
7285 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7286 // use strTarget as a temporary buffer to hold the machine settings dir
7287 strTarget = mData->m_strConfigFileFull;
7288 strTarget.stripFilename();
7289 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7290 {
7291 // is relative: then append what's left
7292 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7293 // for empty paths (only possible for subdirs) use "." to avoid
7294 // triggering default settings for not present config attributes.
7295 if (strTarget.isEmpty())
7296 strTarget = ".";
7297 }
7298 else
7299 // is not relative: then overwrite
7300 strTarget = strSource;
7301}
7302
7303/**
7304 * Returns the full path to the machine's log folder in the
7305 * \a aLogFolder argument.
7306 */
7307void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7308{
7309 AutoCaller autoCaller(this);
7310 AssertComRCReturnVoid(autoCaller.rc());
7311
7312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7313
7314 char szTmp[RTPATH_MAX];
7315 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7316 if (RT_SUCCESS(vrc))
7317 {
7318 if (szTmp[0] && !mUserData.isNull())
7319 {
7320 char szTmp2[RTPATH_MAX];
7321 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7322 if (RT_SUCCESS(vrc))
7323 aLogFolder = Utf8StrFmt("%s%c%s",
7324 szTmp2,
7325 RTPATH_DELIMITER,
7326 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7327 }
7328 else
7329 vrc = VERR_PATH_IS_RELATIVE;
7330 }
7331
7332 if (RT_FAILURE(vrc))
7333 {
7334 // fallback if VBOX_USER_LOGHOME is not set or invalid
7335 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7336 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7337 aLogFolder.append(RTPATH_DELIMITER);
7338 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7339 }
7340}
7341
7342/**
7343 * Returns the full path to the machine's log file for an given index.
7344 */
7345Utf8Str Machine::i_getLogFilename(ULONG idx)
7346{
7347 Utf8Str logFolder;
7348 getLogFolder(logFolder);
7349 Assert(logFolder.length());
7350
7351 Utf8Str log;
7352 if (idx == 0)
7353 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7354#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7355 else if (idx == 1)
7356 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7357 else
7358 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7359#else
7360 else
7361 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7362#endif
7363 return log;
7364}
7365
7366/**
7367 * Returns the full path to the machine's hardened log file.
7368 */
7369Utf8Str Machine::i_getHardeningLogFilename(void)
7370{
7371 Utf8Str strFilename;
7372 getLogFolder(strFilename);
7373 Assert(strFilename.length());
7374 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7375 return strFilename;
7376}
7377
7378
7379/**
7380 * Composes a unique saved state filename based on the current system time. The filename is
7381 * granular to the second so this will work so long as no more than one snapshot is taken on
7382 * a machine per second.
7383 *
7384 * Before version 4.1, we used this formula for saved state files:
7385 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7386 * which no longer works because saved state files can now be shared between the saved state of the
7387 * "saved" machine and an online snapshot, and the following would cause problems:
7388 * 1) save machine
7389 * 2) create online snapshot from that machine state --> reusing saved state file
7390 * 3) save machine again --> filename would be reused, breaking the online snapshot
7391 *
7392 * So instead we now use a timestamp.
7393 *
7394 * @param strStateFilePath
7395 */
7396
7397void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7398{
7399 AutoCaller autoCaller(this);
7400 AssertComRCReturnVoid(autoCaller.rc());
7401
7402 {
7403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7404 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7405 }
7406
7407 RTTIMESPEC ts;
7408 RTTimeNow(&ts);
7409 RTTIME time;
7410 RTTimeExplode(&time, &ts);
7411
7412 strStateFilePath += RTPATH_DELIMITER;
7413 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7414 time.i32Year, time.u8Month, time.u8MonthDay,
7415 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7416}
7417
7418/**
7419 * Returns whether at least one USB controller is present for the VM.
7420 */
7421bool Machine::i_isUSBControllerPresent()
7422{
7423 AutoCaller autoCaller(this);
7424 AssertComRCReturn(autoCaller.rc(), false);
7425
7426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7427
7428 return (mUSBControllers->size() > 0);
7429}
7430
7431/**
7432 * @note Locks this object for writing, calls the client process
7433 * (inside the lock).
7434 */
7435HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7436 const Utf8Str &strFrontend,
7437 const Utf8Str &strEnvironment,
7438 ProgressProxy *aProgress)
7439{
7440 LogFlowThisFuncEnter();
7441
7442 AssertReturn(aControl, E_FAIL);
7443 AssertReturn(aProgress, E_FAIL);
7444 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7445
7446 AutoCaller autoCaller(this);
7447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7448
7449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7450
7451 if (!mData->mRegistered)
7452 return setError(E_UNEXPECTED,
7453 tr("The machine '%s' is not registered"),
7454 mUserData->s.strName.c_str());
7455
7456 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7457
7458 /* The process started when launching a VM with separate UI/VM processes is always
7459 * the UI process, i.e. needs special handling as it won't claim the session. */
7460 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7461
7462 if (fSeparate)
7463 {
7464 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7465 return setError(VBOX_E_INVALID_OBJECT_STATE,
7466 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7467 mUserData->s.strName.c_str());
7468 }
7469 else
7470 {
7471 if ( mData->mSession.mState == SessionState_Locked
7472 || mData->mSession.mState == SessionState_Spawning
7473 || mData->mSession.mState == SessionState_Unlocking)
7474 return setError(VBOX_E_INVALID_OBJECT_STATE,
7475 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7476 mUserData->s.strName.c_str());
7477
7478 /* may not be busy */
7479 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7480 }
7481
7482 /* get the path to the executable */
7483 char szPath[RTPATH_MAX];
7484 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7485 size_t cchBufLeft = strlen(szPath);
7486 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7487 szPath[cchBufLeft] = 0;
7488 char *pszNamePart = szPath + cchBufLeft;
7489 cchBufLeft = sizeof(szPath) - cchBufLeft;
7490
7491 int vrc = VINF_SUCCESS;
7492 RTPROCESS pid = NIL_RTPROCESS;
7493
7494 RTENV env = RTENV_DEFAULT;
7495
7496 if (!strEnvironment.isEmpty())
7497 {
7498 char *newEnvStr = NULL;
7499
7500 do
7501 {
7502 /* clone the current environment */
7503 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7504 AssertRCBreakStmt(vrc2, vrc = vrc2);
7505
7506 newEnvStr = RTStrDup(strEnvironment.c_str());
7507 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7508
7509 /* put new variables to the environment
7510 * (ignore empty variable names here since RTEnv API
7511 * intentionally doesn't do that) */
7512 char *var = newEnvStr;
7513 for (char *p = newEnvStr; *p; ++p)
7514 {
7515 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7516 {
7517 *p = '\0';
7518 if (*var)
7519 {
7520 char *val = strchr(var, '=');
7521 if (val)
7522 {
7523 *val++ = '\0';
7524 vrc2 = RTEnvSetEx(env, var, val);
7525 }
7526 else
7527 vrc2 = RTEnvUnsetEx(env, var);
7528 if (RT_FAILURE(vrc2))
7529 break;
7530 }
7531 var = p + 1;
7532 }
7533 }
7534 if (RT_SUCCESS(vrc2) && *var)
7535 vrc2 = RTEnvPutEx(env, var);
7536
7537 AssertRCBreakStmt(vrc2, vrc = vrc2);
7538 }
7539 while (0);
7540
7541 if (newEnvStr != NULL)
7542 RTStrFree(newEnvStr);
7543 }
7544
7545 /* Hardening logging */
7546#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7547 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7548 {
7549 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7550 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7551 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7552 {
7553 Utf8Str strStartupLogDir = strHardeningLogFile;
7554 strStartupLogDir.stripFilename();
7555 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7556 file without stripping the file. */
7557 }
7558 strSupHardeningLogArg.append(strHardeningLogFile);
7559
7560 /* Remove legacy log filename to avoid confusion. */
7561 Utf8Str strOldStartupLogFile;
7562 getLogFolder(strOldStartupLogFile);
7563 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7564 RTFileDelete(strOldStartupLogFile.c_str());
7565 }
7566 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7567#else
7568 const char *pszSupHardeningLogArg = NULL;
7569#endif
7570
7571 Utf8Str strCanonicalName;
7572
7573#ifdef VBOX_WITH_QTGUI
7574 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7575 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7576 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7577 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7578 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7579 {
7580 strCanonicalName = "GUI/Qt";
7581# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7582 /* Modify the base path so that we don't need to use ".." below. */
7583 RTPathStripTrailingSlash(szPath);
7584 RTPathStripFilename(szPath);
7585 cchBufLeft = strlen(szPath);
7586 pszNamePart = szPath + cchBufLeft;
7587 cchBufLeft = sizeof(szPath) - cchBufLeft;
7588
7589# define OSX_APP_NAME "VirtualBoxVM"
7590# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7591
7592 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7593 if ( strAppOverride.contains(".")
7594 || strAppOverride.contains("/")
7595 || strAppOverride.contains("\\")
7596 || strAppOverride.contains(":"))
7597 strAppOverride.setNull();
7598 Utf8Str strAppPath;
7599 if (!strAppOverride.isEmpty())
7600 {
7601 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7602 Utf8Str strFullPath(szPath);
7603 strFullPath.append(strAppPath);
7604 /* there is a race, but people using this deserve the failure */
7605 if (!RTFileExists(strFullPath.c_str()))
7606 strAppOverride.setNull();
7607 }
7608 if (strAppOverride.isEmpty())
7609 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7610 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7611 strcpy(pszNamePart, strAppPath.c_str());
7612# else
7613 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
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
14068HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14069{
14070 LogFlowThisFunc(("\n"));
14071
14072 AutoCaller autoCaller(this);
14073 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14074
14075 ComPtr<IInternalSessionControl> directControl;
14076 {
14077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14078 if (mData->mSession.mLockType == LockType_VM)
14079 directControl = mData->mSession.mDirectControl;
14080 }
14081
14082 /* ignore notifications sent after #OnSessionEnd() is called */
14083 if (!directControl)
14084 return S_OK;
14085
14086 return directControl->OnVMProcessPriorityChange(aPriority);
14087}
14088
14089/**
14090 * @note Locks this object for reading.
14091 */
14092HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
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->OnCPUChange(aCPU, aRemove);
14111}
14112
14113HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 if (mData->mSession.mLockType == LockType_VM)
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 /* ignore notifications sent after #OnSessionEnd() is called */
14128 if (!directControl)
14129 return S_OK;
14130
14131 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14132}
14133
14134/**
14135 * @note Locks this object for reading.
14136 */
14137HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14138{
14139 LogFlowThisFunc(("\n"));
14140
14141 AutoCaller autoCaller(this);
14142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnVRDEServerChange(aRestart);
14156}
14157
14158/**
14159 * @note Locks this object for reading.
14160 */
14161HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnRecordingChange(aEnable);
14180}
14181
14182/**
14183 * @note Locks this object for reading.
14184 */
14185HRESULT SessionMachine::i_onUSBControllerChange()
14186{
14187 LogFlowThisFunc(("\n"));
14188
14189 AutoCaller autoCaller(this);
14190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14191
14192 ComPtr<IInternalSessionControl> directControl;
14193 {
14194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14195 if (mData->mSession.mLockType == LockType_VM)
14196 directControl = mData->mSession.mDirectControl;
14197 }
14198
14199 /* ignore notifications sent after #OnSessionEnd() is called */
14200 if (!directControl)
14201 return S_OK;
14202
14203 return directControl->OnUSBControllerChange();
14204}
14205
14206/**
14207 * @note Locks this object for reading.
14208 */
14209HRESULT SessionMachine::i_onSharedFolderChange()
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturnRC(autoCaller.rc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 if (mData->mSession.mLockType == LockType_VM)
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturnRC(autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 if (mData->mSession.mLockType == LockType_VM)
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnClipboardModeChange(aClipboardMode);
14252}
14253
14254/**
14255 * @note Locks this object for reading.
14256 */
14257HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturnRC(autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnDnDModeChange(aDnDMode);
14276}
14277
14278/**
14279 * @note Locks this object for reading.
14280 */
14281HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 if (mData->mSession.mLockType == LockType_VM)
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14300}
14301
14302/**
14303 * @note Locks this object for reading.
14304 */
14305HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14306{
14307 LogFlowThisFunc(("\n"));
14308
14309 AutoCaller autoCaller(this);
14310 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14311
14312 ComPtr<IInternalSessionControl> directControl;
14313 {
14314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14315 if (mData->mSession.mLockType == LockType_VM)
14316 directControl = mData->mSession.mDirectControl;
14317 }
14318
14319 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14320
14321 /* ignore notifications sent after #OnSessionEnd() is called */
14322 if (!directControl)
14323 return S_OK;
14324
14325 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14326}
14327
14328/**
14329 * Returns @c true if this machine's USB controller reports it has a matching
14330 * filter for the given USB device and @c false otherwise.
14331 *
14332 * @note locks this object for reading.
14333 */
14334bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14335{
14336 AutoCaller autoCaller(this);
14337 /* silently return if not ready -- this method may be called after the
14338 * direct machine session has been called */
14339 if (!autoCaller.isOk())
14340 return false;
14341
14342#ifdef VBOX_WITH_USB
14343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14344
14345 switch (mData->mMachineState)
14346 {
14347 case MachineState_Starting:
14348 case MachineState_Restoring:
14349 case MachineState_TeleportingIn:
14350 case MachineState_Paused:
14351 case MachineState_Running:
14352 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14353 * elsewhere... */
14354 alock.release();
14355 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14356 default: break;
14357 }
14358#else
14359 NOREF(aDevice);
14360 NOREF(aMaskedIfs);
14361#endif
14362 return false;
14363}
14364
14365/**
14366 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14367 */
14368HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14369 IVirtualBoxErrorInfo *aError,
14370 ULONG aMaskedIfs,
14371 const com::Utf8Str &aCaptureFilename)
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376
14377 /* This notification may happen after the machine object has been
14378 * uninitialized (the session was closed), so don't assert. */
14379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14380
14381 ComPtr<IInternalSessionControl> directControl;
14382 {
14383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14384 if (mData->mSession.mLockType == LockType_VM)
14385 directControl = mData->mSession.mDirectControl;
14386 }
14387
14388 /* fail on notifications sent after #OnSessionEnd() is called, it is
14389 * expected by the caller */
14390 if (!directControl)
14391 return E_FAIL;
14392
14393 /* No locks should be held at this point. */
14394 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14395 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14396
14397 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14398}
14399
14400/**
14401 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14402 */
14403HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14404 IVirtualBoxErrorInfo *aError)
14405{
14406 LogFlowThisFunc(("\n"));
14407
14408 AutoCaller autoCaller(this);
14409
14410 /* This notification may happen after the machine object has been
14411 * uninitialized (the session was closed), so don't assert. */
14412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14413
14414 ComPtr<IInternalSessionControl> directControl;
14415 {
14416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14417 if (mData->mSession.mLockType == LockType_VM)
14418 directControl = mData->mSession.mDirectControl;
14419 }
14420
14421 /* fail on notifications sent after #OnSessionEnd() is called, it is
14422 * expected by the caller */
14423 if (!directControl)
14424 return E_FAIL;
14425
14426 /* No locks should be held at this point. */
14427 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14428 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14429
14430 return directControl->OnUSBDeviceDetach(aId, aError);
14431}
14432
14433// protected methods
14434/////////////////////////////////////////////////////////////////////////////
14435
14436/**
14437 * Deletes the given file if it is no longer in use by either the current machine state
14438 * (if the machine is "saved") or any of the machine's snapshots.
14439 *
14440 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14441 * but is different for each SnapshotMachine. When calling this, the order of calling this
14442 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14443 * is therefore critical. I know, it's all rather messy.
14444 *
14445 * @param strStateFile
14446 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14447 * the test for whether the saved state file is in use.
14448 */
14449void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14450 Snapshot *pSnapshotToIgnore)
14451{
14452 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14453 if ( (strStateFile.isNotEmpty())
14454 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14455 )
14456 // ... and it must also not be shared with other snapshots
14457 if ( !mData->mFirstSnapshot
14458 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14459 // this checks the SnapshotMachine's state file paths
14460 )
14461 RTFileDelete(strStateFile.c_str());
14462}
14463
14464/**
14465 * Locks the attached media.
14466 *
14467 * All attached hard disks are locked for writing and DVD/floppy are locked for
14468 * reading. Parents of attached hard disks (if any) are locked for reading.
14469 *
14470 * This method also performs accessibility check of all media it locks: if some
14471 * media is inaccessible, the method will return a failure and a bunch of
14472 * extended error info objects per each inaccessible medium.
14473 *
14474 * Note that this method is atomic: if it returns a success, all media are
14475 * locked as described above; on failure no media is locked at all (all
14476 * succeeded individual locks will be undone).
14477 *
14478 * The caller is responsible for doing the necessary state sanity checks.
14479 *
14480 * The locks made by this method must be undone by calling #unlockMedia() when
14481 * no more needed.
14482 */
14483HRESULT SessionMachine::i_lockMedia()
14484{
14485 AutoCaller autoCaller(this);
14486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14487
14488 AutoMultiWriteLock2 alock(this->lockHandle(),
14489 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14490
14491 /* bail out if trying to lock things with already set up locking */
14492 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14493
14494 MultiResult mrc(S_OK);
14495
14496 /* Collect locking information for all medium objects attached to the VM. */
14497 for (MediumAttachmentList::const_iterator
14498 it = mMediumAttachments->begin();
14499 it != mMediumAttachments->end();
14500 ++it)
14501 {
14502 MediumAttachment *pAtt = *it;
14503 DeviceType_T devType = pAtt->i_getType();
14504 Medium *pMedium = pAtt->i_getMedium();
14505
14506 MediumLockList *pMediumLockList(new MediumLockList());
14507 // There can be attachments without a medium (floppy/dvd), and thus
14508 // it's impossible to create a medium lock list. It still makes sense
14509 // to have the empty medium lock list in the map in case a medium is
14510 // attached later.
14511 if (pMedium != NULL)
14512 {
14513 MediumType_T mediumType = pMedium->i_getType();
14514 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14515 || mediumType == MediumType_Shareable;
14516 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14517
14518 alock.release();
14519 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14520 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14521 false /* fMediumLockWriteAll */,
14522 NULL,
14523 *pMediumLockList);
14524 alock.acquire();
14525 if (FAILED(mrc))
14526 {
14527 delete pMediumLockList;
14528 mData->mSession.mLockedMedia.Clear();
14529 break;
14530 }
14531 }
14532
14533 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14534 if (FAILED(rc))
14535 {
14536 mData->mSession.mLockedMedia.Clear();
14537 mrc = setError(rc,
14538 tr("Collecting locking information for all attached media failed"));
14539 break;
14540 }
14541 }
14542
14543 if (SUCCEEDED(mrc))
14544 {
14545 /* Now lock all media. If this fails, nothing is locked. */
14546 alock.release();
14547 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14548 alock.acquire();
14549 if (FAILED(rc))
14550 {
14551 mrc = setError(rc,
14552 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14553 }
14554 }
14555
14556 return mrc;
14557}
14558
14559/**
14560 * Undoes the locks made by by #lockMedia().
14561 */
14562HRESULT SessionMachine::i_unlockMedia()
14563{
14564 AutoCaller autoCaller(this);
14565 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14566
14567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14568
14569 /* we may be holding important error info on the current thread;
14570 * preserve it */
14571 ErrorInfoKeeper eik;
14572
14573 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14574 AssertComRC(rc);
14575 return rc;
14576}
14577
14578/**
14579 * Helper to change the machine state (reimplementation).
14580 *
14581 * @note Locks this object for writing.
14582 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14583 * it can cause crashes in random places due to unexpectedly committing
14584 * the current settings. The caller is responsible for that. The call
14585 * to saveStateSettings is fine, because this method does not commit.
14586 */
14587HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14588{
14589 LogFlowThisFuncEnter();
14590 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14591
14592 AutoCaller autoCaller(this);
14593 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14594
14595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14596
14597 MachineState_T oldMachineState = mData->mMachineState;
14598
14599 AssertMsgReturn(oldMachineState != aMachineState,
14600 ("oldMachineState=%s, aMachineState=%s\n",
14601 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14602 E_FAIL);
14603
14604 HRESULT rc = S_OK;
14605
14606 int stsFlags = 0;
14607 bool deleteSavedState = false;
14608
14609 /* detect some state transitions */
14610
14611 if ( ( oldMachineState == MachineState_Saved
14612 && aMachineState == MachineState_Restoring)
14613 || ( ( oldMachineState == MachineState_PoweredOff
14614 || oldMachineState == MachineState_Teleported
14615 || oldMachineState == MachineState_Aborted
14616 )
14617 && ( aMachineState == MachineState_TeleportingIn
14618 || aMachineState == MachineState_Starting
14619 )
14620 )
14621 )
14622 {
14623 /* The EMT thread is about to start */
14624
14625 /* Nothing to do here for now... */
14626
14627 /// @todo NEWMEDIA don't let mDVDDrive and other children
14628 /// change anything when in the Starting/Restoring state
14629 }
14630 else if ( ( oldMachineState == MachineState_Running
14631 || oldMachineState == MachineState_Paused
14632 || oldMachineState == MachineState_Teleporting
14633 || oldMachineState == MachineState_OnlineSnapshotting
14634 || oldMachineState == MachineState_LiveSnapshotting
14635 || oldMachineState == MachineState_Stuck
14636 || oldMachineState == MachineState_Starting
14637 || oldMachineState == MachineState_Stopping
14638 || oldMachineState == MachineState_Saving
14639 || oldMachineState == MachineState_Restoring
14640 || oldMachineState == MachineState_TeleportingPausedVM
14641 || oldMachineState == MachineState_TeleportingIn
14642 )
14643 && ( aMachineState == MachineState_PoweredOff
14644 || aMachineState == MachineState_Saved
14645 || aMachineState == MachineState_Teleported
14646 || aMachineState == MachineState_Aborted
14647 )
14648 )
14649 {
14650 /* The EMT thread has just stopped, unlock attached media. Note that as
14651 * opposed to locking that is done from Console, we do unlocking here
14652 * because the VM process may have aborted before having a chance to
14653 * properly unlock all media it locked. */
14654
14655 unlockMedia();
14656 }
14657
14658 if (oldMachineState == MachineState_Restoring)
14659 {
14660 if (aMachineState != MachineState_Saved)
14661 {
14662 /*
14663 * delete the saved state file once the machine has finished
14664 * restoring from it (note that Console sets the state from
14665 * Restoring to Saved if the VM couldn't restore successfully,
14666 * to give the user an ability to fix an error and retry --
14667 * we keep the saved state file in this case)
14668 */
14669 deleteSavedState = true;
14670 }
14671 }
14672 else if ( oldMachineState == MachineState_Saved
14673 && ( aMachineState == MachineState_PoweredOff
14674 || aMachineState == MachineState_Aborted
14675 || aMachineState == MachineState_Teleported
14676 )
14677 )
14678 {
14679 /*
14680 * delete the saved state after SessionMachine::ForgetSavedState() is called
14681 * or if the VM process (owning a direct VM session) crashed while the
14682 * VM was Saved
14683 */
14684
14685 /// @todo (dmik)
14686 // Not sure that deleting the saved state file just because of the
14687 // client death before it attempted to restore the VM is a good
14688 // thing. But when it crashes we need to go to the Aborted state
14689 // which cannot have the saved state file associated... The only
14690 // way to fix this is to make the Aborted condition not a VM state
14691 // but a bool flag: i.e., when a crash occurs, set it to true and
14692 // change the state to PoweredOff or Saved depending on the
14693 // saved state presence.
14694
14695 deleteSavedState = true;
14696 mData->mCurrentStateModified = TRUE;
14697 stsFlags |= SaveSTS_CurStateModified;
14698 }
14699
14700 if ( aMachineState == MachineState_Starting
14701 || aMachineState == MachineState_Restoring
14702 || aMachineState == MachineState_TeleportingIn
14703 )
14704 {
14705 /* set the current state modified flag to indicate that the current
14706 * state is no more identical to the state in the
14707 * current snapshot */
14708 if (!mData->mCurrentSnapshot.isNull())
14709 {
14710 mData->mCurrentStateModified = TRUE;
14711 stsFlags |= SaveSTS_CurStateModified;
14712 }
14713 }
14714
14715 if (deleteSavedState)
14716 {
14717 if (mRemoveSavedState)
14718 {
14719 Assert(!mSSData->strStateFilePath.isEmpty());
14720
14721 // it is safe to delete the saved state file if ...
14722 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14723 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14724 // ... none of the snapshots share the saved state file
14725 )
14726 RTFileDelete(mSSData->strStateFilePath.c_str());
14727 }
14728
14729 mSSData->strStateFilePath.setNull();
14730 stsFlags |= SaveSTS_StateFilePath;
14731 }
14732
14733 /* redirect to the underlying peer machine */
14734 mPeer->i_setMachineState(aMachineState);
14735
14736 if ( oldMachineState != MachineState_RestoringSnapshot
14737 && ( aMachineState == MachineState_PoweredOff
14738 || aMachineState == MachineState_Teleported
14739 || aMachineState == MachineState_Aborted
14740 || aMachineState == MachineState_Saved))
14741 {
14742 /* the machine has stopped execution
14743 * (or the saved state file was adopted) */
14744 stsFlags |= SaveSTS_StateTimeStamp;
14745 }
14746
14747 if ( ( oldMachineState == MachineState_PoweredOff
14748 || oldMachineState == MachineState_Aborted
14749 || oldMachineState == MachineState_Teleported
14750 )
14751 && aMachineState == MachineState_Saved)
14752 {
14753 /* the saved state file was adopted */
14754 Assert(!mSSData->strStateFilePath.isEmpty());
14755 stsFlags |= SaveSTS_StateFilePath;
14756 }
14757
14758#ifdef VBOX_WITH_GUEST_PROPS
14759 if ( aMachineState == MachineState_PoweredOff
14760 || aMachineState == MachineState_Aborted
14761 || aMachineState == MachineState_Teleported)
14762 {
14763 /* Make sure any transient guest properties get removed from the
14764 * property store on shutdown. */
14765 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14766
14767 /* remove it from the settings representation */
14768 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14769 for (settings::GuestPropertiesList::iterator
14770 it = llGuestProperties.begin();
14771 it != llGuestProperties.end();
14772 /*nothing*/)
14773 {
14774 const settings::GuestProperty &prop = *it;
14775 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14776 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14777 {
14778 it = llGuestProperties.erase(it);
14779 fNeedsSaving = true;
14780 }
14781 else
14782 {
14783 ++it;
14784 }
14785 }
14786
14787 /* Additionally remove it from the HWData representation. Required to
14788 * keep everything in sync, as this is what the API keeps using. */
14789 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14790 for (HWData::GuestPropertyMap::iterator
14791 it = llHWGuestProperties.begin();
14792 it != llHWGuestProperties.end();
14793 /*nothing*/)
14794 {
14795 uint32_t fFlags = it->second.mFlags;
14796 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14797 {
14798 /* iterator where we need to continue after the erase call
14799 * (C++03 is a fact still, and it doesn't return the iterator
14800 * which would allow continuing) */
14801 HWData::GuestPropertyMap::iterator it2 = it;
14802 ++it2;
14803 llHWGuestProperties.erase(it);
14804 it = it2;
14805 fNeedsSaving = true;
14806 }
14807 else
14808 {
14809 ++it;
14810 }
14811 }
14812
14813 if (fNeedsSaving)
14814 {
14815 mData->mCurrentStateModified = TRUE;
14816 stsFlags |= SaveSTS_CurStateModified;
14817 }
14818 }
14819#endif /* VBOX_WITH_GUEST_PROPS */
14820
14821 rc = i_saveStateSettings(stsFlags);
14822
14823 if ( ( oldMachineState != MachineState_PoweredOff
14824 && oldMachineState != MachineState_Aborted
14825 && oldMachineState != MachineState_Teleported
14826 )
14827 && ( aMachineState == MachineState_PoweredOff
14828 || aMachineState == MachineState_Aborted
14829 || aMachineState == MachineState_Teleported
14830 )
14831 )
14832 {
14833 /* we've been shut down for any reason */
14834 /* no special action so far */
14835 }
14836
14837 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14838 LogFlowThisFuncLeave();
14839 return rc;
14840}
14841
14842/**
14843 * Sends the current machine state value to the VM process.
14844 *
14845 * @note Locks this object for reading, then calls a client process.
14846 */
14847HRESULT SessionMachine::i_updateMachineStateOnClient()
14848{
14849 AutoCaller autoCaller(this);
14850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14851
14852 ComPtr<IInternalSessionControl> directControl;
14853 {
14854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14855 AssertReturn(!!mData, E_FAIL);
14856 if (mData->mSession.mLockType == LockType_VM)
14857 directControl = mData->mSession.mDirectControl;
14858
14859 /* directControl may be already set to NULL here in #OnSessionEnd()
14860 * called too early by the direct session process while there is still
14861 * some operation (like deleting the snapshot) in progress. The client
14862 * process in this case is waiting inside Session::close() for the
14863 * "end session" process object to complete, while #uninit() called by
14864 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14865 * operation to complete. For now, we accept this inconsistent behavior
14866 * and simply do nothing here. */
14867
14868 if (mData->mSession.mState == SessionState_Unlocking)
14869 return S_OK;
14870 }
14871
14872 /* ignore notifications sent after #OnSessionEnd() is called */
14873 if (!directControl)
14874 return S_OK;
14875
14876 return directControl->UpdateMachineState(mData->mMachineState);
14877}
14878
14879
14880/*static*/
14881HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14882{
14883 va_list args;
14884 va_start(args, pcszMsg);
14885 HRESULT rc = setErrorInternal(aResultCode,
14886 getStaticClassIID(),
14887 getStaticComponentName(),
14888 Utf8Str(pcszMsg, args),
14889 false /* aWarning */,
14890 true /* aLogIt */);
14891 va_end(args);
14892 return rc;
14893}
14894
14895
14896HRESULT Machine::updateState(MachineState_T aState)
14897{
14898 NOREF(aState);
14899 ReturnComNotImplemented();
14900}
14901
14902HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14903{
14904 NOREF(aProgress);
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::endPowerUp(LONG aResult)
14909{
14910 NOREF(aResult);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14915{
14916 NOREF(aProgress);
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::endPoweringDown(LONG aResult,
14921 const com::Utf8Str &aErrMsg)
14922{
14923 NOREF(aResult);
14924 NOREF(aErrMsg);
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14929 BOOL *aMatched,
14930 ULONG *aMaskedInterfaces)
14931{
14932 NOREF(aDevice);
14933 NOREF(aMatched);
14934 NOREF(aMaskedInterfaces);
14935 ReturnComNotImplemented();
14936
14937}
14938
14939HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14940{
14941 NOREF(aId); NOREF(aCaptureFilename);
14942 ReturnComNotImplemented();
14943}
14944
14945HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14946 BOOL aDone)
14947{
14948 NOREF(aId);
14949 NOREF(aDone);
14950 ReturnComNotImplemented();
14951}
14952
14953HRESULT Machine::autoCaptureUSBDevices()
14954{
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14959{
14960 NOREF(aDone);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14965 ComPtr<IProgress> &aProgress)
14966{
14967 NOREF(aSession);
14968 NOREF(aProgress);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::finishOnlineMergeMedium()
14973{
14974 ReturnComNotImplemented();
14975}
14976
14977HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14978 std::vector<com::Utf8Str> &aValues,
14979 std::vector<LONG64> &aTimestamps,
14980 std::vector<com::Utf8Str> &aFlags)
14981{
14982 NOREF(aNames);
14983 NOREF(aValues);
14984 NOREF(aTimestamps);
14985 NOREF(aFlags);
14986 ReturnComNotImplemented();
14987}
14988
14989HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14990 const com::Utf8Str &aValue,
14991 LONG64 aTimestamp,
14992 const com::Utf8Str &aFlags)
14993{
14994 NOREF(aName);
14995 NOREF(aValue);
14996 NOREF(aTimestamp);
14997 NOREF(aFlags);
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::lockMedia()
15002{
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::unlockMedia()
15007{
15008 ReturnComNotImplemented();
15009}
15010
15011HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15012 ComPtr<IMediumAttachment> &aNewAttachment)
15013{
15014 NOREF(aAttachment);
15015 NOREF(aNewAttachment);
15016 ReturnComNotImplemented();
15017}
15018
15019HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15020 ULONG aCpuUser,
15021 ULONG aCpuKernel,
15022 ULONG aCpuIdle,
15023 ULONG aMemTotal,
15024 ULONG aMemFree,
15025 ULONG aMemBalloon,
15026 ULONG aMemShared,
15027 ULONG aMemCache,
15028 ULONG aPagedTotal,
15029 ULONG aMemAllocTotal,
15030 ULONG aMemFreeTotal,
15031 ULONG aMemBalloonTotal,
15032 ULONG aMemSharedTotal,
15033 ULONG aVmNetRx,
15034 ULONG aVmNetTx)
15035{
15036 NOREF(aValidStats);
15037 NOREF(aCpuUser);
15038 NOREF(aCpuKernel);
15039 NOREF(aCpuIdle);
15040 NOREF(aMemTotal);
15041 NOREF(aMemFree);
15042 NOREF(aMemBalloon);
15043 NOREF(aMemShared);
15044 NOREF(aMemCache);
15045 NOREF(aPagedTotal);
15046 NOREF(aMemAllocTotal);
15047 NOREF(aMemFreeTotal);
15048 NOREF(aMemBalloonTotal);
15049 NOREF(aMemSharedTotal);
15050 NOREF(aVmNetRx);
15051 NOREF(aVmNetTx);
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15056 com::Utf8Str &aResult)
15057{
15058 NOREF(aAuthParams);
15059 NOREF(aResult);
15060 ReturnComNotImplemented();
15061}
15062
15063HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15064{
15065 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15066
15067 AutoCaller autoCaller(this);
15068 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15069
15070 HRESULT rc = S_OK;
15071
15072 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15073 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15074 rc = getUSBDeviceFilters(usbDeviceFilters);
15075 if (FAILED(rc)) return rc;
15076
15077 NOREF(aFlags);
15078 com::Utf8Str osTypeId;
15079 ComObjPtr<GuestOSType> osType = NULL;
15080
15081 /* Get the guest os type as a string from the VB. */
15082 rc = getOSTypeId(osTypeId);
15083 if (FAILED(rc)) return rc;
15084
15085 /* Get the os type obj that coresponds, can be used to get
15086 * the defaults for this guest OS. */
15087 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15088 if (FAILED(rc)) return rc;
15089
15090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15091
15092 /* Let the OS type select 64-bit ness. */
15093 mHWData->mLongMode = osType->i_is64Bit()
15094 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15095
15096 /* Apply network adapters defaults */
15097 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15098 mNetworkAdapters[slot]->i_applyDefaults(osType);
15099
15100 /* Apply serial port defaults */
15101 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15102 mSerialPorts[slot]->i_applyDefaults(osType);
15103
15104 /* Apply parallel port defaults - not OS dependent*/
15105 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15106 mParallelPorts[slot]->i_applyDefaults();
15107
15108
15109 /* Let the OS type enable the X2APIC */
15110 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15111
15112 /* This one covers IOAPICEnabled. */
15113 mBIOSSettings->i_applyDefaults(osType);
15114
15115 /* Initialize default record settings. */
15116 mRecordingSettings->i_applyDefaults();
15117
15118 /* Initialize default BIOS settings here */
15119 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15120 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15121
15122 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15123 if (FAILED(rc)) return rc;
15124
15125 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15126 if (FAILED(rc)) return rc;
15127
15128 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15129 if (FAILED(rc)) return rc;
15130
15131 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15132 if (FAILED(rc)) return rc;
15133
15134 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15135 if (FAILED(rc)) return rc;
15136
15137 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15138 if (FAILED(rc)) return rc;
15139
15140 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15141 if (FAILED(rc)) return rc;
15142
15143 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15144 if (FAILED(rc)) return rc;
15145
15146 BOOL mRTCUseUTC;
15147 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15148 if (FAILED(rc)) return rc;
15149
15150 setRTCUseUTC(mRTCUseUTC);
15151 if (FAILED(rc)) return rc;
15152
15153 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15154 if (FAILED(rc)) return rc;
15155
15156 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15157 if (FAILED(rc)) return rc;
15158
15159 /* Audio stuff. */
15160 AudioCodecType_T audioCodec;
15161 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15162 if (FAILED(rc)) return rc;
15163
15164 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15165 if (FAILED(rc)) return rc;
15166
15167 AudioControllerType_T audioController;
15168 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15169 if (FAILED(rc)) return rc;
15170
15171 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15172 if (FAILED(rc)) return rc;
15173
15174 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15175 if (FAILED(rc)) return rc;
15176
15177 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15178 if (FAILED(rc)) return rc;
15179
15180 /* Storage Controllers */
15181 StorageControllerType_T hdStorageControllerType;
15182 StorageBus_T hdStorageBusType;
15183 StorageControllerType_T dvdStorageControllerType;
15184 StorageBus_T dvdStorageBusType;
15185 BOOL recommendedFloppy;
15186 ComPtr<IStorageController> floppyController;
15187 ComPtr<IStorageController> hdController;
15188 ComPtr<IStorageController> dvdController;
15189 Utf8Str strFloppyName, strDVDName, strHDName;
15190
15191 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15192 strFloppyName = Bstr("Floppy 1").raw();
15193 strDVDName = Bstr("DVD 1").raw();
15194 strHDName = Bstr("HDD 1").raw();
15195
15196 /* Floppy recommended? add one. */
15197 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15198 if (FAILED(rc)) return rc;
15199 if (recommendedFloppy)
15200 {
15201 rc = addStorageController(strFloppyName,
15202 StorageBus_Floppy,
15203 floppyController);
15204 if (FAILED(rc)) return rc;
15205 }
15206
15207 /* Setup one DVD storage controller. */
15208 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15212 if (FAILED(rc)) return rc;
15213
15214 rc = addStorageController(strDVDName,
15215 dvdStorageBusType,
15216 dvdController);
15217 if (FAILED(rc)) return rc;
15218
15219 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15220 if (FAILED(rc)) return rc;
15221
15222 /* Setup one HDD storage controller. */
15223 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15224 if (FAILED(rc)) return rc;
15225
15226 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15227 if (FAILED(rc)) return rc;
15228
15229 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15230 {
15231 rc = addStorageController(strHDName,
15232 hdStorageBusType,
15233 hdController);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15237 if (FAILED(rc)) return rc;
15238 }
15239 else
15240 {
15241 /* The HD controller is the same as DVD: */
15242 hdController = dvdController;
15243 strHDName = Bstr("DVD 1").raw();
15244 }
15245
15246 /* Limit the AHCI port count if it's used because windows has trouble with
15247 * too many ports and other guest (OS X in particular) may take extra long
15248 * boot: */
15249
15250 // pParent = static_cast<Medium*>(aP)
15251 IStorageController *temp = hdController;
15252 ComObjPtr<StorageController> storageController;
15253 storageController = static_cast<StorageController *>(temp);
15254
15255 // tempHDController = aHDController;
15256 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15257 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15258 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15259 storageController->COMSETTER(PortCount)(1);
15260
15261 /* USB stuff */
15262
15263 bool ohciEnabled = false;
15264
15265 ComPtr<IUSBController> usbController;
15266 BOOL recommendedUSB3;
15267 BOOL recommendedUSB;
15268 BOOL usbProxyAvailable;
15269
15270 getUSBProxyAvailable(&usbProxyAvailable);
15271 if (FAILED(rc)) return rc;
15272
15273 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15274 if (FAILED(rc)) return rc;
15275 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15276 if (FAILED(rc)) return rc;
15277
15278 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15279 {
15280#ifdef VBOX_WITH_EXTPACK
15281 /* USB 3.0 is only available if the proper ExtPack is installed. */
15282 ExtPackManager *aManager = mParent->i_getExtPackManager();
15283 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15284 {
15285 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15286 if (FAILED(rc)) return rc;
15287
15288 /* xHci includes OHCI */
15289 ohciEnabled = true;
15290 }
15291#endif
15292 }
15293 if ( !ohciEnabled
15294 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15295 {
15296 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15297 if (FAILED(rc)) return rc;
15298 ohciEnabled = true;
15299
15300#ifdef VBOX_WITH_EXTPACK
15301 /* USB 2.0 is only available if the proper ExtPack is installed.
15302 * Note. Configuring EHCI here and providing messages about
15303 * the missing extpack isn't exactly clean, but it is a
15304 * necessary evil to patch over legacy compatability issues
15305 * introduced by the new distribution model. */
15306 ExtPackManager *manager = mParent->i_getExtPackManager();
15307 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15308 {
15309 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15310 if (FAILED(rc)) return rc;
15311 }
15312#endif
15313 }
15314
15315 /* Set recommended human interface device types: */
15316 BOOL recommendedUSBHID;
15317 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15318 if (FAILED(rc)) return rc;
15319
15320 if (recommendedUSBHID)
15321 {
15322 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15323 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15324 if (!ohciEnabled && !usbDeviceFilters.isNull())
15325 {
15326 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15327 if (FAILED(rc)) return rc;
15328 }
15329 }
15330
15331 BOOL recommendedUSBTablet;
15332 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15333 if (FAILED(rc)) return rc;
15334
15335 if (recommendedUSBTablet)
15336 {
15337 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15338 if (!ohciEnabled && !usbDeviceFilters.isNull())
15339 {
15340 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15341 if (FAILED(rc)) return rc;
15342 }
15343 }
15344 return S_OK;
15345}
15346
15347/* This isn't handled entirely by the wrapper generator yet. */
15348#ifdef VBOX_WITH_XPCOM
15349NS_DECL_CLASSINFO(SessionMachine)
15350NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15351
15352NS_DECL_CLASSINFO(SnapshotMachine)
15353NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15354#endif
Note: See TracBrowser for help on using the repository browser.

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