VirtualBox

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

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

Main/VirtualBox+Machine: Fix bogus saving of VM config when the VM is inaccessible due to the 2nd or further media registry entry, by making sure that the medium registry isn't marked as modified. For good measure block saving of VM configs when they're inaccessible. Thanks, Socratis, for reporting with reproduction instructions in ticketref:17908.

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