VirtualBox

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

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

Main: Eradicate the use of BSTR in regular API code, there were leaks in almost every occurrence. Also do some Bstr->Utf8Str shuffling to reduce the number of conversions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 531.5 KB
Line 
1/* $Id: MachineImpl.cpp 77436 2019-02-22 17:40:00Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mParavirtProvider = ParavirtProvider_Default;
221 mEmulatedUSBCardReaderEnabled = FALSE;
222
223 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
224 mCPUAttached[i] = false;
225
226 mIOCacheEnabled = true;
227 mIOCacheSize = 5; /* 5MB */
228}
229
230Machine::HWData::~HWData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine() :
242#ifdef VBOX_WITH_RESOURCE_USAGE_API
243 mCollectorGuest(NULL),
244#endif
245 mPeer(NULL),
246 mParent(NULL),
247 mSerialPorts(),
248 mParallelPorts(),
249 uRegistryNeedsSaving(0)
250{}
251
252Machine::~Machine()
253{}
254
255HRESULT Machine::FinalConstruct()
256{
257 LogFlowThisFunc(("\n"));
258 return BaseFinalConstruct();
259}
260
261void Machine::FinalRelease()
262{
263 LogFlowThisFunc(("\n"));
264 uninit();
265 BaseFinalRelease();
266}
267
268/**
269 * Initializes a new machine instance; this init() variant creates a new, empty machine.
270 * This gets called from VirtualBox::CreateMachine().
271 *
272 * @param aParent Associated parent object
273 * @param strConfigFile Local file system path to the VM settings file (can
274 * be relative to the VirtualBox config directory).
275 * @param strName name for the machine
276 * @param llGroups list of groups for the machine
277 * @param strOsType OS Type string (stored as is if aOsType is NULL).
278 * @param aOsType OS Type of this machine or NULL.
279 * @param aId UUID for the new machine.
280 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
281 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
282 * scheme (includes the UUID).
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 const Utf8Str &strOsType,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 if (llGroups.size())
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Let the OS type select 64-bit ness. */
345 mHWData->mLongMode = aOsType->i_is64Bit()
346 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
347
348 /* Let the OS type enable the X2APIC */
349 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
350 }
351 else if (!strOsType.isEmpty())
352 {
353 /* Store OS type */
354 mUserData->s.strOsType = strOsType;
355
356 /* No guest OS type object. Pick some plausible defaults which the
357 * host can handle. There's no way to know or validate anything. */
358 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 mHWData->mX2APIC = false;
360 }
361
362 /* Apply BIOS defaults. */
363 mBIOSSettings->i_applyDefaults(aOsType);
364
365 /* Apply record defaults. */
366 mRecordingSettings->i_applyDefaults();
367
368 /* Apply network adapters defaults */
369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
370 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
371
372 /* Apply serial port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
374 mSerialPorts[slot]->i_applyDefaults(aOsType);
375
376 /* Apply parallel port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
378 mParallelPorts[slot]->i_applyDefaults();
379
380 /* At this point the changing of the current state modification
381 * flag is allowed. */
382 i_allowStateModification();
383
384 /* commit all changes made during the initialization */
385 i_commit();
386 }
387
388 /* Confirm a successful initialization when it's the case */
389 if (SUCCEEDED(rc))
390 {
391 if (mData->mAccessible)
392 autoInitSpan.setSucceeded();
393 else
394 autoInitSpan.setLimited();
395 }
396
397 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
398 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
399 mData->mRegistered,
400 mData->mAccessible,
401 rc));
402
403 LogFlowThisFuncLeave();
404
405 return rc;
406}
407
408/**
409 * Initializes a new instance with data from machine XML (formerly Init_Registered).
410 * Gets called in two modes:
411 *
412 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
413 * UUID is specified and we mark the machine as "registered";
414 *
415 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
416 * and the machine remains unregistered until RegisterMachine() is called.
417 *
418 * @param aParent Associated parent object
419 * @param strConfigFile Local file system path to the VM settings file (can
420 * be relative to the VirtualBox config directory).
421 * @param aId UUID of the machine or NULL (see above).
422 *
423 * @return Success indicator. if not S_OK, the machine object is invalid
424 */
425HRESULT Machine::initFromSettings(VirtualBox *aParent,
426 const Utf8Str &strConfigFile,
427 const Guid *aId)
428{
429 LogFlowThisFuncEnter();
430 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 HRESULT rc = initImpl(aParent, strConfigFile);
437 if (FAILED(rc)) return rc;
438
439 if (aId)
440 {
441 // loading a registered VM:
442 unconst(mData->mUuid) = *aId;
443 mData->mRegistered = TRUE;
444 // now load the settings from XML:
445 rc = i_registeredInit();
446 // this calls initDataAndChildObjects() and loadSettings()
447 }
448 else
449 {
450 // opening an unregistered VM (VirtualBox::OpenMachine()):
451 rc = initDataAndChildObjects();
452
453 if (SUCCEEDED(rc))
454 {
455 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
456 mData->mAccessible = TRUE;
457
458 try
459 {
460 // load and parse machine XML; this will throw on XML or logic errors
461 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
462
463 // reject VM UUID duplicates, they can happen if someone
464 // tries to register an already known VM config again
465 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
466 true /* fPermitInaccessible */,
467 false /* aDoSetError */,
468 NULL) != VBOX_E_OBJECT_NOT_FOUND)
469 {
470 throw setError(E_FAIL,
471 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
472 mData->m_strConfigFile.c_str());
473 }
474
475 // use UUID from machine config
476 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
477
478 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
479 NULL /* puuidRegistry */);
480 if (FAILED(rc)) throw rc;
481
482 /* At this point the changing of the current state modification
483 * flag is allowed. */
484 i_allowStateModification();
485
486 i_commit();
487 }
488 catch (HRESULT err)
489 {
490 /* we assume that error info is set by the thrower */
491 rc = err;
492 }
493 catch (...)
494 {
495 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
496 }
497 }
498 }
499
500 /* Confirm a successful initialization when it's the case */
501 if (SUCCEEDED(rc))
502 {
503 if (mData->mAccessible)
504 autoInitSpan.setSucceeded();
505 else
506 {
507 autoInitSpan.setLimited();
508
509 // uninit media from this machine's media registry, or else
510 // reloading the settings will fail
511 mParent->i_unregisterMachineMedia(i_getId());
512 }
513 }
514
515 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
516 "rc=%08X\n",
517 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
518 mData->mRegistered, mData->mAccessible, rc));
519
520 LogFlowThisFuncLeave();
521
522 return rc;
523}
524
525/**
526 * Initializes a new instance from a machine config that is already in memory
527 * (import OVF case). Since we are importing, the UUID in the machine
528 * config is ignored and we always generate a fresh one.
529 *
530 * @param aParent Associated parent object.
531 * @param strName Name for the new machine; this overrides what is specified in config.
532 * @param strSettingsFilename File name of .vbox file.
533 * @param config Machine configuration loaded and parsed from XML.
534 *
535 * @return Success indicator. if not S_OK, the machine object is invalid
536 */
537HRESULT Machine::init(VirtualBox *aParent,
538 const Utf8Str &strName,
539 const Utf8Str &strSettingsFilename,
540 const settings::MachineConfigFile &config)
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 HRESULT rc = initImpl(aParent, strSettingsFilename);
549 if (FAILED(rc)) return rc;
550
551 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
552 if (FAILED(rc)) return rc;
553
554 rc = initDataAndChildObjects();
555
556 if (SUCCEEDED(rc))
557 {
558 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
559 mData->mAccessible = TRUE;
560
561 // create empty machine config for instance data
562 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
563
564 // generate fresh UUID, ignore machine config
565 unconst(mData->mUuid).create();
566
567 rc = i_loadMachineDataFromSettings(config,
568 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
569
570 // override VM name as well, it may be different
571 mUserData->s.strName = strName;
572
573 if (SUCCEEDED(rc))
574 {
575 /* At this point the changing of the current state modification
576 * flag is allowed. */
577 i_allowStateModification();
578
579 /* commit all changes made during the initialization */
580 i_commit();
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 if (SUCCEEDED(rc))
586 {
587 if (mData->mAccessible)
588 autoInitSpan.setSucceeded();
589 else
590 {
591 /* Ignore all errors from unregistering, they would destroy
592- * the more interesting error information we already have,
593- * pinpointing the issue with the VM config. */
594 ErrorInfoKeeper eik;
595
596 autoInitSpan.setLimited();
597
598 // uninit media from this machine's media registry, or else
599 // reloading the settings will fail
600 mParent->i_unregisterMachineMedia(i_getId());
601 }
602 }
603
604 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
605 "rc=%08X\n",
606 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
607 mData->mRegistered, mData->mAccessible, rc));
608
609 LogFlowThisFuncLeave();
610
611 return rc;
612}
613
614/**
615 * Shared code between the various init() implementations.
616 * @param aParent The VirtualBox object.
617 * @param strConfigFile Settings file.
618 * @return
619 */
620HRESULT Machine::initImpl(VirtualBox *aParent,
621 const Utf8Str &strConfigFile)
622{
623 LogFlowThisFuncEnter();
624
625 AssertReturn(aParent, E_INVALIDARG);
626 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
627
628 HRESULT rc = S_OK;
629
630 /* share the parent weakly */
631 unconst(mParent) = aParent;
632
633 /* allocate the essential machine data structure (the rest will be
634 * allocated later by initDataAndChildObjects() */
635 mData.allocate();
636
637 /* memorize the config file name (as provided) */
638 mData->m_strConfigFile = strConfigFile;
639
640 /* get the full file name */
641 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
642 if (RT_FAILURE(vrc1))
643 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
644 tr("Invalid machine settings file name '%s' (%Rrc)"),
645 strConfigFile.c_str(),
646 vrc1);
647
648 LogFlowThisFuncLeave();
649
650 return rc;
651}
652
653/**
654 * Tries to create a machine settings file in the path stored in the machine
655 * instance data. Used when a new machine is created to fail gracefully if
656 * the settings file could not be written (e.g. because machine dir is read-only).
657 * @return
658 */
659HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
660{
661 HRESULT rc = S_OK;
662
663 // when we create a new machine, we must be able to create the settings file
664 RTFILE f = NIL_RTFILE;
665 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
666 if ( RT_SUCCESS(vrc)
667 || vrc == VERR_SHARING_VIOLATION
668 )
669 {
670 if (RT_SUCCESS(vrc))
671 RTFileClose(f);
672 if (!fForceOverwrite)
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Machine settings file '%s' already exists"),
675 mData->m_strConfigFileFull.c_str());
676 else
677 {
678 /* try to delete the config file, as otherwise the creation
679 * of a new settings file will fail. */
680 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
681 if (RT_FAILURE(vrc2))
682 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
683 tr("Could not delete the existing settings file '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(), vrc2);
685 }
686 }
687 else if ( vrc != VERR_FILE_NOT_FOUND
688 && vrc != VERR_PATH_NOT_FOUND
689 )
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
691 tr("Invalid machine settings file name '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(),
693 vrc);
694 return rc;
695}
696
697/**
698 * Initializes the registered machine by loading the settings file.
699 * This method is separated from #init() in order to make it possible to
700 * retry the operation after VirtualBox startup instead of refusing to
701 * startup the whole VirtualBox server in case if the settings file of some
702 * registered VM is invalid or inaccessible.
703 *
704 * @note Must be always called from this object's write lock
705 * (unless called from #init() that doesn't need any locking).
706 * @note Locks the mUSBController method for writing.
707 * @note Subclasses must not call this method.
708 */
709HRESULT Machine::i_registeredInit()
710{
711 AssertReturn(!i_isSessionMachine(), E_FAIL);
712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
713 AssertReturn(mData->mUuid.isValid(), E_FAIL);
714 AssertReturn(!mData->mAccessible, E_FAIL);
715
716 HRESULT rc = initDataAndChildObjects();
717
718 if (SUCCEEDED(rc))
719 {
720 /* Temporarily reset the registered flag in order to let setters
721 * potentially called from loadSettings() succeed (isMutable() used in
722 * all setters will return FALSE for a Machine instance if mRegistered
723 * is TRUE). */
724 mData->mRegistered = FALSE;
725
726 try
727 {
728 // load and parse machine XML; this will throw on XML or logic errors
729 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
730
731 if (mData->mUuid != mData->pMachineConfigFile->uuid)
732 throw setError(E_FAIL,
733 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
734 mData->pMachineConfigFile->uuid.raw(),
735 mData->m_strConfigFileFull.c_str(),
736 mData->mUuid.toString().c_str(),
737 mParent->i_settingsFilePath().c_str());
738
739 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
740 NULL /* const Guid *puuidRegistry */);
741 if (FAILED(rc)) throw rc;
742 }
743 catch (HRESULT err)
744 {
745 /* we assume that error info is set by the thrower */
746 rc = err;
747 }
748 catch (...)
749 {
750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
751 }
752
753 /* Restore the registered flag (even on failure) */
754 mData->mRegistered = TRUE;
755 }
756
757 if (SUCCEEDED(rc))
758 {
759 /* Set mAccessible to TRUE only if we successfully locked and loaded
760 * the settings file */
761 mData->mAccessible = TRUE;
762
763 /* commit all changes made during loading the settings file */
764 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
765 /// @todo r=klaus for some reason the settings loading logic backs up
766 // the settings, and therefore a commit is needed. Should probably be changed.
767 }
768 else
769 {
770 /* If the machine is registered, then, instead of returning a
771 * failure, we mark it as inaccessible and set the result to
772 * success to give it a try later */
773
774 /* fetch the current error info */
775 mData->mAccessError = com::ErrorInfo();
776 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
777
778 /* rollback all changes */
779 i_rollback(false /* aNotify */);
780
781 // uninit media from this machine's media registry, or else
782 // reloading the settings will fail
783 mParent->i_unregisterMachineMedia(i_getId());
784
785 /* uninitialize the common part to make sure all data is reset to
786 * default (null) values */
787 uninitDataAndChildObjects();
788
789 rc = S_OK;
790 }
791
792 return rc;
793}
794
795/**
796 * Uninitializes the instance.
797 * Called either from FinalRelease() or by the parent when it gets destroyed.
798 *
799 * @note The caller of this method must make sure that this object
800 * a) doesn't have active callers on the current thread and b) is not locked
801 * by the current thread; otherwise uninit() will hang either a) due to
802 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
803 * a dead-lock caused by this thread waiting for all callers on the other
804 * threads are done but preventing them from doing so by holding a lock.
805 */
806void Machine::uninit()
807{
808 LogFlowThisFuncEnter();
809
810 Assert(!isWriteLockOnCurrentThread());
811
812 Assert(!uRegistryNeedsSaving);
813 if (uRegistryNeedsSaving)
814 {
815 AutoCaller autoCaller(this);
816 if (SUCCEEDED(autoCaller.rc()))
817 {
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819 i_saveSettings(NULL, Machine::SaveS_Force);
820 }
821 }
822
823 /* Enclose the state transition Ready->InUninit->NotReady */
824 AutoUninitSpan autoUninitSpan(this);
825 if (autoUninitSpan.uninitDone())
826 return;
827
828 Assert(!i_isSnapshotMachine());
829 Assert(!i_isSessionMachine());
830 Assert(!!mData);
831
832 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
833 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 if (!mData->mSession.mMachine.isNull())
838 {
839 /* Theoretically, this can only happen if the VirtualBox server has been
840 * terminated while there were clients running that owned open direct
841 * sessions. Since in this case we are definitely called by
842 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
843 * won't happen on the client watcher thread (because it has a
844 * VirtualBox caller for the duration of the
845 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
846 * cannot happen until the VirtualBox caller is released). This is
847 * important, because SessionMachine::uninit() cannot correctly operate
848 * after we return from this method (it expects the Machine instance is
849 * still valid). We'll call it ourselves below.
850 */
851 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
852 (SessionMachine*)mData->mSession.mMachine));
853
854 if (Global::IsOnlineOrTransient(mData->mMachineState))
855 {
856 Log1WarningThisFunc(("Setting state to Aborted!\n"));
857 /* set machine state using SessionMachine reimplementation */
858 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
859 }
860
861 /*
862 * Uninitialize SessionMachine using public uninit() to indicate
863 * an unexpected uninitialization.
864 */
865 mData->mSession.mMachine->uninit();
866 /* SessionMachine::uninit() must set mSession.mMachine to null */
867 Assert(mData->mSession.mMachine.isNull());
868 }
869
870 // uninit media from this machine's media registry, if they're still there
871 Guid uuidMachine(i_getId());
872
873 /* the lock is no more necessary (SessionMachine is uninitialized) */
874 alock.release();
875
876 /* XXX This will fail with
877 * "cannot be closed because it is still attached to 1 virtual machines"
878 * because at this point we did not call uninitDataAndChildObjects() yet
879 * and therefore also removeBackReference() for all these mediums was not called! */
880
881 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
882 mParent->i_unregisterMachineMedia(uuidMachine);
883
884 // has machine been modified?
885 if (mData->flModifications)
886 {
887 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
888 i_rollback(false /* aNotify */);
889 }
890
891 if (mData->mAccessible)
892 uninitDataAndChildObjects();
893
894 /* free the essential data structure last */
895 mData.free();
896
897 LogFlowThisFuncLeave();
898}
899
900// Wrapped IMachine properties
901/////////////////////////////////////////////////////////////////////////////
902HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
903{
904 /* mParent is constant during life time, no need to lock */
905 ComObjPtr<VirtualBox> pVirtualBox(mParent);
906 aParent = pVirtualBox;
907
908 return S_OK;
909}
910
911
912HRESULT Machine::getAccessible(BOOL *aAccessible)
913{
914 /* In some cases (medium registry related), it is necessary to be able to
915 * go through the list of all machines. Happens when an inaccessible VM
916 * has a sensible medium registry. */
917 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
919
920 HRESULT rc = S_OK;
921
922 if (!mData->mAccessible)
923 {
924 /* try to initialize the VM once more if not accessible */
925
926 AutoReinitSpan autoReinitSpan(this);
927 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
928
929#ifdef DEBUG
930 LogFlowThisFunc(("Dumping media backreferences\n"));
931 mParent->i_dumpAllBackRefs();
932#endif
933
934 if (mData->pMachineConfigFile)
935 {
936 // reset the XML file to force loadSettings() (called from i_registeredInit())
937 // to parse it again; the file might have changed
938 delete mData->pMachineConfigFile;
939 mData->pMachineConfigFile = NULL;
940 }
941
942 rc = i_registeredInit();
943
944 if (SUCCEEDED(rc) && mData->mAccessible)
945 {
946 autoReinitSpan.setSucceeded();
947
948 /* make sure interesting parties will notice the accessibility
949 * state change */
950 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
951 mParent->i_onMachineDataChange(mData->mUuid);
952 }
953 }
954
955 if (SUCCEEDED(rc))
956 *aAccessible = mData->mAccessible;
957
958 LogFlowThisFuncLeave();
959
960 return rc;
961}
962
963HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
964{
965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
968 {
969 /* return shortly */
970 aAccessError = NULL;
971 return S_OK;
972 }
973
974 HRESULT rc = S_OK;
975
976 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
977 rc = errorInfo.createObject();
978 if (SUCCEEDED(rc))
979 {
980 errorInfo->init(mData->mAccessError.getResultCode(),
981 mData->mAccessError.getInterfaceID().ref(),
982 Utf8Str(mData->mAccessError.getComponent()).c_str(),
983 Utf8Str(mData->mAccessError.getText()));
984 aAccessError = errorInfo;
985 }
986
987 return rc;
988}
989
990HRESULT Machine::getName(com::Utf8Str &aName)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 aName = mUserData->s.strName;
995
996 return S_OK;
997}
998
999HRESULT Machine::setName(const com::Utf8Str &aName)
1000{
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = i_checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 i_setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1021{
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 aDescription = mUserData->s.strDescription;
1025
1026 return S_OK;
1027}
1028
1029HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1030{
1031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1032
1033 // this can be done in principle in any state as it doesn't affect the VM
1034 // significantly, but play safe by not messing around while complex
1035 // activities are going on
1036 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1037 if (FAILED(rc)) return rc;
1038
1039 i_setModified(IsModified_MachineData);
1040 mUserData.backup();
1041 mUserData->s.strDescription = aDescription;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getId(com::Guid &aId)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 aId = mData->mUuid;
1051
1052 return S_OK;
1053}
1054
1055HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1056{
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058 aGroups.resize(mUserData->s.llGroups.size());
1059 size_t i = 0;
1060 for (StringsList::const_iterator
1061 it = mUserData->s.llGroups.begin();
1062 it != mUserData->s.llGroups.end();
1063 ++it, ++i)
1064 aGroups[i] = (*it);
1065
1066 return S_OK;
1067}
1068
1069HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1070{
1071 StringsList llGroups;
1072 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1073 if (FAILED(rc))
1074 return rc;
1075
1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 rc = i_checkStateDependency(MutableOrSavedStateDep);
1079 if (FAILED(rc)) return rc;
1080
1081 i_setModified(IsModified_MachineData);
1082 mUserData.backup();
1083 mUserData->s.llGroups = llGroups;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1089{
1090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 aOSTypeId = mUserData->s.strOsType;
1093
1094 return S_OK;
1095}
1096
1097HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1098{
1099 /* look up the object by Id to check it is valid */
1100 ComObjPtr<GuestOSType> pGuestOSType;
1101 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1102
1103 /* when setting, always use the "etalon" value for consistency -- lookup
1104 * by ID is case-insensitive and the input value may have different case */
1105 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 HRESULT rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComObjPtr<GuestOSType> pGuestOSType;
1298 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1299 pGuestOSType);
1300 if (FAILED(hrc2) || pGuestOSType.isNull())
1301 {
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1307 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1308
1309 switch (mHWData->mParavirtProvider)
1310 {
1311 case ParavirtProvider_Legacy:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else
1316 *aParavirtProvider = ParavirtProvider_None;
1317 break;
1318 }
1319
1320 case ParavirtProvider_Default:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else if ( mUserData->s.strOsType == "Windows10"
1325 || mUserData->s.strOsType == "Windows10_64"
1326 || mUserData->s.strOsType == "Windows81"
1327 || mUserData->s.strOsType == "Windows81_64"
1328 || mUserData->s.strOsType == "Windows8"
1329 || mUserData->s.strOsType == "Windows8_64"
1330 || mUserData->s.strOsType == "Windows7"
1331 || mUserData->s.strOsType == "Windows7_64"
1332 || mUserData->s.strOsType == "WindowsVista"
1333 || mUserData->s.strOsType == "WindowsVista_64"
1334 || mUserData->s.strOsType == "Windows2012"
1335 || mUserData->s.strOsType == "Windows2012_64"
1336 || mUserData->s.strOsType == "Windows2008"
1337 || mUserData->s.strOsType == "Windows2008_64")
1338 {
1339 *aParavirtProvider = ParavirtProvider_HyperV;
1340 }
1341 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1342 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1343 || mUserData->s.strOsType == "Linux"
1344 || mUserData->s.strOsType == "Linux_64"
1345 || mUserData->s.strOsType == "ArchLinux"
1346 || mUserData->s.strOsType == "ArchLinux_64"
1347 || mUserData->s.strOsType == "Debian"
1348 || mUserData->s.strOsType == "Debian_64"
1349 || mUserData->s.strOsType == "Fedora"
1350 || mUserData->s.strOsType == "Fedora_64"
1351 || mUserData->s.strOsType == "Gentoo"
1352 || mUserData->s.strOsType == "Gentoo_64"
1353 || mUserData->s.strOsType == "Mandriva"
1354 || mUserData->s.strOsType == "Mandriva_64"
1355 || mUserData->s.strOsType == "OpenSUSE"
1356 || mUserData->s.strOsType == "OpenSUSE_64"
1357 || mUserData->s.strOsType == "Oracle"
1358 || mUserData->s.strOsType == "Oracle_64"
1359 || mUserData->s.strOsType == "RedHat"
1360 || mUserData->s.strOsType == "RedHat_64"
1361 || mUserData->s.strOsType == "Turbolinux"
1362 || mUserData->s.strOsType == "Turbolinux_64"
1363 || mUserData->s.strOsType == "Ubuntu"
1364 || mUserData->s.strOsType == "Ubuntu_64"
1365 || mUserData->s.strOsType == "Xandros"
1366 || mUserData->s.strOsType == "Xandros_64")
1367 {
1368 *aParavirtProvider = ParavirtProvider_KVM;
1369 }
1370 else
1371 *aParavirtProvider = ParavirtProvider_None;
1372 break;
1373 }
1374
1375 default: AssertFailedBreak(); /* Shut up MSC. */
1376 }
1377 break;
1378 }
1379 }
1380
1381 Assert( *aParavirtProvider == ParavirtProvider_None
1382 || *aParavirtProvider == ParavirtProvider_Minimal
1383 || *aParavirtProvider == ParavirtProvider_HyperV
1384 || *aParavirtProvider == ParavirtProvider_KVM);
1385 return S_OK;
1386}
1387
1388HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1389{
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 aHardwareVersion = mHWData->mHWVersion;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1398{
1399 /* check known version */
1400 Utf8Str hwVersion = aHardwareVersion;
1401 if ( hwVersion.compare("1") != 0
1402 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1403 return setError(E_INVALIDARG,
1404 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1405
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mHWVersion = aHardwareVersion;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 if (!mHWData->mHardwareUUID.isZero())
1423 aHardwareUUID = mHWData->mHardwareUUID;
1424 else
1425 aHardwareUUID = mData->mUuid;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1431{
1432 if (!aHardwareUUID.isValid())
1433 return E_INVALIDARG;
1434
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 if (aHardwareUUID == mData->mUuid)
1443 mHWData->mHardwareUUID.clear();
1444 else
1445 mHWData->mHardwareUUID = aHardwareUUID;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aMemorySize = mHWData->mMemorySize;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setMemorySize(ULONG aMemorySize)
1460{
1461 /* check RAM limits */
1462 if ( aMemorySize < MM_RAM_MIN_IN_MB
1463 || aMemorySize > MM_RAM_MAX_IN_MB
1464 )
1465 return setError(E_INVALIDARG,
1466 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1467 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mMemorySize = aMemorySize;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUCount = mHWData->mCPUCount;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUCount(ULONG aCPUCount)
1491{
1492 /* check CPU limits */
1493 if ( aCPUCount < SchemaDefs::MinCPUCount
1494 || aCPUCount > SchemaDefs::MaxCPUCount
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1498 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1503 if (mHWData->mCPUHotPlugEnabled)
1504 {
1505 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1506 {
1507 if (mHWData->mCPUAttached[idx])
1508 return setError(E_INVALIDARG,
1509 tr("There is still a CPU attached to socket %lu."
1510 "Detach the CPU before removing the socket"),
1511 aCPUCount, idx+1);
1512 }
1513 }
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mCPUCount = aCPUCount;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1535{
1536 HRESULT rc = S_OK;
1537
1538 /* check throttle limits */
1539 if ( aCPUExecutionCap < 1
1540 || aCPUExecutionCap > 100
1541 )
1542 return setError(E_INVALIDARG,
1543 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1544 aCPUExecutionCap, 1, 100);
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 alock.release();
1549 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1550 alock.acquire();
1551 if (FAILED(rc)) return rc;
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1556
1557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1558 if (Global::IsOnline(mData->mMachineState))
1559 i_saveSettings(NULL);
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1574{
1575 HRESULT rc = S_OK;
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 rc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(rc)) return rc;
1581
1582 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1583 {
1584 if (aCPUHotPlugEnabled)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588
1589 /* Add the amount of CPUs currently attached */
1590 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1591 mHWData->mCPUAttached[i] = true;
1592 }
1593 else
1594 {
1595 /*
1596 * We can disable hotplug only if the amount of maximum CPUs is equal
1597 * to the amount of attached CPUs
1598 */
1599 unsigned cCpusAttached = 0;
1600 unsigned iHighestId = 0;
1601
1602 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1603 {
1604 if (mHWData->mCPUAttached[i])
1605 {
1606 cCpusAttached++;
1607 iHighestId = i;
1608 }
1609 }
1610
1611 if ( (cCpusAttached != mHWData->mCPUCount)
1612 || (iHighestId >= mHWData->mCPUCount))
1613 return setError(E_INVALIDARG,
1614 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 }
1619 }
1620
1621 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1622
1623 return rc;
1624}
1625
1626HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1636{
1637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1640 if (SUCCEEDED(hrc))
1641 {
1642 i_setModified(IsModified_MachineData);
1643 mHWData.backup();
1644 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1645 }
1646 return hrc;
1647}
1648
1649HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1650{
1651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 aCPUProfile = mHWData->mCpuProfile;
1653 return S_OK;
1654}
1655
1656HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1657{
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1660 if (SUCCEEDED(hrc))
1661 {
1662 i_setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 /* Empty equals 'host'. */
1665 if (aCPUProfile.isNotEmpty())
1666 mHWData->mCpuProfile = aCPUProfile;
1667 else
1668 mHWData->mCpuProfile = "host";
1669 }
1670 return hrc;
1671}
1672
1673HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1679
1680 return S_OK;
1681#else
1682 NOREF(aEmulatedUSBCardReaderEnabled);
1683 return E_NOTIMPL;
1684#endif
1685}
1686
1687HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1688{
1689#ifdef VBOX_WITH_USB_CARDREADER
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1698
1699 return S_OK;
1700#else
1701 NOREF(aEmulatedUSBCardReaderEnabled);
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *aHPETEnabled = mHWData->mHPETEnabled;
1711
1712 return S_OK;
1713}
1714
1715HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 rc = i_checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 i_setModified(IsModified_MachineData);
1725 mHWData.backup();
1726
1727 mHWData->mHPETEnabled = aHPETEnabled;
1728
1729 return rc;
1730}
1731
1732HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1742{
1743 switch (aGraphicsControllerType)
1744 {
1745 case GraphicsControllerType_Null:
1746 case GraphicsControllerType_VBoxVGA:
1747#ifdef VBOX_WITH_VMSVGA
1748 case GraphicsControllerType_VMSVGA:
1749 case GraphicsControllerType_VBoxSVGA:
1750#endif
1751 break;
1752 default:
1753 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1754 }
1755
1756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 HRESULT rc = i_checkStateDependency(MutableStateDep);
1759 if (FAILED(rc)) return rc;
1760
1761 i_setModified(IsModified_MachineData);
1762 mHWData.backup();
1763 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1769{
1770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 *aVRAMSize = mHWData->mVRAMSize;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1778{
1779 /* check VRAM limits */
1780 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1781 return setError(E_INVALIDARG,
1782 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1783 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 HRESULT rc = i_checkStateDependency(MutableStateDep);
1788 if (FAILED(rc)) return rc;
1789
1790 i_setModified(IsModified_MachineData);
1791 mHWData.backup();
1792 mHWData->mVRAMSize = aVRAMSize;
1793
1794 return S_OK;
1795}
1796
1797/** @todo this method should not be public */
1798HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1803
1804 return S_OK;
1805}
1806
1807/**
1808 * Set the memory balloon size.
1809 *
1810 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1811 * we have to make sure that we never call IGuest from here.
1812 */
1813HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1814{
1815 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1816#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1817 /* check limits */
1818 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1819 return setError(E_INVALIDARG,
1820 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1821 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 i_setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1828
1829 return S_OK;
1830#else
1831 NOREF(aMemoryBalloonSize);
1832 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1837{
1838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1845{
1846#ifdef VBOX_WITH_PAGE_SHARING
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1853 return S_OK;
1854#else
1855 NOREF(aPageFusionEnabled);
1856 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1857#endif
1858}
1859
1860HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 HRESULT rc = i_checkStateDependency(MutableStateDep);
1874 if (FAILED(rc)) return rc;
1875
1876 /** @todo check validity! */
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1881
1882 return S_OK;
1883}
1884
1885
1886HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1896{
1897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 HRESULT rc = i_checkStateDependency(MutableStateDep);
1900 if (FAILED(rc)) return rc;
1901
1902 /** @todo check validity! */
1903 i_setModified(IsModified_MachineData);
1904 mHWData.backup();
1905 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1906
1907 return S_OK;
1908}
1909
1910HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1911{
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 *aMonitorCount = mHWData->mMonitorCount;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1920{
1921 /* make sure monitor count is a sensible number */
1922 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1923 return setError(E_INVALIDARG,
1924 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1925 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1926
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mMonitorCount = aMonitorCount;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1940{
1941 /* mBIOSSettings is constant during life time, no need to lock */
1942 aBIOSSettings = mBIOSSettings;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1948{
1949 /* mRecordingSettings is constant during life time, no need to lock */
1950 aRecordingSettings = mRecordingSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1956{
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 switch (aProperty)
1960 {
1961 case CPUPropertyType_PAE:
1962 *aValue = mHWData->mPAEEnabled;
1963 break;
1964
1965 case CPUPropertyType_LongMode:
1966 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1967 *aValue = TRUE;
1968 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1969 *aValue = FALSE;
1970#if HC_ARCH_BITS == 64
1971 else
1972 *aValue = TRUE;
1973#else
1974 else
1975 {
1976 *aValue = FALSE;
1977
1978 ComObjPtr<GuestOSType> pGuestOSType;
1979 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1980 pGuestOSType);
1981 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1982 {
1983 if (pGuestOSType->i_is64Bit())
1984 {
1985 ComObjPtr<Host> pHost = mParent->i_host();
1986 alock.release();
1987
1988 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1989 if (FAILED(hrc2))
1990 *aValue = FALSE;
1991 }
1992 }
1993 }
1994#endif
1995 break;
1996
1997 case CPUPropertyType_TripleFaultReset:
1998 *aValue = mHWData->mTripleFaultReset;
1999 break;
2000
2001 case CPUPropertyType_APIC:
2002 *aValue = mHWData->mAPIC;
2003 break;
2004
2005 case CPUPropertyType_X2APIC:
2006 *aValue = mHWData->mX2APIC;
2007 break;
2008
2009 case CPUPropertyType_IBPBOnVMExit:
2010 *aValue = mHWData->mIBPBOnVMExit;
2011 break;
2012
2013 case CPUPropertyType_IBPBOnVMEntry:
2014 *aValue = mHWData->mIBPBOnVMEntry;
2015 break;
2016
2017 case CPUPropertyType_SpecCtrl:
2018 *aValue = mHWData->mSpecCtrl;
2019 break;
2020
2021 case CPUPropertyType_SpecCtrlByHost:
2022 *aValue = mHWData->mSpecCtrlByHost;
2023 break;
2024
2025 case CPUPropertyType_HWVirt:
2026 *aValue = mHWData->mNestedHWVirt;
2027 break;
2028
2029 case CPUPropertyType_L1DFlushOnEMTScheduling:
2030 *aValue = mHWData->mL1DFlushOnSched;
2031 break;
2032
2033 case CPUPropertyType_L1DFlushOnVMEntry:
2034 *aValue = mHWData->mL1DFlushOnVMEntry;
2035 break;
2036
2037 default:
2038 return E_INVALIDARG;
2039 }
2040 return S_OK;
2041}
2042
2043HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2044{
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = i_checkStateDependency(MutableStateDep);
2048 if (FAILED(rc)) return rc;
2049
2050 switch (aProperty)
2051 {
2052 case CPUPropertyType_PAE:
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mPAEEnabled = !!aValue;
2056 break;
2057
2058 case CPUPropertyType_LongMode:
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2062 break;
2063
2064 case CPUPropertyType_TripleFaultReset:
2065 i_setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mTripleFaultReset = !!aValue;
2068 break;
2069
2070 case CPUPropertyType_APIC:
2071 if (mHWData->mX2APIC)
2072 aValue = TRUE;
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mAPIC = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_X2APIC:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mX2APIC = !!aValue;
2082 if (aValue)
2083 mHWData->mAPIC = !!aValue;
2084 break;
2085
2086 case CPUPropertyType_IBPBOnVMExit:
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mIBPBOnVMExit = !!aValue;
2090 break;
2091
2092 case CPUPropertyType_IBPBOnVMEntry:
2093 i_setModified(IsModified_MachineData);
2094 mHWData.backup();
2095 mHWData->mIBPBOnVMEntry = !!aValue;
2096 break;
2097
2098 case CPUPropertyType_SpecCtrl:
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mSpecCtrl = !!aValue;
2102 break;
2103
2104 case CPUPropertyType_SpecCtrlByHost:
2105 i_setModified(IsModified_MachineData);
2106 mHWData.backup();
2107 mHWData->mSpecCtrlByHost = !!aValue;
2108 break;
2109
2110 case CPUPropertyType_HWVirt:
2111 i_setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mNestedHWVirt = !!aValue;
2114 break;
2115
2116 case CPUPropertyType_L1DFlushOnEMTScheduling:
2117 i_setModified(IsModified_MachineData);
2118 mHWData.backup();
2119 mHWData->mL1DFlushOnSched = !!aValue;
2120 break;
2121
2122 case CPUPropertyType_L1DFlushOnVMEntry:
2123 i_setModified(IsModified_MachineData);
2124 mHWData.backup();
2125 mHWData->mL1DFlushOnVMEntry = !!aValue;
2126 break;
2127
2128 default:
2129 return E_INVALIDARG;
2130 }
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2135 ULONG *aValEcx, ULONG *aValEdx)
2136{
2137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2138 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2139 {
2140 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2141 it != mHWData->mCpuIdLeafList.end();
2142 ++it)
2143 {
2144 if (aOrdinal == 0)
2145 {
2146 const settings::CpuIdLeaf &rLeaf= *it;
2147 *aIdx = rLeaf.idx;
2148 *aSubIdx = rLeaf.idxSub;
2149 *aValEax = rLeaf.uEax;
2150 *aValEbx = rLeaf.uEbx;
2151 *aValEcx = rLeaf.uEcx;
2152 *aValEdx = rLeaf.uEdx;
2153 return S_OK;
2154 }
2155 aOrdinal--;
2156 }
2157 }
2158 return E_INVALIDARG;
2159}
2160
2161HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2162{
2163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 /*
2166 * Search the list.
2167 */
2168 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2169 {
2170 const settings::CpuIdLeaf &rLeaf= *it;
2171 if ( rLeaf.idx == aIdx
2172 && ( aSubIdx == UINT32_MAX
2173 || rLeaf.idxSub == aSubIdx) )
2174 {
2175 *aValEax = rLeaf.uEax;
2176 *aValEbx = rLeaf.uEbx;
2177 *aValEcx = rLeaf.uEcx;
2178 *aValEdx = rLeaf.uEdx;
2179 return S_OK;
2180 }
2181 }
2182
2183 return E_INVALIDARG;
2184}
2185
2186
2187HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2188{
2189 /*
2190 * Validate input before taking locks and checking state.
2191 */
2192 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2193 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2194 if ( aIdx >= UINT32_C(0x20)
2195 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2196 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2197 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2198
2199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2200 HRESULT rc = i_checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 /*
2204 * Impose a maximum number of leaves.
2205 */
2206 if (mHWData->mCpuIdLeafList.size() > 256)
2207 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2208
2209 /*
2210 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2211 */
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214
2215 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2216 {
2217 settings::CpuIdLeaf &rLeaf= *it;
2218 if ( rLeaf.idx == aIdx
2219 && ( aSubIdx == UINT32_MAX
2220 || rLeaf.idxSub == aSubIdx) )
2221 it = mHWData->mCpuIdLeafList.erase(it);
2222 else
2223 ++it;
2224 }
2225
2226 settings::CpuIdLeaf NewLeaf;
2227 NewLeaf.idx = aIdx;
2228 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2229 NewLeaf.uEax = aValEax;
2230 NewLeaf.uEbx = aValEbx;
2231 NewLeaf.uEcx = aValEcx;
2232 NewLeaf.uEdx = aValEdx;
2233 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2234 return S_OK;
2235}
2236
2237HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2238{
2239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2240
2241 HRESULT rc = i_checkStateDependency(MutableStateDep);
2242 if (FAILED(rc)) return rc;
2243
2244 /*
2245 * Do the removal.
2246 */
2247 bool fModified = mHWData.isBackedUp();
2248 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2249 {
2250 settings::CpuIdLeaf &rLeaf= *it;
2251 if ( rLeaf.idx == aIdx
2252 && ( aSubIdx == UINT32_MAX
2253 || rLeaf.idxSub == aSubIdx) )
2254 {
2255 if (!fModified)
2256 {
2257 fModified = true;
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 // Start from the beginning, since mHWData.backup() creates
2261 // a new list, causing iterator mixup. This makes sure that
2262 // the settings are not unnecessarily marked as modified,
2263 // at the price of extra list walking.
2264 it = mHWData->mCpuIdLeafList.begin();
2265 }
2266 else
2267 it = mHWData->mCpuIdLeafList.erase(it);
2268 }
2269 else
2270 ++it;
2271 }
2272
2273 return S_OK;
2274}
2275
2276HRESULT Machine::removeAllCPUIDLeaves()
2277{
2278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2279
2280 HRESULT rc = i_checkStateDependency(MutableStateDep);
2281 if (FAILED(rc)) return rc;
2282
2283 if (mHWData->mCpuIdLeafList.size() > 0)
2284 {
2285 i_setModified(IsModified_MachineData);
2286 mHWData.backup();
2287
2288 mHWData->mCpuIdLeafList.clear();
2289 }
2290
2291 return S_OK;
2292}
2293HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2294{
2295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 switch(aProperty)
2298 {
2299 case HWVirtExPropertyType_Enabled:
2300 *aValue = mHWData->mHWVirtExEnabled;
2301 break;
2302
2303 case HWVirtExPropertyType_VPID:
2304 *aValue = mHWData->mHWVirtExVPIDEnabled;
2305 break;
2306
2307 case HWVirtExPropertyType_NestedPaging:
2308 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2309 break;
2310
2311 case HWVirtExPropertyType_UnrestrictedExecution:
2312 *aValue = mHWData->mHWVirtExUXEnabled;
2313 break;
2314
2315 case HWVirtExPropertyType_LargePages:
2316 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2317#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2318 *aValue = FALSE;
2319#endif
2320 break;
2321
2322 case HWVirtExPropertyType_Force:
2323 *aValue = mHWData->mHWVirtExForceEnabled;
2324 break;
2325
2326 case HWVirtExPropertyType_UseNativeApi:
2327 *aValue = mHWData->mHWVirtExUseNativeApi;
2328 break;
2329
2330 default:
2331 return E_INVALIDARG;
2332 }
2333 return S_OK;
2334}
2335
2336HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2337{
2338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2339
2340 HRESULT rc = i_checkStateDependency(MutableStateDep);
2341 if (FAILED(rc)) return rc;
2342
2343 switch (aProperty)
2344 {
2345 case HWVirtExPropertyType_Enabled:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mHWVirtExEnabled = !!aValue;
2349 break;
2350
2351 case HWVirtExPropertyType_VPID:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2355 break;
2356
2357 case HWVirtExPropertyType_NestedPaging:
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2361 break;
2362
2363 case HWVirtExPropertyType_UnrestrictedExecution:
2364 i_setModified(IsModified_MachineData);
2365 mHWData.backup();
2366 mHWData->mHWVirtExUXEnabled = !!aValue;
2367 break;
2368
2369 case HWVirtExPropertyType_LargePages:
2370 i_setModified(IsModified_MachineData);
2371 mHWData.backup();
2372 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2373 break;
2374
2375 case HWVirtExPropertyType_Force:
2376 i_setModified(IsModified_MachineData);
2377 mHWData.backup();
2378 mHWData->mHWVirtExForceEnabled = !!aValue;
2379 break;
2380
2381 case HWVirtExPropertyType_UseNativeApi:
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 mHWData->mHWVirtExUseNativeApi = !!aValue;
2385 break;
2386
2387 default:
2388 return E_INVALIDARG;
2389 }
2390
2391 return S_OK;
2392}
2393
2394HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2395{
2396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2399
2400 return S_OK;
2401}
2402
2403HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2404{
2405 /** @todo (r=dmik):
2406 * 1. Allow to change the name of the snapshot folder containing snapshots
2407 * 2. Rename the folder on disk instead of just changing the property
2408 * value (to be smart and not to leave garbage). Note that it cannot be
2409 * done here because the change may be rolled back. Thus, the right
2410 * place is #saveSettings().
2411 */
2412
2413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2414
2415 HRESULT rc = i_checkStateDependency(MutableStateDep);
2416 if (FAILED(rc)) return rc;
2417
2418 if (!mData->mCurrentSnapshot.isNull())
2419 return setError(E_FAIL,
2420 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2421
2422 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2423
2424 if (strSnapshotFolder.isEmpty())
2425 strSnapshotFolder = "Snapshots";
2426 int vrc = i_calculateFullPath(strSnapshotFolder,
2427 strSnapshotFolder);
2428 if (RT_FAILURE(vrc))
2429 return setErrorBoth(E_FAIL, vrc,
2430 tr("Invalid snapshot folder '%s' (%Rrc)"),
2431 strSnapshotFolder.c_str(), vrc);
2432
2433 i_setModified(IsModified_MachineData);
2434 mUserData.backup();
2435
2436 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2437
2438 return S_OK;
2439}
2440
2441HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2442{
2443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2444
2445 aMediumAttachments.resize(mMediumAttachments->size());
2446 size_t i = 0;
2447 for (MediumAttachmentList::const_iterator
2448 it = mMediumAttachments->begin();
2449 it != mMediumAttachments->end();
2450 ++it, ++i)
2451 aMediumAttachments[i] = *it;
2452
2453 return S_OK;
2454}
2455
2456HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2457{
2458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 Assert(!!mVRDEServer);
2461
2462 aVRDEServer = mVRDEServer;
2463
2464 return S_OK;
2465}
2466
2467HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2468{
2469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2470
2471 aAudioAdapter = mAudioAdapter;
2472
2473 return S_OK;
2474}
2475
2476HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2477{
2478#ifdef VBOX_WITH_VUSB
2479 clearError();
2480 MultiResult rc(S_OK);
2481
2482# ifdef VBOX_WITH_USB
2483 rc = mParent->i_host()->i_checkUSBProxyService();
2484 if (FAILED(rc)) return rc;
2485# endif
2486
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 aUSBControllers.resize(mUSBControllers->size());
2490 size_t i = 0;
2491 for (USBControllerList::const_iterator
2492 it = mUSBControllers->begin();
2493 it != mUSBControllers->end();
2494 ++it, ++i)
2495 aUSBControllers[i] = *it;
2496
2497 return S_OK;
2498#else
2499 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2500 * extended error info to indicate that USB is simply not available
2501 * (w/o treating it as a failure), for example, as in OSE */
2502 NOREF(aUSBControllers);
2503 ReturnComNotImplemented();
2504#endif /* VBOX_WITH_VUSB */
2505}
2506
2507HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2508{
2509#ifdef VBOX_WITH_VUSB
2510 clearError();
2511 MultiResult rc(S_OK);
2512
2513# ifdef VBOX_WITH_USB
2514 rc = mParent->i_host()->i_checkUSBProxyService();
2515 if (FAILED(rc)) return rc;
2516# endif
2517
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 aUSBDeviceFilters = mUSBDeviceFilters;
2521 return rc;
2522#else
2523 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2524 * extended error info to indicate that USB is simply not available
2525 * (w/o treating it as a failure), for example, as in OSE */
2526 NOREF(aUSBDeviceFilters);
2527 ReturnComNotImplemented();
2528#endif /* VBOX_WITH_VUSB */
2529}
2530
2531HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 aSettingsFilePath = mData->m_strConfigFileFull;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2541{
2542 RT_NOREF(aSettingsFilePath);
2543 ReturnComNotImplemented();
2544}
2545
2546HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2547{
2548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2551 if (FAILED(rc)) return rc;
2552
2553 if (!mData->pMachineConfigFile->fileExists())
2554 // this is a new machine, and no config file exists yet:
2555 *aSettingsModified = TRUE;
2556 else
2557 *aSettingsModified = (mData->flModifications != 0);
2558
2559 return S_OK;
2560}
2561
2562HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2563{
2564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2565
2566 *aSessionState = mData->mSession.mState;
2567
2568 return S_OK;
2569}
2570
2571HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2572{
2573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2574
2575 aSessionName = mData->mSession.mName;
2576
2577 return S_OK;
2578}
2579
2580HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 *aSessionPID = mData->mSession.mPID;
2585
2586 return S_OK;
2587}
2588
2589HRESULT Machine::getState(MachineState_T *aState)
2590{
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 *aState = mData->mMachineState;
2594 Assert(mData->mMachineState != MachineState_Null);
2595
2596 return S_OK;
2597}
2598
2599HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2600{
2601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2602
2603 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2604
2605 return S_OK;
2606}
2607
2608HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2609{
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 aStateFilePath = mSSData->strStateFilePath;
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 i_getLogFolder(aLogFolder);
2622
2623 return S_OK;
2624}
2625
2626HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2627{
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 aCurrentSnapshot = mData->mCurrentSnapshot;
2631
2632 return S_OK;
2633}
2634
2635HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2636{
2637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2638
2639 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2640 ? 0
2641 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 /* Note: for machines with no snapshots, we always return FALSE
2651 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2652 * reasons :) */
2653
2654 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2655 ? FALSE
2656 : mData->mCurrentStateModified;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2662{
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 aSharedFolders.resize(mHWData->mSharedFolders.size());
2666 size_t i = 0;
2667 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2668 it = mHWData->mSharedFolders.begin();
2669 it != mHWData->mSharedFolders.end();
2670 ++it, ++i)
2671 aSharedFolders[i] = *it;
2672
2673 return S_OK;
2674}
2675
2676HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2677{
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 *aClipboardMode = mHWData->mClipboardMode;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2686{
2687 HRESULT rc = S_OK;
2688
2689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 alock.release();
2692 rc = i_onClipboardModeChange(aClipboardMode);
2693 alock.acquire();
2694 if (FAILED(rc)) return rc;
2695
2696 i_setModified(IsModified_MachineData);
2697 mHWData.backup();
2698 mHWData->mClipboardMode = aClipboardMode;
2699
2700 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2701 if (Global::IsOnline(mData->mMachineState))
2702 i_saveSettings(NULL);
2703
2704 return S_OK;
2705}
2706
2707HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2708{
2709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2710
2711 *aDnDMode = mHWData->mDnDMode;
2712
2713 return S_OK;
2714}
2715
2716HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2717{
2718 HRESULT rc = S_OK;
2719
2720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 alock.release();
2723 rc = i_onDnDModeChange(aDnDMode);
2724
2725 alock.acquire();
2726 if (FAILED(rc)) return rc;
2727
2728 i_setModified(IsModified_MachineData);
2729 mHWData.backup();
2730 mHWData->mDnDMode = aDnDMode;
2731
2732 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2733 if (Global::IsOnline(mData->mMachineState))
2734 i_saveSettings(NULL);
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aStorageControllers.resize(mStorageControllers->size());
2744 size_t i = 0;
2745 for (StorageControllerList::const_iterator
2746 it = mStorageControllers->begin();
2747 it != mStorageControllers->end();
2748 ++it, ++i)
2749 aStorageControllers[i] = *it;
2750
2751 return S_OK;
2752}
2753
2754HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 *aEnabled = mUserData->s.fTeleporterEnabled;
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2764{
2765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 /* Only allow it to be set to true when PoweredOff or Aborted.
2768 (Clearing it is always permitted.) */
2769 if ( aTeleporterEnabled
2770 && mData->mRegistered
2771 && ( !i_isSessionMachine()
2772 || ( mData->mMachineState != MachineState_PoweredOff
2773 && mData->mMachineState != MachineState_Teleported
2774 && mData->mMachineState != MachineState_Aborted
2775 )
2776 )
2777 )
2778 return setError(VBOX_E_INVALID_VM_STATE,
2779 tr("The machine is not powered off (state is %s)"),
2780 Global::stringifyMachineState(mData->mMachineState));
2781
2782 i_setModified(IsModified_MachineData);
2783 mUserData.backup();
2784 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2785
2786 return S_OK;
2787}
2788
2789HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2790{
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2794
2795 return S_OK;
2796}
2797
2798HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2799{
2800 if (aTeleporterPort >= _64K)
2801 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2802
2803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2804
2805 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2806 if (FAILED(rc)) return rc;
2807
2808 i_setModified(IsModified_MachineData);
2809 mUserData.backup();
2810 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2816{
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2820
2821 return S_OK;
2822}
2823
2824HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2825{
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2829 if (FAILED(rc)) return rc;
2830
2831 i_setModified(IsModified_MachineData);
2832 mUserData.backup();
2833 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2847{
2848 /*
2849 * Hash the password first.
2850 */
2851 com::Utf8Str aT = aTeleporterPassword;
2852
2853 if (!aT.isEmpty())
2854 {
2855 if (VBoxIsPasswordHashed(&aT))
2856 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2857 VBoxHashPassword(&aT);
2858 }
2859
2860 /*
2861 * Do the update.
2862 */
2863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2864 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2865 if (SUCCEEDED(hrc))
2866 {
2867 i_setModified(IsModified_MachineData);
2868 mUserData.backup();
2869 mUserData->s.strTeleporterPassword = aT;
2870 }
2871
2872 return hrc;
2873}
2874
2875HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2876{
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2880 return S_OK;
2881}
2882
2883HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2884{
2885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 /** @todo deal with running state change. */
2888 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2889 if (FAILED(rc)) return rc;
2890
2891 i_setModified(IsModified_MachineData);
2892 mUserData.backup();
2893 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2894 return S_OK;
2895}
2896
2897HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2898{
2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2902 return S_OK;
2903}
2904
2905HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2906{
2907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 /** @todo deal with running state change. */
2910 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2911 if (FAILED(rc)) return rc;
2912
2913 i_setModified(IsModified_MachineData);
2914 mUserData.backup();
2915 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2916 return S_OK;
2917}
2918
2919HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2920{
2921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2924 return S_OK;
2925}
2926
2927HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 /** @todo deal with running state change. */
2932 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2933 if (FAILED(rc)) return rc;
2934
2935 i_setModified(IsModified_MachineData);
2936 mUserData.backup();
2937 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2938 return S_OK;
2939}
2940
2941HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2942{
2943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2951{
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 /** @todo deal with running state change. */
2955 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2956 if (FAILED(rc)) return rc;
2957
2958 i_setModified(IsModified_MachineData);
2959 mUserData.backup();
2960 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2961
2962 return S_OK;
2963}
2964
2965HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2966{
2967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /** @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2984 return S_OK;
2985}
2986
2987HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2988{
2989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2990
2991 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2992
2993 return S_OK;
2994}
2995
2996HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2997{
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999
3000 /* Only allow it to be set to true when PoweredOff or Aborted.
3001 (Clearing it is always permitted.) */
3002 if ( aRTCUseUTC
3003 && mData->mRegistered
3004 && ( !i_isSessionMachine()
3005 || ( mData->mMachineState != MachineState_PoweredOff
3006 && mData->mMachineState != MachineState_Teleported
3007 && mData->mMachineState != MachineState_Aborted
3008 )
3009 )
3010 )
3011 return setError(VBOX_E_INVALID_VM_STATE,
3012 tr("The machine is not powered off (state is %s)"),
3013 Global::stringifyMachineState(mData->mMachineState));
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3018
3019 return S_OK;
3020}
3021
3022HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3023{
3024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3025
3026 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3027
3028 return S_OK;
3029}
3030
3031HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3032{
3033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 HRESULT rc = i_checkStateDependency(MutableStateDep);
3036 if (FAILED(rc)) return rc;
3037
3038 i_setModified(IsModified_MachineData);
3039 mHWData.backup();
3040 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3041
3042 return S_OK;
3043}
3044
3045HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3046{
3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 *aIOCacheSize = mHWData->mIOCacheSize;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3055{
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 HRESULT rc = i_checkStateDependency(MutableStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mIOCacheSize = aIOCacheSize;
3064
3065 return S_OK;
3066}
3067
3068
3069/**
3070 * @note Locks objects!
3071 */
3072HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3073 LockType_T aLockType)
3074{
3075 /* check the session state */
3076 SessionState_T state;
3077 HRESULT rc = aSession->COMGETTER(State)(&state);
3078 if (FAILED(rc)) return rc;
3079
3080 if (state != SessionState_Unlocked)
3081 return setError(VBOX_E_INVALID_OBJECT_STATE,
3082 tr("The given session is busy"));
3083
3084 // get the client's IInternalSessionControl interface
3085 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3086 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3087 E_INVALIDARG);
3088
3089 // session name (only used in some code paths)
3090 Utf8Str strSessionName;
3091
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 if (!mData->mRegistered)
3095 return setError(E_UNEXPECTED,
3096 tr("The machine '%s' is not registered"),
3097 mUserData->s.strName.c_str());
3098
3099 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3100
3101 SessionState_T oldState = mData->mSession.mState;
3102 /* Hack: in case the session is closing and there is a progress object
3103 * which allows waiting for the session to be closed, take the opportunity
3104 * and do a limited wait (max. 1 second). This helps a lot when the system
3105 * is busy and thus session closing can take a little while. */
3106 if ( mData->mSession.mState == SessionState_Unlocking
3107 && mData->mSession.mProgress)
3108 {
3109 alock.release();
3110 mData->mSession.mProgress->WaitForCompletion(1000);
3111 alock.acquire();
3112 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3113 }
3114
3115 // try again now
3116 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3117 // (i.e. session machine exists)
3118 && (aLockType == LockType_Shared) // caller wants a shared link to the
3119 // existing session that holds the write lock:
3120 )
3121 {
3122 // OK, share the session... we are now dealing with three processes:
3123 // 1) VBoxSVC (where this code runs);
3124 // 2) process C: the caller's client process (who wants a shared session);
3125 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3126
3127 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3128 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3129 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3130 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3131 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3132
3133 /*
3134 * Release the lock before calling the client process. It's safe here
3135 * since the only thing to do after we get the lock again is to add
3136 * the remote control to the list (which doesn't directly influence
3137 * anything).
3138 */
3139 alock.release();
3140
3141 // get the console of the session holding the write lock (this is a remote call)
3142 ComPtr<IConsole> pConsoleW;
3143 if (mData->mSession.mLockType == LockType_VM)
3144 {
3145 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3146 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3147 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3148 if (FAILED(rc))
3149 // the failure may occur w/o any error info (from RPC), so provide one
3150 return setError(VBOX_E_VM_ERROR,
3151 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3152 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3153 }
3154
3155 // share the session machine and W's console with the caller's session
3156 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3157 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3158 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3159
3160 if (FAILED(rc))
3161 // the failure may occur w/o any error info (from RPC), so provide one
3162 return setError(VBOX_E_VM_ERROR,
3163 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3164 alock.acquire();
3165
3166 // need to revalidate the state after acquiring the lock again
3167 if (mData->mSession.mState != SessionState_Locked)
3168 {
3169 pSessionControl->Uninitialize();
3170 return setError(VBOX_E_INVALID_SESSION_STATE,
3171 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3172 mUserData->s.strName.c_str());
3173 }
3174
3175 // add the caller's session to the list
3176 mData->mSession.mRemoteControls.push_back(pSessionControl);
3177 }
3178 else if ( mData->mSession.mState == SessionState_Locked
3179 || mData->mSession.mState == SessionState_Unlocking
3180 )
3181 {
3182 // sharing not permitted, or machine still unlocking:
3183 return setError(VBOX_E_INVALID_OBJECT_STATE,
3184 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3185 mUserData->s.strName.c_str());
3186 }
3187 else
3188 {
3189 // machine is not locked: then write-lock the machine (create the session machine)
3190
3191 // must not be busy
3192 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3193
3194 // get the caller's session PID
3195 RTPROCESS pid = NIL_RTPROCESS;
3196 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3197 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3198 Assert(pid != NIL_RTPROCESS);
3199
3200 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3201
3202 if (fLaunchingVMProcess)
3203 {
3204 if (mData->mSession.mPID == NIL_RTPROCESS)
3205 {
3206 // two or more clients racing for a lock, the one which set the
3207 // session state to Spawning will win, the others will get an
3208 // error as we can't decide here if waiting a little would help
3209 // (only for shared locks this would avoid an error)
3210 return setError(VBOX_E_INVALID_OBJECT_STATE,
3211 tr("The machine '%s' already has a lock request pending"),
3212 mUserData->s.strName.c_str());
3213 }
3214
3215 // this machine is awaiting for a spawning session to be opened:
3216 // then the calling process must be the one that got started by
3217 // LaunchVMProcess()
3218
3219 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3220 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3221
3222#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3223 /* Hardened windows builds spawns three processes when a VM is
3224 launched, the 3rd one is the one that will end up here. */
3225 RTPROCESS ppid;
3226 int rc = RTProcQueryParent(pid, &ppid);
3227 if (RT_SUCCESS(rc))
3228 rc = RTProcQueryParent(ppid, &ppid);
3229 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3230 || rc == VERR_ACCESS_DENIED)
3231 {
3232 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3233 mData->mSession.mPID = pid;
3234 }
3235#endif
3236
3237 if (mData->mSession.mPID != pid)
3238 return setError(E_ACCESSDENIED,
3239 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3240 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3241 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3242 }
3243
3244 // create the mutable SessionMachine from the current machine
3245 ComObjPtr<SessionMachine> sessionMachine;
3246 sessionMachine.createObject();
3247 rc = sessionMachine->init(this);
3248 AssertComRC(rc);
3249
3250 /* NOTE: doing return from this function after this point but
3251 * before the end is forbidden since it may call SessionMachine::uninit()
3252 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3253 * lock while still holding the Machine lock in alock so that a deadlock
3254 * is possible due to the wrong lock order. */
3255
3256 if (SUCCEEDED(rc))
3257 {
3258 /*
3259 * Set the session state to Spawning to protect against subsequent
3260 * attempts to open a session and to unregister the machine after
3261 * we release the lock.
3262 */
3263 SessionState_T origState = mData->mSession.mState;
3264 mData->mSession.mState = SessionState_Spawning;
3265
3266#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3267 /* Get the client token ID to be passed to the client process */
3268 Utf8Str strTokenId;
3269 sessionMachine->i_getTokenId(strTokenId);
3270 Assert(!strTokenId.isEmpty());
3271#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3272 /* Get the client token to be passed to the client process */
3273 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3274 /* The token is now "owned" by pToken, fix refcount */
3275 if (!pToken.isNull())
3276 pToken->Release();
3277#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3278
3279 /*
3280 * Release the lock before calling the client process -- it will call
3281 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3282 * because the state is Spawning, so that LaunchVMProcess() and
3283 * LockMachine() calls will fail. This method, called before we
3284 * acquire the lock again, will fail because of the wrong PID.
3285 *
3286 * Note that mData->mSession.mRemoteControls accessed outside
3287 * the lock may not be modified when state is Spawning, so it's safe.
3288 */
3289 alock.release();
3290
3291 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3292#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3293 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3294#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3295 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3296 /* Now the token is owned by the client process. */
3297 pToken.setNull();
3298#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3299 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3300
3301 /* The failure may occur w/o any error info (from RPC), so provide one */
3302 if (FAILED(rc))
3303 setError(VBOX_E_VM_ERROR,
3304 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3305
3306 // get session name, either to remember or to compare against
3307 // the already known session name.
3308 {
3309 Bstr bstrSessionName;
3310 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3311 if (SUCCEEDED(rc2))
3312 strSessionName = bstrSessionName;
3313 }
3314
3315 if ( SUCCEEDED(rc)
3316 && fLaunchingVMProcess
3317 )
3318 {
3319 /* complete the remote session initialization */
3320
3321 /* get the console from the direct session */
3322 ComPtr<IConsole> console;
3323 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3324 ComAssertComRC(rc);
3325
3326 if (SUCCEEDED(rc) && !console)
3327 {
3328 ComAssert(!!console);
3329 rc = E_FAIL;
3330 }
3331
3332 /* assign machine & console to the remote session */
3333 if (SUCCEEDED(rc))
3334 {
3335 /*
3336 * after LaunchVMProcess(), the first and the only
3337 * entry in remoteControls is that remote session
3338 */
3339 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3340 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3341 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3342
3343 /* The failure may occur w/o any error info (from RPC), so provide one */
3344 if (FAILED(rc))
3345 setError(VBOX_E_VM_ERROR,
3346 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3347 }
3348
3349 if (FAILED(rc))
3350 pSessionControl->Uninitialize();
3351 }
3352
3353 /* acquire the lock again */
3354 alock.acquire();
3355
3356 /* Restore the session state */
3357 mData->mSession.mState = origState;
3358 }
3359
3360 // finalize spawning anyway (this is why we don't return on errors above)
3361 if (fLaunchingVMProcess)
3362 {
3363 Assert(mData->mSession.mName == strSessionName);
3364 /* Note that the progress object is finalized later */
3365 /** @todo Consider checking mData->mSession.mProgress for cancellation
3366 * around here. */
3367
3368 /* We don't reset mSession.mPID here because it is necessary for
3369 * SessionMachine::uninit() to reap the child process later. */
3370
3371 if (FAILED(rc))
3372 {
3373 /* Close the remote session, remove the remote control from the list
3374 * and reset session state to Closed (@note keep the code in sync
3375 * with the relevant part in checkForSpawnFailure()). */
3376
3377 Assert(mData->mSession.mRemoteControls.size() == 1);
3378 if (mData->mSession.mRemoteControls.size() == 1)
3379 {
3380 ErrorInfoKeeper eik;
3381 mData->mSession.mRemoteControls.front()->Uninitialize();
3382 }
3383
3384 mData->mSession.mRemoteControls.clear();
3385 mData->mSession.mState = SessionState_Unlocked;
3386 }
3387 }
3388 else
3389 {
3390 /* memorize PID of the directly opened session */
3391 if (SUCCEEDED(rc))
3392 mData->mSession.mPID = pid;
3393 }
3394
3395 if (SUCCEEDED(rc))
3396 {
3397 mData->mSession.mLockType = aLockType;
3398 /* memorize the direct session control and cache IUnknown for it */
3399 mData->mSession.mDirectControl = pSessionControl;
3400 mData->mSession.mState = SessionState_Locked;
3401 if (!fLaunchingVMProcess)
3402 mData->mSession.mName = strSessionName;
3403 /* associate the SessionMachine with this Machine */
3404 mData->mSession.mMachine = sessionMachine;
3405
3406 /* request an IUnknown pointer early from the remote party for later
3407 * identity checks (it will be internally cached within mDirectControl
3408 * at least on XPCOM) */
3409 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3410 NOREF(unk);
3411 }
3412
3413 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3414 * would break the lock order */
3415 alock.release();
3416
3417 /* uninitialize the created session machine on failure */
3418 if (FAILED(rc))
3419 sessionMachine->uninit();
3420 }
3421
3422 if (SUCCEEDED(rc))
3423 {
3424 /*
3425 * tell the client watcher thread to update the set of
3426 * machines that have open sessions
3427 */
3428 mParent->i_updateClientWatcher();
3429
3430 if (oldState != SessionState_Locked)
3431 /* fire an event */
3432 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3433 }
3434
3435 return rc;
3436}
3437
3438/**
3439 * @note Locks objects!
3440 */
3441HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3442 const com::Utf8Str &aName,
3443 const com::Utf8Str &aEnvironment,
3444 ComPtr<IProgress> &aProgress)
3445{
3446 Utf8Str strFrontend(aName);
3447 /* "emergencystop" doesn't need the session, so skip the checks/interface
3448 * retrieval. This code doesn't quite fit in here, but introducing a
3449 * special API method would be even more effort, and would require explicit
3450 * support by every API client. It's better to hide the feature a bit. */
3451 if (strFrontend != "emergencystop")
3452 CheckComArgNotNull(aSession);
3453
3454 HRESULT rc = S_OK;
3455 if (strFrontend.isEmpty())
3456 {
3457 Bstr bstrFrontend;
3458 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3459 if (FAILED(rc))
3460 return rc;
3461 strFrontend = bstrFrontend;
3462 if (strFrontend.isEmpty())
3463 {
3464 ComPtr<ISystemProperties> systemProperties;
3465 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3466 if (FAILED(rc))
3467 return rc;
3468 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3469 if (FAILED(rc))
3470 return rc;
3471 strFrontend = bstrFrontend;
3472 }
3473 /* paranoia - emergencystop is not a valid default */
3474 if (strFrontend == "emergencystop")
3475 strFrontend = Utf8Str::Empty;
3476 }
3477 /* default frontend: Qt GUI */
3478 if (strFrontend.isEmpty())
3479 strFrontend = "GUI/Qt";
3480
3481 if (strFrontend != "emergencystop")
3482 {
3483 /* check the session state */
3484 SessionState_T state;
3485 rc = aSession->COMGETTER(State)(&state);
3486 if (FAILED(rc))
3487 return rc;
3488
3489 if (state != SessionState_Unlocked)
3490 return setError(VBOX_E_INVALID_OBJECT_STATE,
3491 tr("The given session is busy"));
3492
3493 /* get the IInternalSessionControl interface */
3494 ComPtr<IInternalSessionControl> control(aSession);
3495 ComAssertMsgRet(!control.isNull(),
3496 ("No IInternalSessionControl interface"),
3497 E_INVALIDARG);
3498
3499 /* get the teleporter enable state for the progress object init. */
3500 BOOL fTeleporterEnabled;
3501 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3502 if (FAILED(rc))
3503 return rc;
3504
3505 /* create a progress object */
3506 ComObjPtr<ProgressProxy> progress;
3507 progress.createObject();
3508 rc = progress->init(mParent,
3509 static_cast<IMachine*>(this),
3510 Bstr(tr("Starting VM")).raw(),
3511 TRUE /* aCancelable */,
3512 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3513 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3514 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3515 2 /* uFirstOperationWeight */,
3516 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3517
3518 if (SUCCEEDED(rc))
3519 {
3520 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3521 if (SUCCEEDED(rc))
3522 {
3523 aProgress = progress;
3524
3525 /* signal the client watcher thread */
3526 mParent->i_updateClientWatcher();
3527
3528 /* fire an event */
3529 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3530 }
3531 }
3532 }
3533 else
3534 {
3535 /* no progress object - either instant success or failure */
3536 aProgress = NULL;
3537
3538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3539
3540 if (mData->mSession.mState != SessionState_Locked)
3541 return setError(VBOX_E_INVALID_OBJECT_STATE,
3542 tr("The machine '%s' is not locked by a session"),
3543 mUserData->s.strName.c_str());
3544
3545 /* must have a VM process associated - do not kill normal API clients
3546 * with an open session */
3547 if (!Global::IsOnline(mData->mMachineState))
3548 return setError(VBOX_E_INVALID_OBJECT_STATE,
3549 tr("The machine '%s' does not have a VM process"),
3550 mUserData->s.strName.c_str());
3551
3552 /* forcibly terminate the VM process */
3553 if (mData->mSession.mPID != NIL_RTPROCESS)
3554 RTProcTerminate(mData->mSession.mPID);
3555
3556 /* signal the client watcher thread, as most likely the client has
3557 * been terminated */
3558 mParent->i_updateClientWatcher();
3559 }
3560
3561 return rc;
3562}
3563
3564HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3565{
3566 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3567 return setError(E_INVALIDARG,
3568 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3569 aPosition, SchemaDefs::MaxBootPosition);
3570
3571 if (aDevice == DeviceType_USB)
3572 return setError(E_NOTIMPL,
3573 tr("Booting from USB device is currently not supported"));
3574
3575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3576
3577 HRESULT rc = i_checkStateDependency(MutableStateDep);
3578 if (FAILED(rc)) return rc;
3579
3580 i_setModified(IsModified_MachineData);
3581 mHWData.backup();
3582 mHWData->mBootOrder[aPosition - 1] = aDevice;
3583
3584 return S_OK;
3585}
3586
3587HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3588{
3589 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3590 return setError(E_INVALIDARG,
3591 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3592 aPosition, SchemaDefs::MaxBootPosition);
3593
3594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3595
3596 *aDevice = mHWData->mBootOrder[aPosition - 1];
3597
3598 return S_OK;
3599}
3600
3601HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3602 LONG aControllerPort,
3603 LONG aDevice,
3604 DeviceType_T aType,
3605 const ComPtr<IMedium> &aMedium)
3606{
3607 IMedium *aM = aMedium;
3608 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3609 aName.c_str(), aControllerPort, aDevice, aType, aM));
3610
3611 // request the host lock first, since might be calling Host methods for getting host drives;
3612 // next, protect the media tree all the while we're in here, as well as our member variables
3613 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3614 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3615
3616 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3617 if (FAILED(rc)) return rc;
3618
3619 /// @todo NEWMEDIA implicit machine registration
3620 if (!mData->mRegistered)
3621 return setError(VBOX_E_INVALID_OBJECT_STATE,
3622 tr("Cannot attach storage devices to an unregistered machine"));
3623
3624 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3625
3626 /* Check for an existing controller. */
3627 ComObjPtr<StorageController> ctl;
3628 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3629 if (FAILED(rc)) return rc;
3630
3631 StorageControllerType_T ctrlType;
3632 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3633 if (FAILED(rc))
3634 return setError(E_FAIL,
3635 tr("Could not get type of controller '%s'"),
3636 aName.c_str());
3637
3638 bool fSilent = false;
3639 Utf8Str strReconfig;
3640
3641 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3642 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3643 if ( mData->mMachineState == MachineState_Paused
3644 && strReconfig == "1")
3645 fSilent = true;
3646
3647 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3648 bool fHotplug = false;
3649 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3650 fHotplug = true;
3651
3652 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3653 return setError(VBOX_E_INVALID_VM_STATE,
3654 tr("Controller '%s' does not support hotplugging"),
3655 aName.c_str());
3656
3657 // check that the port and device are not out of range
3658 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3659 if (FAILED(rc)) return rc;
3660
3661 /* check if the device slot is already busy */
3662 MediumAttachment *pAttachTemp;
3663 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3664 aName,
3665 aControllerPort,
3666 aDevice)))
3667 {
3668 Medium *pMedium = pAttachTemp->i_getMedium();
3669 if (pMedium)
3670 {
3671 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3672 return setError(VBOX_E_OBJECT_IN_USE,
3673 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3674 pMedium->i_getLocationFull().c_str(),
3675 aControllerPort,
3676 aDevice,
3677 aName.c_str());
3678 }
3679 else
3680 return setError(VBOX_E_OBJECT_IN_USE,
3681 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3682 aControllerPort, aDevice, aName.c_str());
3683 }
3684
3685 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3686 if (aMedium && medium.isNull())
3687 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3688
3689 AutoCaller mediumCaller(medium);
3690 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3691
3692 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3693
3694 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3695 && !medium.isNull()
3696 )
3697 return setError(VBOX_E_OBJECT_IN_USE,
3698 tr("Medium '%s' is already attached to this virtual machine"),
3699 medium->i_getLocationFull().c_str());
3700
3701 if (!medium.isNull())
3702 {
3703 MediumType_T mtype = medium->i_getType();
3704 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3705 // For DVDs it's not written to the config file, so needs no global config
3706 // version bump. For floppies it's a new attribute "type", which is ignored
3707 // by older VirtualBox version, so needs no global config version bump either.
3708 // For hard disks this type is not accepted.
3709 if (mtype == MediumType_MultiAttach)
3710 {
3711 // This type is new with VirtualBox 4.0 and therefore requires settings
3712 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3713 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3714 // two reasons: The medium type is a property of the media registry tree, which
3715 // can reside in the global config file (for pre-4.0 media); we would therefore
3716 // possibly need to bump the global config version. We don't want to do that though
3717 // because that might make downgrading to pre-4.0 impossible.
3718 // As a result, we can only use these two new types if the medium is NOT in the
3719 // global registry:
3720 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3721 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3722 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3723 )
3724 return setError(VBOX_E_INVALID_OBJECT_STATE,
3725 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3726 "to machines that were created with VirtualBox 4.0 or later"),
3727 medium->i_getLocationFull().c_str());
3728 }
3729 }
3730
3731 bool fIndirect = false;
3732 if (!medium.isNull())
3733 fIndirect = medium->i_isReadOnly();
3734 bool associate = true;
3735
3736 do
3737 {
3738 if ( aType == DeviceType_HardDisk
3739 && mMediumAttachments.isBackedUp())
3740 {
3741 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3742
3743 /* check if the medium was attached to the VM before we started
3744 * changing attachments in which case the attachment just needs to
3745 * be restored */
3746 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3747 {
3748 AssertReturn(!fIndirect, E_FAIL);
3749
3750 /* see if it's the same bus/channel/device */
3751 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3752 {
3753 /* the simplest case: restore the whole attachment
3754 * and return, nothing else to do */
3755 mMediumAttachments->push_back(pAttachTemp);
3756
3757 /* Reattach the medium to the VM. */
3758 if (fHotplug || fSilent)
3759 {
3760 mediumLock.release();
3761 treeLock.release();
3762 alock.release();
3763
3764 MediumLockList *pMediumLockList(new MediumLockList());
3765
3766 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3767 medium /* pToLockWrite */,
3768 false /* fMediumLockWriteAll */,
3769 NULL,
3770 *pMediumLockList);
3771 alock.acquire();
3772 if (FAILED(rc))
3773 delete pMediumLockList;
3774 else
3775 {
3776 mData->mSession.mLockedMedia.Unlock();
3777 alock.release();
3778 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3779 mData->mSession.mLockedMedia.Lock();
3780 alock.acquire();
3781 }
3782 alock.release();
3783
3784 if (SUCCEEDED(rc))
3785 {
3786 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3787 /* Remove lock list in case of error. */
3788 if (FAILED(rc))
3789 {
3790 mData->mSession.mLockedMedia.Unlock();
3791 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3792 mData->mSession.mLockedMedia.Lock();
3793 }
3794 }
3795 }
3796
3797 return S_OK;
3798 }
3799
3800 /* bus/channel/device differ; we need a new attachment object,
3801 * but don't try to associate it again */
3802 associate = false;
3803 break;
3804 }
3805 }
3806
3807 /* go further only if the attachment is to be indirect */
3808 if (!fIndirect)
3809 break;
3810
3811 /* perform the so called smart attachment logic for indirect
3812 * attachments. Note that smart attachment is only applicable to base
3813 * hard disks. */
3814
3815 if (medium->i_getParent().isNull())
3816 {
3817 /* first, investigate the backup copy of the current hard disk
3818 * attachments to make it possible to re-attach existing diffs to
3819 * another device slot w/o losing their contents */
3820 if (mMediumAttachments.isBackedUp())
3821 {
3822 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3823
3824 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3825 uint32_t foundLevel = 0;
3826
3827 for (MediumAttachmentList::const_iterator
3828 it = oldAtts.begin();
3829 it != oldAtts.end();
3830 ++it)
3831 {
3832 uint32_t level = 0;
3833 MediumAttachment *pAttach = *it;
3834 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3835 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3836 if (pMedium.isNull())
3837 continue;
3838
3839 if (pMedium->i_getBase(&level) == medium)
3840 {
3841 /* skip the hard disk if its currently attached (we
3842 * cannot attach the same hard disk twice) */
3843 if (i_findAttachment(*mMediumAttachments.data(),
3844 pMedium))
3845 continue;
3846
3847 /* matched device, channel and bus (i.e. attached to the
3848 * same place) will win and immediately stop the search;
3849 * otherwise the attachment that has the youngest
3850 * descendant of medium will be used
3851 */
3852 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3853 {
3854 /* the simplest case: restore the whole attachment
3855 * and return, nothing else to do */
3856 mMediumAttachments->push_back(*it);
3857
3858 /* Reattach the medium to the VM. */
3859 if (fHotplug || fSilent)
3860 {
3861 mediumLock.release();
3862 treeLock.release();
3863 alock.release();
3864
3865 MediumLockList *pMediumLockList(new MediumLockList());
3866
3867 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3868 medium /* pToLockWrite */,
3869 false /* fMediumLockWriteAll */,
3870 NULL,
3871 *pMediumLockList);
3872 alock.acquire();
3873 if (FAILED(rc))
3874 delete pMediumLockList;
3875 else
3876 {
3877 mData->mSession.mLockedMedia.Unlock();
3878 alock.release();
3879 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3880 mData->mSession.mLockedMedia.Lock();
3881 alock.acquire();
3882 }
3883 alock.release();
3884
3885 if (SUCCEEDED(rc))
3886 {
3887 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3888 /* Remove lock list in case of error. */
3889 if (FAILED(rc))
3890 {
3891 mData->mSession.mLockedMedia.Unlock();
3892 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3893 mData->mSession.mLockedMedia.Lock();
3894 }
3895 }
3896 }
3897
3898 return S_OK;
3899 }
3900 else if ( foundIt == oldAtts.end()
3901 || level > foundLevel /* prefer younger */
3902 )
3903 {
3904 foundIt = it;
3905 foundLevel = level;
3906 }
3907 }
3908 }
3909
3910 if (foundIt != oldAtts.end())
3911 {
3912 /* use the previously attached hard disk */
3913 medium = (*foundIt)->i_getMedium();
3914 mediumCaller.attach(medium);
3915 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3916 mediumLock.attach(medium);
3917 /* not implicit, doesn't require association with this VM */
3918 fIndirect = false;
3919 associate = false;
3920 /* go right to the MediumAttachment creation */
3921 break;
3922 }
3923 }
3924
3925 /* must give up the medium lock and medium tree lock as below we
3926 * go over snapshots, which needs a lock with higher lock order. */
3927 mediumLock.release();
3928 treeLock.release();
3929
3930 /* then, search through snapshots for the best diff in the given
3931 * hard disk's chain to base the new diff on */
3932
3933 ComObjPtr<Medium> base;
3934 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3935 while (snap)
3936 {
3937 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3938
3939 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3940
3941 MediumAttachment *pAttachFound = NULL;
3942 uint32_t foundLevel = 0;
3943
3944 for (MediumAttachmentList::const_iterator
3945 it = snapAtts.begin();
3946 it != snapAtts.end();
3947 ++it)
3948 {
3949 MediumAttachment *pAttach = *it;
3950 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3951 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3952 if (pMedium.isNull())
3953 continue;
3954
3955 uint32_t level = 0;
3956 if (pMedium->i_getBase(&level) == medium)
3957 {
3958 /* matched device, channel and bus (i.e. attached to the
3959 * same place) will win and immediately stop the search;
3960 * otherwise the attachment that has the youngest
3961 * descendant of medium will be used
3962 */
3963 if ( pAttach->i_getDevice() == aDevice
3964 && pAttach->i_getPort() == aControllerPort
3965 && pAttach->i_getControllerName() == aName
3966 )
3967 {
3968 pAttachFound = pAttach;
3969 break;
3970 }
3971 else if ( !pAttachFound
3972 || level > foundLevel /* prefer younger */
3973 )
3974 {
3975 pAttachFound = pAttach;
3976 foundLevel = level;
3977 }
3978 }
3979 }
3980
3981 if (pAttachFound)
3982 {
3983 base = pAttachFound->i_getMedium();
3984 break;
3985 }
3986
3987 snap = snap->i_getParent();
3988 }
3989
3990 /* re-lock medium tree and the medium, as we need it below */
3991 treeLock.acquire();
3992 mediumLock.acquire();
3993
3994 /* found a suitable diff, use it as a base */
3995 if (!base.isNull())
3996 {
3997 medium = base;
3998 mediumCaller.attach(medium);
3999 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4000 mediumLock.attach(medium);
4001 }
4002 }
4003
4004 Utf8Str strFullSnapshotFolder;
4005 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4006
4007 ComObjPtr<Medium> diff;
4008 diff.createObject();
4009 // store this diff in the same registry as the parent
4010 Guid uuidRegistryParent;
4011 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4012 {
4013 // parent image has no registry: this can happen if we're attaching a new immutable
4014 // image that has not yet been attached (medium then points to the base and we're
4015 // creating the diff image for the immutable, and the parent is not yet registered);
4016 // put the parent in the machine registry then
4017 mediumLock.release();
4018 treeLock.release();
4019 alock.release();
4020 i_addMediumToRegistry(medium);
4021 alock.acquire();
4022 treeLock.acquire();
4023 mediumLock.acquire();
4024 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4025 }
4026 rc = diff->init(mParent,
4027 medium->i_getPreferredDiffFormat(),
4028 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4029 uuidRegistryParent,
4030 DeviceType_HardDisk);
4031 if (FAILED(rc)) return rc;
4032
4033 /* Apply the normal locking logic to the entire chain. */
4034 MediumLockList *pMediumLockList(new MediumLockList());
4035 mediumLock.release();
4036 treeLock.release();
4037 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4038 diff /* pToLockWrite */,
4039 false /* fMediumLockWriteAll */,
4040 medium,
4041 *pMediumLockList);
4042 treeLock.acquire();
4043 mediumLock.acquire();
4044 if (SUCCEEDED(rc))
4045 {
4046 mediumLock.release();
4047 treeLock.release();
4048 rc = pMediumLockList->Lock();
4049 treeLock.acquire();
4050 mediumLock.acquire();
4051 if (FAILED(rc))
4052 setError(rc,
4053 tr("Could not lock medium when creating diff '%s'"),
4054 diff->i_getLocationFull().c_str());
4055 else
4056 {
4057 /* will release the lock before the potentially lengthy
4058 * operation, so protect with the special state */
4059 MachineState_T oldState = mData->mMachineState;
4060 i_setMachineState(MachineState_SettingUp);
4061
4062 mediumLock.release();
4063 treeLock.release();
4064 alock.release();
4065
4066 rc = medium->i_createDiffStorage(diff,
4067 medium->i_getPreferredDiffVariant(),
4068 pMediumLockList,
4069 NULL /* aProgress */,
4070 true /* aWait */,
4071 false /* aNotify */);
4072
4073 alock.acquire();
4074 treeLock.acquire();
4075 mediumLock.acquire();
4076
4077 i_setMachineState(oldState);
4078 }
4079 }
4080
4081 /* Unlock the media and free the associated memory. */
4082 delete pMediumLockList;
4083
4084 if (FAILED(rc)) return rc;
4085
4086 /* use the created diff for the actual attachment */
4087 medium = diff;
4088 mediumCaller.attach(medium);
4089 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4090 mediumLock.attach(medium);
4091 }
4092 while (0);
4093
4094 ComObjPtr<MediumAttachment> attachment;
4095 attachment.createObject();
4096 rc = attachment->init(this,
4097 medium,
4098 aName,
4099 aControllerPort,
4100 aDevice,
4101 aType,
4102 fIndirect,
4103 false /* fPassthrough */,
4104 false /* fTempEject */,
4105 false /* fNonRotational */,
4106 false /* fDiscard */,
4107 fHotplug /* fHotPluggable */,
4108 Utf8Str::Empty);
4109 if (FAILED(rc)) return rc;
4110
4111 if (associate && !medium.isNull())
4112 {
4113 // as the last step, associate the medium to the VM
4114 rc = medium->i_addBackReference(mData->mUuid);
4115 // here we can fail because of Deleting, or being in process of creating a Diff
4116 if (FAILED(rc)) return rc;
4117
4118 mediumLock.release();
4119 treeLock.release();
4120 alock.release();
4121 i_addMediumToRegistry(medium);
4122 alock.acquire();
4123 treeLock.acquire();
4124 mediumLock.acquire();
4125 }
4126
4127 /* success: finally remember the attachment */
4128 i_setModified(IsModified_Storage);
4129 mMediumAttachments.backup();
4130 mMediumAttachments->push_back(attachment);
4131
4132 mediumLock.release();
4133 treeLock.release();
4134 alock.release();
4135
4136 if (fHotplug || fSilent)
4137 {
4138 if (!medium.isNull())
4139 {
4140 MediumLockList *pMediumLockList(new MediumLockList());
4141
4142 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4143 medium /* pToLockWrite */,
4144 false /* fMediumLockWriteAll */,
4145 NULL,
4146 *pMediumLockList);
4147 alock.acquire();
4148 if (FAILED(rc))
4149 delete pMediumLockList;
4150 else
4151 {
4152 mData->mSession.mLockedMedia.Unlock();
4153 alock.release();
4154 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4155 mData->mSession.mLockedMedia.Lock();
4156 alock.acquire();
4157 }
4158 alock.release();
4159 }
4160
4161 if (SUCCEEDED(rc))
4162 {
4163 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4164 /* Remove lock list in case of error. */
4165 if (FAILED(rc))
4166 {
4167 mData->mSession.mLockedMedia.Unlock();
4168 mData->mSession.mLockedMedia.Remove(attachment);
4169 mData->mSession.mLockedMedia.Lock();
4170 }
4171 }
4172 }
4173
4174 /* Save modified registries, but skip this machine as it's the caller's
4175 * job to save its settings like all other settings changes. */
4176 mParent->i_unmarkRegistryModified(i_getId());
4177 mParent->i_saveModifiedRegistries();
4178
4179 if (aM)
4180 mParent->i_onMediumConfigChanged(aM);
4181 return rc;
4182}
4183
4184HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4185 LONG aDevice)
4186{
4187 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4188 aName.c_str(), aControllerPort, aDevice));
4189
4190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4191
4192 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4193 if (FAILED(rc)) return rc;
4194
4195 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4196
4197 /* Check for an existing controller. */
4198 ComObjPtr<StorageController> ctl;
4199 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4200 if (FAILED(rc)) return rc;
4201
4202 StorageControllerType_T ctrlType;
4203 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4204 if (FAILED(rc))
4205 return setError(E_FAIL,
4206 tr("Could not get type of controller '%s'"),
4207 aName.c_str());
4208
4209 bool fSilent = false;
4210 Utf8Str strReconfig;
4211
4212 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4213 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4214 if ( mData->mMachineState == MachineState_Paused
4215 && strReconfig == "1")
4216 fSilent = true;
4217
4218 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4219 bool fHotplug = false;
4220 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4221 fHotplug = true;
4222
4223 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4224 return setError(VBOX_E_INVALID_VM_STATE,
4225 tr("Controller '%s' does not support hotplugging"),
4226 aName.c_str());
4227
4228 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4229 aName,
4230 aControllerPort,
4231 aDevice);
4232 if (!pAttach)
4233 return setError(VBOX_E_OBJECT_NOT_FOUND,
4234 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4235 aDevice, aControllerPort, aName.c_str());
4236
4237 if (fHotplug && !pAttach->i_getHotPluggable())
4238 return setError(VBOX_E_NOT_SUPPORTED,
4239 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4240 aDevice, aControllerPort, aName.c_str());
4241
4242 /*
4243 * The VM has to detach the device before we delete any implicit diffs.
4244 * If this fails we can roll back without loosing data.
4245 */
4246 if (fHotplug || fSilent)
4247 {
4248 alock.release();
4249 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4250 alock.acquire();
4251 }
4252 if (FAILED(rc)) return rc;
4253
4254 /* If we are here everything went well and we can delete the implicit now. */
4255 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4256
4257 alock.release();
4258
4259 /* Save modified registries, but skip this machine as it's the caller's
4260 * job to save its settings like all other settings changes. */
4261 mParent->i_unmarkRegistryModified(i_getId());
4262 mParent->i_saveModifiedRegistries();
4263
4264 return rc;
4265}
4266
4267HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4268 LONG aDevice, BOOL aPassthrough)
4269{
4270 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4271 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4272
4273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4274
4275 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4276 if (FAILED(rc)) return rc;
4277
4278 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4279
4280 /* Check for an existing controller. */
4281 ComObjPtr<StorageController> ctl;
4282 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4283 if (FAILED(rc)) return rc;
4284
4285 StorageControllerType_T ctrlType;
4286 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4287 if (FAILED(rc))
4288 return setError(E_FAIL,
4289 tr("Could not get type of controller '%s'"),
4290 aName.c_str());
4291
4292 bool fSilent = false;
4293 Utf8Str strReconfig;
4294
4295 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4296 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4297 if ( mData->mMachineState == MachineState_Paused
4298 && strReconfig == "1")
4299 fSilent = true;
4300
4301 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4302 bool fHotplug = false;
4303 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4304 fHotplug = true;
4305
4306 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4307 return setError(VBOX_E_INVALID_VM_STATE,
4308 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4309 aName.c_str());
4310
4311 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4312 aName,
4313 aControllerPort,
4314 aDevice);
4315 if (!pAttach)
4316 return setError(VBOX_E_OBJECT_NOT_FOUND,
4317 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4318 aDevice, aControllerPort, aName.c_str());
4319
4320
4321 i_setModified(IsModified_Storage);
4322 mMediumAttachments.backup();
4323
4324 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4325
4326 if (pAttach->i_getType() != DeviceType_DVD)
4327 return setError(E_INVALIDARG,
4328 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4329 aDevice, aControllerPort, aName.c_str());
4330 pAttach->i_updatePassthrough(!!aPassthrough);
4331
4332 attLock.release();
4333 alock.release();
4334 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4335
4336 return rc;
4337}
4338
4339HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4340 LONG aDevice, BOOL aTemporaryEject)
4341{
4342
4343 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4344 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4345
4346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4347
4348 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4349 if (FAILED(rc)) return rc;
4350
4351 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4352 aName,
4353 aControllerPort,
4354 aDevice);
4355 if (!pAttach)
4356 return setError(VBOX_E_OBJECT_NOT_FOUND,
4357 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4358 aDevice, aControllerPort, aName.c_str());
4359
4360
4361 i_setModified(IsModified_Storage);
4362 mMediumAttachments.backup();
4363
4364 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4365
4366 if (pAttach->i_getType() != DeviceType_DVD)
4367 return setError(E_INVALIDARG,
4368 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4369 aDevice, aControllerPort, aName.c_str());
4370 pAttach->i_updateTempEject(!!aTemporaryEject);
4371
4372 return S_OK;
4373}
4374
4375HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4376 LONG aDevice, BOOL aNonRotational)
4377{
4378
4379 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4380 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4381
4382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4383
4384 HRESULT rc = i_checkStateDependency(MutableStateDep);
4385 if (FAILED(rc)) return rc;
4386
4387 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4388
4389 if (Global::IsOnlineOrTransient(mData->mMachineState))
4390 return setError(VBOX_E_INVALID_VM_STATE,
4391 tr("Invalid machine state: %s"),
4392 Global::stringifyMachineState(mData->mMachineState));
4393
4394 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4395 aName,
4396 aControllerPort,
4397 aDevice);
4398 if (!pAttach)
4399 return setError(VBOX_E_OBJECT_NOT_FOUND,
4400 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4401 aDevice, aControllerPort, aName.c_str());
4402
4403
4404 i_setModified(IsModified_Storage);
4405 mMediumAttachments.backup();
4406
4407 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4408
4409 if (pAttach->i_getType() != DeviceType_HardDisk)
4410 return setError(E_INVALIDARG,
4411 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"),
4412 aDevice, aControllerPort, aName.c_str());
4413 pAttach->i_updateNonRotational(!!aNonRotational);
4414
4415 return S_OK;
4416}
4417
4418HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4419 LONG aDevice, BOOL aDiscard)
4420{
4421
4422 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4423 aName.c_str(), aControllerPort, aDevice, aDiscard));
4424
4425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4426
4427 HRESULT rc = i_checkStateDependency(MutableStateDep);
4428 if (FAILED(rc)) return rc;
4429
4430 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4431
4432 if (Global::IsOnlineOrTransient(mData->mMachineState))
4433 return setError(VBOX_E_INVALID_VM_STATE,
4434 tr("Invalid machine state: %s"),
4435 Global::stringifyMachineState(mData->mMachineState));
4436
4437 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4438 aName,
4439 aControllerPort,
4440 aDevice);
4441 if (!pAttach)
4442 return setError(VBOX_E_OBJECT_NOT_FOUND,
4443 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4444 aDevice, aControllerPort, aName.c_str());
4445
4446
4447 i_setModified(IsModified_Storage);
4448 mMediumAttachments.backup();
4449
4450 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4451
4452 if (pAttach->i_getType() != DeviceType_HardDisk)
4453 return setError(E_INVALIDARG,
4454 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"),
4455 aDevice, aControllerPort, aName.c_str());
4456 pAttach->i_updateDiscard(!!aDiscard);
4457
4458 return S_OK;
4459}
4460
4461HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4462 LONG aDevice, BOOL aHotPluggable)
4463{
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4465 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4466
4467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4468
4469 HRESULT rc = i_checkStateDependency(MutableStateDep);
4470 if (FAILED(rc)) return rc;
4471
4472 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4473
4474 if (Global::IsOnlineOrTransient(mData->mMachineState))
4475 return setError(VBOX_E_INVALID_VM_STATE,
4476 tr("Invalid machine state: %s"),
4477 Global::stringifyMachineState(mData->mMachineState));
4478
4479 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4480 aName,
4481 aControllerPort,
4482 aDevice);
4483 if (!pAttach)
4484 return setError(VBOX_E_OBJECT_NOT_FOUND,
4485 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4486 aDevice, aControllerPort, aName.c_str());
4487
4488 /* Check for an existing controller. */
4489 ComObjPtr<StorageController> ctl;
4490 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4491 if (FAILED(rc)) return rc;
4492
4493 StorageControllerType_T ctrlType;
4494 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4495 if (FAILED(rc))
4496 return setError(E_FAIL,
4497 tr("Could not get type of controller '%s'"),
4498 aName.c_str());
4499
4500 if (!i_isControllerHotplugCapable(ctrlType))
4501 return setError(VBOX_E_NOT_SUPPORTED,
4502 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4503 aName.c_str());
4504
4505 i_setModified(IsModified_Storage);
4506 mMediumAttachments.backup();
4507
4508 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4509
4510 if (pAttach->i_getType() == DeviceType_Floppy)
4511 return setError(E_INVALIDARG,
4512 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"),
4513 aDevice, aControllerPort, aName.c_str());
4514 pAttach->i_updateHotPluggable(!!aHotPluggable);
4515
4516 return S_OK;
4517}
4518
4519HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4520 LONG aDevice)
4521{
4522 int rc = S_OK;
4523 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4524 aName.c_str(), aControllerPort, aDevice));
4525
4526 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4527
4528 return rc;
4529}
4530
4531HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4532 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4533{
4534 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4535 aName.c_str(), aControllerPort, aDevice));
4536
4537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4538
4539 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4540 if (FAILED(rc)) return rc;
4541
4542 if (Global::IsOnlineOrTransient(mData->mMachineState))
4543 return setError(VBOX_E_INVALID_VM_STATE,
4544 tr("Invalid machine state: %s"),
4545 Global::stringifyMachineState(mData->mMachineState));
4546
4547 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4548 aName,
4549 aControllerPort,
4550 aDevice);
4551 if (!pAttach)
4552 return setError(VBOX_E_OBJECT_NOT_FOUND,
4553 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4554 aDevice, aControllerPort, aName.c_str());
4555
4556
4557 i_setModified(IsModified_Storage);
4558 mMediumAttachments.backup();
4559
4560 IBandwidthGroup *iB = aBandwidthGroup;
4561 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4562 if (aBandwidthGroup && group.isNull())
4563 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4564
4565 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4566
4567 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4568 if (strBandwidthGroupOld.isNotEmpty())
4569 {
4570 /* Get the bandwidth group object and release it - this must not fail. */
4571 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4572 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4573 Assert(SUCCEEDED(rc));
4574
4575 pBandwidthGroupOld->i_release();
4576 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4577 }
4578
4579 if (!group.isNull())
4580 {
4581 group->i_reference();
4582 pAttach->i_updateBandwidthGroup(group->i_getName());
4583 }
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4589 LONG aControllerPort,
4590 LONG aDevice,
4591 DeviceType_T aType)
4592{
4593 HRESULT rc = S_OK;
4594
4595 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4596 aName.c_str(), aControllerPort, aDevice, aType));
4597
4598 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4599
4600 return rc;
4601}
4602
4603
4604HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4605 LONG aControllerPort,
4606 LONG aDevice,
4607 BOOL aForce)
4608{
4609 int rc = S_OK;
4610 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4611 aName.c_str(), aControllerPort, aForce));
4612
4613 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4614
4615 return rc;
4616}
4617
4618HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4619 LONG aControllerPort,
4620 LONG aDevice,
4621 const ComPtr<IMedium> &aMedium,
4622 BOOL aForce)
4623{
4624 int rc = S_OK;
4625 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4626 aName.c_str(), aControllerPort, aDevice, aForce));
4627
4628 // request the host lock first, since might be calling Host methods for getting host drives;
4629 // next, protect the media tree all the while we're in here, as well as our member variables
4630 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4631 this->lockHandle(),
4632 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4633
4634 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4635 aName,
4636 aControllerPort,
4637 aDevice);
4638 if (pAttach.isNull())
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4641 aDevice, aControllerPort, aName.c_str());
4642
4643 /* Remember previously mounted medium. The medium before taking the
4644 * backup is not necessarily the same thing. */
4645 ComObjPtr<Medium> oldmedium;
4646 oldmedium = pAttach->i_getMedium();
4647
4648 IMedium *iM = aMedium;
4649 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4650 if (aMedium && pMedium.isNull())
4651 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4652
4653 AutoCaller mediumCaller(pMedium);
4654 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4655
4656 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4657 if (pMedium)
4658 {
4659 DeviceType_T mediumType = pAttach->i_getType();
4660 switch (mediumType)
4661 {
4662 case DeviceType_DVD:
4663 case DeviceType_Floppy:
4664 break;
4665
4666 default:
4667 return setError(VBOX_E_INVALID_OBJECT_STATE,
4668 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4669 aControllerPort,
4670 aDevice,
4671 aName.c_str());
4672 }
4673 }
4674
4675 i_setModified(IsModified_Storage);
4676 mMediumAttachments.backup();
4677
4678 {
4679 // The backup operation makes the pAttach reference point to the
4680 // old settings. Re-get the correct reference.
4681 pAttach = i_findAttachment(*mMediumAttachments.data(),
4682 aName,
4683 aControllerPort,
4684 aDevice);
4685 if (!oldmedium.isNull())
4686 oldmedium->i_removeBackReference(mData->mUuid);
4687 if (!pMedium.isNull())
4688 {
4689 pMedium->i_addBackReference(mData->mUuid);
4690
4691 mediumLock.release();
4692 multiLock.release();
4693 i_addMediumToRegistry(pMedium);
4694 multiLock.acquire();
4695 mediumLock.acquire();
4696 }
4697
4698 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4699 pAttach->i_updateMedium(pMedium);
4700 }
4701
4702 i_setModified(IsModified_Storage);
4703
4704 mediumLock.release();
4705 multiLock.release();
4706 rc = i_onMediumChange(pAttach, aForce);
4707 multiLock.acquire();
4708 mediumLock.acquire();
4709
4710 /* On error roll back this change only. */
4711 if (FAILED(rc))
4712 {
4713 if (!pMedium.isNull())
4714 pMedium->i_removeBackReference(mData->mUuid);
4715 pAttach = i_findAttachment(*mMediumAttachments.data(),
4716 aName,
4717 aControllerPort,
4718 aDevice);
4719 /* If the attachment is gone in the meantime, bail out. */
4720 if (pAttach.isNull())
4721 return rc;
4722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4723 if (!oldmedium.isNull())
4724 oldmedium->i_addBackReference(mData->mUuid);
4725 pAttach->i_updateMedium(oldmedium);
4726 }
4727
4728 mediumLock.release();
4729 multiLock.release();
4730
4731 /* Save modified registries, but skip this machine as it's the caller's
4732 * job to save its settings like all other settings changes. */
4733 mParent->i_unmarkRegistryModified(i_getId());
4734 mParent->i_saveModifiedRegistries();
4735
4736 return rc;
4737}
4738HRESULT Machine::getMedium(const com::Utf8Str &aName,
4739 LONG aControllerPort,
4740 LONG aDevice,
4741 ComPtr<IMedium> &aMedium)
4742{
4743 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4744 aName.c_str(), aControllerPort, aDevice));
4745
4746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4747
4748 aMedium = NULL;
4749
4750 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4751 aName,
4752 aControllerPort,
4753 aDevice);
4754 if (pAttach.isNull())
4755 return setError(VBOX_E_OBJECT_NOT_FOUND,
4756 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4757 aDevice, aControllerPort, aName.c_str());
4758
4759 aMedium = pAttach->i_getMedium();
4760
4761 return S_OK;
4762}
4763
4764HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4765{
4766
4767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4770
4771 return S_OK;
4772}
4773
4774HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4775{
4776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4777
4778 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4779
4780 return S_OK;
4781}
4782
4783HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4784{
4785 /* Do not assert if slot is out of range, just return the advertised
4786 status. testdriver/vbox.py triggers this in logVmInfo. */
4787 if (aSlot >= mNetworkAdapters.size())
4788 return setError(E_INVALIDARG,
4789 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4790 aSlot, mNetworkAdapters.size());
4791
4792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4793
4794 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4795
4796 return S_OK;
4797}
4798
4799HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4800{
4801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4802
4803 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4804 size_t i = 0;
4805 for (settings::StringsMap::const_iterator
4806 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4807 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4808 ++it, ++i)
4809 aKeys[i] = it->first;
4810
4811 return S_OK;
4812}
4813
4814 /**
4815 * @note Locks this object for reading.
4816 */
4817HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4818 com::Utf8Str &aValue)
4819{
4820 /* start with nothing found */
4821 aValue = "";
4822
4823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4824
4825 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4826 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4827 // found:
4828 aValue = it->second; // source is a Utf8Str
4829
4830 /* return the result to caller (may be empty) */
4831 return S_OK;
4832}
4833
4834 /**
4835 * @note Locks mParent for writing + this object for writing.
4836 */
4837HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4838{
4839 /* Because non-ASCII characters in aKey have caused problems in the settings
4840 * they are rejected unless the key should be deleted. */
4841 if (!aValue.isEmpty())
4842 {
4843 for (size_t i = 0; i < aKey.length(); ++i)
4844 {
4845 char ch = aKey[i];
4846 if (!RTLocCIsPrint(ch))
4847 return E_INVALIDARG;
4848 }
4849 }
4850
4851 Utf8Str strOldValue; // empty
4852
4853 // locking note: we only hold the read lock briefly to look up the old value,
4854 // then release it and call the onExtraCanChange callbacks. There is a small
4855 // chance of a race insofar as the callback might be called twice if two callers
4856 // change the same key at the same time, but that's a much better solution
4857 // than the deadlock we had here before. The actual changing of the extradata
4858 // is then performed under the write lock and race-free.
4859
4860 // look up the old value first; if nothing has changed then we need not do anything
4861 {
4862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4863
4864 // For snapshots don't even think about allowing changes, extradata
4865 // is global for a machine, so there is nothing snapshot specific.
4866 if (i_isSnapshotMachine())
4867 return setError(VBOX_E_INVALID_VM_STATE,
4868 tr("Cannot set extradata for a snapshot"));
4869
4870 // check if the right IMachine instance is used
4871 if (mData->mRegistered && !i_isSessionMachine())
4872 return setError(VBOX_E_INVALID_VM_STATE,
4873 tr("Cannot set extradata for an immutable machine"));
4874
4875 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4876 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4877 strOldValue = it->second;
4878 }
4879
4880 bool fChanged;
4881 if ((fChanged = (strOldValue != aValue)))
4882 {
4883 // ask for permission from all listeners outside the locks;
4884 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4885 // lock to copy the list of callbacks to invoke
4886 Bstr error;
4887 Bstr bstrValue(aValue);
4888
4889 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4890 {
4891 const char *sep = error.isEmpty() ? "" : ": ";
4892 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4893 return setError(E_ACCESSDENIED,
4894 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4895 aKey.c_str(),
4896 aValue.c_str(),
4897 sep,
4898 error.raw());
4899 }
4900
4901 // data is changing and change not vetoed: then write it out under the lock
4902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4903
4904 if (aValue.isEmpty())
4905 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4906 else
4907 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4908 // creates a new key if needed
4909
4910 bool fNeedsGlobalSaveSettings = false;
4911 // This saving of settings is tricky: there is no "old state" for the
4912 // extradata items at all (unlike all other settings), so the old/new
4913 // settings comparison would give a wrong result!
4914 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4915
4916 if (fNeedsGlobalSaveSettings)
4917 {
4918 // save the global settings; for that we should hold only the VirtualBox lock
4919 alock.release();
4920 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4921 mParent->i_saveSettings();
4922 }
4923 }
4924
4925 // fire notification outside the lock
4926 if (fChanged)
4927 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4928
4929 return S_OK;
4930}
4931
4932HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4933{
4934 aProgress = NULL;
4935 NOREF(aSettingsFilePath);
4936 ReturnComNotImplemented();
4937}
4938
4939HRESULT Machine::saveSettings()
4940{
4941 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4942
4943 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4944 if (FAILED(rc)) return rc;
4945
4946 /* the settings file path may never be null */
4947 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4948
4949 /* save all VM data excluding snapshots */
4950 bool fNeedsGlobalSaveSettings = false;
4951 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4952 mlock.release();
4953
4954 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4955 {
4956 // save the global settings; for that we should hold only the VirtualBox lock
4957 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4958 rc = mParent->i_saveSettings();
4959 }
4960
4961 return rc;
4962}
4963
4964
4965HRESULT Machine::discardSettings()
4966{
4967 /*
4968 * We need to take the machine list lock here as well as the machine one
4969 * or we'll get into trouble should any media stuff require rolling back.
4970 *
4971 * Details:
4972 *
4973 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4974 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4975 * 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]
4976 * 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
4977 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4978 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4979 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4980 * 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
4981 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4982 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4983 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4984 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4985 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4986 * 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]
4987 * 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] (*)
4988 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4989 * 0:005> k
4990 * # Child-SP RetAddr Call Site
4991 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4992 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4993 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4994 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4995 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4996 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4997 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4998 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4999 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5000 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5001 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5002 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5003 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5004 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5005 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5006 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5007 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5008 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5009 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5010 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5011 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5012 *
5013 */
5014 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5016
5017 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5018 if (FAILED(rc)) return rc;
5019
5020 /*
5021 * during this rollback, the session will be notified if data has
5022 * been actually changed
5023 */
5024 i_rollback(true /* aNotify */);
5025
5026 return S_OK;
5027}
5028
5029/** @note Locks objects! */
5030HRESULT Machine::unregister(AutoCaller &autoCaller,
5031 CleanupMode_T aCleanupMode,
5032 std::vector<ComPtr<IMedium> > &aMedia)
5033{
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 Guid id(i_getId());
5037
5038 if (mData->mSession.mState != SessionState_Unlocked)
5039 return setError(VBOX_E_INVALID_OBJECT_STATE,
5040 tr("Cannot unregister the machine '%s' while it is locked"),
5041 mUserData->s.strName.c_str());
5042
5043 // wait for state dependents to drop to zero
5044 i_ensureNoStateDependencies();
5045
5046 if (!mData->mAccessible)
5047 {
5048 // inaccessible maschines can only be unregistered; uninitialize ourselves
5049 // here because currently there may be no unregistered that are inaccessible
5050 // (this state combination is not supported). Note releasing the caller and
5051 // leaving the lock before calling uninit()
5052 alock.release();
5053 autoCaller.release();
5054
5055 uninit();
5056
5057 mParent->i_unregisterMachine(this, id);
5058 // calls VirtualBox::i_saveSettings()
5059
5060 return S_OK;
5061 }
5062
5063 HRESULT rc = S_OK;
5064
5065 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5066 // discard saved state
5067 if (mData->mMachineState == MachineState_Saved)
5068 {
5069 // add the saved state file to the list of files the caller should delete
5070 Assert(!mSSData->strStateFilePath.isEmpty());
5071 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5072
5073 mSSData->strStateFilePath.setNull();
5074
5075 // unconditionally set the machine state to powered off, we now
5076 // know no session has locked the machine
5077 mData->mMachineState = MachineState_PoweredOff;
5078 }
5079
5080 size_t cSnapshots = 0;
5081 if (mData->mFirstSnapshot)
5082 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5083 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5084 // fail now before we start detaching media
5085 return setError(VBOX_E_INVALID_OBJECT_STATE,
5086 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5087 mUserData->s.strName.c_str(), cSnapshots);
5088
5089 // This list collects the medium objects from all medium attachments
5090 // which we will detach from the machine and its snapshots, in a specific
5091 // order which allows for closing all media without getting "media in use"
5092 // errors, simply by going through the list from the front to the back:
5093 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5094 // and must be closed before the parent media from the snapshots, or closing the parents
5095 // will fail because they still have children);
5096 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5097 // the root ("first") snapshot of the machine.
5098 MediaList llMedia;
5099
5100 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5101 && mMediumAttachments->size()
5102 )
5103 {
5104 // we have media attachments: detach them all and add the Medium objects to our list
5105 if (aCleanupMode != CleanupMode_UnregisterOnly)
5106 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5107 else
5108 return setError(VBOX_E_INVALID_OBJECT_STATE,
5109 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5110 mUserData->s.strName.c_str(), mMediumAttachments->size());
5111 }
5112
5113 if (cSnapshots)
5114 {
5115 // add the media from the medium attachments of the snapshots to llMedia
5116 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5117 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5118 // into the children first
5119
5120 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5121 MachineState_T oldState = mData->mMachineState;
5122 mData->mMachineState = MachineState_DeletingSnapshot;
5123
5124 // make a copy of the first snapshot so the refcount does not drop to 0
5125 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5126 // because of the AutoCaller voodoo)
5127 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5128
5129 // GO!
5130 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5131
5132 mData->mMachineState = oldState;
5133 }
5134
5135 if (FAILED(rc))
5136 {
5137 i_rollbackMedia();
5138 return rc;
5139 }
5140
5141 // commit all the media changes made above
5142 i_commitMedia();
5143
5144 mData->mRegistered = false;
5145
5146 // machine lock no longer needed
5147 alock.release();
5148
5149 // return media to caller
5150 aMedia.resize(llMedia.size());
5151 size_t i = 0;
5152 for (MediaList::const_iterator
5153 it = llMedia.begin();
5154 it != llMedia.end();
5155 ++it, ++i)
5156 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5157
5158 mParent->i_unregisterMachine(this, id);
5159 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5160
5161 return S_OK;
5162}
5163
5164/**
5165 * Task record for deleting a machine config.
5166 */
5167class Machine::DeleteConfigTask
5168 : public Machine::Task
5169{
5170public:
5171 DeleteConfigTask(Machine *m,
5172 Progress *p,
5173 const Utf8Str &t,
5174 const RTCList<ComPtr<IMedium> > &llMediums,
5175 const StringsList &llFilesToDelete)
5176 : Task(m, p, t),
5177 m_llMediums(llMediums),
5178 m_llFilesToDelete(llFilesToDelete)
5179 {}
5180
5181private:
5182 void handler()
5183 {
5184 try
5185 {
5186 m_pMachine->i_deleteConfigHandler(*this);
5187 }
5188 catch (...)
5189 {
5190 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5191 }
5192 }
5193
5194 RTCList<ComPtr<IMedium> > m_llMediums;
5195 StringsList m_llFilesToDelete;
5196
5197 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5198};
5199
5200/**
5201 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5202 * SessionMachine::taskHandler().
5203 *
5204 * @note Locks this object for writing.
5205 *
5206 * @param task
5207 * @return
5208 */
5209void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5210{
5211 LogFlowThisFuncEnter();
5212
5213 AutoCaller autoCaller(this);
5214 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5215 if (FAILED(autoCaller.rc()))
5216 {
5217 /* we might have been uninitialized because the session was accidentally
5218 * closed by the client, so don't assert */
5219 HRESULT rc = setError(E_FAIL,
5220 tr("The session has been accidentally closed"));
5221 task.m_pProgress->i_notifyComplete(rc);
5222 LogFlowThisFuncLeave();
5223 return;
5224 }
5225
5226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5227
5228 HRESULT rc = S_OK;
5229
5230 try
5231 {
5232 ULONG uLogHistoryCount = 3;
5233 ComPtr<ISystemProperties> systemProperties;
5234 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5235 if (FAILED(rc)) throw rc;
5236
5237 if (!systemProperties.isNull())
5238 {
5239 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5240 if (FAILED(rc)) throw rc;
5241 }
5242
5243 MachineState_T oldState = mData->mMachineState;
5244 i_setMachineState(MachineState_SettingUp);
5245 alock.release();
5246 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5247 {
5248 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5249 {
5250 AutoCaller mac(pMedium);
5251 if (FAILED(mac.rc())) throw mac.rc();
5252 Utf8Str strLocation = pMedium->i_getLocationFull();
5253 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5254 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5255 if (FAILED(rc)) throw rc;
5256 }
5257 if (pMedium->i_isMediumFormatFile())
5258 {
5259 ComPtr<IProgress> pProgress2;
5260 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5261 if (FAILED(rc)) throw rc;
5262 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5263 if (FAILED(rc)) throw rc;
5264 }
5265
5266 /* Close the medium, deliberately without checking the return
5267 * code, and without leaving any trace in the error info, as
5268 * a failure here is a very minor issue, which shouldn't happen
5269 * as above we even managed to delete the medium. */
5270 {
5271 ErrorInfoKeeper eik;
5272 pMedium->Close();
5273 }
5274 }
5275 i_setMachineState(oldState);
5276 alock.acquire();
5277
5278 // delete the files pushed on the task list by Machine::Delete()
5279 // (this includes saved states of the machine and snapshots and
5280 // medium storage files from the IMedium list passed in, and the
5281 // machine XML file)
5282 for (StringsList::const_iterator
5283 it = task.m_llFilesToDelete.begin();
5284 it != task.m_llFilesToDelete.end();
5285 ++it)
5286 {
5287 const Utf8Str &strFile = *it;
5288 LogFunc(("Deleting file %s\n", strFile.c_str()));
5289 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5290 if (FAILED(rc)) throw rc;
5291
5292 int vrc = RTFileDelete(strFile.c_str());
5293 if (RT_FAILURE(vrc))
5294 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5295 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5296 }
5297
5298 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5299 if (FAILED(rc)) throw rc;
5300
5301 /* delete the settings only when the file actually exists */
5302 if (mData->pMachineConfigFile->fileExists())
5303 {
5304 /* Delete any backup or uncommitted XML files. Ignore failures.
5305 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5306 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5307 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5308 RTFileDelete(otherXml.c_str());
5309 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5310 RTFileDelete(otherXml.c_str());
5311
5312 /* delete the Logs folder, nothing important should be left
5313 * there (we don't check for errors because the user might have
5314 * some private files there that we don't want to delete) */
5315 Utf8Str logFolder;
5316 getLogFolder(logFolder);
5317 Assert(logFolder.length());
5318 if (RTDirExists(logFolder.c_str()))
5319 {
5320 /* Delete all VBox.log[.N] files from the Logs folder
5321 * (this must be in sync with the rotation logic in
5322 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5323 * files that may have been created by the GUI. */
5324 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5325 logFolder.c_str(), RTPATH_DELIMITER);
5326 RTFileDelete(log.c_str());
5327 log = Utf8StrFmt("%s%cVBox.png",
5328 logFolder.c_str(), RTPATH_DELIMITER);
5329 RTFileDelete(log.c_str());
5330 for (int i = uLogHistoryCount; i > 0; i--)
5331 {
5332 log = Utf8StrFmt("%s%cVBox.log.%d",
5333 logFolder.c_str(), RTPATH_DELIMITER, i);
5334 RTFileDelete(log.c_str());
5335 log = Utf8StrFmt("%s%cVBox.png.%d",
5336 logFolder.c_str(), RTPATH_DELIMITER, i);
5337 RTFileDelete(log.c_str());
5338 }
5339#if defined(RT_OS_WINDOWS)
5340 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5341 RTFileDelete(log.c_str());
5342 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5343 RTFileDelete(log.c_str());
5344#endif
5345
5346 RTDirRemove(logFolder.c_str());
5347 }
5348
5349 /* delete the Snapshots folder, nothing important should be left
5350 * there (we don't check for errors because the user might have
5351 * some private files there that we don't want to delete) */
5352 Utf8Str strFullSnapshotFolder;
5353 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5354 Assert(!strFullSnapshotFolder.isEmpty());
5355 if (RTDirExists(strFullSnapshotFolder.c_str()))
5356 RTDirRemove(strFullSnapshotFolder.c_str());
5357
5358 // delete the directory that contains the settings file, but only
5359 // if it matches the VM name
5360 Utf8Str settingsDir;
5361 if (i_isInOwnDir(&settingsDir))
5362 RTDirRemove(settingsDir.c_str());
5363 }
5364
5365 alock.release();
5366
5367 mParent->i_saveModifiedRegistries();
5368 }
5369 catch (HRESULT aRC) { rc = aRC; }
5370
5371 task.m_pProgress->i_notifyComplete(rc);
5372
5373 LogFlowThisFuncLeave();
5374}
5375
5376HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5377{
5378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 HRESULT rc = i_checkStateDependency(MutableStateDep);
5381 if (FAILED(rc)) return rc;
5382
5383 if (mData->mRegistered)
5384 return setError(VBOX_E_INVALID_VM_STATE,
5385 tr("Cannot delete settings of a registered machine"));
5386
5387 // collect files to delete
5388 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5389 if (mData->pMachineConfigFile->fileExists())
5390 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5391
5392 RTCList<ComPtr<IMedium> > llMediums;
5393 for (size_t i = 0; i < aMedia.size(); ++i)
5394 {
5395 IMedium *pIMedium(aMedia[i]);
5396 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5397 if (pMedium.isNull())
5398 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5399 SafeArray<BSTR> ids;
5400 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5401 if (FAILED(rc)) return rc;
5402 /* At this point the medium should not have any back references
5403 * anymore. If it has it is attached to another VM and *must* not
5404 * deleted. */
5405 if (ids.size() < 1)
5406 llMediums.append(pMedium);
5407 }
5408
5409 ComObjPtr<Progress> pProgress;
5410 pProgress.createObject();
5411 rc = pProgress->init(i_getVirtualBox(),
5412 static_cast<IMachine*>(this) /* aInitiator */,
5413 tr("Deleting files"),
5414 true /* fCancellable */,
5415 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5416 tr("Collecting file inventory"));
5417 if (FAILED(rc))
5418 return rc;
5419
5420 /* create and start the task on a separate thread (note that it will not
5421 * start working until we release alock) */
5422 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5423 rc = pTask->createThread();
5424 if (FAILED(rc))
5425 return rc;
5426
5427 pProgress.queryInterfaceTo(aProgress.asOutParam());
5428
5429 LogFlowFuncLeave();
5430
5431 return S_OK;
5432}
5433
5434HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5435{
5436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5437
5438 ComObjPtr<Snapshot> pSnapshot;
5439 HRESULT rc;
5440
5441 if (aNameOrId.isEmpty())
5442 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5443 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5444 else
5445 {
5446 Guid uuid(aNameOrId);
5447 if (uuid.isValid())
5448 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5449 else
5450 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5451 }
5452 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5453
5454 return rc;
5455}
5456
5457HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5458 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5459{
5460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5461
5462 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5463 if (FAILED(rc)) return rc;
5464
5465 ComObjPtr<SharedFolder> sharedFolder;
5466 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5467 if (SUCCEEDED(rc))
5468 return setError(VBOX_E_OBJECT_IN_USE,
5469 tr("Shared folder named '%s' already exists"),
5470 aName.c_str());
5471
5472 sharedFolder.createObject();
5473 rc = sharedFolder->init(i_getMachine(),
5474 aName,
5475 aHostPath,
5476 !!aWritable,
5477 !!aAutomount,
5478 aAutoMountPoint,
5479 true /* fFailOnError */);
5480 if (FAILED(rc)) return rc;
5481
5482 i_setModified(IsModified_SharedFolders);
5483 mHWData.backup();
5484 mHWData->mSharedFolders.push_back(sharedFolder);
5485
5486 /* inform the direct session if any */
5487 alock.release();
5488 i_onSharedFolderChange();
5489
5490 return S_OK;
5491}
5492
5493HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5494{
5495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5498 if (FAILED(rc)) return rc;
5499
5500 ComObjPtr<SharedFolder> sharedFolder;
5501 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5502 if (FAILED(rc)) return rc;
5503
5504 i_setModified(IsModified_SharedFolders);
5505 mHWData.backup();
5506 mHWData->mSharedFolders.remove(sharedFolder);
5507
5508 /* inform the direct session if any */
5509 alock.release();
5510 i_onSharedFolderChange();
5511
5512 return S_OK;
5513}
5514
5515HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5516{
5517 /* start with No */
5518 *aCanShow = FALSE;
5519
5520 ComPtr<IInternalSessionControl> directControl;
5521 {
5522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5523
5524 if (mData->mSession.mState != SessionState_Locked)
5525 return setError(VBOX_E_INVALID_VM_STATE,
5526 tr("Machine is not locked for session (session state: %s)"),
5527 Global::stringifySessionState(mData->mSession.mState));
5528
5529 if (mData->mSession.mLockType == LockType_VM)
5530 directControl = mData->mSession.mDirectControl;
5531 }
5532
5533 /* ignore calls made after #OnSessionEnd() is called */
5534 if (!directControl)
5535 return S_OK;
5536
5537 LONG64 dummy;
5538 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5539}
5540
5541HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5542{
5543 ComPtr<IInternalSessionControl> directControl;
5544 {
5545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5546
5547 if (mData->mSession.mState != SessionState_Locked)
5548 return setError(E_FAIL,
5549 tr("Machine is not locked for session (session state: %s)"),
5550 Global::stringifySessionState(mData->mSession.mState));
5551
5552 if (mData->mSession.mLockType == LockType_VM)
5553 directControl = mData->mSession.mDirectControl;
5554 }
5555
5556 /* ignore calls made after #OnSessionEnd() is called */
5557 if (!directControl)
5558 return S_OK;
5559
5560 BOOL dummy;
5561 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5562}
5563
5564#ifdef VBOX_WITH_GUEST_PROPS
5565/**
5566 * Look up a guest property in VBoxSVC's internal structures.
5567 */
5568HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5569 com::Utf8Str &aValue,
5570 LONG64 *aTimestamp,
5571 com::Utf8Str &aFlags) const
5572{
5573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5576 if (it != mHWData->mGuestProperties.end())
5577 {
5578 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5579 aValue = it->second.strValue;
5580 *aTimestamp = it->second.mTimestamp;
5581 GuestPropWriteFlags(it->second.mFlags, szFlags);
5582 aFlags = Utf8Str(szFlags);
5583 }
5584
5585 return S_OK;
5586}
5587
5588/**
5589 * Query the VM that a guest property belongs to for the property.
5590 * @returns E_ACCESSDENIED if the VM process is not available or not
5591 * currently handling queries and the lookup should then be done in
5592 * VBoxSVC.
5593 */
5594HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5595 com::Utf8Str &aValue,
5596 LONG64 *aTimestamp,
5597 com::Utf8Str &aFlags) const
5598{
5599 HRESULT rc = S_OK;
5600 Bstr bstrValue;
5601 Bstr bstrFlags;
5602
5603 ComPtr<IInternalSessionControl> directControl;
5604 {
5605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5606 if (mData->mSession.mLockType == LockType_VM)
5607 directControl = mData->mSession.mDirectControl;
5608 }
5609
5610 /* ignore calls made after #OnSessionEnd() is called */
5611 if (!directControl)
5612 rc = E_ACCESSDENIED;
5613 else
5614 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5615 0 /* accessMode */,
5616 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5617
5618 aValue = bstrValue;
5619 aFlags = bstrFlags;
5620
5621 return rc;
5622}
5623#endif // VBOX_WITH_GUEST_PROPS
5624
5625HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5626 com::Utf8Str &aValue,
5627 LONG64 *aTimestamp,
5628 com::Utf8Str &aFlags)
5629{
5630#ifndef VBOX_WITH_GUEST_PROPS
5631 ReturnComNotImplemented();
5632#else // VBOX_WITH_GUEST_PROPS
5633
5634 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5635
5636 if (rc == E_ACCESSDENIED)
5637 /* The VM is not running or the service is not (yet) accessible */
5638 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5639 return rc;
5640#endif // VBOX_WITH_GUEST_PROPS
5641}
5642
5643HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5644{
5645 LONG64 dummyTimestamp;
5646 com::Utf8Str dummyFlags;
5647 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5648 return rc;
5649
5650}
5651HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5652{
5653 com::Utf8Str dummyFlags;
5654 com::Utf8Str dummyValue;
5655 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5656 return rc;
5657}
5658
5659#ifdef VBOX_WITH_GUEST_PROPS
5660/**
5661 * Set a guest property in VBoxSVC's internal structures.
5662 */
5663HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5664 const com::Utf8Str &aFlags, bool fDelete)
5665{
5666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5667 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5668 if (FAILED(rc)) return rc;
5669
5670 try
5671 {
5672 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5673 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5674 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5675
5676 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5677 if (it == mHWData->mGuestProperties.end())
5678 {
5679 if (!fDelete)
5680 {
5681 i_setModified(IsModified_MachineData);
5682 mHWData.backupEx();
5683
5684 RTTIMESPEC time;
5685 HWData::GuestProperty prop;
5686 prop.strValue = Bstr(aValue).raw();
5687 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5688 prop.mFlags = fFlags;
5689 mHWData->mGuestProperties[aName] = prop;
5690 }
5691 }
5692 else
5693 {
5694 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5695 {
5696 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5697 }
5698 else
5699 {
5700 i_setModified(IsModified_MachineData);
5701 mHWData.backupEx();
5702
5703 /* The backupEx() operation invalidates our iterator,
5704 * so get a new one. */
5705 it = mHWData->mGuestProperties.find(aName);
5706 Assert(it != mHWData->mGuestProperties.end());
5707
5708 if (!fDelete)
5709 {
5710 RTTIMESPEC time;
5711 it->second.strValue = aValue;
5712 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5713 it->second.mFlags = fFlags;
5714 }
5715 else
5716 mHWData->mGuestProperties.erase(it);
5717 }
5718 }
5719
5720 if (SUCCEEDED(rc))
5721 {
5722 alock.release();
5723
5724 mParent->i_onGuestPropertyChange(mData->mUuid,
5725 Bstr(aName).raw(),
5726 Bstr(aValue).raw(),
5727 Bstr(aFlags).raw());
5728 }
5729 }
5730 catch (std::bad_alloc &)
5731 {
5732 rc = E_OUTOFMEMORY;
5733 }
5734
5735 return rc;
5736}
5737
5738/**
5739 * Set a property on the VM that that property belongs to.
5740 * @returns E_ACCESSDENIED if the VM process is not available or not
5741 * currently handling queries and the setting should then be done in
5742 * VBoxSVC.
5743 */
5744HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5745 const com::Utf8Str &aFlags, bool fDelete)
5746{
5747 HRESULT rc;
5748
5749 try
5750 {
5751 ComPtr<IInternalSessionControl> directControl;
5752 {
5753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5754 if (mData->mSession.mLockType == LockType_VM)
5755 directControl = mData->mSession.mDirectControl;
5756 }
5757
5758 Bstr dummy1; /* will not be changed (setter) */
5759 Bstr dummy2; /* will not be changed (setter) */
5760 LONG64 dummy64;
5761 if (!directControl)
5762 rc = E_ACCESSDENIED;
5763 else
5764 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5765 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5766 fDelete ? 2 : 1 /* accessMode */,
5767 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5768 }
5769 catch (std::bad_alloc &)
5770 {
5771 rc = E_OUTOFMEMORY;
5772 }
5773
5774 return rc;
5775}
5776#endif // VBOX_WITH_GUEST_PROPS
5777
5778HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5779 const com::Utf8Str &aFlags)
5780{
5781#ifndef VBOX_WITH_GUEST_PROPS
5782 ReturnComNotImplemented();
5783#else // VBOX_WITH_GUEST_PROPS
5784 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5785 if (rc == E_ACCESSDENIED)
5786 /* The VM is not running or the service is not (yet) accessible */
5787 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5788 return rc;
5789#endif // VBOX_WITH_GUEST_PROPS
5790}
5791
5792HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5793{
5794 return setGuestProperty(aProperty, aValue, "");
5795}
5796
5797HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5798{
5799#ifndef VBOX_WITH_GUEST_PROPS
5800 ReturnComNotImplemented();
5801#else // VBOX_WITH_GUEST_PROPS
5802 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5803 if (rc == E_ACCESSDENIED)
5804 /* The VM is not running or the service is not (yet) accessible */
5805 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5806 return rc;
5807#endif // VBOX_WITH_GUEST_PROPS
5808}
5809
5810#ifdef VBOX_WITH_GUEST_PROPS
5811/**
5812 * Enumerate the guest properties in VBoxSVC's internal structures.
5813 */
5814HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5815 std::vector<com::Utf8Str> &aNames,
5816 std::vector<com::Utf8Str> &aValues,
5817 std::vector<LONG64> &aTimestamps,
5818 std::vector<com::Utf8Str> &aFlags)
5819{
5820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5821 Utf8Str strPatterns(aPatterns);
5822
5823 /*
5824 * Look for matching patterns and build up a list.
5825 */
5826 HWData::GuestPropertyMap propMap;
5827 for (HWData::GuestPropertyMap::const_iterator
5828 it = mHWData->mGuestProperties.begin();
5829 it != mHWData->mGuestProperties.end();
5830 ++it)
5831 {
5832 if ( strPatterns.isEmpty()
5833 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5834 RTSTR_MAX,
5835 it->first.c_str(),
5836 RTSTR_MAX,
5837 NULL)
5838 )
5839 propMap.insert(*it);
5840 }
5841
5842 alock.release();
5843
5844 /*
5845 * And build up the arrays for returning the property information.
5846 */
5847 size_t cEntries = propMap.size();
5848
5849 aNames.resize(cEntries);
5850 aValues.resize(cEntries);
5851 aTimestamps.resize(cEntries);
5852 aFlags.resize(cEntries);
5853
5854 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5855 size_t i = 0;
5856 for (HWData::GuestPropertyMap::const_iterator
5857 it = propMap.begin();
5858 it != propMap.end();
5859 ++it, ++i)
5860 {
5861 aNames[i] = it->first;
5862 aValues[i] = it->second.strValue;
5863 aTimestamps[i] = it->second.mTimestamp;
5864 GuestPropWriteFlags(it->second.mFlags, szFlags);
5865 aFlags[i] = Utf8Str(szFlags);
5866 }
5867
5868 return S_OK;
5869}
5870
5871/**
5872 * Enumerate the properties managed by a VM.
5873 * @returns E_ACCESSDENIED if the VM process is not available or not
5874 * currently handling queries and the setting should then be done in
5875 * VBoxSVC.
5876 */
5877HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5878 std::vector<com::Utf8Str> &aNames,
5879 std::vector<com::Utf8Str> &aValues,
5880 std::vector<LONG64> &aTimestamps,
5881 std::vector<com::Utf8Str> &aFlags)
5882{
5883 HRESULT rc;
5884 ComPtr<IInternalSessionControl> directControl;
5885 {
5886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5887 if (mData->mSession.mLockType == LockType_VM)
5888 directControl = mData->mSession.mDirectControl;
5889 }
5890
5891 com::SafeArray<BSTR> bNames;
5892 com::SafeArray<BSTR> bValues;
5893 com::SafeArray<LONG64> bTimestamps;
5894 com::SafeArray<BSTR> bFlags;
5895
5896 if (!directControl)
5897 rc = E_ACCESSDENIED;
5898 else
5899 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5900 ComSafeArrayAsOutParam(bNames),
5901 ComSafeArrayAsOutParam(bValues),
5902 ComSafeArrayAsOutParam(bTimestamps),
5903 ComSafeArrayAsOutParam(bFlags));
5904 size_t i;
5905 aNames.resize(bNames.size());
5906 for (i = 0; i < bNames.size(); ++i)
5907 aNames[i] = Utf8Str(bNames[i]);
5908 aValues.resize(bValues.size());
5909 for (i = 0; i < bValues.size(); ++i)
5910 aValues[i] = Utf8Str(bValues[i]);
5911 aTimestamps.resize(bTimestamps.size());
5912 for (i = 0; i < bTimestamps.size(); ++i)
5913 aTimestamps[i] = bTimestamps[i];
5914 aFlags.resize(bFlags.size());
5915 for (i = 0; i < bFlags.size(); ++i)
5916 aFlags[i] = Utf8Str(bFlags[i]);
5917
5918 return rc;
5919}
5920#endif // VBOX_WITH_GUEST_PROPS
5921HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5922 std::vector<com::Utf8Str> &aNames,
5923 std::vector<com::Utf8Str> &aValues,
5924 std::vector<LONG64> &aTimestamps,
5925 std::vector<com::Utf8Str> &aFlags)
5926{
5927#ifndef VBOX_WITH_GUEST_PROPS
5928 ReturnComNotImplemented();
5929#else // VBOX_WITH_GUEST_PROPS
5930
5931 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5932
5933 if (rc == E_ACCESSDENIED)
5934 /* The VM is not running or the service is not (yet) accessible */
5935 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5936 return rc;
5937#endif // VBOX_WITH_GUEST_PROPS
5938}
5939
5940HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5941 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5942{
5943 MediumAttachmentList atts;
5944
5945 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5946 if (FAILED(rc)) return rc;
5947
5948 aMediumAttachments.resize(atts.size());
5949 size_t i = 0;
5950 for (MediumAttachmentList::const_iterator
5951 it = atts.begin();
5952 it != atts.end();
5953 ++it, ++i)
5954 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5955
5956 return S_OK;
5957}
5958
5959HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5960 LONG aControllerPort,
5961 LONG aDevice,
5962 ComPtr<IMediumAttachment> &aAttachment)
5963{
5964 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5965 aName.c_str(), aControllerPort, aDevice));
5966
5967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5968
5969 aAttachment = NULL;
5970
5971 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5972 aName,
5973 aControllerPort,
5974 aDevice);
5975 if (pAttach.isNull())
5976 return setError(VBOX_E_OBJECT_NOT_FOUND,
5977 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5978 aDevice, aControllerPort, aName.c_str());
5979
5980 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5981
5982 return S_OK;
5983}
5984
5985
5986HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5987 StorageBus_T aConnectionType,
5988 ComPtr<IStorageController> &aController)
5989{
5990 if ( (aConnectionType <= StorageBus_Null)
5991 || (aConnectionType > StorageBus_PCIe))
5992 return setError(E_INVALIDARG,
5993 tr("Invalid connection type: %d"),
5994 aConnectionType);
5995
5996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5997
5998 HRESULT rc = i_checkStateDependency(MutableStateDep);
5999 if (FAILED(rc)) return rc;
6000
6001 /* try to find one with the name first. */
6002 ComObjPtr<StorageController> ctrl;
6003
6004 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6005 if (SUCCEEDED(rc))
6006 return setError(VBOX_E_OBJECT_IN_USE,
6007 tr("Storage controller named '%s' already exists"),
6008 aName.c_str());
6009
6010 ctrl.createObject();
6011
6012 /* get a new instance number for the storage controller */
6013 ULONG ulInstance = 0;
6014 bool fBootable = true;
6015 for (StorageControllerList::const_iterator
6016 it = mStorageControllers->begin();
6017 it != mStorageControllers->end();
6018 ++it)
6019 {
6020 if ((*it)->i_getStorageBus() == aConnectionType)
6021 {
6022 ULONG ulCurInst = (*it)->i_getInstance();
6023
6024 if (ulCurInst >= ulInstance)
6025 ulInstance = ulCurInst + 1;
6026
6027 /* Only one controller of each type can be marked as bootable. */
6028 if ((*it)->i_getBootable())
6029 fBootable = false;
6030 }
6031 }
6032
6033 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6034 if (FAILED(rc)) return rc;
6035
6036 i_setModified(IsModified_Storage);
6037 mStorageControllers.backup();
6038 mStorageControllers->push_back(ctrl);
6039
6040 ctrl.queryInterfaceTo(aController.asOutParam());
6041
6042 /* inform the direct session if any */
6043 alock.release();
6044 i_onStorageControllerChange();
6045
6046 return S_OK;
6047}
6048
6049HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6050 ComPtr<IStorageController> &aStorageController)
6051{
6052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6053
6054 ComObjPtr<StorageController> ctrl;
6055
6056 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6057 if (SUCCEEDED(rc))
6058 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6059
6060 return rc;
6061}
6062
6063HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6064 ULONG aInstance,
6065 ComPtr<IStorageController> &aStorageController)
6066{
6067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6068
6069 for (StorageControllerList::const_iterator
6070 it = mStorageControllers->begin();
6071 it != mStorageControllers->end();
6072 ++it)
6073 {
6074 if ( (*it)->i_getStorageBus() == aConnectionType
6075 && (*it)->i_getInstance() == aInstance)
6076 {
6077 (*it).queryInterfaceTo(aStorageController.asOutParam());
6078 return S_OK;
6079 }
6080 }
6081
6082 return setError(VBOX_E_OBJECT_NOT_FOUND,
6083 tr("Could not find a storage controller with instance number '%lu'"),
6084 aInstance);
6085}
6086
6087HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6088{
6089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6090
6091 HRESULT rc = i_checkStateDependency(MutableStateDep);
6092 if (FAILED(rc)) return rc;
6093
6094 ComObjPtr<StorageController> ctrl;
6095
6096 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6097 if (SUCCEEDED(rc))
6098 {
6099 /* Ensure that only one controller of each type is marked as bootable. */
6100 if (aBootable == TRUE)
6101 {
6102 for (StorageControllerList::const_iterator
6103 it = mStorageControllers->begin();
6104 it != mStorageControllers->end();
6105 ++it)
6106 {
6107 ComObjPtr<StorageController> aCtrl = (*it);
6108
6109 if ( (aCtrl->i_getName() != aName)
6110 && aCtrl->i_getBootable() == TRUE
6111 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6112 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6113 {
6114 aCtrl->i_setBootable(FALSE);
6115 break;
6116 }
6117 }
6118 }
6119
6120 if (SUCCEEDED(rc))
6121 {
6122 ctrl->i_setBootable(aBootable);
6123 i_setModified(IsModified_Storage);
6124 }
6125 }
6126
6127 if (SUCCEEDED(rc))
6128 {
6129 /* inform the direct session if any */
6130 alock.release();
6131 i_onStorageControllerChange();
6132 }
6133
6134 return rc;
6135}
6136
6137HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6138{
6139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6140
6141 HRESULT rc = i_checkStateDependency(MutableStateDep);
6142 if (FAILED(rc)) return rc;
6143
6144 ComObjPtr<StorageController> ctrl;
6145 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6146 if (FAILED(rc)) return rc;
6147
6148 {
6149 /* find all attached devices to the appropriate storage controller and detach them all */
6150 // make a temporary list because detachDevice invalidates iterators into
6151 // mMediumAttachments
6152 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6153
6154 for (MediumAttachmentList::const_iterator
6155 it = llAttachments2.begin();
6156 it != llAttachments2.end();
6157 ++it)
6158 {
6159 MediumAttachment *pAttachTemp = *it;
6160
6161 AutoCaller localAutoCaller(pAttachTemp);
6162 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6163
6164 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6165
6166 if (pAttachTemp->i_getControllerName() == aName)
6167 {
6168 rc = i_detachDevice(pAttachTemp, alock, NULL);
6169 if (FAILED(rc)) return rc;
6170 }
6171 }
6172 }
6173
6174 /* We can remove it now. */
6175 i_setModified(IsModified_Storage);
6176 mStorageControllers.backup();
6177
6178 ctrl->i_unshare();
6179
6180 mStorageControllers->remove(ctrl);
6181
6182 /* inform the direct session if any */
6183 alock.release();
6184 i_onStorageControllerChange();
6185
6186 return S_OK;
6187}
6188
6189HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6190 ComPtr<IUSBController> &aController)
6191{
6192 if ( (aType <= USBControllerType_Null)
6193 || (aType >= USBControllerType_Last))
6194 return setError(E_INVALIDARG,
6195 tr("Invalid USB controller type: %d"),
6196 aType);
6197
6198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6199
6200 HRESULT rc = i_checkStateDependency(MutableStateDep);
6201 if (FAILED(rc)) return rc;
6202
6203 /* try to find one with the same type first. */
6204 ComObjPtr<USBController> ctrl;
6205
6206 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6207 if (SUCCEEDED(rc))
6208 return setError(VBOX_E_OBJECT_IN_USE,
6209 tr("USB controller named '%s' already exists"),
6210 aName.c_str());
6211
6212 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6213 ULONG maxInstances;
6214 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6215 if (FAILED(rc))
6216 return rc;
6217
6218 ULONG cInstances = i_getUSBControllerCountByType(aType);
6219 if (cInstances >= maxInstances)
6220 return setError(E_INVALIDARG,
6221 tr("Too many USB controllers of this type"));
6222
6223 ctrl.createObject();
6224
6225 rc = ctrl->init(this, aName, aType);
6226 if (FAILED(rc)) return rc;
6227
6228 i_setModified(IsModified_USB);
6229 mUSBControllers.backup();
6230 mUSBControllers->push_back(ctrl);
6231
6232 ctrl.queryInterfaceTo(aController.asOutParam());
6233
6234 /* inform the direct session if any */
6235 alock.release();
6236 i_onUSBControllerChange();
6237
6238 return S_OK;
6239}
6240
6241HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6242{
6243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6244
6245 ComObjPtr<USBController> ctrl;
6246
6247 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6248 if (SUCCEEDED(rc))
6249 ctrl.queryInterfaceTo(aController.asOutParam());
6250
6251 return rc;
6252}
6253
6254HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6255 ULONG *aControllers)
6256{
6257 if ( (aType <= USBControllerType_Null)
6258 || (aType >= USBControllerType_Last))
6259 return setError(E_INVALIDARG,
6260 tr("Invalid USB controller type: %d"),
6261 aType);
6262
6263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 ComObjPtr<USBController> ctrl;
6266
6267 *aControllers = i_getUSBControllerCountByType(aType);
6268
6269 return S_OK;
6270}
6271
6272HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6273{
6274
6275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6276
6277 HRESULT rc = i_checkStateDependency(MutableStateDep);
6278 if (FAILED(rc)) return rc;
6279
6280 ComObjPtr<USBController> ctrl;
6281 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6282 if (FAILED(rc)) return rc;
6283
6284 i_setModified(IsModified_USB);
6285 mUSBControllers.backup();
6286
6287 ctrl->i_unshare();
6288
6289 mUSBControllers->remove(ctrl);
6290
6291 /* inform the direct session if any */
6292 alock.release();
6293 i_onUSBControllerChange();
6294
6295 return S_OK;
6296}
6297
6298HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6299 ULONG *aOriginX,
6300 ULONG *aOriginY,
6301 ULONG *aWidth,
6302 ULONG *aHeight,
6303 BOOL *aEnabled)
6304{
6305 uint32_t u32OriginX= 0;
6306 uint32_t u32OriginY= 0;
6307 uint32_t u32Width = 0;
6308 uint32_t u32Height = 0;
6309 uint16_t u16Flags = 0;
6310
6311 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6312 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6313 if (RT_FAILURE(vrc))
6314 {
6315#ifdef RT_OS_WINDOWS
6316 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6317 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6318 * So just assign fEnable to TRUE again.
6319 * The right fix would be to change GUI API wrappers to make sure that parameters
6320 * are changed only if API succeeds.
6321 */
6322 *aEnabled = TRUE;
6323#endif
6324 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6325 tr("Saved guest size is not available (%Rrc)"),
6326 vrc);
6327 }
6328
6329 *aOriginX = u32OriginX;
6330 *aOriginY = u32OriginY;
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6334
6335 return S_OK;
6336}
6337
6338HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6339 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6340{
6341 if (aScreenId != 0)
6342 return E_NOTIMPL;
6343
6344 if ( aBitmapFormat != BitmapFormat_BGR0
6345 && aBitmapFormat != BitmapFormat_BGRA
6346 && aBitmapFormat != BitmapFormat_RGBA
6347 && aBitmapFormat != BitmapFormat_PNG)
6348 return setError(E_NOTIMPL,
6349 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6350
6351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6352
6353 uint8_t *pu8Data = NULL;
6354 uint32_t cbData = 0;
6355 uint32_t u32Width = 0;
6356 uint32_t u32Height = 0;
6357
6358 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6359
6360 if (RT_FAILURE(vrc))
6361 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6362 tr("Saved thumbnail data is not available (%Rrc)"),
6363 vrc);
6364
6365 HRESULT hr = S_OK;
6366
6367 *aWidth = u32Width;
6368 *aHeight = u32Height;
6369
6370 if (cbData > 0)
6371 {
6372 /* Convert pixels to the format expected by the API caller. */
6373 if (aBitmapFormat == BitmapFormat_BGR0)
6374 {
6375 /* [0] B, [1] G, [2] R, [3] 0. */
6376 aData.resize(cbData);
6377 memcpy(&aData.front(), pu8Data, cbData);
6378 }
6379 else if (aBitmapFormat == BitmapFormat_BGRA)
6380 {
6381 /* [0] B, [1] G, [2] R, [3] A. */
6382 aData.resize(cbData);
6383 for (uint32_t i = 0; i < cbData; i += 4)
6384 {
6385 aData[i] = pu8Data[i];
6386 aData[i + 1] = pu8Data[i + 1];
6387 aData[i + 2] = pu8Data[i + 2];
6388 aData[i + 3] = 0xff;
6389 }
6390 }
6391 else if (aBitmapFormat == BitmapFormat_RGBA)
6392 {
6393 /* [0] R, [1] G, [2] B, [3] A. */
6394 aData.resize(cbData);
6395 for (uint32_t i = 0; i < cbData; i += 4)
6396 {
6397 aData[i] = pu8Data[i + 2];
6398 aData[i + 1] = pu8Data[i + 1];
6399 aData[i + 2] = pu8Data[i];
6400 aData[i + 3] = 0xff;
6401 }
6402 }
6403 else if (aBitmapFormat == BitmapFormat_PNG)
6404 {
6405 uint8_t *pu8PNG = NULL;
6406 uint32_t cbPNG = 0;
6407 uint32_t cxPNG = 0;
6408 uint32_t cyPNG = 0;
6409
6410 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6411
6412 if (RT_SUCCESS(vrc))
6413 {
6414 aData.resize(cbPNG);
6415 if (cbPNG)
6416 memcpy(&aData.front(), pu8PNG, cbPNG);
6417 }
6418 else
6419 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6420 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6421 vrc);
6422
6423 RTMemFree(pu8PNG);
6424 }
6425 }
6426
6427 freeSavedDisplayScreenshot(pu8Data);
6428
6429 return hr;
6430}
6431
6432HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6433 ULONG *aWidth,
6434 ULONG *aHeight,
6435 std::vector<BitmapFormat_T> &aBitmapFormats)
6436{
6437 if (aScreenId != 0)
6438 return E_NOTIMPL;
6439
6440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6441
6442 uint8_t *pu8Data = NULL;
6443 uint32_t cbData = 0;
6444 uint32_t u32Width = 0;
6445 uint32_t u32Height = 0;
6446
6447 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6448
6449 if (RT_FAILURE(vrc))
6450 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6451 tr("Saved screenshot data is not available (%Rrc)"),
6452 vrc);
6453
6454 *aWidth = u32Width;
6455 *aHeight = u32Height;
6456 aBitmapFormats.resize(1);
6457 aBitmapFormats[0] = BitmapFormat_PNG;
6458
6459 freeSavedDisplayScreenshot(pu8Data);
6460
6461 return S_OK;
6462}
6463
6464HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6465 BitmapFormat_T aBitmapFormat,
6466 ULONG *aWidth,
6467 ULONG *aHeight,
6468 std::vector<BYTE> &aData)
6469{
6470 if (aScreenId != 0)
6471 return E_NOTIMPL;
6472
6473 if (aBitmapFormat != BitmapFormat_PNG)
6474 return E_NOTIMPL;
6475
6476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 uint8_t *pu8Data = NULL;
6479 uint32_t cbData = 0;
6480 uint32_t u32Width = 0;
6481 uint32_t u32Height = 0;
6482
6483 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6484
6485 if (RT_FAILURE(vrc))
6486 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6487 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6488 vrc);
6489
6490 *aWidth = u32Width;
6491 *aHeight = u32Height;
6492
6493 aData.resize(cbData);
6494 if (cbData)
6495 memcpy(&aData.front(), pu8Data, cbData);
6496
6497 freeSavedDisplayScreenshot(pu8Data);
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::hotPlugCPU(ULONG aCpu)
6503{
6504 HRESULT rc = S_OK;
6505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6506
6507 if (!mHWData->mCPUHotPlugEnabled)
6508 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6509
6510 if (aCpu >= mHWData->mCPUCount)
6511 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6512
6513 if (mHWData->mCPUAttached[aCpu])
6514 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6515
6516 alock.release();
6517 rc = i_onCPUChange(aCpu, false);
6518 alock.acquire();
6519 if (FAILED(rc)) return rc;
6520
6521 i_setModified(IsModified_MachineData);
6522 mHWData.backup();
6523 mHWData->mCPUAttached[aCpu] = true;
6524
6525 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6526 if (Global::IsOnline(mData->mMachineState))
6527 i_saveSettings(NULL);
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6533{
6534 HRESULT rc = S_OK;
6535
6536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6537
6538 if (!mHWData->mCPUHotPlugEnabled)
6539 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6540
6541 if (aCpu >= SchemaDefs::MaxCPUCount)
6542 return setError(E_INVALIDARG,
6543 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6544 SchemaDefs::MaxCPUCount);
6545
6546 if (!mHWData->mCPUAttached[aCpu])
6547 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6548
6549 /* CPU 0 can't be detached */
6550 if (aCpu == 0)
6551 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6552
6553 alock.release();
6554 rc = i_onCPUChange(aCpu, true);
6555 alock.acquire();
6556 if (FAILED(rc)) return rc;
6557
6558 i_setModified(IsModified_MachineData);
6559 mHWData.backup();
6560 mHWData->mCPUAttached[aCpu] = false;
6561
6562 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6563 if (Global::IsOnline(mData->mMachineState))
6564 i_saveSettings(NULL);
6565
6566 return S_OK;
6567}
6568
6569HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6570{
6571 *aAttached = false;
6572
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 /* If hotplug is enabled the CPU is always enabled. */
6576 if (!mHWData->mCPUHotPlugEnabled)
6577 {
6578 if (aCpu < mHWData->mCPUCount)
6579 *aAttached = true;
6580 }
6581 else
6582 {
6583 if (aCpu < SchemaDefs::MaxCPUCount)
6584 *aAttached = mHWData->mCPUAttached[aCpu];
6585 }
6586
6587 return S_OK;
6588}
6589
6590HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6591{
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 Utf8Str log = i_getLogFilename(aIdx);
6595 if (!RTFileExists(log.c_str()))
6596 log.setNull();
6597 aFilename = log;
6598
6599 return S_OK;
6600}
6601
6602HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6603{
6604 if (aSize < 0)
6605 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6606
6607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 HRESULT rc = S_OK;
6610 Utf8Str log = i_getLogFilename(aIdx);
6611
6612 /* do not unnecessarily hold the lock while doing something which does
6613 * not need the lock and potentially takes a long time. */
6614 alock.release();
6615
6616 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6617 * keeps the SOAP reply size under 1M for the webservice (we're using
6618 * base64 encoded strings for binary data for years now, avoiding the
6619 * expansion of each byte array element to approx. 25 bytes of XML. */
6620 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6621 aData.resize(cbData);
6622
6623 RTFILE LogFile;
6624 int vrc = RTFileOpen(&LogFile, log.c_str(),
6625 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6626 if (RT_SUCCESS(vrc))
6627 {
6628 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6629 if (RT_SUCCESS(vrc))
6630 aData.resize(cbData);
6631 else
6632 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6633 tr("Could not read log file '%s' (%Rrc)"),
6634 log.c_str(), vrc);
6635 RTFileClose(LogFile);
6636 }
6637 else
6638 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6639 tr("Could not open log file '%s' (%Rrc)"),
6640 log.c_str(), vrc);
6641
6642 if (FAILED(rc))
6643 aData.resize(0);
6644
6645 return rc;
6646}
6647
6648
6649/**
6650 * Currently this method doesn't attach device to the running VM,
6651 * just makes sure it's plugged on next VM start.
6652 */
6653HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6654{
6655 // lock scope
6656 {
6657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6658
6659 HRESULT rc = i_checkStateDependency(MutableStateDep);
6660 if (FAILED(rc)) return rc;
6661
6662 ChipsetType_T aChipset = ChipsetType_PIIX3;
6663 COMGETTER(ChipsetType)(&aChipset);
6664
6665 if (aChipset != ChipsetType_ICH9)
6666 {
6667 return setError(E_INVALIDARG,
6668 tr("Host PCI attachment only supported with ICH9 chipset"));
6669 }
6670
6671 // check if device with this host PCI address already attached
6672 for (HWData::PCIDeviceAssignmentList::const_iterator
6673 it = mHWData->mPCIDeviceAssignments.begin();
6674 it != mHWData->mPCIDeviceAssignments.end();
6675 ++it)
6676 {
6677 LONG iHostAddress = -1;
6678 ComPtr<PCIDeviceAttachment> pAttach;
6679 pAttach = *it;
6680 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6681 if (iHostAddress == aHostAddress)
6682 return setError(E_INVALIDARG,
6683 tr("Device with host PCI address already attached to this VM"));
6684 }
6685
6686 ComObjPtr<PCIDeviceAttachment> pda;
6687 char name[32];
6688
6689 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6690 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6691 pda.createObject();
6692 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6693 i_setModified(IsModified_MachineData);
6694 mHWData.backup();
6695 mHWData->mPCIDeviceAssignments.push_back(pda);
6696 }
6697
6698 return S_OK;
6699}
6700
6701/**
6702 * Currently this method doesn't detach device from the running VM,
6703 * just makes sure it's not plugged on next VM start.
6704 */
6705HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6706{
6707 ComObjPtr<PCIDeviceAttachment> pAttach;
6708 bool fRemoved = false;
6709 HRESULT rc;
6710
6711 // lock scope
6712 {
6713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6714
6715 rc = i_checkStateDependency(MutableStateDep);
6716 if (FAILED(rc)) return rc;
6717
6718 for (HWData::PCIDeviceAssignmentList::const_iterator
6719 it = mHWData->mPCIDeviceAssignments.begin();
6720 it != mHWData->mPCIDeviceAssignments.end();
6721 ++it)
6722 {
6723 LONG iHostAddress = -1;
6724 pAttach = *it;
6725 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6726 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6727 {
6728 i_setModified(IsModified_MachineData);
6729 mHWData.backup();
6730 mHWData->mPCIDeviceAssignments.remove(pAttach);
6731 fRemoved = true;
6732 break;
6733 }
6734 }
6735 }
6736
6737
6738 /* Fire event outside of the lock */
6739 if (fRemoved)
6740 {
6741 Assert(!pAttach.isNull());
6742 ComPtr<IEventSource> es;
6743 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6744 Assert(SUCCEEDED(rc));
6745 Bstr mid;
6746 rc = this->COMGETTER(Id)(mid.asOutParam());
6747 Assert(SUCCEEDED(rc));
6748 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6749 }
6750
6751 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6752 tr("No host PCI device %08x attached"),
6753 aHostAddress
6754 );
6755}
6756
6757HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6758{
6759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6760
6761 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6762 size_t i = 0;
6763 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6764 it = mHWData->mPCIDeviceAssignments.begin();
6765 it != mHWData->mPCIDeviceAssignments.end();
6766 ++it, ++i)
6767 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6768
6769 return S_OK;
6770}
6771
6772HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6773{
6774 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6775
6776 return S_OK;
6777}
6778
6779HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6780{
6781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6782
6783 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6784
6785 return S_OK;
6786}
6787
6788HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6789{
6790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6791 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6792 if (SUCCEEDED(hrc))
6793 {
6794 hrc = mHWData.backupEx();
6795 if (SUCCEEDED(hrc))
6796 {
6797 i_setModified(IsModified_MachineData);
6798 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6799 }
6800 }
6801 return hrc;
6802}
6803
6804HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6805{
6806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6808 return S_OK;
6809}
6810
6811HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6812{
6813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6814 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6815 if (SUCCEEDED(hrc))
6816 {
6817 hrc = mHWData.backupEx();
6818 if (SUCCEEDED(hrc))
6819 {
6820 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6821 if (SUCCEEDED(hrc))
6822 i_setModified(IsModified_MachineData);
6823 }
6824 }
6825 return hrc;
6826}
6827
6828HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6829{
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831
6832 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6833
6834 return S_OK;
6835}
6836
6837HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6838{
6839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6840 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6841 if (SUCCEEDED(hrc))
6842 {
6843 hrc = mHWData.backupEx();
6844 if (SUCCEEDED(hrc))
6845 {
6846 i_setModified(IsModified_MachineData);
6847 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6848 }
6849 }
6850 return hrc;
6851}
6852
6853HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6854{
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856
6857 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6858
6859 return S_OK;
6860}
6861
6862HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6863{
6864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6865
6866 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6867 if ( SUCCEEDED(hrc)
6868 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6869 {
6870 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6871 int vrc;
6872
6873 if (aAutostartEnabled)
6874 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6875 else
6876 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6877
6878 if (RT_SUCCESS(vrc))
6879 {
6880 hrc = mHWData.backupEx();
6881 if (SUCCEEDED(hrc))
6882 {
6883 i_setModified(IsModified_MachineData);
6884 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6885 }
6886 }
6887 else if (vrc == VERR_NOT_SUPPORTED)
6888 hrc = setError(VBOX_E_NOT_SUPPORTED,
6889 tr("The VM autostart feature is not supported on this platform"));
6890 else if (vrc == VERR_PATH_NOT_FOUND)
6891 hrc = setError(E_FAIL,
6892 tr("The path to the autostart database is not set"));
6893 else
6894 hrc = setError(E_UNEXPECTED,
6895 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6896 aAutostartEnabled ? "Adding" : "Removing",
6897 mUserData->s.strName.c_str(), vrc);
6898 }
6899 return hrc;
6900}
6901
6902HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6907
6908 return S_OK;
6909}
6910
6911HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6912{
6913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6914 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6915 if (SUCCEEDED(hrc))
6916 {
6917 hrc = mHWData.backupEx();
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6922 }
6923 }
6924 return hrc;
6925}
6926
6927HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6928{
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930
6931 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6932
6933 return S_OK;
6934}
6935
6936HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6937{
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6940 if ( SUCCEEDED(hrc)
6941 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6942 {
6943 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6944 int vrc;
6945
6946 if (aAutostopType != AutostopType_Disabled)
6947 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6948 else
6949 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6950
6951 if (RT_SUCCESS(vrc))
6952 {
6953 hrc = mHWData.backupEx();
6954 if (SUCCEEDED(hrc))
6955 {
6956 i_setModified(IsModified_MachineData);
6957 mHWData->mAutostart.enmAutostopType = aAutostopType;
6958 }
6959 }
6960 else if (vrc == VERR_NOT_SUPPORTED)
6961 hrc = setError(VBOX_E_NOT_SUPPORTED,
6962 tr("The VM autostop feature is not supported on this platform"));
6963 else if (vrc == VERR_PATH_NOT_FOUND)
6964 hrc = setError(E_FAIL,
6965 tr("The path to the autostart database is not set"));
6966 else
6967 hrc = setError(E_UNEXPECTED,
6968 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6969 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6970 mUserData->s.strName.c_str(), vrc);
6971 }
6972 return hrc;
6973}
6974
6975HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6976{
6977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 aDefaultFrontend = mHWData->mDefaultFrontend;
6980
6981 return S_OK;
6982}
6983
6984HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6985{
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6988 if (SUCCEEDED(hrc))
6989 {
6990 hrc = mHWData.backupEx();
6991 if (SUCCEEDED(hrc))
6992 {
6993 i_setModified(IsModified_MachineData);
6994 mHWData->mDefaultFrontend = aDefaultFrontend;
6995 }
6996 }
6997 return hrc;
6998}
6999
7000HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7001{
7002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7003 size_t cbIcon = mUserData->s.ovIcon.size();
7004 aIcon.resize(cbIcon);
7005 if (cbIcon)
7006 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7007 return S_OK;
7008}
7009
7010HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7011{
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7014 if (SUCCEEDED(hrc))
7015 {
7016 i_setModified(IsModified_MachineData);
7017 mUserData.backup();
7018 size_t cbIcon = aIcon.size();
7019 mUserData->s.ovIcon.resize(cbIcon);
7020 if (cbIcon)
7021 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7022 }
7023 return hrc;
7024}
7025
7026HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7027{
7028#ifdef VBOX_WITH_USB
7029 *aUSBProxyAvailable = true;
7030#else
7031 *aUSBProxyAvailable = false;
7032#endif
7033 return S_OK;
7034}
7035
7036HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7037{
7038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7039
7040 aVMProcessPriority = mUserData->s.strVMPriority;
7041
7042 return S_OK;
7043}
7044
7045HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7046{
7047 RT_NOREF(aVMProcessPriority);
7048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7049 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7050 if (SUCCEEDED(hrc))
7051 {
7052 /** @todo r=klaus: currently this is marked as not implemented, as
7053 * the code for setting the priority of the process is not there
7054 * (neither when starting the VM nor at runtime). */
7055 ReturnComNotImplemented();
7056#if 0
7057 hrc = mUserData.backupEx();
7058 if (SUCCEEDED(hrc))
7059 {
7060 i_setModified(IsModified_MachineData);
7061 mUserData->s.strVMPriority = aVMProcessPriority;
7062 }
7063#endif
7064 }
7065 return hrc;
7066}
7067
7068HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7069 ComPtr<IProgress> &aProgress)
7070{
7071 ComObjPtr<Progress> pP;
7072 Progress *ppP = pP;
7073 IProgress *iP = static_cast<IProgress *>(ppP);
7074 IProgress **pProgress = &iP;
7075
7076 IMachine *pTarget = aTarget;
7077
7078 /* Convert the options. */
7079 RTCList<CloneOptions_T> optList;
7080 if (aOptions.size())
7081 for (size_t i = 0; i < aOptions.size(); ++i)
7082 optList.append(aOptions[i]);
7083
7084 if (optList.contains(CloneOptions_Link))
7085 {
7086 if (!i_isSnapshotMachine())
7087 return setError(E_INVALIDARG,
7088 tr("Linked clone can only be created from a snapshot"));
7089 if (aMode != CloneMode_MachineState)
7090 return setError(E_INVALIDARG,
7091 tr("Linked clone can only be created for a single machine state"));
7092 }
7093 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7094
7095 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7096
7097 HRESULT rc = pWorker->start(pProgress);
7098
7099 pP = static_cast<Progress *>(*pProgress);
7100 pP.queryInterfaceTo(aProgress.asOutParam());
7101
7102 return rc;
7103
7104}
7105
7106HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7107 const com::Utf8Str &aType,
7108 ComPtr<IProgress> &aProgress)
7109{
7110 LogFlowThisFuncEnter();
7111
7112 ComObjPtr<Progress> progress;
7113
7114 progress.createObject();
7115
7116 HRESULT rc = S_OK;
7117 Utf8Str targetPath = aTargetPath;
7118 Utf8Str type = aType;
7119
7120 /* Initialize our worker task */
7121 MachineMoveVM* task = NULL;
7122 try
7123 {
7124 task = new MachineMoveVM(this, targetPath, type, progress);
7125 }
7126 catch(...)
7127 {
7128 delete task;
7129 return rc;
7130 }
7131
7132 /*
7133 * task pointer will be owned by the ThreadTask class.
7134 * There is no need to call operator "delete" in the end.
7135 */
7136 rc = task->init();
7137 if (SUCCEEDED(rc))
7138 {
7139 rc = task->createThread();
7140 if (FAILED(rc))
7141 {
7142 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7143 }
7144
7145 /* Return progress to the caller */
7146 progress.queryInterfaceTo(aProgress.asOutParam());
7147 }
7148
7149 LogFlowThisFuncLeave();
7150 return rc;
7151
7152}
7153
7154HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7155{
7156 NOREF(aProgress);
7157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7158
7159 // This check should always fail.
7160 HRESULT rc = i_checkStateDependency(MutableStateDep);
7161 if (FAILED(rc)) return rc;
7162
7163 AssertFailedReturn(E_NOTIMPL);
7164}
7165
7166HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7167{
7168 NOREF(aSavedStateFile);
7169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7170
7171 // This check should always fail.
7172 HRESULT rc = i_checkStateDependency(MutableStateDep);
7173 if (FAILED(rc)) return rc;
7174
7175 AssertFailedReturn(E_NOTIMPL);
7176}
7177
7178HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7179{
7180 NOREF(aFRemoveFile);
7181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 // This check should always fail.
7184 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7185 if (FAILED(rc)) return rc;
7186
7187 AssertFailedReturn(E_NOTIMPL);
7188}
7189
7190// public methods for internal purposes
7191/////////////////////////////////////////////////////////////////////////////
7192
7193/**
7194 * Adds the given IsModified_* flag to the dirty flags of the machine.
7195 * This must be called either during i_loadSettings or under the machine write lock.
7196 * @param fl Flag
7197 * @param fAllowStateModification If state modifications are allowed.
7198 */
7199void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7200{
7201 mData->flModifications |= fl;
7202 if (fAllowStateModification && i_isStateModificationAllowed())
7203 mData->mCurrentStateModified = true;
7204}
7205
7206/**
7207 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7208 * care of the write locking.
7209 *
7210 * @param fModification The flag to add.
7211 * @param fAllowStateModification If state modifications are allowed.
7212 */
7213void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7214{
7215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7216 i_setModified(fModification, fAllowStateModification);
7217}
7218
7219/**
7220 * Saves the registry entry of this machine to the given configuration node.
7221 *
7222 * @param data Machine registry data.
7223 *
7224 * @note locks this object for reading.
7225 */
7226HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7227{
7228 AutoLimitedCaller autoCaller(this);
7229 AssertComRCReturnRC(autoCaller.rc());
7230
7231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7232
7233 data.uuid = mData->mUuid;
7234 data.strSettingsFile = mData->m_strConfigFile;
7235
7236 return S_OK;
7237}
7238
7239/**
7240 * Calculates the absolute path of the given path taking the directory of the
7241 * machine settings file as the current directory.
7242 *
7243 * @param strPath Path to calculate the absolute path for.
7244 * @param aResult Where to put the result (used only on success, can be the
7245 * same Utf8Str instance as passed in @a aPath).
7246 * @return IPRT result.
7247 *
7248 * @note Locks this object for reading.
7249 */
7250int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7251{
7252 AutoCaller autoCaller(this);
7253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7258
7259 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7260
7261 strSettingsDir.stripFilename();
7262 char folder[RTPATH_MAX];
7263 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7264 if (RT_SUCCESS(vrc))
7265 aResult = folder;
7266
7267 return vrc;
7268}
7269
7270/**
7271 * Copies strSource to strTarget, making it relative to the machine folder
7272 * if it is a subdirectory thereof, or simply copying it otherwise.
7273 *
7274 * @param strSource Path to evaluate and copy.
7275 * @param strTarget Buffer to receive target path.
7276 *
7277 * @note Locks this object for reading.
7278 */
7279void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7280 Utf8Str &strTarget)
7281{
7282 AutoCaller autoCaller(this);
7283 AssertComRCReturn(autoCaller.rc(), (void)0);
7284
7285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7286
7287 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7288 // use strTarget as a temporary buffer to hold the machine settings dir
7289 strTarget = mData->m_strConfigFileFull;
7290 strTarget.stripFilename();
7291 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7292 {
7293 // is relative: then append what's left
7294 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7295 // for empty paths (only possible for subdirs) use "." to avoid
7296 // triggering default settings for not present config attributes.
7297 if (strTarget.isEmpty())
7298 strTarget = ".";
7299 }
7300 else
7301 // is not relative: then overwrite
7302 strTarget = strSource;
7303}
7304
7305/**
7306 * Returns the full path to the machine's log folder in the
7307 * \a aLogFolder argument.
7308 */
7309void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7310{
7311 AutoCaller autoCaller(this);
7312 AssertComRCReturnVoid(autoCaller.rc());
7313
7314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7315
7316 char szTmp[RTPATH_MAX];
7317 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7318 if (RT_SUCCESS(vrc))
7319 {
7320 if (szTmp[0] && !mUserData.isNull())
7321 {
7322 char szTmp2[RTPATH_MAX];
7323 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7324 if (RT_SUCCESS(vrc))
7325 aLogFolder = Utf8StrFmt("%s%c%s",
7326 szTmp2,
7327 RTPATH_DELIMITER,
7328 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7329 }
7330 else
7331 vrc = VERR_PATH_IS_RELATIVE;
7332 }
7333
7334 if (RT_FAILURE(vrc))
7335 {
7336 // fallback if VBOX_USER_LOGHOME is not set or invalid
7337 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7338 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7339 aLogFolder.append(RTPATH_DELIMITER);
7340 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7341 }
7342}
7343
7344/**
7345 * Returns the full path to the machine's log file for an given index.
7346 */
7347Utf8Str Machine::i_getLogFilename(ULONG idx)
7348{
7349 Utf8Str logFolder;
7350 getLogFolder(logFolder);
7351 Assert(logFolder.length());
7352
7353 Utf8Str log;
7354 if (idx == 0)
7355 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7356#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7357 else if (idx == 1)
7358 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7359 else
7360 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7361#else
7362 else
7363 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7364#endif
7365 return log;
7366}
7367
7368/**
7369 * Returns the full path to the machine's hardened log file.
7370 */
7371Utf8Str Machine::i_getHardeningLogFilename(void)
7372{
7373 Utf8Str strFilename;
7374 getLogFolder(strFilename);
7375 Assert(strFilename.length());
7376 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7377 return strFilename;
7378}
7379
7380
7381/**
7382 * Composes a unique saved state filename based on the current system time. The filename is
7383 * granular to the second so this will work so long as no more than one snapshot is taken on
7384 * a machine per second.
7385 *
7386 * Before version 4.1, we used this formula for saved state files:
7387 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7388 * which no longer works because saved state files can now be shared between the saved state of the
7389 * "saved" machine and an online snapshot, and the following would cause problems:
7390 * 1) save machine
7391 * 2) create online snapshot from that machine state --> reusing saved state file
7392 * 3) save machine again --> filename would be reused, breaking the online snapshot
7393 *
7394 * So instead we now use a timestamp.
7395 *
7396 * @param strStateFilePath
7397 */
7398
7399void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7400{
7401 AutoCaller autoCaller(this);
7402 AssertComRCReturnVoid(autoCaller.rc());
7403
7404 {
7405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7406 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7407 }
7408
7409 RTTIMESPEC ts;
7410 RTTimeNow(&ts);
7411 RTTIME time;
7412 RTTimeExplode(&time, &ts);
7413
7414 strStateFilePath += RTPATH_DELIMITER;
7415 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7416 time.i32Year, time.u8Month, time.u8MonthDay,
7417 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7418}
7419
7420/**
7421 * Returns whether at least one USB controller is present for the VM.
7422 */
7423bool Machine::i_isUSBControllerPresent()
7424{
7425 AutoCaller autoCaller(this);
7426 AssertComRCReturn(autoCaller.rc(), false);
7427
7428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7429
7430 return (mUSBControllers->size() > 0);
7431}
7432
7433/**
7434 * @note Locks this object for writing, calls the client process
7435 * (inside the lock).
7436 */
7437HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7438 const Utf8Str &strFrontend,
7439 const Utf8Str &strEnvironment,
7440 ProgressProxy *aProgress)
7441{
7442 LogFlowThisFuncEnter();
7443
7444 AssertReturn(aControl, E_FAIL);
7445 AssertReturn(aProgress, E_FAIL);
7446 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7447
7448 AutoCaller autoCaller(this);
7449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7450
7451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 if (!mData->mRegistered)
7454 return setError(E_UNEXPECTED,
7455 tr("The machine '%s' is not registered"),
7456 mUserData->s.strName.c_str());
7457
7458 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7459
7460 /* The process started when launching a VM with separate UI/VM processes is always
7461 * the UI process, i.e. needs special handling as it won't claim the session. */
7462 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7463
7464 if (fSeparate)
7465 {
7466 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7467 return setError(VBOX_E_INVALID_OBJECT_STATE,
7468 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7469 mUserData->s.strName.c_str());
7470 }
7471 else
7472 {
7473 if ( mData->mSession.mState == SessionState_Locked
7474 || mData->mSession.mState == SessionState_Spawning
7475 || mData->mSession.mState == SessionState_Unlocking)
7476 return setError(VBOX_E_INVALID_OBJECT_STATE,
7477 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7478 mUserData->s.strName.c_str());
7479
7480 /* may not be busy */
7481 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7482 }
7483
7484 /* get the path to the executable */
7485 char szPath[RTPATH_MAX];
7486 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7487 size_t cchBufLeft = strlen(szPath);
7488 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7489 szPath[cchBufLeft] = 0;
7490 char *pszNamePart = szPath + cchBufLeft;
7491 cchBufLeft = sizeof(szPath) - cchBufLeft;
7492
7493 int vrc = VINF_SUCCESS;
7494 RTPROCESS pid = NIL_RTPROCESS;
7495
7496 RTENV env = RTENV_DEFAULT;
7497
7498 if (!strEnvironment.isEmpty())
7499 {
7500 char *newEnvStr = NULL;
7501
7502 do
7503 {
7504 /* clone the current environment */
7505 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7506 AssertRCBreakStmt(vrc2, vrc = vrc2);
7507
7508 newEnvStr = RTStrDup(strEnvironment.c_str());
7509 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7510
7511 /* put new variables to the environment
7512 * (ignore empty variable names here since RTEnv API
7513 * intentionally doesn't do that) */
7514 char *var = newEnvStr;
7515 for (char *p = newEnvStr; *p; ++p)
7516 {
7517 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7518 {
7519 *p = '\0';
7520 if (*var)
7521 {
7522 char *val = strchr(var, '=');
7523 if (val)
7524 {
7525 *val++ = '\0';
7526 vrc2 = RTEnvSetEx(env, var, val);
7527 }
7528 else
7529 vrc2 = RTEnvUnsetEx(env, var);
7530 if (RT_FAILURE(vrc2))
7531 break;
7532 }
7533 var = p + 1;
7534 }
7535 }
7536 if (RT_SUCCESS(vrc2) && *var)
7537 vrc2 = RTEnvPutEx(env, var);
7538
7539 AssertRCBreakStmt(vrc2, vrc = vrc2);
7540 }
7541 while (0);
7542
7543 if (newEnvStr != NULL)
7544 RTStrFree(newEnvStr);
7545 }
7546
7547 /* Hardening logging */
7548#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7549 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7550 {
7551 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7552 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7553 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7554 {
7555 Utf8Str strStartupLogDir = strHardeningLogFile;
7556 strStartupLogDir.stripFilename();
7557 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7558 file without stripping the file. */
7559 }
7560 strSupHardeningLogArg.append(strHardeningLogFile);
7561
7562 /* Remove legacy log filename to avoid confusion. */
7563 Utf8Str strOldStartupLogFile;
7564 getLogFolder(strOldStartupLogFile);
7565 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7566 RTFileDelete(strOldStartupLogFile.c_str());
7567 }
7568 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7569#else
7570 const char *pszSupHardeningLogArg = NULL;
7571#endif
7572
7573 Utf8Str strCanonicalName;
7574
7575#ifdef VBOX_WITH_QTGUI
7576 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7577 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7578 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7579 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7580 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7581 {
7582 strCanonicalName = "GUI/Qt";
7583# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7584 /* Modify the base path so that we don't need to use ".." below. */
7585 RTPathStripTrailingSlash(szPath);
7586 RTPathStripFilename(szPath);
7587 cchBufLeft = strlen(szPath);
7588 pszNamePart = szPath + cchBufLeft;
7589 cchBufLeft = sizeof(szPath) - cchBufLeft;
7590
7591# define OSX_APP_NAME "VirtualBoxVM"
7592# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7593
7594 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7595 if ( strAppOverride.contains(".")
7596 || strAppOverride.contains("/")
7597 || strAppOverride.contains("\\")
7598 || strAppOverride.contains(":"))
7599 strAppOverride.setNull();
7600 Utf8Str strAppPath;
7601 if (!strAppOverride.isEmpty())
7602 {
7603 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7604 Utf8Str strFullPath(szPath);
7605 strFullPath.append(strAppPath);
7606 /* there is a race, but people using this deserve the failure */
7607 if (!RTFileExists(strFullPath.c_str()))
7608 strAppOverride.setNull();
7609 }
7610 if (strAppOverride.isEmpty())
7611 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7612 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7613 strcpy(pszNamePart, strAppPath.c_str());
7614# else
7615 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7616 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7617 strcpy(pszNamePart, s_szVirtualBox_exe);
7618# endif
7619
7620 Utf8Str idStr = mData->mUuid.toString();
7621 const char *apszArgs[] =
7622 {
7623 szPath,
7624 "--comment", mUserData->s.strName.c_str(),
7625 "--startvm", idStr.c_str(),
7626 "--no-startvm-errormsgbox",
7627 NULL, /* For "--separate". */
7628 NULL, /* For "--sup-startup-log". */
7629 NULL
7630 };
7631 unsigned iArg = 6;
7632 if (fSeparate)
7633 apszArgs[iArg++] = "--separate";
7634 apszArgs[iArg++] = pszSupHardeningLogArg;
7635
7636 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7637 }
7638#else /* !VBOX_WITH_QTGUI */
7639 if (0)
7640 ;
7641#endif /* VBOX_WITH_QTGUI */
7642
7643 else
7644
7645#ifdef VBOX_WITH_VBOXSDL
7646 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7647 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7648 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7649 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7650 {
7651 strCanonicalName = "GUI/SDL";
7652 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7653 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7654 strcpy(pszNamePart, s_szVBoxSDL_exe);
7655
7656 Utf8Str idStr = mData->mUuid.toString();
7657 const char *apszArgs[] =
7658 {
7659 szPath,
7660 "--comment", mUserData->s.strName.c_str(),
7661 "--startvm", idStr.c_str(),
7662 NULL, /* For "--separate". */
7663 NULL, /* For "--sup-startup-log". */
7664 NULL
7665 };
7666 unsigned iArg = 5;
7667 if (fSeparate)
7668 apszArgs[iArg++] = "--separate";
7669 apszArgs[iArg++] = pszSupHardeningLogArg;
7670
7671 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7672 }
7673#else /* !VBOX_WITH_VBOXSDL */
7674 if (0)
7675 ;
7676#endif /* !VBOX_WITH_VBOXSDL */
7677
7678 else
7679
7680#ifdef VBOX_WITH_HEADLESS
7681 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7682 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7683 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7684 )
7685 {
7686 strCanonicalName = "headless";
7687 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7688 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7689 * and a VM works even if the server has not been installed.
7690 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7691 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7692 * differently in 4.0 and 3.x.
7693 */
7694 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7695 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7696 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7697
7698 Utf8Str idStr = mData->mUuid.toString();
7699 const char *apszArgs[] =
7700 {
7701 szPath,
7702 "--comment", mUserData->s.strName.c_str(),
7703 "--startvm", idStr.c_str(),
7704 "--vrde", "config",
7705 NULL, /* For "--capture". */
7706 NULL, /* For "--sup-startup-log". */
7707 NULL
7708 };
7709 unsigned iArg = 7;
7710 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7711 apszArgs[iArg++] = "--capture";
7712 apszArgs[iArg++] = pszSupHardeningLogArg;
7713
7714# ifdef RT_OS_WINDOWS
7715 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7716# else
7717 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7718# endif
7719 }
7720#else /* !VBOX_WITH_HEADLESS */
7721 if (0)
7722 ;
7723#endif /* !VBOX_WITH_HEADLESS */
7724 else
7725 {
7726 RTEnvDestroy(env);
7727 return setError(E_INVALIDARG,
7728 tr("Invalid frontend name: '%s'"),
7729 strFrontend.c_str());
7730 }
7731
7732 RTEnvDestroy(env);
7733
7734 if (RT_FAILURE(vrc))
7735 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7736 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7737 mUserData->s.strName.c_str(), vrc);
7738
7739 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7740
7741 if (!fSeparate)
7742 {
7743 /*
7744 * Note that we don't release the lock here before calling the client,
7745 * because it doesn't need to call us back if called with a NULL argument.
7746 * Releasing the lock here is dangerous because we didn't prepare the
7747 * launch data yet, but the client we've just started may happen to be
7748 * too fast and call LockMachine() that will fail (because of PID, etc.),
7749 * so that the Machine will never get out of the Spawning session state.
7750 */
7751
7752 /* inform the session that it will be a remote one */
7753 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7754#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7755 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7756#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7757 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7758#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7759 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7760
7761 if (FAILED(rc))
7762 {
7763 /* restore the session state */
7764 mData->mSession.mState = SessionState_Unlocked;
7765 alock.release();
7766 mParent->i_addProcessToReap(pid);
7767 /* The failure may occur w/o any error info (from RPC), so provide one */
7768 return setError(VBOX_E_VM_ERROR,
7769 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7770 }
7771
7772 /* attach launch data to the machine */
7773 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7774 mData->mSession.mRemoteControls.push_back(aControl);
7775 mData->mSession.mProgress = aProgress;
7776 mData->mSession.mPID = pid;
7777 mData->mSession.mState = SessionState_Spawning;
7778 Assert(strCanonicalName.isNotEmpty());
7779 mData->mSession.mName = strCanonicalName;
7780 }
7781 else
7782 {
7783 /* For separate UI process we declare the launch as completed instantly, as the
7784 * actual headless VM start may or may not come. No point in remembering anything
7785 * yet, as what matters for us is when the headless VM gets started. */
7786 aProgress->i_notifyComplete(S_OK);
7787 }
7788
7789 alock.release();
7790 mParent->i_addProcessToReap(pid);
7791
7792 LogFlowThisFuncLeave();
7793 return S_OK;
7794}
7795
7796/**
7797 * Returns @c true if the given session machine instance has an open direct
7798 * session (and optionally also for direct sessions which are closing) and
7799 * returns the session control machine instance if so.
7800 *
7801 * Note that when the method returns @c false, the arguments remain unchanged.
7802 *
7803 * @param aMachine Session machine object.
7804 * @param aControl Direct session control object (optional).
7805 * @param aRequireVM If true then only allow VM sessions.
7806 * @param aAllowClosing If true then additionally a session which is currently
7807 * being closed will also be allowed.
7808 *
7809 * @note locks this object for reading.
7810 */
7811bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7812 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7813 bool aRequireVM /*= false*/,
7814 bool aAllowClosing /*= false*/)
7815{
7816 AutoLimitedCaller autoCaller(this);
7817 AssertComRCReturn(autoCaller.rc(), false);
7818
7819 /* just return false for inaccessible machines */
7820 if (getObjectState().getState() != ObjectState::Ready)
7821 return false;
7822
7823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 if ( ( mData->mSession.mState == SessionState_Locked
7826 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7827 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7828 )
7829 {
7830 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7831
7832 aMachine = mData->mSession.mMachine;
7833
7834 if (aControl != NULL)
7835 *aControl = mData->mSession.mDirectControl;
7836
7837 return true;
7838 }
7839
7840 return false;
7841}
7842
7843/**
7844 * Returns @c true if the given machine has an spawning direct session.
7845 *
7846 * @note locks this object for reading.
7847 */
7848bool Machine::i_isSessionSpawning()
7849{
7850 AutoLimitedCaller autoCaller(this);
7851 AssertComRCReturn(autoCaller.rc(), false);
7852
7853 /* just return false for inaccessible machines */
7854 if (getObjectState().getState() != ObjectState::Ready)
7855 return false;
7856
7857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7858
7859 if (mData->mSession.mState == SessionState_Spawning)
7860 return true;
7861
7862 return false;
7863}
7864
7865/**
7866 * Called from the client watcher thread to check for unexpected client process
7867 * death during Session_Spawning state (e.g. before it successfully opened a
7868 * direct session).
7869 *
7870 * On Win32 and on OS/2, this method is called only when we've got the
7871 * direct client's process termination notification, so it always returns @c
7872 * true.
7873 *
7874 * On other platforms, this method returns @c true if the client process is
7875 * terminated and @c false if it's still alive.
7876 *
7877 * @note Locks this object for writing.
7878 */
7879bool Machine::i_checkForSpawnFailure()
7880{
7881 AutoCaller autoCaller(this);
7882 if (!autoCaller.isOk())
7883 {
7884 /* nothing to do */
7885 LogFlowThisFunc(("Already uninitialized!\n"));
7886 return true;
7887 }
7888
7889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7890
7891 if (mData->mSession.mState != SessionState_Spawning)
7892 {
7893 /* nothing to do */
7894 LogFlowThisFunc(("Not spawning any more!\n"));
7895 return true;
7896 }
7897
7898 HRESULT rc = S_OK;
7899
7900 /* PID not yet initialized, skip check. */
7901 if (mData->mSession.mPID == NIL_RTPROCESS)
7902 return false;
7903
7904 RTPROCSTATUS status;
7905 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7906
7907 if (vrc != VERR_PROCESS_RUNNING)
7908 {
7909 Utf8Str strExtraInfo;
7910
7911#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7912 /* If the startup logfile exists and is of non-zero length, tell the
7913 user to look there for more details to encourage them to attach it
7914 when reporting startup issues. */
7915 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7916 uint64_t cbStartupLogFile = 0;
7917 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7918 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7919 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7920#endif
7921
7922 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7923 rc = setError(E_FAIL,
7924 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7925 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7926 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7927 rc = setError(E_FAIL,
7928 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7929 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7930 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7931 rc = setError(E_FAIL,
7932 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7933 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7934 else
7935 rc = setErrorBoth(E_FAIL, vrc,
7936 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7937 i_getName().c_str(), vrc, strExtraInfo.c_str());
7938 }
7939
7940 if (FAILED(rc))
7941 {
7942 /* Close the remote session, remove the remote control from the list
7943 * and reset session state to Closed (@note keep the code in sync with
7944 * the relevant part in LockMachine()). */
7945
7946 Assert(mData->mSession.mRemoteControls.size() == 1);
7947 if (mData->mSession.mRemoteControls.size() == 1)
7948 {
7949 ErrorInfoKeeper eik;
7950 mData->mSession.mRemoteControls.front()->Uninitialize();
7951 }
7952
7953 mData->mSession.mRemoteControls.clear();
7954 mData->mSession.mState = SessionState_Unlocked;
7955
7956 /* finalize the progress after setting the state */
7957 if (!mData->mSession.mProgress.isNull())
7958 {
7959 mData->mSession.mProgress->notifyComplete(rc);
7960 mData->mSession.mProgress.setNull();
7961 }
7962
7963 mData->mSession.mPID = NIL_RTPROCESS;
7964
7965 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7966 return true;
7967 }
7968
7969 return false;
7970}
7971
7972/**
7973 * Checks whether the machine can be registered. If so, commits and saves
7974 * all settings.
7975 *
7976 * @note Must be called from mParent's write lock. Locks this object and
7977 * children for writing.
7978 */
7979HRESULT Machine::i_prepareRegister()
7980{
7981 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7982
7983 AutoLimitedCaller autoCaller(this);
7984 AssertComRCReturnRC(autoCaller.rc());
7985
7986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7987
7988 /* wait for state dependents to drop to zero */
7989 i_ensureNoStateDependencies();
7990
7991 if (!mData->mAccessible)
7992 return setError(VBOX_E_INVALID_OBJECT_STATE,
7993 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7994 mUserData->s.strName.c_str(),
7995 mData->mUuid.toString().c_str());
7996
7997 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7998
7999 if (mData->mRegistered)
8000 return setError(VBOX_E_INVALID_OBJECT_STATE,
8001 tr("The machine '%s' with UUID {%s} is already registered"),
8002 mUserData->s.strName.c_str(),
8003 mData->mUuid.toString().c_str());
8004
8005 HRESULT rc = S_OK;
8006
8007 // Ensure the settings are saved. If we are going to be registered and
8008 // no config file exists yet, create it by calling i_saveSettings() too.
8009 if ( (mData->flModifications)
8010 || (!mData->pMachineConfigFile->fileExists())
8011 )
8012 {
8013 rc = i_saveSettings(NULL);
8014 // no need to check whether VirtualBox.xml needs saving too since
8015 // we can't have a machine XML file rename pending
8016 if (FAILED(rc)) return rc;
8017 }
8018
8019 /* more config checking goes here */
8020
8021 if (SUCCEEDED(rc))
8022 {
8023 /* we may have had implicit modifications we want to fix on success */
8024 i_commit();
8025
8026 mData->mRegistered = true;
8027 }
8028 else
8029 {
8030 /* we may have had implicit modifications we want to cancel on failure*/
8031 i_rollback(false /* aNotify */);
8032 }
8033
8034 return rc;
8035}
8036
8037/**
8038 * Increases the number of objects dependent on the machine state or on the
8039 * registered state. Guarantees that these two states will not change at least
8040 * until #i_releaseStateDependency() is called.
8041 *
8042 * Depending on the @a aDepType value, additional state checks may be made.
8043 * These checks will set extended error info on failure. See
8044 * #i_checkStateDependency() for more info.
8045 *
8046 * If this method returns a failure, the dependency is not added and the caller
8047 * is not allowed to rely on any particular machine state or registration state
8048 * value and may return the failed result code to the upper level.
8049 *
8050 * @param aDepType Dependency type to add.
8051 * @param aState Current machine state (NULL if not interested).
8052 * @param aRegistered Current registered state (NULL if not interested).
8053 *
8054 * @note Locks this object for writing.
8055 */
8056HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8057 MachineState_T *aState /* = NULL */,
8058 BOOL *aRegistered /* = NULL */)
8059{
8060 AutoCaller autoCaller(this);
8061 AssertComRCReturnRC(autoCaller.rc());
8062
8063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8064
8065 HRESULT rc = i_checkStateDependency(aDepType);
8066 if (FAILED(rc)) return rc;
8067
8068 {
8069 if (mData->mMachineStateChangePending != 0)
8070 {
8071 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8072 * drop to zero so don't add more. It may make sense to wait a bit
8073 * and retry before reporting an error (since the pending state
8074 * transition should be really quick) but let's just assert for
8075 * now to see if it ever happens on practice. */
8076
8077 AssertFailed();
8078
8079 return setError(E_ACCESSDENIED,
8080 tr("Machine state change is in progress. Please retry the operation later."));
8081 }
8082
8083 ++mData->mMachineStateDeps;
8084 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8085 }
8086
8087 if (aState)
8088 *aState = mData->mMachineState;
8089 if (aRegistered)
8090 *aRegistered = mData->mRegistered;
8091
8092 return S_OK;
8093}
8094
8095/**
8096 * Decreases the number of objects dependent on the machine state.
8097 * Must always complete the #i_addStateDependency() call after the state
8098 * dependency is no more necessary.
8099 */
8100void Machine::i_releaseStateDependency()
8101{
8102 AutoCaller autoCaller(this);
8103 AssertComRCReturnVoid(autoCaller.rc());
8104
8105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8106
8107 /* releaseStateDependency() w/o addStateDependency()? */
8108 AssertReturnVoid(mData->mMachineStateDeps != 0);
8109 -- mData->mMachineStateDeps;
8110
8111 if (mData->mMachineStateDeps == 0)
8112 {
8113 /* inform i_ensureNoStateDependencies() that there are no more deps */
8114 if (mData->mMachineStateChangePending != 0)
8115 {
8116 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8117 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8118 }
8119 }
8120}
8121
8122Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8123{
8124 /* start with nothing found */
8125 Utf8Str strResult("");
8126
8127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8128
8129 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8130 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8131 // found:
8132 strResult = it->second; // source is a Utf8Str
8133
8134 return strResult;
8135}
8136
8137// protected methods
8138/////////////////////////////////////////////////////////////////////////////
8139
8140/**
8141 * Performs machine state checks based on the @a aDepType value. If a check
8142 * fails, this method will set extended error info, otherwise it will return
8143 * S_OK. It is supposed, that on failure, the caller will immediately return
8144 * the return value of this method to the upper level.
8145 *
8146 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8147 *
8148 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8149 * current state of this machine object allows to change settings of the
8150 * machine (i.e. the machine is not registered, or registered but not running
8151 * and not saved). It is useful to call this method from Machine setters
8152 * before performing any change.
8153 *
8154 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8155 * as for MutableStateDep except that if the machine is saved, S_OK is also
8156 * returned. This is useful in setters which allow changing machine
8157 * properties when it is in the saved state.
8158 *
8159 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8160 * if the current state of this machine object allows to change runtime
8161 * changeable settings of the machine (i.e. the machine is not registered, or
8162 * registered but either running or not running and not saved). It is useful
8163 * to call this method from Machine setters before performing any changes to
8164 * runtime changeable settings.
8165 *
8166 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8167 * the same as for MutableOrRunningStateDep except that if the machine is
8168 * saved, S_OK is also returned. This is useful in setters which allow
8169 * changing runtime and saved state changeable machine properties.
8170 *
8171 * @param aDepType Dependency type to check.
8172 *
8173 * @note Non Machine based classes should use #i_addStateDependency() and
8174 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8175 * template.
8176 *
8177 * @note This method must be called from under this object's read or write
8178 * lock.
8179 */
8180HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8181{
8182 switch (aDepType)
8183 {
8184 case AnyStateDep:
8185 {
8186 break;
8187 }
8188 case MutableStateDep:
8189 {
8190 if ( mData->mRegistered
8191 && ( !i_isSessionMachine()
8192 || ( mData->mMachineState != MachineState_Aborted
8193 && mData->mMachineState != MachineState_Teleported
8194 && mData->mMachineState != MachineState_PoweredOff
8195 )
8196 )
8197 )
8198 return setError(VBOX_E_INVALID_VM_STATE,
8199 tr("The machine is not mutable (state is %s)"),
8200 Global::stringifyMachineState(mData->mMachineState));
8201 break;
8202 }
8203 case MutableOrSavedStateDep:
8204 {
8205 if ( mData->mRegistered
8206 && ( !i_isSessionMachine()
8207 || ( mData->mMachineState != MachineState_Aborted
8208 && mData->mMachineState != MachineState_Teleported
8209 && mData->mMachineState != MachineState_Saved
8210 && mData->mMachineState != MachineState_PoweredOff
8211 )
8212 )
8213 )
8214 return setError(VBOX_E_INVALID_VM_STATE,
8215 tr("The machine is not mutable or saved (state is %s)"),
8216 Global::stringifyMachineState(mData->mMachineState));
8217 break;
8218 }
8219 case MutableOrRunningStateDep:
8220 {
8221 if ( mData->mRegistered
8222 && ( !i_isSessionMachine()
8223 || ( mData->mMachineState != MachineState_Aborted
8224 && mData->mMachineState != MachineState_Teleported
8225 && mData->mMachineState != MachineState_PoweredOff
8226 && !Global::IsOnline(mData->mMachineState)
8227 )
8228 )
8229 )
8230 return setError(VBOX_E_INVALID_VM_STATE,
8231 tr("The machine is not mutable or running (state is %s)"),
8232 Global::stringifyMachineState(mData->mMachineState));
8233 break;
8234 }
8235 case MutableOrSavedOrRunningStateDep:
8236 {
8237 if ( mData->mRegistered
8238 && ( !i_isSessionMachine()
8239 || ( mData->mMachineState != MachineState_Aborted
8240 && mData->mMachineState != MachineState_Teleported
8241 && mData->mMachineState != MachineState_Saved
8242 && mData->mMachineState != MachineState_PoweredOff
8243 && !Global::IsOnline(mData->mMachineState)
8244 )
8245 )
8246 )
8247 return setError(VBOX_E_INVALID_VM_STATE,
8248 tr("The machine is not mutable, saved or running (state is %s)"),
8249 Global::stringifyMachineState(mData->mMachineState));
8250 break;
8251 }
8252 }
8253
8254 return S_OK;
8255}
8256
8257/**
8258 * Helper to initialize all associated child objects and allocate data
8259 * structures.
8260 *
8261 * This method must be called as a part of the object's initialization procedure
8262 * (usually done in the #init() method).
8263 *
8264 * @note Must be called only from #init() or from #i_registeredInit().
8265 */
8266HRESULT Machine::initDataAndChildObjects()
8267{
8268 AutoCaller autoCaller(this);
8269 AssertComRCReturnRC(autoCaller.rc());
8270 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8271 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8272
8273 AssertReturn(!mData->mAccessible, E_FAIL);
8274
8275 /* allocate data structures */
8276 mSSData.allocate();
8277 mUserData.allocate();
8278 mHWData.allocate();
8279 mMediumAttachments.allocate();
8280 mStorageControllers.allocate();
8281 mUSBControllers.allocate();
8282
8283 /* initialize mOSTypeId */
8284 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8285
8286/** @todo r=bird: init() methods never fails, right? Why don't we make them
8287 * return void then! */
8288
8289 /* create associated BIOS settings object */
8290 unconst(mBIOSSettings).createObject();
8291 mBIOSSettings->init(this);
8292
8293 /* create associated record settings object */
8294 unconst(mRecordingSettings).createObject();
8295 mRecordingSettings->init(this);
8296
8297 /* create an associated VRDE object (default is disabled) */
8298 unconst(mVRDEServer).createObject();
8299 mVRDEServer->init(this);
8300
8301 /* create associated serial port objects */
8302 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8303 {
8304 unconst(mSerialPorts[slot]).createObject();
8305 mSerialPorts[slot]->init(this, slot);
8306 }
8307
8308 /* create associated parallel port objects */
8309 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8310 {
8311 unconst(mParallelPorts[slot]).createObject();
8312 mParallelPorts[slot]->init(this, slot);
8313 }
8314
8315 /* create the audio adapter object (always present, default is disabled) */
8316 unconst(mAudioAdapter).createObject();
8317 mAudioAdapter->init(this);
8318
8319 /* create the USB device filters object (always present) */
8320 unconst(mUSBDeviceFilters).createObject();
8321 mUSBDeviceFilters->init(this);
8322
8323 /* create associated network adapter objects */
8324 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8325 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8326 {
8327 unconst(mNetworkAdapters[slot]).createObject();
8328 mNetworkAdapters[slot]->init(this, slot);
8329 }
8330
8331 /* create the bandwidth control */
8332 unconst(mBandwidthControl).createObject();
8333 mBandwidthControl->init(this);
8334
8335 return S_OK;
8336}
8337
8338/**
8339 * Helper to uninitialize all associated child objects and to free all data
8340 * structures.
8341 *
8342 * This method must be called as a part of the object's uninitialization
8343 * procedure (usually done in the #uninit() method).
8344 *
8345 * @note Must be called only from #uninit() or from #i_registeredInit().
8346 */
8347void Machine::uninitDataAndChildObjects()
8348{
8349 AutoCaller autoCaller(this);
8350 AssertComRCReturnVoid(autoCaller.rc());
8351 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8352 || getObjectState().getState() == ObjectState::Limited);
8353
8354 /* tell all our other child objects we've been uninitialized */
8355 if (mBandwidthControl)
8356 {
8357 mBandwidthControl->uninit();
8358 unconst(mBandwidthControl).setNull();
8359 }
8360
8361 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8362 {
8363 if (mNetworkAdapters[slot])
8364 {
8365 mNetworkAdapters[slot]->uninit();
8366 unconst(mNetworkAdapters[slot]).setNull();
8367 }
8368 }
8369
8370 if (mUSBDeviceFilters)
8371 {
8372 mUSBDeviceFilters->uninit();
8373 unconst(mUSBDeviceFilters).setNull();
8374 }
8375
8376 if (mAudioAdapter)
8377 {
8378 mAudioAdapter->uninit();
8379 unconst(mAudioAdapter).setNull();
8380 }
8381
8382 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8383 {
8384 if (mParallelPorts[slot])
8385 {
8386 mParallelPorts[slot]->uninit();
8387 unconst(mParallelPorts[slot]).setNull();
8388 }
8389 }
8390
8391 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8392 {
8393 if (mSerialPorts[slot])
8394 {
8395 mSerialPorts[slot]->uninit();
8396 unconst(mSerialPorts[slot]).setNull();
8397 }
8398 }
8399
8400 if (mVRDEServer)
8401 {
8402 mVRDEServer->uninit();
8403 unconst(mVRDEServer).setNull();
8404 }
8405
8406 if (mBIOSSettings)
8407 {
8408 mBIOSSettings->uninit();
8409 unconst(mBIOSSettings).setNull();
8410 }
8411
8412 if (mRecordingSettings)
8413 {
8414 mRecordingSettings->uninit();
8415 unconst(mRecordingSettings).setNull();
8416 }
8417
8418 /* Deassociate media (only when a real Machine or a SnapshotMachine
8419 * instance is uninitialized; SessionMachine instances refer to real
8420 * Machine media). This is necessary for a clean re-initialization of
8421 * the VM after successfully re-checking the accessibility state. Note
8422 * that in case of normal Machine or SnapshotMachine uninitialization (as
8423 * a result of unregistering or deleting the snapshot), outdated media
8424 * attachments will already be uninitialized and deleted, so this
8425 * code will not affect them. */
8426 if ( !mMediumAttachments.isNull()
8427 && !i_isSessionMachine()
8428 )
8429 {
8430 for (MediumAttachmentList::const_iterator
8431 it = mMediumAttachments->begin();
8432 it != mMediumAttachments->end();
8433 ++it)
8434 {
8435 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8436 if (pMedium.isNull())
8437 continue;
8438 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8439 AssertComRC(rc);
8440 }
8441 }
8442
8443 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8444 {
8445 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8446 if (mData->mFirstSnapshot)
8447 {
8448 // snapshots tree is protected by machine write lock; strictly
8449 // this isn't necessary here since we're deleting the entire
8450 // machine, but otherwise we assert in Snapshot::uninit()
8451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8452 mData->mFirstSnapshot->uninit();
8453 mData->mFirstSnapshot.setNull();
8454 }
8455
8456 mData->mCurrentSnapshot.setNull();
8457 }
8458
8459 /* free data structures (the essential mData structure is not freed here
8460 * since it may be still in use) */
8461 mMediumAttachments.free();
8462 mStorageControllers.free();
8463 mUSBControllers.free();
8464 mHWData.free();
8465 mUserData.free();
8466 mSSData.free();
8467}
8468
8469/**
8470 * Returns a pointer to the Machine object for this machine that acts like a
8471 * parent for complex machine data objects such as shared folders, etc.
8472 *
8473 * For primary Machine objects and for SnapshotMachine objects, returns this
8474 * object's pointer itself. For SessionMachine objects, returns the peer
8475 * (primary) machine pointer.
8476 */
8477Machine *Machine::i_getMachine()
8478{
8479 if (i_isSessionMachine())
8480 return (Machine*)mPeer;
8481 return this;
8482}
8483
8484/**
8485 * Makes sure that there are no machine state dependents. If necessary, waits
8486 * for the number of dependents to drop to zero.
8487 *
8488 * Make sure this method is called from under this object's write lock to
8489 * guarantee that no new dependents may be added when this method returns
8490 * control to the caller.
8491 *
8492 * @note Locks this object for writing. The lock will be released while waiting
8493 * (if necessary).
8494 *
8495 * @warning To be used only in methods that change the machine state!
8496 */
8497void Machine::i_ensureNoStateDependencies()
8498{
8499 AssertReturnVoid(isWriteLockOnCurrentThread());
8500
8501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8502
8503 /* Wait for all state dependents if necessary */
8504 if (mData->mMachineStateDeps != 0)
8505 {
8506 /* lazy semaphore creation */
8507 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8508 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8509
8510 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8511 mData->mMachineStateDeps));
8512
8513 ++mData->mMachineStateChangePending;
8514
8515 /* reset the semaphore before waiting, the last dependent will signal
8516 * it */
8517 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8518
8519 alock.release();
8520
8521 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8522
8523 alock.acquire();
8524
8525 -- mData->mMachineStateChangePending;
8526 }
8527}
8528
8529/**
8530 * Changes the machine state and informs callbacks.
8531 *
8532 * This method is not intended to fail so it either returns S_OK or asserts (and
8533 * returns a failure).
8534 *
8535 * @note Locks this object for writing.
8536 */
8537HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8538{
8539 LogFlowThisFuncEnter();
8540 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8541 Assert(aMachineState != MachineState_Null);
8542
8543 AutoCaller autoCaller(this);
8544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8545
8546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8547
8548 /* wait for state dependents to drop to zero */
8549 i_ensureNoStateDependencies();
8550
8551 MachineState_T const enmOldState = mData->mMachineState;
8552 if (enmOldState != aMachineState)
8553 {
8554 mData->mMachineState = aMachineState;
8555 RTTimeNow(&mData->mLastStateChange);
8556
8557#ifdef VBOX_WITH_DTRACE_R3_MAIN
8558 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8559#endif
8560 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8561 }
8562
8563 LogFlowThisFuncLeave();
8564 return S_OK;
8565}
8566
8567/**
8568 * Searches for a shared folder with the given logical name
8569 * in the collection of shared folders.
8570 *
8571 * @param aName logical name of the shared folder
8572 * @param aSharedFolder where to return the found object
8573 * @param aSetError whether to set the error info if the folder is
8574 * not found
8575 * @return
8576 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8577 *
8578 * @note
8579 * must be called from under the object's lock!
8580 */
8581HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8582 ComObjPtr<SharedFolder> &aSharedFolder,
8583 bool aSetError /* = false */)
8584{
8585 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8586 for (HWData::SharedFolderList::const_iterator
8587 it = mHWData->mSharedFolders.begin();
8588 it != mHWData->mSharedFolders.end();
8589 ++it)
8590 {
8591 SharedFolder *pSF = *it;
8592 AutoCaller autoCaller(pSF);
8593 if (pSF->i_getName() == aName)
8594 {
8595 aSharedFolder = pSF;
8596 rc = S_OK;
8597 break;
8598 }
8599 }
8600
8601 if (aSetError && FAILED(rc))
8602 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8603
8604 return rc;
8605}
8606
8607/**
8608 * Initializes all machine instance data from the given settings structures
8609 * from XML. The exception is the machine UUID which needs special handling
8610 * depending on the caller's use case, so the caller needs to set that herself.
8611 *
8612 * This gets called in several contexts during machine initialization:
8613 *
8614 * -- When machine XML exists on disk already and needs to be loaded into memory,
8615 * for example, from #i_registeredInit() to load all registered machines on
8616 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8617 * attached to the machine should be part of some media registry already.
8618 *
8619 * -- During OVF import, when a machine config has been constructed from an
8620 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8621 * ensure that the media listed as attachments in the config (which have
8622 * been imported from the OVF) receive the correct registry ID.
8623 *
8624 * -- During VM cloning.
8625 *
8626 * @param config Machine settings from XML.
8627 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8628 * for each attached medium in the config.
8629 * @return
8630 */
8631HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8632 const Guid *puuidRegistry)
8633{
8634 // copy name, description, OS type, teleporter, UTC etc.
8635 mUserData->s = config.machineUserData;
8636
8637 // look up the object by Id to check it is valid
8638 ComObjPtr<GuestOSType> pGuestOSType;
8639 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8640 if (!pGuestOSType.isNull())
8641 mUserData->s.strOsType = pGuestOSType->i_id();
8642
8643 // stateFile (optional)
8644 if (config.strStateFile.isEmpty())
8645 mSSData->strStateFilePath.setNull();
8646 else
8647 {
8648 Utf8Str stateFilePathFull(config.strStateFile);
8649 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8650 if (RT_FAILURE(vrc))
8651 return setErrorBoth(E_FAIL, vrc,
8652 tr("Invalid saved state file path '%s' (%Rrc)"),
8653 config.strStateFile.c_str(),
8654 vrc);
8655 mSSData->strStateFilePath = stateFilePathFull;
8656 }
8657
8658 // snapshot folder needs special processing so set it again
8659 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8660 if (FAILED(rc)) return rc;
8661
8662 /* Copy the extra data items (config may or may not be the same as
8663 * mData->pMachineConfigFile) if necessary. When loading the XML files
8664 * from disk they are the same, but not for OVF import. */
8665 if (mData->pMachineConfigFile != &config)
8666 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8667
8668 /* currentStateModified (optional, default is true) */
8669 mData->mCurrentStateModified = config.fCurrentStateModified;
8670
8671 mData->mLastStateChange = config.timeLastStateChange;
8672
8673 /*
8674 * note: all mUserData members must be assigned prior this point because
8675 * we need to commit changes in order to let mUserData be shared by all
8676 * snapshot machine instances.
8677 */
8678 mUserData.commitCopy();
8679
8680 // machine registry, if present (must be loaded before snapshots)
8681 if (config.canHaveOwnMediaRegistry())
8682 {
8683 // determine machine folder
8684 Utf8Str strMachineFolder = i_getSettingsFileFull();
8685 strMachineFolder.stripFilename();
8686 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8687 config.mediaRegistry,
8688 strMachineFolder);
8689 if (FAILED(rc)) return rc;
8690 }
8691
8692 /* Snapshot node (optional) */
8693 size_t cRootSnapshots;
8694 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8695 {
8696 // there must be only one root snapshot
8697 Assert(cRootSnapshots == 1);
8698
8699 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8700
8701 rc = i_loadSnapshot(snap,
8702 config.uuidCurrentSnapshot,
8703 NULL); // no parent == first snapshot
8704 if (FAILED(rc)) return rc;
8705 }
8706
8707 // hardware data
8708 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8709 if (FAILED(rc)) return rc;
8710
8711 /*
8712 * NOTE: the assignment below must be the last thing to do,
8713 * otherwise it will be not possible to change the settings
8714 * somewhere in the code above because all setters will be
8715 * blocked by i_checkStateDependency(MutableStateDep).
8716 */
8717
8718 /* set the machine state to Aborted or Saved when appropriate */
8719 if (config.fAborted)
8720 {
8721 mSSData->strStateFilePath.setNull();
8722
8723 /* no need to use i_setMachineState() during init() */
8724 mData->mMachineState = MachineState_Aborted;
8725 }
8726 else if (!mSSData->strStateFilePath.isEmpty())
8727 {
8728 /* no need to use i_setMachineState() during init() */
8729 mData->mMachineState = MachineState_Saved;
8730 }
8731
8732 // after loading settings, we are no longer different from the XML on disk
8733 mData->flModifications = 0;
8734
8735 return S_OK;
8736}
8737
8738/**
8739 * Recursively loads all snapshots starting from the given.
8740 *
8741 * @param data snapshot settings.
8742 * @param aCurSnapshotId Current snapshot ID from the settings file.
8743 * @param aParentSnapshot Parent snapshot.
8744 */
8745HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8746 const Guid &aCurSnapshotId,
8747 Snapshot *aParentSnapshot)
8748{
8749 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8750 AssertReturn(!i_isSessionMachine(), E_FAIL);
8751
8752 HRESULT rc = S_OK;
8753
8754 Utf8Str strStateFile;
8755 if (!data.strStateFile.isEmpty())
8756 {
8757 /* optional */
8758 strStateFile = data.strStateFile;
8759 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8760 if (RT_FAILURE(vrc))
8761 return setErrorBoth(E_FAIL, vrc,
8762 tr("Invalid saved state file path '%s' (%Rrc)"),
8763 strStateFile.c_str(),
8764 vrc);
8765 }
8766
8767 /* create a snapshot machine object */
8768 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8769 pSnapshotMachine.createObject();
8770 rc = pSnapshotMachine->initFromSettings(this,
8771 data.hardware,
8772 &data.debugging,
8773 &data.autostart,
8774 data.uuid.ref(),
8775 strStateFile);
8776 if (FAILED(rc)) return rc;
8777
8778 /* create a snapshot object */
8779 ComObjPtr<Snapshot> pSnapshot;
8780 pSnapshot.createObject();
8781 /* initialize the snapshot */
8782 rc = pSnapshot->init(mParent, // VirtualBox object
8783 data.uuid,
8784 data.strName,
8785 data.strDescription,
8786 data.timestamp,
8787 pSnapshotMachine,
8788 aParentSnapshot);
8789 if (FAILED(rc)) return rc;
8790
8791 /* memorize the first snapshot if necessary */
8792 if (!mData->mFirstSnapshot)
8793 mData->mFirstSnapshot = pSnapshot;
8794
8795 /* memorize the current snapshot when appropriate */
8796 if ( !mData->mCurrentSnapshot
8797 && pSnapshot->i_getId() == aCurSnapshotId
8798 )
8799 mData->mCurrentSnapshot = pSnapshot;
8800
8801 // now create the children
8802 for (settings::SnapshotsList::const_iterator
8803 it = data.llChildSnapshots.begin();
8804 it != data.llChildSnapshots.end();
8805 ++it)
8806 {
8807 const settings::Snapshot &childData = *it;
8808 // recurse
8809 rc = i_loadSnapshot(childData,
8810 aCurSnapshotId,
8811 pSnapshot); // parent = the one we created above
8812 if (FAILED(rc)) return rc;
8813 }
8814
8815 return rc;
8816}
8817
8818/**
8819 * Loads settings into mHWData.
8820 *
8821 * @param puuidRegistry Registry ID.
8822 * @param puuidSnapshot Snapshot ID
8823 * @param data Reference to the hardware settings.
8824 * @param pDbg Pointer to the debugging settings.
8825 * @param pAutostart Pointer to the autostart settings.
8826 */
8827HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8828 const Guid *puuidSnapshot,
8829 const settings::Hardware &data,
8830 const settings::Debugging *pDbg,
8831 const settings::Autostart *pAutostart)
8832{
8833 AssertReturn(!i_isSessionMachine(), E_FAIL);
8834
8835 HRESULT rc = S_OK;
8836
8837 try
8838 {
8839 ComObjPtr<GuestOSType> pGuestOSType;
8840 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8841
8842 /* The hardware version attribute (optional). */
8843 mHWData->mHWVersion = data.strVersion;
8844 mHWData->mHardwareUUID = data.uuid;
8845
8846 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8847 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8848 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8849 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8850 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8851 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8852 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8853 mHWData->mPAEEnabled = data.fPAE;
8854 mHWData->mLongMode = data.enmLongMode;
8855 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8856 mHWData->mAPIC = data.fAPIC;
8857 mHWData->mX2APIC = data.fX2APIC;
8858 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8859 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8860 mHWData->mSpecCtrl = data.fSpecCtrl;
8861 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8862 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8863 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8864 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8865 mHWData->mCPUCount = data.cCPUs;
8866 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8867 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8868 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8869 mHWData->mCpuProfile = data.strCpuProfile;
8870
8871 // cpu
8872 if (mHWData->mCPUHotPlugEnabled)
8873 {
8874 for (settings::CpuList::const_iterator
8875 it = data.llCpus.begin();
8876 it != data.llCpus.end();
8877 ++it)
8878 {
8879 const settings::Cpu &cpu = *it;
8880
8881 mHWData->mCPUAttached[cpu.ulId] = true;
8882 }
8883 }
8884
8885 // cpuid leafs
8886 for (settings::CpuIdLeafsList::const_iterator
8887 it = data.llCpuIdLeafs.begin();
8888 it != data.llCpuIdLeafs.end();
8889 ++it)
8890 {
8891 const settings::CpuIdLeaf &rLeaf= *it;
8892 if ( rLeaf.idx < UINT32_C(0x20)
8893 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8894 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8895 mHWData->mCpuIdLeafList.push_back(rLeaf);
8896 /* else: just ignore */
8897 }
8898
8899 mHWData->mMemorySize = data.ulMemorySizeMB;
8900 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8901
8902 // boot order
8903 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8904 {
8905 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8906 if (it == data.mapBootOrder.end())
8907 mHWData->mBootOrder[i] = DeviceType_Null;
8908 else
8909 mHWData->mBootOrder[i] = it->second;
8910 }
8911
8912 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8913 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8914 mHWData->mMonitorCount = data.cMonitors;
8915 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8916 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8917 mHWData->mFirmwareType = data.firmwareType;
8918 mHWData->mPointingHIDType = data.pointingHIDType;
8919 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8920 mHWData->mChipsetType = data.chipsetType;
8921 mHWData->mParavirtProvider = data.paravirtProvider;
8922 mHWData->mParavirtDebug = data.strParavirtDebug;
8923 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8924 mHWData->mHPETEnabled = data.fHPETEnabled;
8925
8926 /* VRDEServer */
8927 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8928 if (FAILED(rc)) return rc;
8929
8930 /* BIOS */
8931 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8932 if (FAILED(rc)) return rc;
8933
8934 /* Recording settings */
8935 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8936 if (FAILED(rc)) return rc;
8937
8938 // Bandwidth control (must come before network adapters)
8939 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8940 if (FAILED(rc)) return rc;
8941
8942 /* USB controllers */
8943 for (settings::USBControllerList::const_iterator
8944 it = data.usbSettings.llUSBControllers.begin();
8945 it != data.usbSettings.llUSBControllers.end();
8946 ++it)
8947 {
8948 const settings::USBController &settingsCtrl = *it;
8949 ComObjPtr<USBController> newCtrl;
8950
8951 newCtrl.createObject();
8952 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8953 mUSBControllers->push_back(newCtrl);
8954 }
8955
8956 /* USB device filters */
8957 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8958 if (FAILED(rc)) return rc;
8959
8960 // network adapters (establish array size first and apply defaults, to
8961 // ensure reading the same settings as we saved, since the list skips
8962 // adapters having defaults)
8963 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8964 size_t oldCount = mNetworkAdapters.size();
8965 if (newCount > oldCount)
8966 {
8967 mNetworkAdapters.resize(newCount);
8968 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8969 {
8970 unconst(mNetworkAdapters[slot]).createObject();
8971 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8972 }
8973 }
8974 else if (newCount < oldCount)
8975 mNetworkAdapters.resize(newCount);
8976 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8977 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8978 for (settings::NetworkAdaptersList::const_iterator
8979 it = data.llNetworkAdapters.begin();
8980 it != data.llNetworkAdapters.end();
8981 ++it)
8982 {
8983 const settings::NetworkAdapter &nic = *it;
8984
8985 /* slot uniqueness is guaranteed by XML Schema */
8986 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8987 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8988 if (FAILED(rc)) return rc;
8989 }
8990
8991 // serial ports (establish defaults first, to ensure reading the same
8992 // settings as we saved, since the list skips ports having defaults)
8993 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8994 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8995 for (settings::SerialPortsList::const_iterator
8996 it = data.llSerialPorts.begin();
8997 it != data.llSerialPorts.end();
8998 ++it)
8999 {
9000 const settings::SerialPort &s = *it;
9001
9002 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9003 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9004 if (FAILED(rc)) return rc;
9005 }
9006
9007 // parallel ports (establish defaults first, to ensure reading the same
9008 // settings as we saved, since the list skips ports having defaults)
9009 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9010 mParallelPorts[i]->i_applyDefaults();
9011 for (settings::ParallelPortsList::const_iterator
9012 it = data.llParallelPorts.begin();
9013 it != data.llParallelPorts.end();
9014 ++it)
9015 {
9016 const settings::ParallelPort &p = *it;
9017
9018 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9019 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9020 if (FAILED(rc)) return rc;
9021 }
9022
9023 /* AudioAdapter */
9024 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9025 if (FAILED(rc)) return rc;
9026
9027 /* storage controllers */
9028 rc = i_loadStorageControllers(data.storage,
9029 puuidRegistry,
9030 puuidSnapshot);
9031 if (FAILED(rc)) return rc;
9032
9033 /* Shared folders */
9034 for (settings::SharedFoldersList::const_iterator
9035 it = data.llSharedFolders.begin();
9036 it != data.llSharedFolders.end();
9037 ++it)
9038 {
9039 const settings::SharedFolder &sf = *it;
9040
9041 ComObjPtr<SharedFolder> sharedFolder;
9042 /* Check for double entries. Not allowed! */
9043 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9044 if (SUCCEEDED(rc))
9045 return setError(VBOX_E_OBJECT_IN_USE,
9046 tr("Shared folder named '%s' already exists"),
9047 sf.strName.c_str());
9048
9049 /* Create the new shared folder. Don't break on error. This will be
9050 * reported when the machine starts. */
9051 sharedFolder.createObject();
9052 rc = sharedFolder->init(i_getMachine(),
9053 sf.strName,
9054 sf.strHostPath,
9055 RT_BOOL(sf.fWritable),
9056 RT_BOOL(sf.fAutoMount),
9057 sf.strAutoMountPoint,
9058 false /* fFailOnError */);
9059 if (FAILED(rc)) return rc;
9060 mHWData->mSharedFolders.push_back(sharedFolder);
9061 }
9062
9063 // Clipboard
9064 mHWData->mClipboardMode = data.clipboardMode;
9065
9066 // drag'n'drop
9067 mHWData->mDnDMode = data.dndMode;
9068
9069 // guest settings
9070 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9071
9072 // IO settings
9073 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9074 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9075
9076 // Host PCI devices
9077 for (settings::HostPCIDeviceAttachmentList::const_iterator
9078 it = data.pciAttachments.begin();
9079 it != data.pciAttachments.end();
9080 ++it)
9081 {
9082 const settings::HostPCIDeviceAttachment &hpda = *it;
9083 ComObjPtr<PCIDeviceAttachment> pda;
9084
9085 pda.createObject();
9086 pda->i_loadSettings(this, hpda);
9087 mHWData->mPCIDeviceAssignments.push_back(pda);
9088 }
9089
9090 /*
9091 * (The following isn't really real hardware, but it lives in HWData
9092 * for reasons of convenience.)
9093 */
9094
9095#ifdef VBOX_WITH_GUEST_PROPS
9096 /* Guest properties (optional) */
9097
9098 /* Only load transient guest properties for configs which have saved
9099 * state, because there shouldn't be any for powered off VMs. The same
9100 * logic applies for snapshots, as offline snapshots shouldn't have
9101 * any such properties. They confuse the code in various places.
9102 * Note: can't rely on the machine state, as it isn't set yet. */
9103 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9104 /* apologies for the hacky unconst() usage, but this needs hacking
9105 * actually inconsistent settings into consistency, otherwise there
9106 * will be some corner cases where the inconsistency survives
9107 * surprisingly long without getting fixed, especially for snapshots
9108 * as there are no config changes. */
9109 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9110 for (settings::GuestPropertiesList::iterator
9111 it = llGuestProperties.begin();
9112 it != llGuestProperties.end();
9113 /*nothing*/)
9114 {
9115 const settings::GuestProperty &prop = *it;
9116 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9117 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9118 if ( fSkipTransientGuestProperties
9119 && ( fFlags & GUEST_PROP_F_TRANSIENT
9120 || fFlags & GUEST_PROP_F_TRANSRESET))
9121 {
9122 it = llGuestProperties.erase(it);
9123 continue;
9124 }
9125 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9126 mHWData->mGuestProperties[prop.strName] = property;
9127 ++it;
9128 }
9129#endif /* VBOX_WITH_GUEST_PROPS defined */
9130
9131 rc = i_loadDebugging(pDbg);
9132 if (FAILED(rc))
9133 return rc;
9134
9135 mHWData->mAutostart = *pAutostart;
9136
9137 /* default frontend */
9138 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9139 }
9140 catch (std::bad_alloc &)
9141 {
9142 return E_OUTOFMEMORY;
9143 }
9144
9145 AssertComRC(rc);
9146 return rc;
9147}
9148
9149/**
9150 * Called from i_loadHardware() to load the debugging settings of the
9151 * machine.
9152 *
9153 * @param pDbg Pointer to the settings.
9154 */
9155HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9156{
9157 mHWData->mDebugging = *pDbg;
9158 /* no more processing currently required, this will probably change. */
9159 return S_OK;
9160}
9161
9162/**
9163 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9164 *
9165 * @param data storage settings.
9166 * @param puuidRegistry media registry ID to set media to or NULL;
9167 * see Machine::i_loadMachineDataFromSettings()
9168 * @param puuidSnapshot snapshot ID
9169 * @return
9170 */
9171HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9172 const Guid *puuidRegistry,
9173 const Guid *puuidSnapshot)
9174{
9175 AssertReturn(!i_isSessionMachine(), E_FAIL);
9176
9177 HRESULT rc = S_OK;
9178
9179 for (settings::StorageControllersList::const_iterator
9180 it = data.llStorageControllers.begin();
9181 it != data.llStorageControllers.end();
9182 ++it)
9183 {
9184 const settings::StorageController &ctlData = *it;
9185
9186 ComObjPtr<StorageController> pCtl;
9187 /* Try to find one with the name first. */
9188 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9189 if (SUCCEEDED(rc))
9190 return setError(VBOX_E_OBJECT_IN_USE,
9191 tr("Storage controller named '%s' already exists"),
9192 ctlData.strName.c_str());
9193
9194 pCtl.createObject();
9195 rc = pCtl->init(this,
9196 ctlData.strName,
9197 ctlData.storageBus,
9198 ctlData.ulInstance,
9199 ctlData.fBootable);
9200 if (FAILED(rc)) return rc;
9201
9202 mStorageControllers->push_back(pCtl);
9203
9204 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9205 if (FAILED(rc)) return rc;
9206
9207 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9208 if (FAILED(rc)) return rc;
9209
9210 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9211 if (FAILED(rc)) return rc;
9212
9213 /* Load the attached devices now. */
9214 rc = i_loadStorageDevices(pCtl,
9215 ctlData,
9216 puuidRegistry,
9217 puuidSnapshot);
9218 if (FAILED(rc)) return rc;
9219 }
9220
9221 return S_OK;
9222}
9223
9224/**
9225 * Called from i_loadStorageControllers for a controller's devices.
9226 *
9227 * @param aStorageController
9228 * @param data
9229 * @param puuidRegistry media registry ID to set media to or NULL; see
9230 * Machine::i_loadMachineDataFromSettings()
9231 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9232 * @return
9233 */
9234HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9235 const settings::StorageController &data,
9236 const Guid *puuidRegistry,
9237 const Guid *puuidSnapshot)
9238{
9239 HRESULT rc = S_OK;
9240
9241 /* paranoia: detect duplicate attachments */
9242 for (settings::AttachedDevicesList::const_iterator
9243 it = data.llAttachedDevices.begin();
9244 it != data.llAttachedDevices.end();
9245 ++it)
9246 {
9247 const settings::AttachedDevice &ad = *it;
9248
9249 for (settings::AttachedDevicesList::const_iterator it2 = it;
9250 it2 != data.llAttachedDevices.end();
9251 ++it2)
9252 {
9253 if (it == it2)
9254 continue;
9255
9256 const settings::AttachedDevice &ad2 = *it2;
9257
9258 if ( ad.lPort == ad2.lPort
9259 && ad.lDevice == ad2.lDevice)
9260 {
9261 return setError(E_FAIL,
9262 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9263 aStorageController->i_getName().c_str(),
9264 ad.lPort,
9265 ad.lDevice,
9266 mUserData->s.strName.c_str());
9267 }
9268 }
9269 }
9270
9271 for (settings::AttachedDevicesList::const_iterator
9272 it = data.llAttachedDevices.begin();
9273 it != data.llAttachedDevices.end();
9274 ++it)
9275 {
9276 const settings::AttachedDevice &dev = *it;
9277 ComObjPtr<Medium> medium;
9278
9279 switch (dev.deviceType)
9280 {
9281 case DeviceType_Floppy:
9282 case DeviceType_DVD:
9283 if (dev.strHostDriveSrc.isNotEmpty())
9284 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9285 false /* fRefresh */, medium);
9286 else
9287 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9288 dev.uuid,
9289 false /* fRefresh */,
9290 false /* aSetError */,
9291 medium);
9292 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9293 // This is not an error. The host drive or UUID might have vanished, so just go
9294 // ahead without this removeable medium attachment
9295 rc = S_OK;
9296 break;
9297
9298 case DeviceType_HardDisk:
9299 {
9300 /* find a hard disk by UUID */
9301 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9302 if (FAILED(rc))
9303 {
9304 if (i_isSnapshotMachine())
9305 {
9306 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9307 // so the user knows that the bad disk is in a snapshot somewhere
9308 com::ErrorInfo info;
9309 return setError(E_FAIL,
9310 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9311 puuidSnapshot->raw(),
9312 info.getText().raw());
9313 }
9314 else
9315 return rc;
9316 }
9317
9318 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9319
9320 if (medium->i_getType() == MediumType_Immutable)
9321 {
9322 if (i_isSnapshotMachine())
9323 return setError(E_FAIL,
9324 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9325 "of the virtual machine '%s' ('%s')"),
9326 medium->i_getLocationFull().c_str(),
9327 dev.uuid.raw(),
9328 puuidSnapshot->raw(),
9329 mUserData->s.strName.c_str(),
9330 mData->m_strConfigFileFull.c_str());
9331
9332 return setError(E_FAIL,
9333 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9334 medium->i_getLocationFull().c_str(),
9335 dev.uuid.raw(),
9336 mUserData->s.strName.c_str(),
9337 mData->m_strConfigFileFull.c_str());
9338 }
9339
9340 if (medium->i_getType() == MediumType_MultiAttach)
9341 {
9342 if (i_isSnapshotMachine())
9343 return setError(E_FAIL,
9344 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9345 "of the virtual machine '%s' ('%s')"),
9346 medium->i_getLocationFull().c_str(),
9347 dev.uuid.raw(),
9348 puuidSnapshot->raw(),
9349 mUserData->s.strName.c_str(),
9350 mData->m_strConfigFileFull.c_str());
9351
9352 return setError(E_FAIL,
9353 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9354 medium->i_getLocationFull().c_str(),
9355 dev.uuid.raw(),
9356 mUserData->s.strName.c_str(),
9357 mData->m_strConfigFileFull.c_str());
9358 }
9359
9360 if ( !i_isSnapshotMachine()
9361 && medium->i_getChildren().size() != 0
9362 )
9363 return setError(E_FAIL,
9364 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9365 "because it has %d differencing child hard disks"),
9366 medium->i_getLocationFull().c_str(),
9367 dev.uuid.raw(),
9368 mUserData->s.strName.c_str(),
9369 mData->m_strConfigFileFull.c_str(),
9370 medium->i_getChildren().size());
9371
9372 if (i_findAttachment(*mMediumAttachments.data(),
9373 medium))
9374 return setError(E_FAIL,
9375 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9376 medium->i_getLocationFull().c_str(),
9377 dev.uuid.raw(),
9378 mUserData->s.strName.c_str(),
9379 mData->m_strConfigFileFull.c_str());
9380
9381 break;
9382 }
9383
9384 default:
9385 return setError(E_FAIL,
9386 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9387 medium->i_getLocationFull().c_str(),
9388 mUserData->s.strName.c_str(),
9389 mData->m_strConfigFileFull.c_str());
9390 }
9391
9392 if (FAILED(rc))
9393 break;
9394
9395 /* Bandwidth groups are loaded at this point. */
9396 ComObjPtr<BandwidthGroup> pBwGroup;
9397
9398 if (!dev.strBwGroup.isEmpty())
9399 {
9400 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9401 if (FAILED(rc))
9402 return setError(E_FAIL,
9403 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9404 medium->i_getLocationFull().c_str(),
9405 dev.strBwGroup.c_str(),
9406 mUserData->s.strName.c_str(),
9407 mData->m_strConfigFileFull.c_str());
9408 pBwGroup->i_reference();
9409 }
9410
9411 const Utf8Str controllerName = aStorageController->i_getName();
9412 ComObjPtr<MediumAttachment> pAttachment;
9413 pAttachment.createObject();
9414 rc = pAttachment->init(this,
9415 medium,
9416 controllerName,
9417 dev.lPort,
9418 dev.lDevice,
9419 dev.deviceType,
9420 false,
9421 dev.fPassThrough,
9422 dev.fTempEject,
9423 dev.fNonRotational,
9424 dev.fDiscard,
9425 dev.fHotPluggable,
9426 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9427 if (FAILED(rc)) break;
9428
9429 /* associate the medium with this machine and snapshot */
9430 if (!medium.isNull())
9431 {
9432 AutoCaller medCaller(medium);
9433 if (FAILED(medCaller.rc())) return medCaller.rc();
9434 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9435
9436 if (i_isSnapshotMachine())
9437 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9438 else
9439 rc = medium->i_addBackReference(mData->mUuid);
9440 /* If the medium->addBackReference fails it sets an appropriate
9441 * error message, so no need to do any guesswork here. */
9442
9443 if (puuidRegistry)
9444 // caller wants registry ID to be set on all attached media (OVF import case)
9445 medium->i_addRegistry(*puuidRegistry);
9446 }
9447
9448 if (FAILED(rc))
9449 break;
9450
9451 /* back up mMediumAttachments to let registeredInit() properly rollback
9452 * on failure (= limited accessibility) */
9453 i_setModified(IsModified_Storage);
9454 mMediumAttachments.backup();
9455 mMediumAttachments->push_back(pAttachment);
9456 }
9457
9458 return rc;
9459}
9460
9461/**
9462 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9463 *
9464 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9465 * @param aSnapshot where to return the found snapshot
9466 * @param aSetError true to set extended error info on failure
9467 */
9468HRESULT Machine::i_findSnapshotById(const Guid &aId,
9469 ComObjPtr<Snapshot> &aSnapshot,
9470 bool aSetError /* = false */)
9471{
9472 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9473
9474 if (!mData->mFirstSnapshot)
9475 {
9476 if (aSetError)
9477 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9478 return E_FAIL;
9479 }
9480
9481 if (aId.isZero())
9482 aSnapshot = mData->mFirstSnapshot;
9483 else
9484 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9485
9486 if (!aSnapshot)
9487 {
9488 if (aSetError)
9489 return setError(E_FAIL,
9490 tr("Could not find a snapshot with UUID {%s}"),
9491 aId.toString().c_str());
9492 return E_FAIL;
9493 }
9494
9495 return S_OK;
9496}
9497
9498/**
9499 * Returns the snapshot with the given name or fails of no such snapshot.
9500 *
9501 * @param strName snapshot name to find
9502 * @param aSnapshot where to return the found snapshot
9503 * @param aSetError true to set extended error info on failure
9504 */
9505HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9506 ComObjPtr<Snapshot> &aSnapshot,
9507 bool aSetError /* = false */)
9508{
9509 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9510
9511 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9512
9513 if (!mData->mFirstSnapshot)
9514 {
9515 if (aSetError)
9516 return setError(VBOX_E_OBJECT_NOT_FOUND,
9517 tr("This machine does not have any snapshots"));
9518 return VBOX_E_OBJECT_NOT_FOUND;
9519 }
9520
9521 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9522
9523 if (!aSnapshot)
9524 {
9525 if (aSetError)
9526 return setError(VBOX_E_OBJECT_NOT_FOUND,
9527 tr("Could not find a snapshot named '%s'"), strName.c_str());
9528 return VBOX_E_OBJECT_NOT_FOUND;
9529 }
9530
9531 return S_OK;
9532}
9533
9534/**
9535 * Returns a storage controller object with the given name.
9536 *
9537 * @param aName storage controller name to find
9538 * @param aStorageController where to return the found storage controller
9539 * @param aSetError true to set extended error info on failure
9540 */
9541HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9542 ComObjPtr<StorageController> &aStorageController,
9543 bool aSetError /* = false */)
9544{
9545 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9546
9547 for (StorageControllerList::const_iterator
9548 it = mStorageControllers->begin();
9549 it != mStorageControllers->end();
9550 ++it)
9551 {
9552 if ((*it)->i_getName() == aName)
9553 {
9554 aStorageController = (*it);
9555 return S_OK;
9556 }
9557 }
9558
9559 if (aSetError)
9560 return setError(VBOX_E_OBJECT_NOT_FOUND,
9561 tr("Could not find a storage controller named '%s'"),
9562 aName.c_str());
9563 return VBOX_E_OBJECT_NOT_FOUND;
9564}
9565
9566/**
9567 * Returns a USB controller object with the given name.
9568 *
9569 * @param aName USB controller name to find
9570 * @param aUSBController where to return the found USB controller
9571 * @param aSetError true to set extended error info on failure
9572 */
9573HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9574 ComObjPtr<USBController> &aUSBController,
9575 bool aSetError /* = false */)
9576{
9577 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9578
9579 for (USBControllerList::const_iterator
9580 it = mUSBControllers->begin();
9581 it != mUSBControllers->end();
9582 ++it)
9583 {
9584 if ((*it)->i_getName() == aName)
9585 {
9586 aUSBController = (*it);
9587 return S_OK;
9588 }
9589 }
9590
9591 if (aSetError)
9592 return setError(VBOX_E_OBJECT_NOT_FOUND,
9593 tr("Could not find a storage controller named '%s'"),
9594 aName.c_str());
9595 return VBOX_E_OBJECT_NOT_FOUND;
9596}
9597
9598/**
9599 * Returns the number of USB controller instance of the given type.
9600 *
9601 * @param enmType USB controller type.
9602 */
9603ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9604{
9605 ULONG cCtrls = 0;
9606
9607 for (USBControllerList::const_iterator
9608 it = mUSBControllers->begin();
9609 it != mUSBControllers->end();
9610 ++it)
9611 {
9612 if ((*it)->i_getControllerType() == enmType)
9613 cCtrls++;
9614 }
9615
9616 return cCtrls;
9617}
9618
9619HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9620 MediumAttachmentList &atts)
9621{
9622 AutoCaller autoCaller(this);
9623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9624
9625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9626
9627 for (MediumAttachmentList::const_iterator
9628 it = mMediumAttachments->begin();
9629 it != mMediumAttachments->end();
9630 ++it)
9631 {
9632 const ComObjPtr<MediumAttachment> &pAtt = *it;
9633 // should never happen, but deal with NULL pointers in the list.
9634 AssertContinue(!pAtt.isNull());
9635
9636 // getControllerName() needs caller+read lock
9637 AutoCaller autoAttCaller(pAtt);
9638 if (FAILED(autoAttCaller.rc()))
9639 {
9640 atts.clear();
9641 return autoAttCaller.rc();
9642 }
9643 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9644
9645 if (pAtt->i_getControllerName() == aName)
9646 atts.push_back(pAtt);
9647 }
9648
9649 return S_OK;
9650}
9651
9652
9653/**
9654 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9655 * file if the machine name was changed and about creating a new settings file
9656 * if this is a new machine.
9657 *
9658 * @note Must be never called directly but only from #saveSettings().
9659 */
9660HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9661{
9662 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9663
9664 HRESULT rc = S_OK;
9665
9666 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9667
9668 /// @todo need to handle primary group change, too
9669
9670 /* attempt to rename the settings file if machine name is changed */
9671 if ( mUserData->s.fNameSync
9672 && mUserData.isBackedUp()
9673 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9674 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9675 )
9676 {
9677 bool dirRenamed = false;
9678 bool fileRenamed = false;
9679
9680 Utf8Str configFile, newConfigFile;
9681 Utf8Str configFilePrev, newConfigFilePrev;
9682 Utf8Str configDir, newConfigDir;
9683
9684 do
9685 {
9686 int vrc = VINF_SUCCESS;
9687
9688 Utf8Str name = mUserData.backedUpData()->s.strName;
9689 Utf8Str newName = mUserData->s.strName;
9690 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9691 if (group == "/")
9692 group.setNull();
9693 Utf8Str newGroup = mUserData->s.llGroups.front();
9694 if (newGroup == "/")
9695 newGroup.setNull();
9696
9697 configFile = mData->m_strConfigFileFull;
9698
9699 /* first, rename the directory if it matches the group and machine name */
9700 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9701 group.c_str(), RTPATH_DELIMITER, name.c_str());
9702 /** @todo hack, make somehow use of ComposeMachineFilename */
9703 if (mUserData->s.fDirectoryIncludesUUID)
9704 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9705 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9706 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9707 /** @todo hack, make somehow use of ComposeMachineFilename */
9708 if (mUserData->s.fDirectoryIncludesUUID)
9709 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9710 configDir = configFile;
9711 configDir.stripFilename();
9712 newConfigDir = configDir;
9713 if ( configDir.length() >= groupPlusName.length()
9714 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9715 groupPlusName.c_str()))
9716 {
9717 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9718 Utf8Str newConfigBaseDir(newConfigDir);
9719 newConfigDir.append(newGroupPlusName);
9720 /* consistency: use \ if appropriate on the platform */
9721 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9722 /* new dir and old dir cannot be equal here because of 'if'
9723 * above and because name != newName */
9724 Assert(configDir != newConfigDir);
9725 if (!fSettingsFileIsNew)
9726 {
9727 /* perform real rename only if the machine is not new */
9728 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9729 if ( vrc == VERR_FILE_NOT_FOUND
9730 || vrc == VERR_PATH_NOT_FOUND)
9731 {
9732 /* create the parent directory, then retry renaming */
9733 Utf8Str parent(newConfigDir);
9734 parent.stripFilename();
9735 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9736 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9737 }
9738 if (RT_FAILURE(vrc))
9739 {
9740 rc = setErrorBoth(E_FAIL, vrc,
9741 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9742 configDir.c_str(),
9743 newConfigDir.c_str(),
9744 vrc);
9745 break;
9746 }
9747 /* delete subdirectories which are no longer needed */
9748 Utf8Str dir(configDir);
9749 dir.stripFilename();
9750 while (dir != newConfigBaseDir && dir != ".")
9751 {
9752 vrc = RTDirRemove(dir.c_str());
9753 if (RT_FAILURE(vrc))
9754 break;
9755 dir.stripFilename();
9756 }
9757 dirRenamed = true;
9758 }
9759 }
9760
9761 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9762 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9763
9764 /* then try to rename the settings file itself */
9765 if (newConfigFile != configFile)
9766 {
9767 /* get the path to old settings file in renamed directory */
9768 configFile = Utf8StrFmt("%s%c%s",
9769 newConfigDir.c_str(),
9770 RTPATH_DELIMITER,
9771 RTPathFilename(configFile.c_str()));
9772 if (!fSettingsFileIsNew)
9773 {
9774 /* perform real rename only if the machine is not new */
9775 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9776 if (RT_FAILURE(vrc))
9777 {
9778 rc = setErrorBoth(E_FAIL, vrc,
9779 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9780 configFile.c_str(),
9781 newConfigFile.c_str(),
9782 vrc);
9783 break;
9784 }
9785 fileRenamed = true;
9786 configFilePrev = configFile;
9787 configFilePrev += "-prev";
9788 newConfigFilePrev = newConfigFile;
9789 newConfigFilePrev += "-prev";
9790 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9791 }
9792 }
9793
9794 // update m_strConfigFileFull amd mConfigFile
9795 mData->m_strConfigFileFull = newConfigFile;
9796 // compute the relative path too
9797 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9798
9799 // store the old and new so that VirtualBox::i_saveSettings() can update
9800 // the media registry
9801 if ( mData->mRegistered
9802 && (configDir != newConfigDir || configFile != newConfigFile))
9803 {
9804 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9805
9806 if (pfNeedsGlobalSaveSettings)
9807 *pfNeedsGlobalSaveSettings = true;
9808 }
9809
9810 // in the saved state file path, replace the old directory with the new directory
9811 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9812 {
9813 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9814 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9815 }
9816
9817 // and do the same thing for the saved state file paths of all the online snapshots
9818 if (mData->mFirstSnapshot)
9819 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9820 newConfigDir.c_str());
9821 }
9822 while (0);
9823
9824 if (FAILED(rc))
9825 {
9826 /* silently try to rename everything back */
9827 if (fileRenamed)
9828 {
9829 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9830 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9831 }
9832 if (dirRenamed)
9833 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9834 }
9835
9836 if (FAILED(rc)) return rc;
9837 }
9838
9839 if (fSettingsFileIsNew)
9840 {
9841 /* create a virgin config file */
9842 int vrc = VINF_SUCCESS;
9843
9844 /* ensure the settings directory exists */
9845 Utf8Str path(mData->m_strConfigFileFull);
9846 path.stripFilename();
9847 if (!RTDirExists(path.c_str()))
9848 {
9849 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9850 if (RT_FAILURE(vrc))
9851 {
9852 return setErrorBoth(E_FAIL, vrc,
9853 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9854 path.c_str(),
9855 vrc);
9856 }
9857 }
9858
9859 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9860 path = Utf8Str(mData->m_strConfigFileFull);
9861 RTFILE f = NIL_RTFILE;
9862 vrc = RTFileOpen(&f, path.c_str(),
9863 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9864 if (RT_FAILURE(vrc))
9865 return setErrorBoth(E_FAIL, vrc,
9866 tr("Could not create the settings file '%s' (%Rrc)"),
9867 path.c_str(),
9868 vrc);
9869 RTFileClose(f);
9870 }
9871
9872 return rc;
9873}
9874
9875/**
9876 * Saves and commits machine data, user data and hardware data.
9877 *
9878 * Note that on failure, the data remains uncommitted.
9879 *
9880 * @a aFlags may combine the following flags:
9881 *
9882 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9883 * Used when saving settings after an operation that makes them 100%
9884 * correspond to the settings from the current snapshot.
9885 * - SaveS_Force: settings will be saved without doing a deep compare of the
9886 * settings structures. This is used when this is called because snapshots
9887 * have changed to avoid the overhead of the deep compare.
9888 *
9889 * @note Must be called from under this object's write lock. Locks children for
9890 * writing.
9891 *
9892 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9893 * initialized to false and that will be set to true by this function if
9894 * the caller must invoke VirtualBox::i_saveSettings() because the global
9895 * settings have changed. This will happen if a machine rename has been
9896 * saved and the global machine and media registries will therefore need
9897 * updating.
9898 * @param aFlags Flags.
9899 */
9900HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9901 int aFlags /*= 0*/)
9902{
9903 LogFlowThisFuncEnter();
9904
9905 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9906
9907 /* make sure child objects are unable to modify the settings while we are
9908 * saving them */
9909 i_ensureNoStateDependencies();
9910
9911 AssertReturn(!i_isSnapshotMachine(),
9912 E_FAIL);
9913
9914 HRESULT rc = S_OK;
9915 bool fNeedsWrite = false;
9916
9917 /* First, prepare to save settings. It will care about renaming the
9918 * settings directory and file if the machine name was changed and about
9919 * creating a new settings file if this is a new machine. */
9920 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9921 if (FAILED(rc)) return rc;
9922
9923 // keep a pointer to the current settings structures
9924 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9925 settings::MachineConfigFile *pNewConfig = NULL;
9926
9927 try
9928 {
9929 // make a fresh one to have everyone write stuff into
9930 pNewConfig = new settings::MachineConfigFile(NULL);
9931 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9932
9933 // now go and copy all the settings data from COM to the settings structures
9934 // (this calls i_saveSettings() on all the COM objects in the machine)
9935 i_copyMachineDataToSettings(*pNewConfig);
9936
9937 if (aFlags & SaveS_ResetCurStateModified)
9938 {
9939 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9940 mData->mCurrentStateModified = FALSE;
9941 fNeedsWrite = true; // always, no need to compare
9942 }
9943 else if (aFlags & SaveS_Force)
9944 {
9945 fNeedsWrite = true; // always, no need to compare
9946 }
9947 else
9948 {
9949 if (!mData->mCurrentStateModified)
9950 {
9951 // do a deep compare of the settings that we just saved with the settings
9952 // previously stored in the config file; this invokes MachineConfigFile::operator==
9953 // which does a deep compare of all the settings, which is expensive but less expensive
9954 // than writing out XML in vain
9955 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9956
9957 // could still be modified if any settings changed
9958 mData->mCurrentStateModified = fAnySettingsChanged;
9959
9960 fNeedsWrite = fAnySettingsChanged;
9961 }
9962 else
9963 fNeedsWrite = true;
9964 }
9965
9966 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9967
9968 if (fNeedsWrite)
9969 // now spit it all out!
9970 pNewConfig->write(mData->m_strConfigFileFull);
9971
9972 mData->pMachineConfigFile = pNewConfig;
9973 delete pOldConfig;
9974 i_commit();
9975
9976 // after saving settings, we are no longer different from the XML on disk
9977 mData->flModifications = 0;
9978 }
9979 catch (HRESULT err)
9980 {
9981 // we assume that error info is set by the thrower
9982 rc = err;
9983
9984 // restore old config
9985 delete pNewConfig;
9986 mData->pMachineConfigFile = pOldConfig;
9987 }
9988 catch (...)
9989 {
9990 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9991 }
9992
9993 if (fNeedsWrite)
9994 {
9995 /* Fire the data change event, even on failure (since we've already
9996 * committed all data). This is done only for SessionMachines because
9997 * mutable Machine instances are always not registered (i.e. private
9998 * to the client process that creates them) and thus don't need to
9999 * inform callbacks. */
10000 if (i_isSessionMachine())
10001 mParent->i_onMachineDataChange(mData->mUuid);
10002 }
10003
10004 LogFlowThisFunc(("rc=%08X\n", rc));
10005 LogFlowThisFuncLeave();
10006 return rc;
10007}
10008
10009/**
10010 * Implementation for saving the machine settings into the given
10011 * settings::MachineConfigFile instance. This copies machine extradata
10012 * from the previous machine config file in the instance data, if any.
10013 *
10014 * This gets called from two locations:
10015 *
10016 * -- Machine::i_saveSettings(), during the regular XML writing;
10017 *
10018 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10019 * exported to OVF and we write the VirtualBox proprietary XML
10020 * into a <vbox:Machine> tag.
10021 *
10022 * This routine fills all the fields in there, including snapshots, *except*
10023 * for the following:
10024 *
10025 * -- fCurrentStateModified. There is some special logic associated with that.
10026 *
10027 * The caller can then call MachineConfigFile::write() or do something else
10028 * with it.
10029 *
10030 * Caller must hold the machine lock!
10031 *
10032 * This throws XML errors and HRESULT, so the caller must have a catch block!
10033 */
10034void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10035{
10036 // deep copy extradata, being extra careful with self assignment (the STL
10037 // map assignment on Mac OS X clang based Xcode isn't checking)
10038 if (&config != mData->pMachineConfigFile)
10039 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10040
10041 config.uuid = mData->mUuid;
10042
10043 // copy name, description, OS type, teleport, UTC etc.
10044 config.machineUserData = mUserData->s;
10045
10046 if ( mData->mMachineState == MachineState_Saved
10047 || mData->mMachineState == MachineState_Restoring
10048 // when doing certain snapshot operations we may or may not have
10049 // a saved state in the current state, so keep everything as is
10050 || ( ( mData->mMachineState == MachineState_Snapshotting
10051 || mData->mMachineState == MachineState_DeletingSnapshot
10052 || mData->mMachineState == MachineState_RestoringSnapshot)
10053 && (!mSSData->strStateFilePath.isEmpty())
10054 )
10055 )
10056 {
10057 Assert(!mSSData->strStateFilePath.isEmpty());
10058 /* try to make the file name relative to the settings file dir */
10059 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10060 }
10061 else
10062 {
10063 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10064 config.strStateFile.setNull();
10065 }
10066
10067 if (mData->mCurrentSnapshot)
10068 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10069 else
10070 config.uuidCurrentSnapshot.clear();
10071
10072 config.timeLastStateChange = mData->mLastStateChange;
10073 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10074 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10075
10076 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10077 if (FAILED(rc)) throw rc;
10078
10079 // save machine's media registry if this is VirtualBox 4.0 or later
10080 if (config.canHaveOwnMediaRegistry())
10081 {
10082 // determine machine folder
10083 Utf8Str strMachineFolder = i_getSettingsFileFull();
10084 strMachineFolder.stripFilename();
10085 mParent->i_saveMediaRegistry(config.mediaRegistry,
10086 i_getId(), // only media with registry ID == machine UUID
10087 strMachineFolder);
10088 // this throws HRESULT
10089 }
10090
10091 // save snapshots
10092 rc = i_saveAllSnapshots(config);
10093 if (FAILED(rc)) throw rc;
10094}
10095
10096/**
10097 * Saves all snapshots of the machine into the given machine config file. Called
10098 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10099 * @param config
10100 * @return
10101 */
10102HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10103{
10104 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10105
10106 HRESULT rc = S_OK;
10107
10108 try
10109 {
10110 config.llFirstSnapshot.clear();
10111
10112 if (mData->mFirstSnapshot)
10113 {
10114 // the settings use a list for "the first snapshot"
10115 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10116
10117 // get reference to the snapshot on the list and work on that
10118 // element straight in the list to avoid excessive copying later
10119 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10120 if (FAILED(rc)) throw rc;
10121 }
10122
10123// if (mType == IsSessionMachine)
10124// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10125
10126 }
10127 catch (HRESULT err)
10128 {
10129 /* we assume that error info is set by the thrower */
10130 rc = err;
10131 }
10132 catch (...)
10133 {
10134 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10135 }
10136
10137 return rc;
10138}
10139
10140/**
10141 * Saves the VM hardware configuration. It is assumed that the
10142 * given node is empty.
10143 *
10144 * @param data Reference to the settings object for the hardware config.
10145 * @param pDbg Pointer to the settings object for the debugging config
10146 * which happens to live in mHWData.
10147 * @param pAutostart Pointer to the settings object for the autostart config
10148 * which happens to live in mHWData.
10149 */
10150HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10151 settings::Autostart *pAutostart)
10152{
10153 HRESULT rc = S_OK;
10154
10155 try
10156 {
10157 /* The hardware version attribute (optional).
10158 Automatically upgrade from 1 to current default hardware version
10159 when there is no saved state. (ugly!) */
10160 if ( mHWData->mHWVersion == "1"
10161 && mSSData->strStateFilePath.isEmpty()
10162 )
10163 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10164
10165 data.strVersion = mHWData->mHWVersion;
10166 data.uuid = mHWData->mHardwareUUID;
10167
10168 // CPU
10169 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10170 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10171 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10172 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10173 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10174 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10175 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10176 data.fPAE = !!mHWData->mPAEEnabled;
10177 data.enmLongMode = mHWData->mLongMode;
10178 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10179 data.fAPIC = !!mHWData->mAPIC;
10180 data.fX2APIC = !!mHWData->mX2APIC;
10181 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10182 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10183 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10184 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10185 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10186 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10187 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10188 data.cCPUs = mHWData->mCPUCount;
10189 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10190 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10191 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10192 data.strCpuProfile = mHWData->mCpuProfile;
10193
10194 data.llCpus.clear();
10195 if (data.fCpuHotPlug)
10196 {
10197 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10198 {
10199 if (mHWData->mCPUAttached[idx])
10200 {
10201 settings::Cpu cpu;
10202 cpu.ulId = idx;
10203 data.llCpus.push_back(cpu);
10204 }
10205 }
10206 }
10207
10208 /* Standard and Extended CPUID leafs. */
10209 data.llCpuIdLeafs.clear();
10210 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10211
10212 // memory
10213 data.ulMemorySizeMB = mHWData->mMemorySize;
10214 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10215
10216 // firmware
10217 data.firmwareType = mHWData->mFirmwareType;
10218
10219 // HID
10220 data.pointingHIDType = mHWData->mPointingHIDType;
10221 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10222
10223 // chipset
10224 data.chipsetType = mHWData->mChipsetType;
10225
10226 // paravirt
10227 data.paravirtProvider = mHWData->mParavirtProvider;
10228 data.strParavirtDebug = mHWData->mParavirtDebug;
10229
10230 // emulated USB card reader
10231 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10232
10233 // HPET
10234 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10235
10236 // boot order
10237 data.mapBootOrder.clear();
10238 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10239 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10240
10241 // display
10242 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10243 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10244 data.cMonitors = mHWData->mMonitorCount;
10245 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10246 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10247
10248 /* VRDEServer settings (optional) */
10249 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10250 if (FAILED(rc)) throw rc;
10251
10252 /* BIOS settings (required) */
10253 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10254 if (FAILED(rc)) throw rc;
10255
10256 /* Recording settings (required) */
10257 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10258 if (FAILED(rc)) throw rc;
10259
10260 /* USB Controller (required) */
10261 data.usbSettings.llUSBControllers.clear();
10262 for (USBControllerList::const_iterator
10263 it = mUSBControllers->begin();
10264 it != mUSBControllers->end();
10265 ++it)
10266 {
10267 ComObjPtr<USBController> ctrl = *it;
10268 settings::USBController settingsCtrl;
10269
10270 settingsCtrl.strName = ctrl->i_getName();
10271 settingsCtrl.enmType = ctrl->i_getControllerType();
10272
10273 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10274 }
10275
10276 /* USB device filters (required) */
10277 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10278 if (FAILED(rc)) throw rc;
10279
10280 /* Network adapters (required) */
10281 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10282 data.llNetworkAdapters.clear();
10283 /* Write out only the nominal number of network adapters for this
10284 * chipset type. Since Machine::commit() hasn't been called there
10285 * may be extra NIC settings in the vector. */
10286 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10287 {
10288 settings::NetworkAdapter nic;
10289 nic.ulSlot = (uint32_t)slot;
10290 /* paranoia check... must not be NULL, but must not crash either. */
10291 if (mNetworkAdapters[slot])
10292 {
10293 if (mNetworkAdapters[slot]->i_hasDefaults())
10294 continue;
10295
10296 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10297 if (FAILED(rc)) throw rc;
10298
10299 data.llNetworkAdapters.push_back(nic);
10300 }
10301 }
10302
10303 /* Serial ports */
10304 data.llSerialPorts.clear();
10305 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10306 {
10307 if (mSerialPorts[slot]->i_hasDefaults())
10308 continue;
10309
10310 settings::SerialPort s;
10311 s.ulSlot = slot;
10312 rc = mSerialPorts[slot]->i_saveSettings(s);
10313 if (FAILED(rc)) return rc;
10314
10315 data.llSerialPorts.push_back(s);
10316 }
10317
10318 /* Parallel ports */
10319 data.llParallelPorts.clear();
10320 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10321 {
10322 if (mParallelPorts[slot]->i_hasDefaults())
10323 continue;
10324
10325 settings::ParallelPort p;
10326 p.ulSlot = slot;
10327 rc = mParallelPorts[slot]->i_saveSettings(p);
10328 if (FAILED(rc)) return rc;
10329
10330 data.llParallelPorts.push_back(p);
10331 }
10332
10333 /* Audio adapter */
10334 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10335 if (FAILED(rc)) return rc;
10336
10337 rc = i_saveStorageControllers(data.storage);
10338 if (FAILED(rc)) return rc;
10339
10340 /* Shared folders */
10341 data.llSharedFolders.clear();
10342 for (HWData::SharedFolderList::const_iterator
10343 it = mHWData->mSharedFolders.begin();
10344 it != mHWData->mSharedFolders.end();
10345 ++it)
10346 {
10347 SharedFolder *pSF = *it;
10348 AutoCaller sfCaller(pSF);
10349 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10350 settings::SharedFolder sf;
10351 sf.strName = pSF->i_getName();
10352 sf.strHostPath = pSF->i_getHostPath();
10353 sf.fWritable = !!pSF->i_isWritable();
10354 sf.fAutoMount = !!pSF->i_isAutoMounted();
10355 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10356
10357 data.llSharedFolders.push_back(sf);
10358 }
10359
10360 // clipboard
10361 data.clipboardMode = mHWData->mClipboardMode;
10362
10363 // drag'n'drop
10364 data.dndMode = mHWData->mDnDMode;
10365
10366 /* Guest */
10367 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10368
10369 // IO settings
10370 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10371 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10372
10373 /* BandwidthControl (required) */
10374 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10375 if (FAILED(rc)) throw rc;
10376
10377 /* Host PCI devices */
10378 data.pciAttachments.clear();
10379 for (HWData::PCIDeviceAssignmentList::const_iterator
10380 it = mHWData->mPCIDeviceAssignments.begin();
10381 it != mHWData->mPCIDeviceAssignments.end();
10382 ++it)
10383 {
10384 ComObjPtr<PCIDeviceAttachment> pda = *it;
10385 settings::HostPCIDeviceAttachment hpda;
10386
10387 rc = pda->i_saveSettings(hpda);
10388 if (FAILED(rc)) throw rc;
10389
10390 data.pciAttachments.push_back(hpda);
10391 }
10392
10393 // guest properties
10394 data.llGuestProperties.clear();
10395#ifdef VBOX_WITH_GUEST_PROPS
10396 for (HWData::GuestPropertyMap::const_iterator
10397 it = mHWData->mGuestProperties.begin();
10398 it != mHWData->mGuestProperties.end();
10399 ++it)
10400 {
10401 HWData::GuestProperty property = it->second;
10402
10403 /* Remove transient guest properties at shutdown unless we
10404 * are saving state. Note that restoring snapshot intentionally
10405 * keeps them, they will be removed if appropriate once the final
10406 * machine state is set (as crashes etc. need to work). */
10407 if ( ( mData->mMachineState == MachineState_PoweredOff
10408 || mData->mMachineState == MachineState_Aborted
10409 || mData->mMachineState == MachineState_Teleported)
10410 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10411 continue;
10412 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10413 prop.strName = it->first;
10414 prop.strValue = property.strValue;
10415 prop.timestamp = property.mTimestamp;
10416 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10417 GuestPropWriteFlags(property.mFlags, szFlags);
10418 prop.strFlags = szFlags;
10419
10420 data.llGuestProperties.push_back(prop);
10421 }
10422
10423 /* I presume this doesn't require a backup(). */
10424 mData->mGuestPropertiesModified = FALSE;
10425#endif /* VBOX_WITH_GUEST_PROPS defined */
10426
10427 *pDbg = mHWData->mDebugging;
10428 *pAutostart = mHWData->mAutostart;
10429
10430 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10431 }
10432 catch (std::bad_alloc &)
10433 {
10434 return E_OUTOFMEMORY;
10435 }
10436
10437 AssertComRC(rc);
10438 return rc;
10439}
10440
10441/**
10442 * Saves the storage controller configuration.
10443 *
10444 * @param data storage settings.
10445 */
10446HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10447{
10448 data.llStorageControllers.clear();
10449
10450 for (StorageControllerList::const_iterator
10451 it = mStorageControllers->begin();
10452 it != mStorageControllers->end();
10453 ++it)
10454 {
10455 HRESULT rc;
10456 ComObjPtr<StorageController> pCtl = *it;
10457
10458 settings::StorageController ctl;
10459 ctl.strName = pCtl->i_getName();
10460 ctl.controllerType = pCtl->i_getControllerType();
10461 ctl.storageBus = pCtl->i_getStorageBus();
10462 ctl.ulInstance = pCtl->i_getInstance();
10463 ctl.fBootable = pCtl->i_getBootable();
10464
10465 /* Save the port count. */
10466 ULONG portCount;
10467 rc = pCtl->COMGETTER(PortCount)(&portCount);
10468 ComAssertComRCRet(rc, rc);
10469 ctl.ulPortCount = portCount;
10470
10471 /* Save fUseHostIOCache */
10472 BOOL fUseHostIOCache;
10473 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10474 ComAssertComRCRet(rc, rc);
10475 ctl.fUseHostIOCache = !!fUseHostIOCache;
10476
10477 /* save the devices now. */
10478 rc = i_saveStorageDevices(pCtl, ctl);
10479 ComAssertComRCRet(rc, rc);
10480
10481 data.llStorageControllers.push_back(ctl);
10482 }
10483
10484 return S_OK;
10485}
10486
10487/**
10488 * Saves the hard disk configuration.
10489 */
10490HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10491 settings::StorageController &data)
10492{
10493 MediumAttachmentList atts;
10494
10495 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10496 if (FAILED(rc)) return rc;
10497
10498 data.llAttachedDevices.clear();
10499 for (MediumAttachmentList::const_iterator
10500 it = atts.begin();
10501 it != atts.end();
10502 ++it)
10503 {
10504 settings::AttachedDevice dev;
10505 IMediumAttachment *iA = *it;
10506 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10507 Medium *pMedium = pAttach->i_getMedium();
10508
10509 dev.deviceType = pAttach->i_getType();
10510 dev.lPort = pAttach->i_getPort();
10511 dev.lDevice = pAttach->i_getDevice();
10512 dev.fPassThrough = pAttach->i_getPassthrough();
10513 dev.fHotPluggable = pAttach->i_getHotPluggable();
10514 if (pMedium)
10515 {
10516 if (pMedium->i_isHostDrive())
10517 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10518 else
10519 dev.uuid = pMedium->i_getId();
10520 dev.fTempEject = pAttach->i_getTempEject();
10521 dev.fNonRotational = pAttach->i_getNonRotational();
10522 dev.fDiscard = pAttach->i_getDiscard();
10523 }
10524
10525 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10526
10527 data.llAttachedDevices.push_back(dev);
10528 }
10529
10530 return S_OK;
10531}
10532
10533/**
10534 * Saves machine state settings as defined by aFlags
10535 * (SaveSTS_* values).
10536 *
10537 * @param aFlags Combination of SaveSTS_* flags.
10538 *
10539 * @note Locks objects for writing.
10540 */
10541HRESULT Machine::i_saveStateSettings(int aFlags)
10542{
10543 if (aFlags == 0)
10544 return S_OK;
10545
10546 AutoCaller autoCaller(this);
10547 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10548
10549 /* This object's write lock is also necessary to serialize file access
10550 * (prevent concurrent reads and writes) */
10551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10552
10553 HRESULT rc = S_OK;
10554
10555 Assert(mData->pMachineConfigFile);
10556
10557 try
10558 {
10559 if (aFlags & SaveSTS_CurStateModified)
10560 mData->pMachineConfigFile->fCurrentStateModified = true;
10561
10562 if (aFlags & SaveSTS_StateFilePath)
10563 {
10564 if (!mSSData->strStateFilePath.isEmpty())
10565 /* try to make the file name relative to the settings file dir */
10566 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10567 else
10568 mData->pMachineConfigFile->strStateFile.setNull();
10569 }
10570
10571 if (aFlags & SaveSTS_StateTimeStamp)
10572 {
10573 Assert( mData->mMachineState != MachineState_Aborted
10574 || mSSData->strStateFilePath.isEmpty());
10575
10576 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10577
10578 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10579/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10580 }
10581
10582 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10583 }
10584 catch (...)
10585 {
10586 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10587 }
10588
10589 return rc;
10590}
10591
10592/**
10593 * Ensures that the given medium is added to a media registry. If this machine
10594 * was created with 4.0 or later, then the machine registry is used. Otherwise
10595 * the global VirtualBox media registry is used.
10596 *
10597 * Caller must NOT hold machine lock, media tree or any medium locks!
10598 *
10599 * @param pMedium
10600 */
10601void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10602{
10603 /* Paranoia checks: do not hold machine or media tree locks. */
10604 AssertReturnVoid(!isWriteLockOnCurrentThread());
10605 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10606
10607 ComObjPtr<Medium> pBase;
10608 {
10609 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10610 pBase = pMedium->i_getBase();
10611 }
10612
10613 /* Paranoia checks: do not hold medium locks. */
10614 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10615 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10616
10617 // decide which medium registry to use now that the medium is attached:
10618 Guid uuid;
10619 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10620 if (fCanHaveOwnMediaRegistry)
10621 // machine XML is VirtualBox 4.0 or higher:
10622 uuid = i_getId(); // machine UUID
10623 else
10624 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10625
10626 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10627 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10628 if (pMedium->i_addRegistry(uuid))
10629 mParent->i_markRegistryModified(uuid);
10630
10631 /* For more complex hard disk structures it can happen that the base
10632 * medium isn't yet associated with any medium registry. Do that now. */
10633 if (pMedium != pBase)
10634 {
10635 /* Tree lock needed by Medium::addRegistry when recursing. */
10636 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10637 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10638 {
10639 treeLock.release();
10640 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10641 treeLock.acquire();
10642 }
10643 if (pBase->i_addRegistryRecursive(uuid))
10644 {
10645 treeLock.release();
10646 mParent->i_markRegistryModified(uuid);
10647 }
10648 }
10649}
10650
10651/**
10652 * Creates differencing hard disks for all normal hard disks attached to this
10653 * machine and a new set of attachments to refer to created disks.
10654 *
10655 * Used when taking a snapshot or when deleting the current state. Gets called
10656 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10657 *
10658 * This method assumes that mMediumAttachments contains the original hard disk
10659 * attachments it needs to create diffs for. On success, these attachments will
10660 * be replaced with the created diffs.
10661 *
10662 * Attachments with non-normal hard disks are left as is.
10663 *
10664 * If @a aOnline is @c false then the original hard disks that require implicit
10665 * diffs will be locked for reading. Otherwise it is assumed that they are
10666 * already locked for writing (when the VM was started). Note that in the latter
10667 * case it is responsibility of the caller to lock the newly created diffs for
10668 * writing if this method succeeds.
10669 *
10670 * @param aProgress Progress object to run (must contain at least as
10671 * many operations left as the number of hard disks
10672 * attached).
10673 * @param aWeight Weight of this operation.
10674 * @param aOnline Whether the VM was online prior to this operation.
10675 *
10676 * @note The progress object is not marked as completed, neither on success nor
10677 * on failure. This is a responsibility of the caller.
10678 *
10679 * @note Locks this object and the media tree for writing.
10680 */
10681HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10682 ULONG aWeight,
10683 bool aOnline)
10684{
10685 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10686
10687 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10688 AssertReturn(!!pProgressControl, E_INVALIDARG);
10689
10690 AutoCaller autoCaller(this);
10691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10692
10693 AutoMultiWriteLock2 alock(this->lockHandle(),
10694 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10695
10696 /* must be in a protective state because we release the lock below */
10697 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10698 || mData->mMachineState == MachineState_OnlineSnapshotting
10699 || mData->mMachineState == MachineState_LiveSnapshotting
10700 || mData->mMachineState == MachineState_RestoringSnapshot
10701 || mData->mMachineState == MachineState_DeletingSnapshot
10702 , E_FAIL);
10703
10704 HRESULT rc = S_OK;
10705
10706 // use appropriate locked media map (online or offline)
10707 MediumLockListMap lockedMediaOffline;
10708 MediumLockListMap *lockedMediaMap;
10709 if (aOnline)
10710 lockedMediaMap = &mData->mSession.mLockedMedia;
10711 else
10712 lockedMediaMap = &lockedMediaOffline;
10713
10714 try
10715 {
10716 if (!aOnline)
10717 {
10718 /* lock all attached hard disks early to detect "in use"
10719 * situations before creating actual diffs */
10720 for (MediumAttachmentList::const_iterator
10721 it = mMediumAttachments->begin();
10722 it != mMediumAttachments->end();
10723 ++it)
10724 {
10725 MediumAttachment *pAtt = *it;
10726 if (pAtt->i_getType() == DeviceType_HardDisk)
10727 {
10728 Medium *pMedium = pAtt->i_getMedium();
10729 Assert(pMedium);
10730
10731 MediumLockList *pMediumLockList(new MediumLockList());
10732 alock.release();
10733 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10734 NULL /* pToLockWrite */,
10735 false /* fMediumLockWriteAll */,
10736 NULL,
10737 *pMediumLockList);
10738 alock.acquire();
10739 if (FAILED(rc))
10740 {
10741 delete pMediumLockList;
10742 throw rc;
10743 }
10744 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10745 if (FAILED(rc))
10746 {
10747 throw setError(rc,
10748 tr("Collecting locking information for all attached media failed"));
10749 }
10750 }
10751 }
10752
10753 /* Now lock all media. If this fails, nothing is locked. */
10754 alock.release();
10755 rc = lockedMediaMap->Lock();
10756 alock.acquire();
10757 if (FAILED(rc))
10758 {
10759 throw setError(rc,
10760 tr("Locking of attached media failed"));
10761 }
10762 }
10763
10764 /* remember the current list (note that we don't use backup() since
10765 * mMediumAttachments may be already backed up) */
10766 MediumAttachmentList atts = *mMediumAttachments.data();
10767
10768 /* start from scratch */
10769 mMediumAttachments->clear();
10770
10771 /* go through remembered attachments and create diffs for normal hard
10772 * disks and attach them */
10773 for (MediumAttachmentList::const_iterator
10774 it = atts.begin();
10775 it != atts.end();
10776 ++it)
10777 {
10778 MediumAttachment *pAtt = *it;
10779
10780 DeviceType_T devType = pAtt->i_getType();
10781 Medium *pMedium = pAtt->i_getMedium();
10782
10783 if ( devType != DeviceType_HardDisk
10784 || pMedium == NULL
10785 || pMedium->i_getType() != MediumType_Normal)
10786 {
10787 /* copy the attachment as is */
10788
10789 /** @todo the progress object created in SessionMachine::TakeSnaphot
10790 * only expects operations for hard disks. Later other
10791 * device types need to show up in the progress as well. */
10792 if (devType == DeviceType_HardDisk)
10793 {
10794 if (pMedium == NULL)
10795 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10796 aWeight); // weight
10797 else
10798 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10799 pMedium->i_getBase()->i_getName().c_str()).raw(),
10800 aWeight); // weight
10801 }
10802
10803 mMediumAttachments->push_back(pAtt);
10804 continue;
10805 }
10806
10807 /* need a diff */
10808 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10809 pMedium->i_getBase()->i_getName().c_str()).raw(),
10810 aWeight); // weight
10811
10812 Utf8Str strFullSnapshotFolder;
10813 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10814
10815 ComObjPtr<Medium> diff;
10816 diff.createObject();
10817 // store the diff in the same registry as the parent
10818 // (this cannot fail here because we can't create implicit diffs for
10819 // unregistered images)
10820 Guid uuidRegistryParent;
10821 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10822 Assert(fInRegistry); NOREF(fInRegistry);
10823 rc = diff->init(mParent,
10824 pMedium->i_getPreferredDiffFormat(),
10825 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10826 uuidRegistryParent,
10827 DeviceType_HardDisk);
10828 if (FAILED(rc)) throw rc;
10829
10830 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10831 * the push_back? Looks like we're going to release medium with the
10832 * wrong kind of lock (general issue with if we fail anywhere at all)
10833 * and an orphaned VDI in the snapshots folder. */
10834
10835 /* update the appropriate lock list */
10836 MediumLockList *pMediumLockList;
10837 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10838 AssertComRCThrowRC(rc);
10839 if (aOnline)
10840 {
10841 alock.release();
10842 /* The currently attached medium will be read-only, change
10843 * the lock type to read. */
10844 rc = pMediumLockList->Update(pMedium, false);
10845 alock.acquire();
10846 AssertComRCThrowRC(rc);
10847 }
10848
10849 /* release the locks before the potentially lengthy operation */
10850 alock.release();
10851 rc = pMedium->i_createDiffStorage(diff,
10852 pMedium->i_getPreferredDiffVariant(),
10853 pMediumLockList,
10854 NULL /* aProgress */,
10855 true /* aWait */,
10856 false /* aNotify */);
10857 alock.acquire();
10858 if (FAILED(rc)) throw rc;
10859
10860 /* actual lock list update is done in Machine::i_commitMedia */
10861
10862 rc = diff->i_addBackReference(mData->mUuid);
10863 AssertComRCThrowRC(rc);
10864
10865 /* add a new attachment */
10866 ComObjPtr<MediumAttachment> attachment;
10867 attachment.createObject();
10868 rc = attachment->init(this,
10869 diff,
10870 pAtt->i_getControllerName(),
10871 pAtt->i_getPort(),
10872 pAtt->i_getDevice(),
10873 DeviceType_HardDisk,
10874 true /* aImplicit */,
10875 false /* aPassthrough */,
10876 false /* aTempEject */,
10877 pAtt->i_getNonRotational(),
10878 pAtt->i_getDiscard(),
10879 pAtt->i_getHotPluggable(),
10880 pAtt->i_getBandwidthGroup());
10881 if (FAILED(rc)) throw rc;
10882
10883 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10884 AssertComRCThrowRC(rc);
10885 mMediumAttachments->push_back(attachment);
10886 }
10887 }
10888 catch (HRESULT aRC) { rc = aRC; }
10889
10890 /* unlock all hard disks we locked when there is no VM */
10891 if (!aOnline)
10892 {
10893 ErrorInfoKeeper eik;
10894
10895 HRESULT rc1 = lockedMediaMap->Clear();
10896 AssertComRC(rc1);
10897 }
10898
10899 return rc;
10900}
10901
10902/**
10903 * Deletes implicit differencing hard disks created either by
10904 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10905 * mMediumAttachments.
10906 *
10907 * Note that to delete hard disks created by #attachDevice() this method is
10908 * called from #i_rollbackMedia() when the changes are rolled back.
10909 *
10910 * @note Locks this object and the media tree for writing.
10911 */
10912HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10913{
10914 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10915
10916 AutoCaller autoCaller(this);
10917 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10918
10919 AutoMultiWriteLock2 alock(this->lockHandle(),
10920 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10921
10922 /* We absolutely must have backed up state. */
10923 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10924
10925 /* Check if there are any implicitly created diff images. */
10926 bool fImplicitDiffs = false;
10927 for (MediumAttachmentList::const_iterator
10928 it = mMediumAttachments->begin();
10929 it != mMediumAttachments->end();
10930 ++it)
10931 {
10932 const ComObjPtr<MediumAttachment> &pAtt = *it;
10933 if (pAtt->i_isImplicit())
10934 {
10935 fImplicitDiffs = true;
10936 break;
10937 }
10938 }
10939 /* If there is nothing to do, leave early. This saves lots of image locking
10940 * effort. It also avoids a MachineStateChanged event without real reason.
10941 * This is important e.g. when loading a VM config, because there should be
10942 * no events. Otherwise API clients can become thoroughly confused for
10943 * inaccessible VMs (the code for loading VM configs uses this method for
10944 * cleanup if the config makes no sense), as they take such events as an
10945 * indication that the VM is alive, and they would force the VM config to
10946 * be reread, leading to an endless loop. */
10947 if (!fImplicitDiffs)
10948 return S_OK;
10949
10950 HRESULT rc = S_OK;
10951 MachineState_T oldState = mData->mMachineState;
10952
10953 /* will release the lock before the potentially lengthy operation,
10954 * so protect with the special state (unless already protected) */
10955 if ( oldState != MachineState_Snapshotting
10956 && oldState != MachineState_OnlineSnapshotting
10957 && oldState != MachineState_LiveSnapshotting
10958 && oldState != MachineState_RestoringSnapshot
10959 && oldState != MachineState_DeletingSnapshot
10960 && oldState != MachineState_DeletingSnapshotOnline
10961 && oldState != MachineState_DeletingSnapshotPaused
10962 )
10963 i_setMachineState(MachineState_SettingUp);
10964
10965 // use appropriate locked media map (online or offline)
10966 MediumLockListMap lockedMediaOffline;
10967 MediumLockListMap *lockedMediaMap;
10968 if (aOnline)
10969 lockedMediaMap = &mData->mSession.mLockedMedia;
10970 else
10971 lockedMediaMap = &lockedMediaOffline;
10972
10973 try
10974 {
10975 if (!aOnline)
10976 {
10977 /* lock all attached hard disks early to detect "in use"
10978 * situations before deleting actual diffs */
10979 for (MediumAttachmentList::const_iterator
10980 it = mMediumAttachments->begin();
10981 it != mMediumAttachments->end();
10982 ++it)
10983 {
10984 MediumAttachment *pAtt = *it;
10985 if (pAtt->i_getType() == DeviceType_HardDisk)
10986 {
10987 Medium *pMedium = pAtt->i_getMedium();
10988 Assert(pMedium);
10989
10990 MediumLockList *pMediumLockList(new MediumLockList());
10991 alock.release();
10992 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10993 NULL /* pToLockWrite */,
10994 false /* fMediumLockWriteAll */,
10995 NULL,
10996 *pMediumLockList);
10997 alock.acquire();
10998
10999 if (FAILED(rc))
11000 {
11001 delete pMediumLockList;
11002 throw rc;
11003 }
11004
11005 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11006 if (FAILED(rc))
11007 throw rc;
11008 }
11009 }
11010
11011 if (FAILED(rc))
11012 throw rc;
11013 } // end of offline
11014
11015 /* Lock lists are now up to date and include implicitly created media */
11016
11017 /* Go through remembered attachments and delete all implicitly created
11018 * diffs and fix up the attachment information */
11019 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11020 MediumAttachmentList implicitAtts;
11021 for (MediumAttachmentList::const_iterator
11022 it = mMediumAttachments->begin();
11023 it != mMediumAttachments->end();
11024 ++it)
11025 {
11026 ComObjPtr<MediumAttachment> pAtt = *it;
11027 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11028 if (pMedium.isNull())
11029 continue;
11030
11031 // Implicit attachments go on the list for deletion and back references are removed.
11032 if (pAtt->i_isImplicit())
11033 {
11034 /* Deassociate and mark for deletion */
11035 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11036 rc = pMedium->i_removeBackReference(mData->mUuid);
11037 if (FAILED(rc))
11038 throw rc;
11039 implicitAtts.push_back(pAtt);
11040 continue;
11041 }
11042
11043 /* Was this medium attached before? */
11044 if (!i_findAttachment(oldAtts, pMedium))
11045 {
11046 /* no: de-associate */
11047 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11048 rc = pMedium->i_removeBackReference(mData->mUuid);
11049 if (FAILED(rc))
11050 throw rc;
11051 continue;
11052 }
11053 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11054 }
11055
11056 /* If there are implicit attachments to delete, throw away the lock
11057 * map contents (which will unlock all media) since the medium
11058 * attachments will be rolled back. Below we need to completely
11059 * recreate the lock map anyway since it is infinitely complex to
11060 * do this incrementally (would need reconstructing each attachment
11061 * change, which would be extremely hairy). */
11062 if (implicitAtts.size() != 0)
11063 {
11064 ErrorInfoKeeper eik;
11065
11066 HRESULT rc1 = lockedMediaMap->Clear();
11067 AssertComRC(rc1);
11068 }
11069
11070 /* rollback hard disk changes */
11071 mMediumAttachments.rollback();
11072
11073 MultiResult mrc(S_OK);
11074
11075 // Delete unused implicit diffs.
11076 if (implicitAtts.size() != 0)
11077 {
11078 alock.release();
11079
11080 for (MediumAttachmentList::const_iterator
11081 it = implicitAtts.begin();
11082 it != implicitAtts.end();
11083 ++it)
11084 {
11085 // Remove medium associated with this attachment.
11086 ComObjPtr<MediumAttachment> pAtt = *it;
11087 Assert(pAtt);
11088 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11089 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11090 Assert(pMedium);
11091
11092 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11093 // continue on delete failure, just collect error messages
11094 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11095 pMedium->i_getLocationFull().c_str() ));
11096 mrc = rc;
11097 }
11098 // Clear the list of deleted implicit attachments now, while not
11099 // holding the lock, as it will ultimately trigger Medium::uninit()
11100 // calls which assume that the media tree lock isn't held.
11101 implicitAtts.clear();
11102
11103 alock.acquire();
11104
11105 /* if there is a VM recreate media lock map as mentioned above,
11106 * otherwise it is a waste of time and we leave things unlocked */
11107 if (aOnline)
11108 {
11109 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11110 /* must never be NULL, but better safe than sorry */
11111 if (!pMachine.isNull())
11112 {
11113 alock.release();
11114 rc = mData->mSession.mMachine->i_lockMedia();
11115 alock.acquire();
11116 if (FAILED(rc))
11117 throw rc;
11118 }
11119 }
11120 }
11121 }
11122 catch (HRESULT aRC) {rc = aRC;}
11123
11124 if (mData->mMachineState == MachineState_SettingUp)
11125 i_setMachineState(oldState);
11126
11127 /* unlock all hard disks we locked when there is no VM */
11128 if (!aOnline)
11129 {
11130 ErrorInfoKeeper eik;
11131
11132 HRESULT rc1 = lockedMediaMap->Clear();
11133 AssertComRC(rc1);
11134 }
11135
11136 return rc;
11137}
11138
11139
11140/**
11141 * Looks through the given list of media attachments for one with the given parameters
11142 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11143 * can be searched as well if needed.
11144 *
11145 * @param ll
11146 * @param aControllerName
11147 * @param aControllerPort
11148 * @param aDevice
11149 * @return
11150 */
11151MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11152 const Utf8Str &aControllerName,
11153 LONG aControllerPort,
11154 LONG aDevice)
11155{
11156 for (MediumAttachmentList::const_iterator
11157 it = ll.begin();
11158 it != ll.end();
11159 ++it)
11160 {
11161 MediumAttachment *pAttach = *it;
11162 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11163 return pAttach;
11164 }
11165
11166 return NULL;
11167}
11168
11169/**
11170 * Looks through the given list of media attachments for one with the given parameters
11171 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11172 * can be searched as well if needed.
11173 *
11174 * @param ll
11175 * @param pMedium
11176 * @return
11177 */
11178MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11179 ComObjPtr<Medium> pMedium)
11180{
11181 for (MediumAttachmentList::const_iterator
11182 it = ll.begin();
11183 it != ll.end();
11184 ++it)
11185 {
11186 MediumAttachment *pAttach = *it;
11187 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11188 if (pMediumThis == pMedium)
11189 return pAttach;
11190 }
11191
11192 return NULL;
11193}
11194
11195/**
11196 * Looks through the given list of media attachments for one with the given parameters
11197 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11198 * can be searched as well if needed.
11199 *
11200 * @param ll
11201 * @param id
11202 * @return
11203 */
11204MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11205 Guid &id)
11206{
11207 for (MediumAttachmentList::const_iterator
11208 it = ll.begin();
11209 it != ll.end();
11210 ++it)
11211 {
11212 MediumAttachment *pAttach = *it;
11213 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11214 if (pMediumThis->i_getId() == id)
11215 return pAttach;
11216 }
11217
11218 return NULL;
11219}
11220
11221/**
11222 * Main implementation for Machine::DetachDevice. This also gets called
11223 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11224 *
11225 * @param pAttach Medium attachment to detach.
11226 * @param writeLock Machine write lock which the caller must have locked once.
11227 * This may be released temporarily in here.
11228 * @param pSnapshot If NULL, then the detachment is for the current machine.
11229 * Otherwise this is for a SnapshotMachine, and this must be
11230 * its snapshot.
11231 * @return
11232 */
11233HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11234 AutoWriteLock &writeLock,
11235 Snapshot *pSnapshot)
11236{
11237 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11238 DeviceType_T mediumType = pAttach->i_getType();
11239
11240 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11241
11242 if (pAttach->i_isImplicit())
11243 {
11244 /* attempt to implicitly delete the implicitly created diff */
11245
11246 /// @todo move the implicit flag from MediumAttachment to Medium
11247 /// and forbid any hard disk operation when it is implicit. Or maybe
11248 /// a special media state for it to make it even more simple.
11249
11250 Assert(mMediumAttachments.isBackedUp());
11251
11252 /* will release the lock before the potentially lengthy operation, so
11253 * protect with the special state */
11254 MachineState_T oldState = mData->mMachineState;
11255 i_setMachineState(MachineState_SettingUp);
11256
11257 writeLock.release();
11258
11259 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11260 true /*aWait*/,
11261 false /*aNotify*/);
11262
11263 writeLock.acquire();
11264
11265 i_setMachineState(oldState);
11266
11267 if (FAILED(rc)) return rc;
11268 }
11269
11270 i_setModified(IsModified_Storage);
11271 mMediumAttachments.backup();
11272 mMediumAttachments->remove(pAttach);
11273
11274 if (!oldmedium.isNull())
11275 {
11276 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11277 if (pSnapshot)
11278 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11279 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11280 else if (mediumType != DeviceType_HardDisk)
11281 oldmedium->i_removeBackReference(mData->mUuid);
11282 }
11283
11284 return S_OK;
11285}
11286
11287/**
11288 * Goes thru all media of the given list and
11289 *
11290 * 1) calls i_detachDevice() on each of them for this machine and
11291 * 2) adds all Medium objects found in the process to the given list,
11292 * depending on cleanupMode.
11293 *
11294 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11295 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11296 * media to the list.
11297 *
11298 * This gets called from Machine::Unregister, both for the actual Machine and
11299 * the SnapshotMachine objects that might be found in the snapshots.
11300 *
11301 * Requires caller and locking. The machine lock must be passed in because it
11302 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11303 *
11304 * @param writeLock Machine lock from top-level caller; this gets passed to
11305 * i_detachDevice.
11306 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11307 * object if called for a SnapshotMachine.
11308 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11309 * added to llMedia; if Full, then all media get added;
11310 * otherwise no media get added.
11311 * @param llMedia Caller's list to receive Medium objects which got detached so
11312 * caller can close() them, depending on cleanupMode.
11313 * @return
11314 */
11315HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11316 Snapshot *pSnapshot,
11317 CleanupMode_T cleanupMode,
11318 MediaList &llMedia)
11319{
11320 Assert(isWriteLockOnCurrentThread());
11321
11322 HRESULT rc;
11323
11324 // make a temporary list because i_detachDevice invalidates iterators into
11325 // mMediumAttachments
11326 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11327
11328 for (MediumAttachmentList::iterator
11329 it = llAttachments2.begin();
11330 it != llAttachments2.end();
11331 ++it)
11332 {
11333 ComObjPtr<MediumAttachment> &pAttach = *it;
11334 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11335
11336 if (!pMedium.isNull())
11337 {
11338 AutoCaller mac(pMedium);
11339 if (FAILED(mac.rc())) return mac.rc();
11340 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11341 DeviceType_T devType = pMedium->i_getDeviceType();
11342 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11343 && devType == DeviceType_HardDisk)
11344 || (cleanupMode == CleanupMode_Full)
11345 )
11346 {
11347 llMedia.push_back(pMedium);
11348 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11349 /* Not allowed to keep this lock as below we need the parent
11350 * medium lock, and the lock order is parent to child. */
11351 lock.release();
11352 /*
11353 * Search for medias which are not attached to any machine, but
11354 * in the chain to an attached disk. Mediums are only consided
11355 * if they are:
11356 * - have only one child
11357 * - no references to any machines
11358 * - are of normal medium type
11359 */
11360 while (!pParent.isNull())
11361 {
11362 AutoCaller mac1(pParent);
11363 if (FAILED(mac1.rc())) return mac1.rc();
11364 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11365 if (pParent->i_getChildren().size() == 1)
11366 {
11367 if ( pParent->i_getMachineBackRefCount() == 0
11368 && pParent->i_getType() == MediumType_Normal
11369 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11370 llMedia.push_back(pParent);
11371 }
11372 else
11373 break;
11374 pParent = pParent->i_getParent();
11375 }
11376 }
11377 }
11378
11379 // real machine: then we need to use the proper method
11380 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11381
11382 if (FAILED(rc))
11383 return rc;
11384 }
11385
11386 return S_OK;
11387}
11388
11389/**
11390 * Perform deferred hard disk detachments.
11391 *
11392 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11393 * changed (not backed up).
11394 *
11395 * If @a aOnline is @c true then this method will also unlock the old hard
11396 * disks for which the new implicit diffs were created and will lock these new
11397 * diffs for writing.
11398 *
11399 * @param aOnline Whether the VM was online prior to this operation.
11400 *
11401 * @note Locks this object for writing!
11402 */
11403void Machine::i_commitMedia(bool aOnline /*= false*/)
11404{
11405 AutoCaller autoCaller(this);
11406 AssertComRCReturnVoid(autoCaller.rc());
11407
11408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11409
11410 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11411
11412 HRESULT rc = S_OK;
11413
11414 /* no attach/detach operations -- nothing to do */
11415 if (!mMediumAttachments.isBackedUp())
11416 return;
11417
11418 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11419 bool fMediaNeedsLocking = false;
11420
11421 /* enumerate new attachments */
11422 for (MediumAttachmentList::const_iterator
11423 it = mMediumAttachments->begin();
11424 it != mMediumAttachments->end();
11425 ++it)
11426 {
11427 MediumAttachment *pAttach = *it;
11428
11429 pAttach->i_commit();
11430
11431 Medium *pMedium = pAttach->i_getMedium();
11432 bool fImplicit = pAttach->i_isImplicit();
11433
11434 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11435 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11436 fImplicit));
11437
11438 /** @todo convert all this Machine-based voodoo to MediumAttachment
11439 * based commit logic. */
11440 if (fImplicit)
11441 {
11442 /* convert implicit attachment to normal */
11443 pAttach->i_setImplicit(false);
11444
11445 if ( aOnline
11446 && pMedium
11447 && pAttach->i_getType() == DeviceType_HardDisk
11448 )
11449 {
11450 /* update the appropriate lock list */
11451 MediumLockList *pMediumLockList;
11452 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11453 AssertComRC(rc);
11454 if (pMediumLockList)
11455 {
11456 /* unlock if there's a need to change the locking */
11457 if (!fMediaNeedsLocking)
11458 {
11459 rc = mData->mSession.mLockedMedia.Unlock();
11460 AssertComRC(rc);
11461 fMediaNeedsLocking = true;
11462 }
11463 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11464 AssertComRC(rc);
11465 rc = pMediumLockList->Append(pMedium, true);
11466 AssertComRC(rc);
11467 }
11468 }
11469
11470 continue;
11471 }
11472
11473 if (pMedium)
11474 {
11475 /* was this medium attached before? */
11476 for (MediumAttachmentList::iterator
11477 oldIt = oldAtts.begin();
11478 oldIt != oldAtts.end();
11479 ++oldIt)
11480 {
11481 MediumAttachment *pOldAttach = *oldIt;
11482 if (pOldAttach->i_getMedium() == pMedium)
11483 {
11484 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11485
11486 /* yes: remove from old to avoid de-association */
11487 oldAtts.erase(oldIt);
11488 break;
11489 }
11490 }
11491 }
11492 }
11493
11494 /* enumerate remaining old attachments and de-associate from the
11495 * current machine state */
11496 for (MediumAttachmentList::const_iterator
11497 it = oldAtts.begin();
11498 it != oldAtts.end();
11499 ++it)
11500 {
11501 MediumAttachment *pAttach = *it;
11502 Medium *pMedium = pAttach->i_getMedium();
11503
11504 /* Detach only hard disks, since DVD/floppy media is detached
11505 * instantly in MountMedium. */
11506 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11507 {
11508 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11509
11510 /* now de-associate from the current machine state */
11511 rc = pMedium->i_removeBackReference(mData->mUuid);
11512 AssertComRC(rc);
11513
11514 if (aOnline)
11515 {
11516 /* unlock since medium is not used anymore */
11517 MediumLockList *pMediumLockList;
11518 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11519 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11520 {
11521 /* this happens for online snapshots, there the attachment
11522 * is changing, but only to a diff image created under
11523 * the old one, so there is no separate lock list */
11524 Assert(!pMediumLockList);
11525 }
11526 else
11527 {
11528 AssertComRC(rc);
11529 if (pMediumLockList)
11530 {
11531 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11532 AssertComRC(rc);
11533 }
11534 }
11535 }
11536 }
11537 }
11538
11539 /* take media locks again so that the locking state is consistent */
11540 if (fMediaNeedsLocking)
11541 {
11542 Assert(aOnline);
11543 rc = mData->mSession.mLockedMedia.Lock();
11544 AssertComRC(rc);
11545 }
11546
11547 /* commit the hard disk changes */
11548 mMediumAttachments.commit();
11549
11550 if (i_isSessionMachine())
11551 {
11552 /*
11553 * Update the parent machine to point to the new owner.
11554 * This is necessary because the stored parent will point to the
11555 * session machine otherwise and cause crashes or errors later
11556 * when the session machine gets invalid.
11557 */
11558 /** @todo Change the MediumAttachment class to behave like any other
11559 * class in this regard by creating peer MediumAttachment
11560 * objects for session machines and share the data with the peer
11561 * machine.
11562 */
11563 for (MediumAttachmentList::const_iterator
11564 it = mMediumAttachments->begin();
11565 it != mMediumAttachments->end();
11566 ++it)
11567 (*it)->i_updateParentMachine(mPeer);
11568
11569 /* attach new data to the primary machine and reshare it */
11570 mPeer->mMediumAttachments.attach(mMediumAttachments);
11571 }
11572
11573 return;
11574}
11575
11576/**
11577 * Perform deferred deletion of implicitly created diffs.
11578 *
11579 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11580 * changed (not backed up).
11581 *
11582 * @note Locks this object for writing!
11583 */
11584void Machine::i_rollbackMedia()
11585{
11586 AutoCaller autoCaller(this);
11587 AssertComRCReturnVoid(autoCaller.rc());
11588
11589 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11590 LogFlowThisFunc(("Entering rollbackMedia\n"));
11591
11592 HRESULT rc = S_OK;
11593
11594 /* no attach/detach operations -- nothing to do */
11595 if (!mMediumAttachments.isBackedUp())
11596 return;
11597
11598 /* enumerate new attachments */
11599 for (MediumAttachmentList::const_iterator
11600 it = mMediumAttachments->begin();
11601 it != mMediumAttachments->end();
11602 ++it)
11603 {
11604 MediumAttachment *pAttach = *it;
11605 /* Fix up the backrefs for DVD/floppy media. */
11606 if (pAttach->i_getType() != DeviceType_HardDisk)
11607 {
11608 Medium *pMedium = pAttach->i_getMedium();
11609 if (pMedium)
11610 {
11611 rc = pMedium->i_removeBackReference(mData->mUuid);
11612 AssertComRC(rc);
11613 }
11614 }
11615
11616 (*it)->i_rollback();
11617
11618 pAttach = *it;
11619 /* Fix up the backrefs for DVD/floppy media. */
11620 if (pAttach->i_getType() != DeviceType_HardDisk)
11621 {
11622 Medium *pMedium = pAttach->i_getMedium();
11623 if (pMedium)
11624 {
11625 rc = pMedium->i_addBackReference(mData->mUuid);
11626 AssertComRC(rc);
11627 }
11628 }
11629 }
11630
11631 /** @todo convert all this Machine-based voodoo to MediumAttachment
11632 * based rollback logic. */
11633 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11634
11635 return;
11636}
11637
11638/**
11639 * Returns true if the settings file is located in the directory named exactly
11640 * as the machine; this means, among other things, that the machine directory
11641 * should be auto-renamed.
11642 *
11643 * @param aSettingsDir if not NULL, the full machine settings file directory
11644 * name will be assigned there.
11645 *
11646 * @note Doesn't lock anything.
11647 * @note Not thread safe (must be called from this object's lock).
11648 */
11649bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11650{
11651 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11652 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11653 if (aSettingsDir)
11654 *aSettingsDir = strMachineDirName;
11655 strMachineDirName.stripPath(); // vmname
11656 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11657 strConfigFileOnly.stripPath() // vmname.vbox
11658 .stripSuffix(); // vmname
11659 /** @todo hack, make somehow use of ComposeMachineFilename */
11660 if (mUserData->s.fDirectoryIncludesUUID)
11661 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11662
11663 AssertReturn(!strMachineDirName.isEmpty(), false);
11664 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11665
11666 return strMachineDirName == strConfigFileOnly;
11667}
11668
11669/**
11670 * Discards all changes to machine settings.
11671 *
11672 * @param aNotify Whether to notify the direct session about changes or not.
11673 *
11674 * @note Locks objects for writing!
11675 */
11676void Machine::i_rollback(bool aNotify)
11677{
11678 AutoCaller autoCaller(this);
11679 AssertComRCReturn(autoCaller.rc(), (void)0);
11680
11681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11682
11683 if (!mStorageControllers.isNull())
11684 {
11685 if (mStorageControllers.isBackedUp())
11686 {
11687 /* unitialize all new devices (absent in the backed up list). */
11688 StorageControllerList *backedList = mStorageControllers.backedUpData();
11689 for (StorageControllerList::const_iterator
11690 it = mStorageControllers->begin();
11691 it != mStorageControllers->end();
11692 ++it)
11693 {
11694 if ( std::find(backedList->begin(), backedList->end(), *it)
11695 == backedList->end()
11696 )
11697 {
11698 (*it)->uninit();
11699 }
11700 }
11701
11702 /* restore the list */
11703 mStorageControllers.rollback();
11704 }
11705
11706 /* rollback any changes to devices after restoring the list */
11707 if (mData->flModifications & IsModified_Storage)
11708 {
11709 for (StorageControllerList::const_iterator
11710 it = mStorageControllers->begin();
11711 it != mStorageControllers->end();
11712 ++it)
11713 {
11714 (*it)->i_rollback();
11715 }
11716 }
11717 }
11718
11719 if (!mUSBControllers.isNull())
11720 {
11721 if (mUSBControllers.isBackedUp())
11722 {
11723 /* unitialize all new devices (absent in the backed up list). */
11724 USBControllerList *backedList = mUSBControllers.backedUpData();
11725 for (USBControllerList::const_iterator
11726 it = mUSBControllers->begin();
11727 it != mUSBControllers->end();
11728 ++it)
11729 {
11730 if ( std::find(backedList->begin(), backedList->end(), *it)
11731 == backedList->end()
11732 )
11733 {
11734 (*it)->uninit();
11735 }
11736 }
11737
11738 /* restore the list */
11739 mUSBControllers.rollback();
11740 }
11741
11742 /* rollback any changes to devices after restoring the list */
11743 if (mData->flModifications & IsModified_USB)
11744 {
11745 for (USBControllerList::const_iterator
11746 it = mUSBControllers->begin();
11747 it != mUSBControllers->end();
11748 ++it)
11749 {
11750 (*it)->i_rollback();
11751 }
11752 }
11753 }
11754
11755 mUserData.rollback();
11756
11757 mHWData.rollback();
11758
11759 if (mData->flModifications & IsModified_Storage)
11760 i_rollbackMedia();
11761
11762 if (mBIOSSettings)
11763 mBIOSSettings->i_rollback();
11764
11765 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11766 mRecordingSettings->i_rollback();
11767
11768 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11769 mVRDEServer->i_rollback();
11770
11771 if (mAudioAdapter)
11772 mAudioAdapter->i_rollback();
11773
11774 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11775 mUSBDeviceFilters->i_rollback();
11776
11777 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11778 mBandwidthControl->i_rollback();
11779
11780 if (!mHWData.isNull())
11781 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11782 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11783 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11784 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11785
11786 if (mData->flModifications & IsModified_NetworkAdapters)
11787 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11788 if ( mNetworkAdapters[slot]
11789 && mNetworkAdapters[slot]->i_isModified())
11790 {
11791 mNetworkAdapters[slot]->i_rollback();
11792 networkAdapters[slot] = mNetworkAdapters[slot];
11793 }
11794
11795 if (mData->flModifications & IsModified_SerialPorts)
11796 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11797 if ( mSerialPorts[slot]
11798 && mSerialPorts[slot]->i_isModified())
11799 {
11800 mSerialPorts[slot]->i_rollback();
11801 serialPorts[slot] = mSerialPorts[slot];
11802 }
11803
11804 if (mData->flModifications & IsModified_ParallelPorts)
11805 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11806 if ( mParallelPorts[slot]
11807 && mParallelPorts[slot]->i_isModified())
11808 {
11809 mParallelPorts[slot]->i_rollback();
11810 parallelPorts[slot] = mParallelPorts[slot];
11811 }
11812
11813 if (aNotify)
11814 {
11815 /* inform the direct session about changes */
11816
11817 ComObjPtr<Machine> that = this;
11818 uint32_t flModifications = mData->flModifications;
11819 alock.release();
11820
11821 if (flModifications & IsModified_SharedFolders)
11822 that->i_onSharedFolderChange();
11823
11824 if (flModifications & IsModified_VRDEServer)
11825 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11826 if (flModifications & IsModified_USB)
11827 that->i_onUSBControllerChange();
11828
11829 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11830 if (networkAdapters[slot])
11831 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11832 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11833 if (serialPorts[slot])
11834 that->i_onSerialPortChange(serialPorts[slot]);
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11836 if (parallelPorts[slot])
11837 that->i_onParallelPortChange(parallelPorts[slot]);
11838
11839 if (flModifications & IsModified_Storage)
11840 that->i_onStorageControllerChange();
11841
11842#if 0
11843 if (flModifications & IsModified_BandwidthControl)
11844 that->onBandwidthControlChange();
11845#endif
11846 }
11847}
11848
11849/**
11850 * Commits all the changes to machine settings.
11851 *
11852 * Note that this operation is supposed to never fail.
11853 *
11854 * @note Locks this object and children for writing.
11855 */
11856void Machine::i_commit()
11857{
11858 AutoCaller autoCaller(this);
11859 AssertComRCReturnVoid(autoCaller.rc());
11860
11861 AutoCaller peerCaller(mPeer);
11862 AssertComRCReturnVoid(peerCaller.rc());
11863
11864 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11865
11866 /*
11867 * use safe commit to ensure Snapshot machines (that share mUserData)
11868 * will still refer to a valid memory location
11869 */
11870 mUserData.commitCopy();
11871
11872 mHWData.commit();
11873
11874 if (mMediumAttachments.isBackedUp())
11875 i_commitMedia(Global::IsOnline(mData->mMachineState));
11876
11877 mBIOSSettings->i_commit();
11878 mRecordingSettings->i_commit();
11879 mVRDEServer->i_commit();
11880 mAudioAdapter->i_commit();
11881 mUSBDeviceFilters->i_commit();
11882 mBandwidthControl->i_commit();
11883
11884 /* Since mNetworkAdapters is a list which might have been changed (resized)
11885 * without using the Backupable<> template we need to handle the copying
11886 * of the list entries manually, including the creation of peers for the
11887 * new objects. */
11888 bool commitNetworkAdapters = false;
11889 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11890 if (mPeer)
11891 {
11892 /* commit everything, even the ones which will go away */
11893 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11894 mNetworkAdapters[slot]->i_commit();
11895 /* copy over the new entries, creating a peer and uninit the original */
11896 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11897 for (size_t slot = 0; slot < newSize; slot++)
11898 {
11899 /* look if this adapter has a peer device */
11900 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11901 if (!peer)
11902 {
11903 /* no peer means the adapter is a newly created one;
11904 * create a peer owning data this data share it with */
11905 peer.createObject();
11906 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11907 }
11908 mPeer->mNetworkAdapters[slot] = peer;
11909 }
11910 /* uninit any no longer needed network adapters */
11911 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11912 mNetworkAdapters[slot]->uninit();
11913 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11914 {
11915 if (mPeer->mNetworkAdapters[slot])
11916 mPeer->mNetworkAdapters[slot]->uninit();
11917 }
11918 /* Keep the original network adapter count until this point, so that
11919 * discarding a chipset type change will not lose settings. */
11920 mNetworkAdapters.resize(newSize);
11921 mPeer->mNetworkAdapters.resize(newSize);
11922 }
11923 else
11924 {
11925 /* we have no peer (our parent is the newly created machine);
11926 * just commit changes to the network adapters */
11927 commitNetworkAdapters = true;
11928 }
11929 if (commitNetworkAdapters)
11930 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11931 mNetworkAdapters[slot]->i_commit();
11932
11933 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11934 mSerialPorts[slot]->i_commit();
11935 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11936 mParallelPorts[slot]->i_commit();
11937
11938 bool commitStorageControllers = false;
11939
11940 if (mStorageControllers.isBackedUp())
11941 {
11942 mStorageControllers.commit();
11943
11944 if (mPeer)
11945 {
11946 /* Commit all changes to new controllers (this will reshare data with
11947 * peers for those who have peers) */
11948 StorageControllerList *newList = new StorageControllerList();
11949 for (StorageControllerList::const_iterator
11950 it = mStorageControllers->begin();
11951 it != mStorageControllers->end();
11952 ++it)
11953 {
11954 (*it)->i_commit();
11955
11956 /* look if this controller has a peer device */
11957 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11958 if (!peer)
11959 {
11960 /* no peer means the device is a newly created one;
11961 * create a peer owning data this device share it with */
11962 peer.createObject();
11963 peer->init(mPeer, *it, true /* aReshare */);
11964 }
11965 else
11966 {
11967 /* remove peer from the old list */
11968 mPeer->mStorageControllers->remove(peer);
11969 }
11970 /* and add it to the new list */
11971 newList->push_back(peer);
11972 }
11973
11974 /* uninit old peer's controllers that are left */
11975 for (StorageControllerList::const_iterator
11976 it = mPeer->mStorageControllers->begin();
11977 it != mPeer->mStorageControllers->end();
11978 ++it)
11979 {
11980 (*it)->uninit();
11981 }
11982
11983 /* attach new list of controllers to our peer */
11984 mPeer->mStorageControllers.attach(newList);
11985 }
11986 else
11987 {
11988 /* we have no peer (our parent is the newly created machine);
11989 * just commit changes to devices */
11990 commitStorageControllers = true;
11991 }
11992 }
11993 else
11994 {
11995 /* the list of controllers itself is not changed,
11996 * just commit changes to controllers themselves */
11997 commitStorageControllers = true;
11998 }
11999
12000 if (commitStorageControllers)
12001 {
12002 for (StorageControllerList::const_iterator
12003 it = mStorageControllers->begin();
12004 it != mStorageControllers->end();
12005 ++it)
12006 {
12007 (*it)->i_commit();
12008 }
12009 }
12010
12011 bool commitUSBControllers = false;
12012
12013 if (mUSBControllers.isBackedUp())
12014 {
12015 mUSBControllers.commit();
12016
12017 if (mPeer)
12018 {
12019 /* Commit all changes to new controllers (this will reshare data with
12020 * peers for those who have peers) */
12021 USBControllerList *newList = new USBControllerList();
12022 for (USBControllerList::const_iterator
12023 it = mUSBControllers->begin();
12024 it != mUSBControllers->end();
12025 ++it)
12026 {
12027 (*it)->i_commit();
12028
12029 /* look if this controller has a peer device */
12030 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12031 if (!peer)
12032 {
12033 /* no peer means the device is a newly created one;
12034 * create a peer owning data this device share it with */
12035 peer.createObject();
12036 peer->init(mPeer, *it, true /* aReshare */);
12037 }
12038 else
12039 {
12040 /* remove peer from the old list */
12041 mPeer->mUSBControllers->remove(peer);
12042 }
12043 /* and add it to the new list */
12044 newList->push_back(peer);
12045 }
12046
12047 /* uninit old peer's controllers that are left */
12048 for (USBControllerList::const_iterator
12049 it = mPeer->mUSBControllers->begin();
12050 it != mPeer->mUSBControllers->end();
12051 ++it)
12052 {
12053 (*it)->uninit();
12054 }
12055
12056 /* attach new list of controllers to our peer */
12057 mPeer->mUSBControllers.attach(newList);
12058 }
12059 else
12060 {
12061 /* we have no peer (our parent is the newly created machine);
12062 * just commit changes to devices */
12063 commitUSBControllers = true;
12064 }
12065 }
12066 else
12067 {
12068 /* the list of controllers itself is not changed,
12069 * just commit changes to controllers themselves */
12070 commitUSBControllers = true;
12071 }
12072
12073 if (commitUSBControllers)
12074 {
12075 for (USBControllerList::const_iterator
12076 it = mUSBControllers->begin();
12077 it != mUSBControllers->end();
12078 ++it)
12079 {
12080 (*it)->i_commit();
12081 }
12082 }
12083
12084 if (i_isSessionMachine())
12085 {
12086 /* attach new data to the primary machine and reshare it */
12087 mPeer->mUserData.attach(mUserData);
12088 mPeer->mHWData.attach(mHWData);
12089 /* mmMediumAttachments is reshared by fixupMedia */
12090 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12091 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12092 }
12093}
12094
12095/**
12096 * Copies all the hardware data from the given machine.
12097 *
12098 * Currently, only called when the VM is being restored from a snapshot. In
12099 * particular, this implies that the VM is not running during this method's
12100 * call.
12101 *
12102 * @note This method must be called from under this object's lock.
12103 *
12104 * @note This method doesn't call #i_commit(), so all data remains backed up and
12105 * unsaved.
12106 */
12107void Machine::i_copyFrom(Machine *aThat)
12108{
12109 AssertReturnVoid(!i_isSnapshotMachine());
12110 AssertReturnVoid(aThat->i_isSnapshotMachine());
12111
12112 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12113
12114 mHWData.assignCopy(aThat->mHWData);
12115
12116 // create copies of all shared folders (mHWData after attaching a copy
12117 // contains just references to original objects)
12118 for (HWData::SharedFolderList::iterator
12119 it = mHWData->mSharedFolders.begin();
12120 it != mHWData->mSharedFolders.end();
12121 ++it)
12122 {
12123 ComObjPtr<SharedFolder> folder;
12124 folder.createObject();
12125 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12126 AssertComRC(rc);
12127 *it = folder;
12128 }
12129
12130 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12131 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12132 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12133 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12134 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12135 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12136
12137 /* create private copies of all controllers */
12138 mStorageControllers.backup();
12139 mStorageControllers->clear();
12140 for (StorageControllerList::const_iterator
12141 it = aThat->mStorageControllers->begin();
12142 it != aThat->mStorageControllers->end();
12143 ++it)
12144 {
12145 ComObjPtr<StorageController> ctrl;
12146 ctrl.createObject();
12147 ctrl->initCopy(this, *it);
12148 mStorageControllers->push_back(ctrl);
12149 }
12150
12151 /* create private copies of all USB controllers */
12152 mUSBControllers.backup();
12153 mUSBControllers->clear();
12154 for (USBControllerList::const_iterator
12155 it = aThat->mUSBControllers->begin();
12156 it != aThat->mUSBControllers->end();
12157 ++it)
12158 {
12159 ComObjPtr<USBController> ctrl;
12160 ctrl.createObject();
12161 ctrl->initCopy(this, *it);
12162 mUSBControllers->push_back(ctrl);
12163 }
12164
12165 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12166 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12167 {
12168 if (mNetworkAdapters[slot].isNotNull())
12169 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12170 else
12171 {
12172 unconst(mNetworkAdapters[slot]).createObject();
12173 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12174 }
12175 }
12176 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12177 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12178 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12179 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12180}
12181
12182/**
12183 * Returns whether the given storage controller is hotplug capable.
12184 *
12185 * @returns true if the controller supports hotplugging
12186 * false otherwise.
12187 * @param enmCtrlType The controller type to check for.
12188 */
12189bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12190{
12191 ComPtr<ISystemProperties> systemProperties;
12192 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12193 if (FAILED(rc))
12194 return false;
12195
12196 BOOL aHotplugCapable = FALSE;
12197 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12198
12199 return RT_BOOL(aHotplugCapable);
12200}
12201
12202#ifdef VBOX_WITH_RESOURCE_USAGE_API
12203
12204void Machine::i_getDiskList(MediaList &list)
12205{
12206 for (MediumAttachmentList::const_iterator
12207 it = mMediumAttachments->begin();
12208 it != mMediumAttachments->end();
12209 ++it)
12210 {
12211 MediumAttachment *pAttach = *it;
12212 /* just in case */
12213 AssertContinue(pAttach);
12214
12215 AutoCaller localAutoCallerA(pAttach);
12216 if (FAILED(localAutoCallerA.rc())) continue;
12217
12218 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12219
12220 if (pAttach->i_getType() == DeviceType_HardDisk)
12221 list.push_back(pAttach->i_getMedium());
12222 }
12223}
12224
12225void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12226{
12227 AssertReturnVoid(isWriteLockOnCurrentThread());
12228 AssertPtrReturnVoid(aCollector);
12229
12230 pm::CollectorHAL *hal = aCollector->getHAL();
12231 /* Create sub metrics */
12232 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12233 "Percentage of processor time spent in user mode by the VM process.");
12234 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12235 "Percentage of processor time spent in kernel mode by the VM process.");
12236 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12237 "Size of resident portion of VM process in memory.");
12238 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12239 "Actual size of all VM disks combined.");
12240 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12241 "Network receive rate.");
12242 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12243 "Network transmit rate.");
12244 /* Create and register base metrics */
12245 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12246 cpuLoadUser, cpuLoadKernel);
12247 aCollector->registerBaseMetric(cpuLoad);
12248 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12249 ramUsageUsed);
12250 aCollector->registerBaseMetric(ramUsage);
12251 MediaList disks;
12252 i_getDiskList(disks);
12253 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12254 diskUsageUsed);
12255 aCollector->registerBaseMetric(diskUsage);
12256
12257 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12258 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12259 new pm::AggregateAvg()));
12260 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12261 new pm::AggregateMin()));
12262 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12263 new pm::AggregateMax()));
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12265 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12266 new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12268 new pm::AggregateMin()));
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12270 new pm::AggregateMax()));
12271
12272 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12273 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12274 new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12276 new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12278 new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12281 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12282 new pm::AggregateAvg()));
12283 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12284 new pm::AggregateMin()));
12285 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12286 new pm::AggregateMax()));
12287
12288
12289 /* Guest metrics collector */
12290 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12291 aCollector->registerGuest(mCollectorGuest);
12292 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12293
12294 /* Create sub metrics */
12295 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12296 "Percentage of processor time spent in user mode as seen by the guest.");
12297 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12298 "Percentage of processor time spent in kernel mode as seen by the guest.");
12299 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12300 "Percentage of processor time spent idling as seen by the guest.");
12301
12302 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12303 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12304 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12305 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12306 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12307 pm::SubMetric *guestMemCache = new pm::SubMetric(
12308 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12309
12310 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12311 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12312
12313 /* Create and register base metrics */
12314 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12315 machineNetRx, machineNetTx);
12316 aCollector->registerBaseMetric(machineNetRate);
12317
12318 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12319 guestLoadUser, guestLoadKernel, guestLoadIdle);
12320 aCollector->registerBaseMetric(guestCpuLoad);
12321
12322 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12323 guestMemTotal, guestMemFree,
12324 guestMemBalloon, guestMemShared,
12325 guestMemCache, guestPagedTotal);
12326 aCollector->registerBaseMetric(guestCpuMem);
12327
12328 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12329 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12331 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12332
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12334 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12337
12338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12342
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12347
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12349 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12352
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12357
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12362
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12367
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12372
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12377
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12382}
12383
12384void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12385{
12386 AssertReturnVoid(isWriteLockOnCurrentThread());
12387
12388 if (aCollector)
12389 {
12390 aCollector->unregisterMetricsFor(aMachine);
12391 aCollector->unregisterBaseMetricsFor(aMachine);
12392 }
12393}
12394
12395#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12396
12397
12398////////////////////////////////////////////////////////////////////////////////
12399
12400DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12401
12402HRESULT SessionMachine::FinalConstruct()
12403{
12404 LogFlowThisFunc(("\n"));
12405
12406 mClientToken = NULL;
12407
12408 return BaseFinalConstruct();
12409}
12410
12411void SessionMachine::FinalRelease()
12412{
12413 LogFlowThisFunc(("\n"));
12414
12415 Assert(!mClientToken);
12416 /* paranoia, should not hang around any more */
12417 if (mClientToken)
12418 {
12419 delete mClientToken;
12420 mClientToken = NULL;
12421 }
12422
12423 uninit(Uninit::Unexpected);
12424
12425 BaseFinalRelease();
12426}
12427
12428/**
12429 * @note Must be called only by Machine::LockMachine() from its own write lock.
12430 */
12431HRESULT SessionMachine::init(Machine *aMachine)
12432{
12433 LogFlowThisFuncEnter();
12434 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12435
12436 AssertReturn(aMachine, E_INVALIDARG);
12437
12438 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12439
12440 /* Enclose the state transition NotReady->InInit->Ready */
12441 AutoInitSpan autoInitSpan(this);
12442 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12443
12444 HRESULT rc = S_OK;
12445
12446 RT_ZERO(mAuthLibCtx);
12447
12448 /* create the machine client token */
12449 try
12450 {
12451 mClientToken = new ClientToken(aMachine, this);
12452 if (!mClientToken->isReady())
12453 {
12454 delete mClientToken;
12455 mClientToken = NULL;
12456 rc = E_FAIL;
12457 }
12458 }
12459 catch (std::bad_alloc &)
12460 {
12461 rc = E_OUTOFMEMORY;
12462 }
12463 if (FAILED(rc))
12464 return rc;
12465
12466 /* memorize the peer Machine */
12467 unconst(mPeer) = aMachine;
12468 /* share the parent pointer */
12469 unconst(mParent) = aMachine->mParent;
12470
12471 /* take the pointers to data to share */
12472 mData.share(aMachine->mData);
12473 mSSData.share(aMachine->mSSData);
12474
12475 mUserData.share(aMachine->mUserData);
12476 mHWData.share(aMachine->mHWData);
12477 mMediumAttachments.share(aMachine->mMediumAttachments);
12478
12479 mStorageControllers.allocate();
12480 for (StorageControllerList::const_iterator
12481 it = aMachine->mStorageControllers->begin();
12482 it != aMachine->mStorageControllers->end();
12483 ++it)
12484 {
12485 ComObjPtr<StorageController> ctl;
12486 ctl.createObject();
12487 ctl->init(this, *it);
12488 mStorageControllers->push_back(ctl);
12489 }
12490
12491 mUSBControllers.allocate();
12492 for (USBControllerList::const_iterator
12493 it = aMachine->mUSBControllers->begin();
12494 it != aMachine->mUSBControllers->end();
12495 ++it)
12496 {
12497 ComObjPtr<USBController> ctl;
12498 ctl.createObject();
12499 ctl->init(this, *it);
12500 mUSBControllers->push_back(ctl);
12501 }
12502
12503 unconst(mBIOSSettings).createObject();
12504 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12505 unconst(mRecordingSettings).createObject();
12506 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12507 /* create another VRDEServer object that will be mutable */
12508 unconst(mVRDEServer).createObject();
12509 mVRDEServer->init(this, aMachine->mVRDEServer);
12510 /* create another audio adapter object that will be mutable */
12511 unconst(mAudioAdapter).createObject();
12512 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12513 /* create a list of serial ports that will be mutable */
12514 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12515 {
12516 unconst(mSerialPorts[slot]).createObject();
12517 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12518 }
12519 /* create a list of parallel ports that will be mutable */
12520 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12521 {
12522 unconst(mParallelPorts[slot]).createObject();
12523 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12524 }
12525
12526 /* create another USB device filters object that will be mutable */
12527 unconst(mUSBDeviceFilters).createObject();
12528 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12529
12530 /* create a list of network adapters that will be mutable */
12531 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12532 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12533 {
12534 unconst(mNetworkAdapters[slot]).createObject();
12535 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12536 }
12537
12538 /* create another bandwidth control object that will be mutable */
12539 unconst(mBandwidthControl).createObject();
12540 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12541
12542 /* default is to delete saved state on Saved -> PoweredOff transition */
12543 mRemoveSavedState = true;
12544
12545 /* Confirm a successful initialization when it's the case */
12546 autoInitSpan.setSucceeded();
12547
12548 miNATNetworksStarted = 0;
12549
12550 LogFlowThisFuncLeave();
12551 return rc;
12552}
12553
12554/**
12555 * Uninitializes this session object. If the reason is other than
12556 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12557 * or the client watcher code.
12558 *
12559 * @param aReason uninitialization reason
12560 *
12561 * @note Locks mParent + this object for writing.
12562 */
12563void SessionMachine::uninit(Uninit::Reason aReason)
12564{
12565 LogFlowThisFuncEnter();
12566 LogFlowThisFunc(("reason=%d\n", aReason));
12567
12568 /*
12569 * Strongly reference ourselves to prevent this object deletion after
12570 * mData->mSession.mMachine.setNull() below (which can release the last
12571 * reference and call the destructor). Important: this must be done before
12572 * accessing any members (and before AutoUninitSpan that does it as well).
12573 * This self reference will be released as the very last step on return.
12574 */
12575 ComObjPtr<SessionMachine> selfRef;
12576 if (aReason != Uninit::Unexpected)
12577 selfRef = this;
12578
12579 /* Enclose the state transition Ready->InUninit->NotReady */
12580 AutoUninitSpan autoUninitSpan(this);
12581 if (autoUninitSpan.uninitDone())
12582 {
12583 LogFlowThisFunc(("Already uninitialized\n"));
12584 LogFlowThisFuncLeave();
12585 return;
12586 }
12587
12588 if (autoUninitSpan.initFailed())
12589 {
12590 /* We've been called by init() because it's failed. It's not really
12591 * necessary (nor it's safe) to perform the regular uninit sequence
12592 * below, the following is enough.
12593 */
12594 LogFlowThisFunc(("Initialization failed.\n"));
12595 /* destroy the machine client token */
12596 if (mClientToken)
12597 {
12598 delete mClientToken;
12599 mClientToken = NULL;
12600 }
12601 uninitDataAndChildObjects();
12602 mData.free();
12603 unconst(mParent) = NULL;
12604 unconst(mPeer) = NULL;
12605 LogFlowThisFuncLeave();
12606 return;
12607 }
12608
12609 MachineState_T lastState;
12610 {
12611 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12612 lastState = mData->mMachineState;
12613 }
12614 NOREF(lastState);
12615
12616#ifdef VBOX_WITH_USB
12617 // release all captured USB devices, but do this before requesting the locks below
12618 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12619 {
12620 /* Console::captureUSBDevices() is called in the VM process only after
12621 * setting the machine state to Starting or Restoring.
12622 * Console::detachAllUSBDevices() will be called upon successful
12623 * termination. So, we need to release USB devices only if there was
12624 * an abnormal termination of a running VM.
12625 *
12626 * This is identical to SessionMachine::DetachAllUSBDevices except
12627 * for the aAbnormal argument. */
12628 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12629 AssertComRC(rc);
12630 NOREF(rc);
12631
12632 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12633 if (service)
12634 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12635 }
12636#endif /* VBOX_WITH_USB */
12637
12638 // we need to lock this object in uninit() because the lock is shared
12639 // with mPeer (as well as data we modify below). mParent lock is needed
12640 // by several calls to it.
12641 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12642
12643#ifdef VBOX_WITH_RESOURCE_USAGE_API
12644 /*
12645 * It is safe to call Machine::i_unregisterMetrics() here because
12646 * PerformanceCollector::samplerCallback no longer accesses guest methods
12647 * holding the lock.
12648 */
12649 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12650 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12651 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12652 if (mCollectorGuest)
12653 {
12654 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12655 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12656 mCollectorGuest = NULL;
12657 }
12658#endif
12659
12660 if (aReason == Uninit::Abnormal)
12661 {
12662 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12663
12664 /* reset the state to Aborted */
12665 if (mData->mMachineState != MachineState_Aborted)
12666 i_setMachineState(MachineState_Aborted);
12667 }
12668
12669 // any machine settings modified?
12670 if (mData->flModifications)
12671 {
12672 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12673 i_rollback(false /* aNotify */);
12674 }
12675
12676 mData->mSession.mPID = NIL_RTPROCESS;
12677
12678 if (aReason == Uninit::Unexpected)
12679 {
12680 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12681 * client watcher thread to update the set of machines that have open
12682 * sessions. */
12683 mParent->i_updateClientWatcher();
12684 }
12685
12686 /* uninitialize all remote controls */
12687 if (mData->mSession.mRemoteControls.size())
12688 {
12689 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12690 mData->mSession.mRemoteControls.size()));
12691
12692 /* Always restart a the beginning, since the iterator is invalidated
12693 * by using erase(). */
12694 for (Data::Session::RemoteControlList::iterator
12695 it = mData->mSession.mRemoteControls.begin();
12696 it != mData->mSession.mRemoteControls.end();
12697 it = mData->mSession.mRemoteControls.begin())
12698 {
12699 ComPtr<IInternalSessionControl> pControl = *it;
12700 mData->mSession.mRemoteControls.erase(it);
12701 multilock.release();
12702 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12703 HRESULT rc = pControl->Uninitialize();
12704 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12705 if (FAILED(rc))
12706 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12707 multilock.acquire();
12708 }
12709 mData->mSession.mRemoteControls.clear();
12710 }
12711
12712 /* Remove all references to the NAT network service. The service will stop
12713 * if all references (also from other VMs) are removed. */
12714 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12715 {
12716 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12717 {
12718 BOOL enabled;
12719 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12720 if ( FAILED(hrc)
12721 || !enabled)
12722 continue;
12723
12724 NetworkAttachmentType_T type;
12725 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12726 if ( SUCCEEDED(hrc)
12727 && type == NetworkAttachmentType_NATNetwork)
12728 {
12729 Bstr name;
12730 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12731 if (SUCCEEDED(hrc))
12732 {
12733 multilock.release();
12734 Utf8Str strName(name);
12735 LogRel(("VM '%s' stops using NAT network '%s'\n",
12736 mUserData->s.strName.c_str(), strName.c_str()));
12737 mParent->i_natNetworkRefDec(strName);
12738 multilock.acquire();
12739 }
12740 }
12741 }
12742 }
12743
12744 /*
12745 * An expected uninitialization can come only from #i_checkForDeath().
12746 * Otherwise it means that something's gone really wrong (for example,
12747 * the Session implementation has released the VirtualBox reference
12748 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12749 * etc). However, it's also possible, that the client releases the IPC
12750 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12751 * but the VirtualBox release event comes first to the server process.
12752 * This case is practically possible, so we should not assert on an
12753 * unexpected uninit, just log a warning.
12754 */
12755
12756 if (aReason == Uninit::Unexpected)
12757 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12758
12759 if (aReason != Uninit::Normal)
12760 {
12761 mData->mSession.mDirectControl.setNull();
12762 }
12763 else
12764 {
12765 /* this must be null here (see #OnSessionEnd()) */
12766 Assert(mData->mSession.mDirectControl.isNull());
12767 Assert(mData->mSession.mState == SessionState_Unlocking);
12768 Assert(!mData->mSession.mProgress.isNull());
12769 }
12770 if (mData->mSession.mProgress)
12771 {
12772 if (aReason == Uninit::Normal)
12773 mData->mSession.mProgress->i_notifyComplete(S_OK);
12774 else
12775 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12776 COM_IIDOF(ISession),
12777 getComponentName(),
12778 tr("The VM session was aborted"));
12779 mData->mSession.mProgress.setNull();
12780 }
12781
12782 if (mConsoleTaskData.mProgress)
12783 {
12784 Assert(aReason == Uninit::Abnormal);
12785 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12786 COM_IIDOF(ISession),
12787 getComponentName(),
12788 tr("The VM session was aborted"));
12789 mConsoleTaskData.mProgress.setNull();
12790 }
12791
12792 /* remove the association between the peer machine and this session machine */
12793 Assert( (SessionMachine*)mData->mSession.mMachine == this
12794 || aReason == Uninit::Unexpected);
12795
12796 /* reset the rest of session data */
12797 mData->mSession.mLockType = LockType_Null;
12798 mData->mSession.mMachine.setNull();
12799 mData->mSession.mState = SessionState_Unlocked;
12800 mData->mSession.mName.setNull();
12801
12802 /* destroy the machine client token before leaving the exclusive lock */
12803 if (mClientToken)
12804 {
12805 delete mClientToken;
12806 mClientToken = NULL;
12807 }
12808
12809 /* fire an event */
12810 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12811
12812 uninitDataAndChildObjects();
12813
12814 /* free the essential data structure last */
12815 mData.free();
12816
12817 /* release the exclusive lock before setting the below two to NULL */
12818 multilock.release();
12819
12820 unconst(mParent) = NULL;
12821 unconst(mPeer) = NULL;
12822
12823 AuthLibUnload(&mAuthLibCtx);
12824
12825 LogFlowThisFuncLeave();
12826}
12827
12828// util::Lockable interface
12829////////////////////////////////////////////////////////////////////////////////
12830
12831/**
12832 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12833 * with the primary Machine instance (mPeer).
12834 */
12835RWLockHandle *SessionMachine::lockHandle() const
12836{
12837 AssertReturn(mPeer != NULL, NULL);
12838 return mPeer->lockHandle();
12839}
12840
12841// IInternalMachineControl methods
12842////////////////////////////////////////////////////////////////////////////////
12843
12844/**
12845 * Passes collected guest statistics to performance collector object
12846 */
12847HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12848 ULONG aCpuKernel, ULONG aCpuIdle,
12849 ULONG aMemTotal, ULONG aMemFree,
12850 ULONG aMemBalloon, ULONG aMemShared,
12851 ULONG aMemCache, ULONG aPageTotal,
12852 ULONG aAllocVMM, ULONG aFreeVMM,
12853 ULONG aBalloonedVMM, ULONG aSharedVMM,
12854 ULONG aVmNetRx, ULONG aVmNetTx)
12855{
12856#ifdef VBOX_WITH_RESOURCE_USAGE_API
12857 if (mCollectorGuest)
12858 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12859 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12860 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12861 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12862
12863 return S_OK;
12864#else
12865 NOREF(aValidStats);
12866 NOREF(aCpuUser);
12867 NOREF(aCpuKernel);
12868 NOREF(aCpuIdle);
12869 NOREF(aMemTotal);
12870 NOREF(aMemFree);
12871 NOREF(aMemBalloon);
12872 NOREF(aMemShared);
12873 NOREF(aMemCache);
12874 NOREF(aPageTotal);
12875 NOREF(aAllocVMM);
12876 NOREF(aFreeVMM);
12877 NOREF(aBalloonedVMM);
12878 NOREF(aSharedVMM);
12879 NOREF(aVmNetRx);
12880 NOREF(aVmNetTx);
12881 return E_NOTIMPL;
12882#endif
12883}
12884
12885////////////////////////////////////////////////////////////////////////////////
12886//
12887// SessionMachine task records
12888//
12889////////////////////////////////////////////////////////////////////////////////
12890
12891/**
12892 * Task record for saving the machine state.
12893 */
12894class SessionMachine::SaveStateTask
12895 : public Machine::Task
12896{
12897public:
12898 SaveStateTask(SessionMachine *m,
12899 Progress *p,
12900 const Utf8Str &t,
12901 Reason_T enmReason,
12902 const Utf8Str &strStateFilePath)
12903 : Task(m, p, t),
12904 m_enmReason(enmReason),
12905 m_strStateFilePath(strStateFilePath)
12906 {}
12907
12908private:
12909 void handler()
12910 {
12911 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12912 }
12913
12914 Reason_T m_enmReason;
12915 Utf8Str m_strStateFilePath;
12916
12917 friend class SessionMachine;
12918};
12919
12920/**
12921 * Task thread implementation for SessionMachine::SaveState(), called from
12922 * SessionMachine::taskHandler().
12923 *
12924 * @note Locks this object for writing.
12925 *
12926 * @param task
12927 * @return
12928 */
12929void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12930{
12931 LogFlowThisFuncEnter();
12932
12933 AutoCaller autoCaller(this);
12934 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12935 if (FAILED(autoCaller.rc()))
12936 {
12937 /* we might have been uninitialized because the session was accidentally
12938 * closed by the client, so don't assert */
12939 HRESULT rc = setError(E_FAIL,
12940 tr("The session has been accidentally closed"));
12941 task.m_pProgress->i_notifyComplete(rc);
12942 LogFlowThisFuncLeave();
12943 return;
12944 }
12945
12946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12947
12948 HRESULT rc = S_OK;
12949
12950 try
12951 {
12952 ComPtr<IInternalSessionControl> directControl;
12953 if (mData->mSession.mLockType == LockType_VM)
12954 directControl = mData->mSession.mDirectControl;
12955 if (directControl.isNull())
12956 throw setError(VBOX_E_INVALID_VM_STATE,
12957 tr("Trying to save state without a running VM"));
12958 alock.release();
12959 BOOL fSuspendedBySave;
12960 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12961 Assert(!fSuspendedBySave);
12962 alock.acquire();
12963
12964 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12965 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12966 throw E_FAIL);
12967
12968 if (SUCCEEDED(rc))
12969 {
12970 mSSData->strStateFilePath = task.m_strStateFilePath;
12971
12972 /* save all VM settings */
12973 rc = i_saveSettings(NULL);
12974 // no need to check whether VirtualBox.xml needs saving also since
12975 // we can't have a name change pending at this point
12976 }
12977 else
12978 {
12979 // On failure, set the state to the state we had at the beginning.
12980 i_setMachineState(task.m_machineStateBackup);
12981 i_updateMachineStateOnClient();
12982
12983 // Delete the saved state file (might have been already created).
12984 // No need to check whether this is shared with a snapshot here
12985 // because we certainly created a fresh saved state file here.
12986 RTFileDelete(task.m_strStateFilePath.c_str());
12987 }
12988 }
12989 catch (HRESULT aRC) { rc = aRC; }
12990
12991 task.m_pProgress->i_notifyComplete(rc);
12992
12993 LogFlowThisFuncLeave();
12994}
12995
12996/**
12997 * @note Locks this object for writing.
12998 */
12999HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13000{
13001 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13002}
13003
13004HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13005{
13006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13007
13008 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13009 if (FAILED(rc)) return rc;
13010
13011 if ( mData->mMachineState != MachineState_Running
13012 && mData->mMachineState != MachineState_Paused
13013 )
13014 return setError(VBOX_E_INVALID_VM_STATE,
13015 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13016 Global::stringifyMachineState(mData->mMachineState));
13017
13018 ComObjPtr<Progress> pProgress;
13019 pProgress.createObject();
13020 rc = pProgress->init(i_getVirtualBox(),
13021 static_cast<IMachine *>(this) /* aInitiator */,
13022 tr("Saving the execution state of the virtual machine"),
13023 FALSE /* aCancelable */);
13024 if (FAILED(rc))
13025 return rc;
13026
13027 Utf8Str strStateFilePath;
13028 i_composeSavedStateFilename(strStateFilePath);
13029
13030 /* create and start the task on a separate thread (note that it will not
13031 * start working until we release alock) */
13032 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13033 rc = pTask->createThread();
13034 if (FAILED(rc))
13035 return rc;
13036
13037 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13038 i_setMachineState(MachineState_Saving);
13039 i_updateMachineStateOnClient();
13040
13041 pProgress.queryInterfaceTo(aProgress.asOutParam());
13042
13043 return S_OK;
13044}
13045
13046/**
13047 * @note Locks this object for writing.
13048 */
13049HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13050{
13051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13052
13053 HRESULT rc = i_checkStateDependency(MutableStateDep);
13054 if (FAILED(rc)) return rc;
13055
13056 if ( mData->mMachineState != MachineState_PoweredOff
13057 && mData->mMachineState != MachineState_Teleported
13058 && mData->mMachineState != MachineState_Aborted
13059 )
13060 return setError(VBOX_E_INVALID_VM_STATE,
13061 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13062 Global::stringifyMachineState(mData->mMachineState));
13063
13064 com::Utf8Str stateFilePathFull;
13065 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13066 if (RT_FAILURE(vrc))
13067 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13068 tr("Invalid saved state file path '%s' (%Rrc)"),
13069 aSavedStateFile.c_str(),
13070 vrc);
13071
13072 mSSData->strStateFilePath = stateFilePathFull;
13073
13074 /* The below i_setMachineState() will detect the state transition and will
13075 * update the settings file */
13076
13077 return i_setMachineState(MachineState_Saved);
13078}
13079
13080/**
13081 * @note Locks this object for writing.
13082 */
13083HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13084{
13085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13086
13087 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13088 if (FAILED(rc)) return rc;
13089
13090 if (mData->mMachineState != MachineState_Saved)
13091 return setError(VBOX_E_INVALID_VM_STATE,
13092 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13093 Global::stringifyMachineState(mData->mMachineState));
13094
13095 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13096
13097 /*
13098 * Saved -> PoweredOff transition will be detected in the SessionMachine
13099 * and properly handled.
13100 */
13101 rc = i_setMachineState(MachineState_PoweredOff);
13102 return rc;
13103}
13104
13105
13106/**
13107 * @note Locks the same as #i_setMachineState() does.
13108 */
13109HRESULT SessionMachine::updateState(MachineState_T aState)
13110{
13111 return i_setMachineState(aState);
13112}
13113
13114/**
13115 * @note Locks this object for writing.
13116 */
13117HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13118{
13119 IProgress *pProgress(aProgress);
13120
13121 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13122
13123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13124
13125 if (mData->mSession.mState != SessionState_Locked)
13126 return VBOX_E_INVALID_OBJECT_STATE;
13127
13128 if (!mData->mSession.mProgress.isNull())
13129 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13130
13131 /* If we didn't reference the NAT network service yet, add a reference to
13132 * force a start */
13133 if (miNATNetworksStarted < 1)
13134 {
13135 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13136 {
13137 BOOL enabled;
13138 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13139 if ( FAILED(hrc)
13140 || !enabled)
13141 continue;
13142
13143 NetworkAttachmentType_T type;
13144 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13145 if ( SUCCEEDED(hrc)
13146 && type == NetworkAttachmentType_NATNetwork)
13147 {
13148 Bstr name;
13149 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13150 if (SUCCEEDED(hrc))
13151 {
13152 Utf8Str strName(name);
13153 LogRel(("VM '%s' starts using NAT network '%s'\n",
13154 mUserData->s.strName.c_str(), strName.c_str()));
13155 mPeer->lockHandle()->unlockWrite();
13156 mParent->i_natNetworkRefInc(strName);
13157#ifdef RT_LOCK_STRICT
13158 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13159#else
13160 mPeer->lockHandle()->lockWrite();
13161#endif
13162 }
13163 }
13164 }
13165 miNATNetworksStarted++;
13166 }
13167
13168 LogFlowThisFunc(("returns S_OK.\n"));
13169 return S_OK;
13170}
13171
13172/**
13173 * @note Locks this object for writing.
13174 */
13175HRESULT SessionMachine::endPowerUp(LONG aResult)
13176{
13177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13178
13179 if (mData->mSession.mState != SessionState_Locked)
13180 return VBOX_E_INVALID_OBJECT_STATE;
13181
13182 /* Finalize the LaunchVMProcess progress object. */
13183 if (mData->mSession.mProgress)
13184 {
13185 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13186 mData->mSession.mProgress.setNull();
13187 }
13188
13189 if (SUCCEEDED((HRESULT)aResult))
13190 {
13191#ifdef VBOX_WITH_RESOURCE_USAGE_API
13192 /* The VM has been powered up successfully, so it makes sense
13193 * now to offer the performance metrics for a running machine
13194 * object. Doing it earlier wouldn't be safe. */
13195 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13196 mData->mSession.mPID);
13197#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13198 }
13199
13200 return S_OK;
13201}
13202
13203/**
13204 * @note Locks this object for writing.
13205 */
13206HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13207{
13208 LogFlowThisFuncEnter();
13209
13210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13211
13212 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13213 E_FAIL);
13214
13215 /* create a progress object to track operation completion */
13216 ComObjPtr<Progress> pProgress;
13217 pProgress.createObject();
13218 pProgress->init(i_getVirtualBox(),
13219 static_cast<IMachine *>(this) /* aInitiator */,
13220 tr("Stopping the virtual machine"),
13221 FALSE /* aCancelable */);
13222
13223 /* fill in the console task data */
13224 mConsoleTaskData.mLastState = mData->mMachineState;
13225 mConsoleTaskData.mProgress = pProgress;
13226
13227 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13228 i_setMachineState(MachineState_Stopping);
13229
13230 pProgress.queryInterfaceTo(aProgress.asOutParam());
13231
13232 return S_OK;
13233}
13234
13235/**
13236 * @note Locks this object for writing.
13237 */
13238HRESULT SessionMachine::endPoweringDown(LONG aResult,
13239 const com::Utf8Str &aErrMsg)
13240{
13241 LogFlowThisFuncEnter();
13242
13243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13244
13245 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13246 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13247 && mConsoleTaskData.mLastState != MachineState_Null,
13248 E_FAIL);
13249
13250 /*
13251 * On failure, set the state to the state we had when BeginPoweringDown()
13252 * was called (this is expected by Console::PowerDown() and the associated
13253 * task). On success the VM process already changed the state to
13254 * MachineState_PoweredOff, so no need to do anything.
13255 */
13256 if (FAILED(aResult))
13257 i_setMachineState(mConsoleTaskData.mLastState);
13258
13259 /* notify the progress object about operation completion */
13260 Assert(mConsoleTaskData.mProgress);
13261 if (SUCCEEDED(aResult))
13262 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13263 else
13264 {
13265 if (aErrMsg.length())
13266 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13267 COM_IIDOF(ISession),
13268 getComponentName(),
13269 aErrMsg.c_str());
13270 else
13271 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13272 }
13273
13274 /* clear out the temporary saved state data */
13275 mConsoleTaskData.mLastState = MachineState_Null;
13276 mConsoleTaskData.mProgress.setNull();
13277
13278 LogFlowThisFuncLeave();
13279 return S_OK;
13280}
13281
13282
13283/**
13284 * Goes through the USB filters of the given machine to see if the given
13285 * device matches any filter or not.
13286 *
13287 * @note Locks the same as USBController::hasMatchingFilter() does.
13288 */
13289HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13290 BOOL *aMatched,
13291 ULONG *aMaskedInterfaces)
13292{
13293 LogFlowThisFunc(("\n"));
13294
13295#ifdef VBOX_WITH_USB
13296 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13297#else
13298 NOREF(aDevice);
13299 NOREF(aMaskedInterfaces);
13300 *aMatched = FALSE;
13301#endif
13302
13303 return S_OK;
13304}
13305
13306/**
13307 * @note Locks the same as Host::captureUSBDevice() does.
13308 */
13309HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13310{
13311 LogFlowThisFunc(("\n"));
13312
13313#ifdef VBOX_WITH_USB
13314 /* if captureDeviceForVM() fails, it must have set extended error info */
13315 clearError();
13316 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13317 if (FAILED(rc)) return rc;
13318
13319 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13320 AssertReturn(service, E_FAIL);
13321 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13322#else
13323 NOREF(aId);
13324 return E_NOTIMPL;
13325#endif
13326}
13327
13328/**
13329 * @note Locks the same as Host::detachUSBDevice() does.
13330 */
13331HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13332 BOOL aDone)
13333{
13334 LogFlowThisFunc(("\n"));
13335
13336#ifdef VBOX_WITH_USB
13337 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13338 AssertReturn(service, E_FAIL);
13339 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13340#else
13341 NOREF(aId);
13342 NOREF(aDone);
13343 return E_NOTIMPL;
13344#endif
13345}
13346
13347/**
13348 * Inserts all machine filters to the USB proxy service and then calls
13349 * Host::autoCaptureUSBDevices().
13350 *
13351 * Called by Console from the VM process upon VM startup.
13352 *
13353 * @note Locks what called methods lock.
13354 */
13355HRESULT SessionMachine::autoCaptureUSBDevices()
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359#ifdef VBOX_WITH_USB
13360 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13361 AssertComRC(rc);
13362 NOREF(rc);
13363
13364 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13365 AssertReturn(service, E_FAIL);
13366 return service->autoCaptureDevicesForVM(this);
13367#else
13368 return S_OK;
13369#endif
13370}
13371
13372/**
13373 * Removes all machine filters from the USB proxy service and then calls
13374 * Host::detachAllUSBDevices().
13375 *
13376 * Called by Console from the VM process upon normal VM termination or by
13377 * SessionMachine::uninit() upon abnormal VM termination (from under the
13378 * Machine/SessionMachine lock).
13379 *
13380 * @note Locks what called methods lock.
13381 */
13382HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13383{
13384 LogFlowThisFunc(("\n"));
13385
13386#ifdef VBOX_WITH_USB
13387 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13388 AssertComRC(rc);
13389 NOREF(rc);
13390
13391 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13392 AssertReturn(service, E_FAIL);
13393 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13394#else
13395 NOREF(aDone);
13396 return S_OK;
13397#endif
13398}
13399
13400/**
13401 * @note Locks this object for writing.
13402 */
13403HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13404 ComPtr<IProgress> &aProgress)
13405{
13406 LogFlowThisFuncEnter();
13407
13408 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13409 /*
13410 * We don't assert below because it might happen that a non-direct session
13411 * informs us it is closed right after we've been uninitialized -- it's ok.
13412 */
13413
13414 /* get IInternalSessionControl interface */
13415 ComPtr<IInternalSessionControl> control(aSession);
13416
13417 ComAssertRet(!control.isNull(), E_INVALIDARG);
13418
13419 /* Creating a Progress object requires the VirtualBox lock, and
13420 * thus locking it here is required by the lock order rules. */
13421 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13422
13423 if (control == mData->mSession.mDirectControl)
13424 {
13425 /* The direct session is being normally closed by the client process
13426 * ----------------------------------------------------------------- */
13427
13428 /* go to the closing state (essential for all open*Session() calls and
13429 * for #i_checkForDeath()) */
13430 Assert(mData->mSession.mState == SessionState_Locked);
13431 mData->mSession.mState = SessionState_Unlocking;
13432
13433 /* set direct control to NULL to release the remote instance */
13434 mData->mSession.mDirectControl.setNull();
13435 LogFlowThisFunc(("Direct control is set to NULL\n"));
13436
13437 if (mData->mSession.mProgress)
13438 {
13439 /* finalize the progress, someone might wait if a frontend
13440 * closes the session before powering on the VM. */
13441 mData->mSession.mProgress->notifyComplete(E_FAIL,
13442 COM_IIDOF(ISession),
13443 getComponentName(),
13444 tr("The VM session was closed before any attempt to power it on"));
13445 mData->mSession.mProgress.setNull();
13446 }
13447
13448 /* Create the progress object the client will use to wait until
13449 * #i_checkForDeath() is called to uninitialize this session object after
13450 * it releases the IPC semaphore.
13451 * Note! Because we're "reusing" mProgress here, this must be a proxy
13452 * object just like for LaunchVMProcess. */
13453 Assert(mData->mSession.mProgress.isNull());
13454 ComObjPtr<ProgressProxy> progress;
13455 progress.createObject();
13456 ComPtr<IUnknown> pPeer(mPeer);
13457 progress->init(mParent, pPeer,
13458 Bstr(tr("Closing session")).raw(),
13459 FALSE /* aCancelable */);
13460 progress.queryInterfaceTo(aProgress.asOutParam());
13461 mData->mSession.mProgress = progress;
13462 }
13463 else
13464 {
13465 /* the remote session is being normally closed */
13466 bool found = false;
13467 for (Data::Session::RemoteControlList::iterator
13468 it = mData->mSession.mRemoteControls.begin();
13469 it != mData->mSession.mRemoteControls.end();
13470 ++it)
13471 {
13472 if (control == *it)
13473 {
13474 found = true;
13475 // This MUST be erase(it), not remove(*it) as the latter
13476 // triggers a very nasty use after free due to the place where
13477 // the value "lives".
13478 mData->mSession.mRemoteControls.erase(it);
13479 break;
13480 }
13481 }
13482 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13483 E_INVALIDARG);
13484 }
13485
13486 /* signal the client watcher thread, because the client is going away */
13487 mParent->i_updateClientWatcher();
13488
13489 LogFlowThisFuncLeave();
13490 return S_OK;
13491}
13492
13493HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13494 std::vector<com::Utf8Str> &aValues,
13495 std::vector<LONG64> &aTimestamps,
13496 std::vector<com::Utf8Str> &aFlags)
13497{
13498 LogFlowThisFunc(("\n"));
13499
13500#ifdef VBOX_WITH_GUEST_PROPS
13501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13502
13503 size_t cEntries = mHWData->mGuestProperties.size();
13504 aNames.resize(cEntries);
13505 aValues.resize(cEntries);
13506 aTimestamps.resize(cEntries);
13507 aFlags.resize(cEntries);
13508
13509 size_t i = 0;
13510 for (HWData::GuestPropertyMap::const_iterator
13511 it = mHWData->mGuestProperties.begin();
13512 it != mHWData->mGuestProperties.end();
13513 ++it, ++i)
13514 {
13515 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13516 aNames[i] = it->first;
13517 aValues[i] = it->second.strValue;
13518 aTimestamps[i] = it->second.mTimestamp;
13519
13520 /* If it is NULL, keep it NULL. */
13521 if (it->second.mFlags)
13522 {
13523 GuestPropWriteFlags(it->second.mFlags, szFlags);
13524 aFlags[i] = szFlags;
13525 }
13526 else
13527 aFlags[i] = "";
13528 }
13529 return S_OK;
13530#else
13531 ReturnComNotImplemented();
13532#endif
13533}
13534
13535HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13536 const com::Utf8Str &aValue,
13537 LONG64 aTimestamp,
13538 const com::Utf8Str &aFlags)
13539{
13540 LogFlowThisFunc(("\n"));
13541
13542#ifdef VBOX_WITH_GUEST_PROPS
13543 try
13544 {
13545 /*
13546 * Convert input up front.
13547 */
13548 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13549 if (aFlags.length())
13550 {
13551 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13552 AssertRCReturn(vrc, E_INVALIDARG);
13553 }
13554
13555 /*
13556 * Now grab the object lock, validate the state and do the update.
13557 */
13558
13559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13560
13561 if (!Global::IsOnline(mData->mMachineState))
13562 {
13563 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13564 VBOX_E_INVALID_VM_STATE);
13565 }
13566
13567 i_setModified(IsModified_MachineData);
13568 mHWData.backup();
13569
13570 bool fDelete = !aValue.length();
13571 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13572 if (it != mHWData->mGuestProperties.end())
13573 {
13574 if (!fDelete)
13575 {
13576 it->second.strValue = aValue;
13577 it->second.mTimestamp = aTimestamp;
13578 it->second.mFlags = fFlags;
13579 }
13580 else
13581 mHWData->mGuestProperties.erase(it);
13582
13583 mData->mGuestPropertiesModified = TRUE;
13584 }
13585 else if (!fDelete)
13586 {
13587 HWData::GuestProperty prop;
13588 prop.strValue = aValue;
13589 prop.mTimestamp = aTimestamp;
13590 prop.mFlags = fFlags;
13591
13592 mHWData->mGuestProperties[aName] = prop;
13593 mData->mGuestPropertiesModified = TRUE;
13594 }
13595
13596 alock.release();
13597
13598 mParent->i_onGuestPropertyChange(mData->mUuid,
13599 Bstr(aName).raw(),
13600 Bstr(aValue).raw(),
13601 Bstr(aFlags).raw());
13602 }
13603 catch (...)
13604 {
13605 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13606 }
13607 return S_OK;
13608#else
13609 ReturnComNotImplemented();
13610#endif
13611}
13612
13613
13614HRESULT SessionMachine::lockMedia()
13615{
13616 AutoMultiWriteLock2 alock(this->lockHandle(),
13617 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13618
13619 AssertReturn( mData->mMachineState == MachineState_Starting
13620 || mData->mMachineState == MachineState_Restoring
13621 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13622
13623 clearError();
13624 alock.release();
13625 return i_lockMedia();
13626}
13627
13628HRESULT SessionMachine::unlockMedia()
13629{
13630 HRESULT hrc = i_unlockMedia();
13631 return hrc;
13632}
13633
13634HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13635 ComPtr<IMediumAttachment> &aNewAttachment)
13636{
13637 // request the host lock first, since might be calling Host methods for getting host drives;
13638 // next, protect the media tree all the while we're in here, as well as our member variables
13639 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13640 this->lockHandle(),
13641 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13642
13643 IMediumAttachment *iAttach = aAttachment;
13644 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13645
13646 Utf8Str ctrlName;
13647 LONG lPort;
13648 LONG lDevice;
13649 bool fTempEject;
13650 {
13651 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13652
13653 /* Need to query the details first, as the IMediumAttachment reference
13654 * might be to the original settings, which we are going to change. */
13655 ctrlName = pAttach->i_getControllerName();
13656 lPort = pAttach->i_getPort();
13657 lDevice = pAttach->i_getDevice();
13658 fTempEject = pAttach->i_getTempEject();
13659 }
13660
13661 if (!fTempEject)
13662 {
13663 /* Remember previously mounted medium. The medium before taking the
13664 * backup is not necessarily the same thing. */
13665 ComObjPtr<Medium> oldmedium;
13666 oldmedium = pAttach->i_getMedium();
13667
13668 i_setModified(IsModified_Storage);
13669 mMediumAttachments.backup();
13670
13671 // The backup operation makes the pAttach reference point to the
13672 // old settings. Re-get the correct reference.
13673 pAttach = i_findAttachment(*mMediumAttachments.data(),
13674 ctrlName,
13675 lPort,
13676 lDevice);
13677
13678 {
13679 AutoCaller autoAttachCaller(this);
13680 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13681
13682 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13683 if (!oldmedium.isNull())
13684 oldmedium->i_removeBackReference(mData->mUuid);
13685
13686 pAttach->i_updateMedium(NULL);
13687 pAttach->i_updateEjected();
13688 }
13689
13690 i_setModified(IsModified_Storage);
13691 }
13692 else
13693 {
13694 {
13695 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13696 pAttach->i_updateEjected();
13697 }
13698 }
13699
13700 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13701
13702 return S_OK;
13703}
13704
13705HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13706 com::Utf8Str &aResult)
13707{
13708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13709
13710 HRESULT hr = S_OK;
13711
13712 if (!mAuthLibCtx.hAuthLibrary)
13713 {
13714 /* Load the external authentication library. */
13715 Bstr authLibrary;
13716 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13717
13718 Utf8Str filename = authLibrary;
13719
13720 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13721 if (RT_FAILURE(vrc))
13722 hr = setErrorBoth(E_FAIL, vrc,
13723 tr("Could not load the external authentication library '%s' (%Rrc)"),
13724 filename.c_str(), vrc);
13725 }
13726
13727 /* The auth library might need the machine lock. */
13728 alock.release();
13729
13730 if (FAILED(hr))
13731 return hr;
13732
13733 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13734 {
13735 enum VRDEAuthParams
13736 {
13737 parmUuid = 1,
13738 parmGuestJudgement,
13739 parmUser,
13740 parmPassword,
13741 parmDomain,
13742 parmClientId
13743 };
13744
13745 AuthResult result = AuthResultAccessDenied;
13746
13747 Guid uuid(aAuthParams[parmUuid]);
13748 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13749 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13750
13751 result = AuthLibAuthenticate(&mAuthLibCtx,
13752 uuid.raw(), guestJudgement,
13753 aAuthParams[parmUser].c_str(),
13754 aAuthParams[parmPassword].c_str(),
13755 aAuthParams[parmDomain].c_str(),
13756 u32ClientId);
13757
13758 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13759 size_t cbPassword = aAuthParams[parmPassword].length();
13760 if (cbPassword)
13761 {
13762 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13763 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13764 }
13765
13766 if (result == AuthResultAccessGranted)
13767 aResult = "granted";
13768 else
13769 aResult = "denied";
13770
13771 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13772 aAuthParams[parmUser].c_str(), aResult.c_str()));
13773 }
13774 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13775 {
13776 enum VRDEAuthDisconnectParams
13777 {
13778 parmUuid = 1,
13779 parmClientId
13780 };
13781
13782 Guid uuid(aAuthParams[parmUuid]);
13783 uint32_t u32ClientId = 0;
13784 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13785 }
13786 else
13787 {
13788 hr = E_INVALIDARG;
13789 }
13790
13791 return hr;
13792}
13793
13794// public methods only for internal purposes
13795/////////////////////////////////////////////////////////////////////////////
13796
13797#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13798/**
13799 * Called from the client watcher thread to check for expected or unexpected
13800 * death of the client process that has a direct session to this machine.
13801 *
13802 * On Win32 and on OS/2, this method is called only when we've got the
13803 * mutex (i.e. the client has either died or terminated normally) so it always
13804 * returns @c true (the client is terminated, the session machine is
13805 * uninitialized).
13806 *
13807 * On other platforms, the method returns @c true if the client process has
13808 * terminated normally or abnormally and the session machine was uninitialized,
13809 * and @c false if the client process is still alive.
13810 *
13811 * @note Locks this object for writing.
13812 */
13813bool SessionMachine::i_checkForDeath()
13814{
13815 Uninit::Reason reason;
13816 bool terminated = false;
13817
13818 /* Enclose autoCaller with a block because calling uninit() from under it
13819 * will deadlock. */
13820 {
13821 AutoCaller autoCaller(this);
13822 if (!autoCaller.isOk())
13823 {
13824 /* return true if not ready, to cause the client watcher to exclude
13825 * the corresponding session from watching */
13826 LogFlowThisFunc(("Already uninitialized!\n"));
13827 return true;
13828 }
13829
13830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13831
13832 /* Determine the reason of death: if the session state is Closing here,
13833 * everything is fine. Otherwise it means that the client did not call
13834 * OnSessionEnd() before it released the IPC semaphore. This may happen
13835 * either because the client process has abnormally terminated, or
13836 * because it simply forgot to call ISession::Close() before exiting. We
13837 * threat the latter also as an abnormal termination (see
13838 * Session::uninit() for details). */
13839 reason = mData->mSession.mState == SessionState_Unlocking ?
13840 Uninit::Normal :
13841 Uninit::Abnormal;
13842
13843 if (mClientToken)
13844 terminated = mClientToken->release();
13845 } /* AutoCaller block */
13846
13847 if (terminated)
13848 uninit(reason);
13849
13850 return terminated;
13851}
13852
13853void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13854{
13855 LogFlowThisFunc(("\n"));
13856
13857 strTokenId.setNull();
13858
13859 AutoCaller autoCaller(this);
13860 AssertComRCReturnVoid(autoCaller.rc());
13861
13862 Assert(mClientToken);
13863 if (mClientToken)
13864 mClientToken->getId(strTokenId);
13865}
13866#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13867IToken *SessionMachine::i_getToken()
13868{
13869 LogFlowThisFunc(("\n"));
13870
13871 AutoCaller autoCaller(this);
13872 AssertComRCReturn(autoCaller.rc(), NULL);
13873
13874 Assert(mClientToken);
13875 if (mClientToken)
13876 return mClientToken->getToken();
13877 else
13878 return NULL;
13879}
13880#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13881
13882Machine::ClientToken *SessionMachine::i_getClientToken()
13883{
13884 LogFlowThisFunc(("\n"));
13885
13886 AutoCaller autoCaller(this);
13887 AssertComRCReturn(autoCaller.rc(), NULL);
13888
13889 return mClientToken;
13890}
13891
13892
13893/**
13894 * @note Locks this object for reading.
13895 */
13896HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13897{
13898 LogFlowThisFunc(("\n"));
13899
13900 AutoCaller autoCaller(this);
13901 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13902
13903 ComPtr<IInternalSessionControl> directControl;
13904 {
13905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13906 if (mData->mSession.mLockType == LockType_VM)
13907 directControl = mData->mSession.mDirectControl;
13908 }
13909
13910 /* ignore notifications sent after #OnSessionEnd() is called */
13911 if (!directControl)
13912 return S_OK;
13913
13914 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13915}
13916
13917/**
13918 * @note Locks this object for reading.
13919 */
13920HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13921 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13922 IN_BSTR aGuestIp, LONG aGuestPort)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13928
13929 ComPtr<IInternalSessionControl> directControl;
13930 {
13931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13932 if (mData->mSession.mLockType == LockType_VM)
13933 directControl = mData->mSession.mDirectControl;
13934 }
13935
13936 /* ignore notifications sent after #OnSessionEnd() is called */
13937 if (!directControl)
13938 return S_OK;
13939 /*
13940 * instead acting like callback we ask IVirtualBox deliver corresponding event
13941 */
13942
13943 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13944 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13945 return S_OK;
13946}
13947
13948/**
13949 * @note Locks this object for reading.
13950 */
13951HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13952{
13953 LogFlowThisFunc(("\n"));
13954
13955 AutoCaller autoCaller(this);
13956 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13957
13958 ComPtr<IInternalSessionControl> directControl;
13959 {
13960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13961 if (mData->mSession.mLockType == LockType_VM)
13962 directControl = mData->mSession.mDirectControl;
13963 }
13964
13965 /* ignore notifications sent after #OnSessionEnd() is called */
13966 if (!directControl)
13967 return S_OK;
13968
13969 return directControl->OnAudioAdapterChange(audioAdapter);
13970}
13971
13972/**
13973 * @note Locks this object for reading.
13974 */
13975HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13976{
13977 LogFlowThisFunc(("\n"));
13978
13979 AutoCaller autoCaller(this);
13980 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13981
13982 ComPtr<IInternalSessionControl> directControl;
13983 {
13984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13985 if (mData->mSession.mLockType == LockType_VM)
13986 directControl = mData->mSession.mDirectControl;
13987 }
13988
13989 /* ignore notifications sent after #OnSessionEnd() is called */
13990 if (!directControl)
13991 return S_OK;
13992
13993 return directControl->OnSerialPortChange(serialPort);
13994}
13995
13996/**
13997 * @note Locks this object for reading.
13998 */
13999HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14000{
14001 LogFlowThisFunc(("\n"));
14002
14003 AutoCaller autoCaller(this);
14004 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14005
14006 ComPtr<IInternalSessionControl> directControl;
14007 {
14008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14009 if (mData->mSession.mLockType == LockType_VM)
14010 directControl = mData->mSession.mDirectControl;
14011 }
14012
14013 /* ignore notifications sent after #OnSessionEnd() is called */
14014 if (!directControl)
14015 return S_OK;
14016
14017 return directControl->OnParallelPortChange(parallelPort);
14018}
14019
14020/**
14021 * @note Locks this object for reading.
14022 */
14023HRESULT SessionMachine::i_onStorageControllerChange()
14024{
14025 LogFlowThisFunc(("\n"));
14026
14027 AutoCaller autoCaller(this);
14028 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14029
14030 ComPtr<IInternalSessionControl> directControl;
14031 {
14032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14033 if (mData->mSession.mLockType == LockType_VM)
14034 directControl = mData->mSession.mDirectControl;
14035 }
14036
14037 /* ignore notifications sent after #OnSessionEnd() is called */
14038 if (!directControl)
14039 return S_OK;
14040
14041 return directControl->OnStorageControllerChange();
14042}
14043
14044/**
14045 * @note Locks this object for reading.
14046 */
14047HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14048{
14049 LogFlowThisFunc(("\n"));
14050
14051 AutoCaller autoCaller(this);
14052 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14053
14054 ComPtr<IInternalSessionControl> directControl;
14055 {
14056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14057 if (mData->mSession.mLockType == LockType_VM)
14058 directControl = mData->mSession.mDirectControl;
14059 }
14060
14061 mParent->i_onMediumChanged(aAttachment);
14062
14063 /* ignore notifications sent after #OnSessionEnd() is called */
14064 if (!directControl)
14065 return S_OK;
14066
14067 return directControl->OnMediumChange(aAttachment, aForce);
14068}
14069
14070/**
14071 * @note Locks this object for reading.
14072 */
14073HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
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->OnCPUChange(aCPU, aRemove);
14092}
14093
14094HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14100
14101 ComPtr<IInternalSessionControl> directControl;
14102 {
14103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14104 if (mData->mSession.mLockType == LockType_VM)
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
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->OnVRDEServerChange(aRestart);
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
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->OnRecordingChange(aEnable);
14161}
14162
14163/**
14164 * @note Locks this object for reading.
14165 */
14166HRESULT SessionMachine::i_onUSBControllerChange()
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->OnUSBControllerChange();
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onSharedFolderChange()
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturnRC(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->OnSharedFolderChange(FALSE /* aGlobal */);
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
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->OnClipboardModeChange(aClipboardMode);
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
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->OnDnDModeChange(aDnDMode);
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturn(autoCaller.rc(), 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->OnBandwidthGroupChange(aBandwidthGroup);
14281}
14282
14283/**
14284 * @note Locks this object for reading.
14285 */
14286HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
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 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14301
14302 /* ignore notifications sent after #OnSessionEnd() is called */
14303 if (!directControl)
14304 return S_OK;
14305
14306 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14307}
14308
14309/**
14310 * Returns @c true if this machine's USB controller reports it has a matching
14311 * filter for the given USB device and @c false otherwise.
14312 *
14313 * @note locks this object for reading.
14314 */
14315bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14316{
14317 AutoCaller autoCaller(this);
14318 /* silently return if not ready -- this method may be called after the
14319 * direct machine session has been called */
14320 if (!autoCaller.isOk())
14321 return false;
14322
14323#ifdef VBOX_WITH_USB
14324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14325
14326 switch (mData->mMachineState)
14327 {
14328 case MachineState_Starting:
14329 case MachineState_Restoring:
14330 case MachineState_TeleportingIn:
14331 case MachineState_Paused:
14332 case MachineState_Running:
14333 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14334 * elsewhere... */
14335 alock.release();
14336 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14337 default: break;
14338 }
14339#else
14340 NOREF(aDevice);
14341 NOREF(aMaskedIfs);
14342#endif
14343 return false;
14344}
14345
14346/**
14347 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14348 */
14349HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14350 IVirtualBoxErrorInfo *aError,
14351 ULONG aMaskedIfs,
14352 const com::Utf8Str &aCaptureFilename)
14353{
14354 LogFlowThisFunc(("\n"));
14355
14356 AutoCaller autoCaller(this);
14357
14358 /* This notification may happen after the machine object has been
14359 * uninitialized (the session was closed), so don't assert. */
14360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14361
14362 ComPtr<IInternalSessionControl> directControl;
14363 {
14364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14365 if (mData->mSession.mLockType == LockType_VM)
14366 directControl = mData->mSession.mDirectControl;
14367 }
14368
14369 /* fail on notifications sent after #OnSessionEnd() is called, it is
14370 * expected by the caller */
14371 if (!directControl)
14372 return E_FAIL;
14373
14374 /* No locks should be held at this point. */
14375 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14376 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14377
14378 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14379}
14380
14381/**
14382 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14383 */
14384HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14385 IVirtualBoxErrorInfo *aError)
14386{
14387 LogFlowThisFunc(("\n"));
14388
14389 AutoCaller autoCaller(this);
14390
14391 /* This notification may happen after the machine object has been
14392 * uninitialized (the session was closed), so don't assert. */
14393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14394
14395 ComPtr<IInternalSessionControl> directControl;
14396 {
14397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14398 if (mData->mSession.mLockType == LockType_VM)
14399 directControl = mData->mSession.mDirectControl;
14400 }
14401
14402 /* fail on notifications sent after #OnSessionEnd() is called, it is
14403 * expected by the caller */
14404 if (!directControl)
14405 return E_FAIL;
14406
14407 /* No locks should be held at this point. */
14408 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14409 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14410
14411 return directControl->OnUSBDeviceDetach(aId, aError);
14412}
14413
14414// protected methods
14415/////////////////////////////////////////////////////////////////////////////
14416
14417/**
14418 * Deletes the given file if it is no longer in use by either the current machine state
14419 * (if the machine is "saved") or any of the machine's snapshots.
14420 *
14421 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14422 * but is different for each SnapshotMachine. When calling this, the order of calling this
14423 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14424 * is therefore critical. I know, it's all rather messy.
14425 *
14426 * @param strStateFile
14427 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14428 * the test for whether the saved state file is in use.
14429 */
14430void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14431 Snapshot *pSnapshotToIgnore)
14432{
14433 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14434 if ( (strStateFile.isNotEmpty())
14435 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14436 )
14437 // ... and it must also not be shared with other snapshots
14438 if ( !mData->mFirstSnapshot
14439 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14440 // this checks the SnapshotMachine's state file paths
14441 )
14442 RTFileDelete(strStateFile.c_str());
14443}
14444
14445/**
14446 * Locks the attached media.
14447 *
14448 * All attached hard disks are locked for writing and DVD/floppy are locked for
14449 * reading. Parents of attached hard disks (if any) are locked for reading.
14450 *
14451 * This method also performs accessibility check of all media it locks: if some
14452 * media is inaccessible, the method will return a failure and a bunch of
14453 * extended error info objects per each inaccessible medium.
14454 *
14455 * Note that this method is atomic: if it returns a success, all media are
14456 * locked as described above; on failure no media is locked at all (all
14457 * succeeded individual locks will be undone).
14458 *
14459 * The caller is responsible for doing the necessary state sanity checks.
14460 *
14461 * The locks made by this method must be undone by calling #unlockMedia() when
14462 * no more needed.
14463 */
14464HRESULT SessionMachine::i_lockMedia()
14465{
14466 AutoCaller autoCaller(this);
14467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14468
14469 AutoMultiWriteLock2 alock(this->lockHandle(),
14470 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14471
14472 /* bail out if trying to lock things with already set up locking */
14473 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14474
14475 MultiResult mrc(S_OK);
14476
14477 /* Collect locking information for all medium objects attached to the VM. */
14478 for (MediumAttachmentList::const_iterator
14479 it = mMediumAttachments->begin();
14480 it != mMediumAttachments->end();
14481 ++it)
14482 {
14483 MediumAttachment *pAtt = *it;
14484 DeviceType_T devType = pAtt->i_getType();
14485 Medium *pMedium = pAtt->i_getMedium();
14486
14487 MediumLockList *pMediumLockList(new MediumLockList());
14488 // There can be attachments without a medium (floppy/dvd), and thus
14489 // it's impossible to create a medium lock list. It still makes sense
14490 // to have the empty medium lock list in the map in case a medium is
14491 // attached later.
14492 if (pMedium != NULL)
14493 {
14494 MediumType_T mediumType = pMedium->i_getType();
14495 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14496 || mediumType == MediumType_Shareable;
14497 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14498
14499 alock.release();
14500 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14501 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14502 false /* fMediumLockWriteAll */,
14503 NULL,
14504 *pMediumLockList);
14505 alock.acquire();
14506 if (FAILED(mrc))
14507 {
14508 delete pMediumLockList;
14509 mData->mSession.mLockedMedia.Clear();
14510 break;
14511 }
14512 }
14513
14514 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14515 if (FAILED(rc))
14516 {
14517 mData->mSession.mLockedMedia.Clear();
14518 mrc = setError(rc,
14519 tr("Collecting locking information for all attached media failed"));
14520 break;
14521 }
14522 }
14523
14524 if (SUCCEEDED(mrc))
14525 {
14526 /* Now lock all media. If this fails, nothing is locked. */
14527 alock.release();
14528 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14529 alock.acquire();
14530 if (FAILED(rc))
14531 {
14532 mrc = setError(rc,
14533 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14534 }
14535 }
14536
14537 return mrc;
14538}
14539
14540/**
14541 * Undoes the locks made by by #lockMedia().
14542 */
14543HRESULT SessionMachine::i_unlockMedia()
14544{
14545 AutoCaller autoCaller(this);
14546 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14547
14548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14549
14550 /* we may be holding important error info on the current thread;
14551 * preserve it */
14552 ErrorInfoKeeper eik;
14553
14554 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14555 AssertComRC(rc);
14556 return rc;
14557}
14558
14559/**
14560 * Helper to change the machine state (reimplementation).
14561 *
14562 * @note Locks this object for writing.
14563 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14564 * it can cause crashes in random places due to unexpectedly committing
14565 * the current settings. The caller is responsible for that. The call
14566 * to saveStateSettings is fine, because this method does not commit.
14567 */
14568HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14569{
14570 LogFlowThisFuncEnter();
14571 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14572
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14575
14576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14577
14578 MachineState_T oldMachineState = mData->mMachineState;
14579
14580 AssertMsgReturn(oldMachineState != aMachineState,
14581 ("oldMachineState=%s, aMachineState=%s\n",
14582 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14583 E_FAIL);
14584
14585 HRESULT rc = S_OK;
14586
14587 int stsFlags = 0;
14588 bool deleteSavedState = false;
14589
14590 /* detect some state transitions */
14591
14592 if ( ( oldMachineState == MachineState_Saved
14593 && aMachineState == MachineState_Restoring)
14594 || ( ( oldMachineState == MachineState_PoweredOff
14595 || oldMachineState == MachineState_Teleported
14596 || oldMachineState == MachineState_Aborted
14597 )
14598 && ( aMachineState == MachineState_TeleportingIn
14599 || aMachineState == MachineState_Starting
14600 )
14601 )
14602 )
14603 {
14604 /* The EMT thread is about to start */
14605
14606 /* Nothing to do here for now... */
14607
14608 /// @todo NEWMEDIA don't let mDVDDrive and other children
14609 /// change anything when in the Starting/Restoring state
14610 }
14611 else if ( ( oldMachineState == MachineState_Running
14612 || oldMachineState == MachineState_Paused
14613 || oldMachineState == MachineState_Teleporting
14614 || oldMachineState == MachineState_OnlineSnapshotting
14615 || oldMachineState == MachineState_LiveSnapshotting
14616 || oldMachineState == MachineState_Stuck
14617 || oldMachineState == MachineState_Starting
14618 || oldMachineState == MachineState_Stopping
14619 || oldMachineState == MachineState_Saving
14620 || oldMachineState == MachineState_Restoring
14621 || oldMachineState == MachineState_TeleportingPausedVM
14622 || oldMachineState == MachineState_TeleportingIn
14623 )
14624 && ( aMachineState == MachineState_PoweredOff
14625 || aMachineState == MachineState_Saved
14626 || aMachineState == MachineState_Teleported
14627 || aMachineState == MachineState_Aborted
14628 )
14629 )
14630 {
14631 /* The EMT thread has just stopped, unlock attached media. Note that as
14632 * opposed to locking that is done from Console, we do unlocking here
14633 * because the VM process may have aborted before having a chance to
14634 * properly unlock all media it locked. */
14635
14636 unlockMedia();
14637 }
14638
14639 if (oldMachineState == MachineState_Restoring)
14640 {
14641 if (aMachineState != MachineState_Saved)
14642 {
14643 /*
14644 * delete the saved state file once the machine has finished
14645 * restoring from it (note that Console sets the state from
14646 * Restoring to Saved if the VM couldn't restore successfully,
14647 * to give the user an ability to fix an error and retry --
14648 * we keep the saved state file in this case)
14649 */
14650 deleteSavedState = true;
14651 }
14652 }
14653 else if ( oldMachineState == MachineState_Saved
14654 && ( aMachineState == MachineState_PoweredOff
14655 || aMachineState == MachineState_Aborted
14656 || aMachineState == MachineState_Teleported
14657 )
14658 )
14659 {
14660 /*
14661 * delete the saved state after SessionMachine::ForgetSavedState() is called
14662 * or if the VM process (owning a direct VM session) crashed while the
14663 * VM was Saved
14664 */
14665
14666 /// @todo (dmik)
14667 // Not sure that deleting the saved state file just because of the
14668 // client death before it attempted to restore the VM is a good
14669 // thing. But when it crashes we need to go to the Aborted state
14670 // which cannot have the saved state file associated... The only
14671 // way to fix this is to make the Aborted condition not a VM state
14672 // but a bool flag: i.e., when a crash occurs, set it to true and
14673 // change the state to PoweredOff or Saved depending on the
14674 // saved state presence.
14675
14676 deleteSavedState = true;
14677 mData->mCurrentStateModified = TRUE;
14678 stsFlags |= SaveSTS_CurStateModified;
14679 }
14680
14681 if ( aMachineState == MachineState_Starting
14682 || aMachineState == MachineState_Restoring
14683 || aMachineState == MachineState_TeleportingIn
14684 )
14685 {
14686 /* set the current state modified flag to indicate that the current
14687 * state is no more identical to the state in the
14688 * current snapshot */
14689 if (!mData->mCurrentSnapshot.isNull())
14690 {
14691 mData->mCurrentStateModified = TRUE;
14692 stsFlags |= SaveSTS_CurStateModified;
14693 }
14694 }
14695
14696 if (deleteSavedState)
14697 {
14698 if (mRemoveSavedState)
14699 {
14700 Assert(!mSSData->strStateFilePath.isEmpty());
14701
14702 // it is safe to delete the saved state file if ...
14703 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14704 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14705 // ... none of the snapshots share the saved state file
14706 )
14707 RTFileDelete(mSSData->strStateFilePath.c_str());
14708 }
14709
14710 mSSData->strStateFilePath.setNull();
14711 stsFlags |= SaveSTS_StateFilePath;
14712 }
14713
14714 /* redirect to the underlying peer machine */
14715 mPeer->i_setMachineState(aMachineState);
14716
14717 if ( oldMachineState != MachineState_RestoringSnapshot
14718 && ( aMachineState == MachineState_PoweredOff
14719 || aMachineState == MachineState_Teleported
14720 || aMachineState == MachineState_Aborted
14721 || aMachineState == MachineState_Saved))
14722 {
14723 /* the machine has stopped execution
14724 * (or the saved state file was adopted) */
14725 stsFlags |= SaveSTS_StateTimeStamp;
14726 }
14727
14728 if ( ( oldMachineState == MachineState_PoweredOff
14729 || oldMachineState == MachineState_Aborted
14730 || oldMachineState == MachineState_Teleported
14731 )
14732 && aMachineState == MachineState_Saved)
14733 {
14734 /* the saved state file was adopted */
14735 Assert(!mSSData->strStateFilePath.isEmpty());
14736 stsFlags |= SaveSTS_StateFilePath;
14737 }
14738
14739#ifdef VBOX_WITH_GUEST_PROPS
14740 if ( aMachineState == MachineState_PoweredOff
14741 || aMachineState == MachineState_Aborted
14742 || aMachineState == MachineState_Teleported)
14743 {
14744 /* Make sure any transient guest properties get removed from the
14745 * property store on shutdown. */
14746 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14747
14748 /* remove it from the settings representation */
14749 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14750 for (settings::GuestPropertiesList::iterator
14751 it = llGuestProperties.begin();
14752 it != llGuestProperties.end();
14753 /*nothing*/)
14754 {
14755 const settings::GuestProperty &prop = *it;
14756 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14757 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14758 {
14759 it = llGuestProperties.erase(it);
14760 fNeedsSaving = true;
14761 }
14762 else
14763 {
14764 ++it;
14765 }
14766 }
14767
14768 /* Additionally remove it from the HWData representation. Required to
14769 * keep everything in sync, as this is what the API keeps using. */
14770 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14771 for (HWData::GuestPropertyMap::iterator
14772 it = llHWGuestProperties.begin();
14773 it != llHWGuestProperties.end();
14774 /*nothing*/)
14775 {
14776 uint32_t fFlags = it->second.mFlags;
14777 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14778 {
14779 /* iterator where we need to continue after the erase call
14780 * (C++03 is a fact still, and it doesn't return the iterator
14781 * which would allow continuing) */
14782 HWData::GuestPropertyMap::iterator it2 = it;
14783 ++it2;
14784 llHWGuestProperties.erase(it);
14785 it = it2;
14786 fNeedsSaving = true;
14787 }
14788 else
14789 {
14790 ++it;
14791 }
14792 }
14793
14794 if (fNeedsSaving)
14795 {
14796 mData->mCurrentStateModified = TRUE;
14797 stsFlags |= SaveSTS_CurStateModified;
14798 }
14799 }
14800#endif /* VBOX_WITH_GUEST_PROPS */
14801
14802 rc = i_saveStateSettings(stsFlags);
14803
14804 if ( ( oldMachineState != MachineState_PoweredOff
14805 && oldMachineState != MachineState_Aborted
14806 && oldMachineState != MachineState_Teleported
14807 )
14808 && ( aMachineState == MachineState_PoweredOff
14809 || aMachineState == MachineState_Aborted
14810 || aMachineState == MachineState_Teleported
14811 )
14812 )
14813 {
14814 /* we've been shut down for any reason */
14815 /* no special action so far */
14816 }
14817
14818 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14819 LogFlowThisFuncLeave();
14820 return rc;
14821}
14822
14823/**
14824 * Sends the current machine state value to the VM process.
14825 *
14826 * @note Locks this object for reading, then calls a client process.
14827 */
14828HRESULT SessionMachine::i_updateMachineStateOnClient()
14829{
14830 AutoCaller autoCaller(this);
14831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14832
14833 ComPtr<IInternalSessionControl> directControl;
14834 {
14835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14836 AssertReturn(!!mData, E_FAIL);
14837 if (mData->mSession.mLockType == LockType_VM)
14838 directControl = mData->mSession.mDirectControl;
14839
14840 /* directControl may be already set to NULL here in #OnSessionEnd()
14841 * called too early by the direct session process while there is still
14842 * some operation (like deleting the snapshot) in progress. The client
14843 * process in this case is waiting inside Session::close() for the
14844 * "end session" process object to complete, while #uninit() called by
14845 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14846 * operation to complete. For now, we accept this inconsistent behavior
14847 * and simply do nothing here. */
14848
14849 if (mData->mSession.mState == SessionState_Unlocking)
14850 return S_OK;
14851 }
14852
14853 /* ignore notifications sent after #OnSessionEnd() is called */
14854 if (!directControl)
14855 return S_OK;
14856
14857 return directControl->UpdateMachineState(mData->mMachineState);
14858}
14859
14860
14861/*static*/
14862HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14863{
14864 va_list args;
14865 va_start(args, pcszMsg);
14866 HRESULT rc = setErrorInternal(aResultCode,
14867 getStaticClassIID(),
14868 getStaticComponentName(),
14869 Utf8Str(pcszMsg, args),
14870 false /* aWarning */,
14871 true /* aLogIt */);
14872 va_end(args);
14873 return rc;
14874}
14875
14876
14877HRESULT Machine::updateState(MachineState_T aState)
14878{
14879 NOREF(aState);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14884{
14885 NOREF(aProgress);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::endPowerUp(LONG aResult)
14890{
14891 NOREF(aResult);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14896{
14897 NOREF(aProgress);
14898 ReturnComNotImplemented();
14899}
14900
14901HRESULT Machine::endPoweringDown(LONG aResult,
14902 const com::Utf8Str &aErrMsg)
14903{
14904 NOREF(aResult);
14905 NOREF(aErrMsg);
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14910 BOOL *aMatched,
14911 ULONG *aMaskedInterfaces)
14912{
14913 NOREF(aDevice);
14914 NOREF(aMatched);
14915 NOREF(aMaskedInterfaces);
14916 ReturnComNotImplemented();
14917
14918}
14919
14920HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14921{
14922 NOREF(aId); NOREF(aCaptureFilename);
14923 ReturnComNotImplemented();
14924}
14925
14926HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14927 BOOL aDone)
14928{
14929 NOREF(aId);
14930 NOREF(aDone);
14931 ReturnComNotImplemented();
14932}
14933
14934HRESULT Machine::autoCaptureUSBDevices()
14935{
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14940{
14941 NOREF(aDone);
14942 ReturnComNotImplemented();
14943}
14944
14945HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14946 ComPtr<IProgress> &aProgress)
14947{
14948 NOREF(aSession);
14949 NOREF(aProgress);
14950 ReturnComNotImplemented();
14951}
14952
14953HRESULT Machine::finishOnlineMergeMedium()
14954{
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14959 std::vector<com::Utf8Str> &aValues,
14960 std::vector<LONG64> &aTimestamps,
14961 std::vector<com::Utf8Str> &aFlags)
14962{
14963 NOREF(aNames);
14964 NOREF(aValues);
14965 NOREF(aTimestamps);
14966 NOREF(aFlags);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14971 const com::Utf8Str &aValue,
14972 LONG64 aTimestamp,
14973 const com::Utf8Str &aFlags)
14974{
14975 NOREF(aName);
14976 NOREF(aValue);
14977 NOREF(aTimestamp);
14978 NOREF(aFlags);
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::lockMedia()
14983{
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::unlockMedia()
14988{
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14993 ComPtr<IMediumAttachment> &aNewAttachment)
14994{
14995 NOREF(aAttachment);
14996 NOREF(aNewAttachment);
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15001 ULONG aCpuUser,
15002 ULONG aCpuKernel,
15003 ULONG aCpuIdle,
15004 ULONG aMemTotal,
15005 ULONG aMemFree,
15006 ULONG aMemBalloon,
15007 ULONG aMemShared,
15008 ULONG aMemCache,
15009 ULONG aPagedTotal,
15010 ULONG aMemAllocTotal,
15011 ULONG aMemFreeTotal,
15012 ULONG aMemBalloonTotal,
15013 ULONG aMemSharedTotal,
15014 ULONG aVmNetRx,
15015 ULONG aVmNetTx)
15016{
15017 NOREF(aValidStats);
15018 NOREF(aCpuUser);
15019 NOREF(aCpuKernel);
15020 NOREF(aCpuIdle);
15021 NOREF(aMemTotal);
15022 NOREF(aMemFree);
15023 NOREF(aMemBalloon);
15024 NOREF(aMemShared);
15025 NOREF(aMemCache);
15026 NOREF(aPagedTotal);
15027 NOREF(aMemAllocTotal);
15028 NOREF(aMemFreeTotal);
15029 NOREF(aMemBalloonTotal);
15030 NOREF(aMemSharedTotal);
15031 NOREF(aVmNetRx);
15032 NOREF(aVmNetTx);
15033 ReturnComNotImplemented();
15034}
15035
15036HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15037 com::Utf8Str &aResult)
15038{
15039 NOREF(aAuthParams);
15040 NOREF(aResult);
15041 ReturnComNotImplemented();
15042}
15043
15044HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15045{
15046 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15047
15048 AutoCaller autoCaller(this);
15049 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15050
15051 HRESULT rc = S_OK;
15052
15053 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15054 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15055 rc = getUSBDeviceFilters(usbDeviceFilters);
15056 if (FAILED(rc)) return rc;
15057
15058 NOREF(aFlags);
15059 com::Utf8Str osTypeId;
15060 ComObjPtr<GuestOSType> osType = NULL;
15061
15062 /* Get the guest os type as a string from the VB. */
15063 rc = getOSTypeId(osTypeId);
15064 if (FAILED(rc)) return rc;
15065
15066 /* Get the os type obj that coresponds, can be used to get
15067 * the defaults for this guest OS. */
15068 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15069 if (FAILED(rc)) return rc;
15070
15071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15072
15073 /* Let the OS type select 64-bit ness. */
15074 mHWData->mLongMode = osType->i_is64Bit()
15075 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15076
15077 /* Apply network adapters defaults */
15078 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15079 mNetworkAdapters[slot]->i_applyDefaults(osType);
15080
15081 /* Apply serial port defaults */
15082 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15083 mSerialPorts[slot]->i_applyDefaults(osType);
15084
15085 /* Apply parallel port defaults - not OS dependent*/
15086 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15087 mParallelPorts[slot]->i_applyDefaults();
15088
15089
15090 /* Let the OS type enable the X2APIC */
15091 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15092
15093 /* This one covers IOAPICEnabled. */
15094 mBIOSSettings->i_applyDefaults(osType);
15095
15096 /* Initialize default record settings. */
15097 mRecordingSettings->i_applyDefaults();
15098
15099 /* Initialize default BIOS settings here */
15100 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15101 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15102
15103 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15104 if (FAILED(rc)) return rc;
15105
15106 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15107 if (FAILED(rc)) return rc;
15108
15109 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15110 if (FAILED(rc)) return rc;
15111
15112 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15113 if (FAILED(rc)) return rc;
15114
15115 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15116 if (FAILED(rc)) return rc;
15117
15118 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15119 if (FAILED(rc)) return rc;
15120
15121 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15122 if (FAILED(rc)) return rc;
15123
15124 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15125 if (FAILED(rc)) return rc;
15126
15127 BOOL mRTCUseUTC;
15128 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15129 if (FAILED(rc)) return rc;
15130
15131 setRTCUseUTC(mRTCUseUTC);
15132 if (FAILED(rc)) return rc;
15133
15134 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15135 if (FAILED(rc)) return rc;
15136
15137 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15138 if (FAILED(rc)) return rc;
15139
15140 /* Audio stuff. */
15141 AudioCodecType_T audioCodec;
15142 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15143 if (FAILED(rc)) return rc;
15144
15145 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15146 if (FAILED(rc)) return rc;
15147
15148 AudioControllerType_T audioController;
15149 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15150 if (FAILED(rc)) return rc;
15151
15152 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15153 if (FAILED(rc)) return rc;
15154
15155 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15156 if (FAILED(rc)) return rc;
15157
15158 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15159 if (FAILED(rc)) return rc;
15160
15161 /* Storage Controllers */
15162 StorageControllerType_T hdStorageControllerType;
15163 StorageBus_T hdStorageBusType;
15164 StorageControllerType_T dvdStorageControllerType;
15165 StorageBus_T dvdStorageBusType;
15166 BOOL recommendedFloppy;
15167 ComPtr<IStorageController> floppyController;
15168 ComPtr<IStorageController> hdController;
15169 ComPtr<IStorageController> dvdController;
15170 Utf8Str strFloppyName, strDVDName, strHDName;
15171
15172 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15173 strFloppyName = Bstr("Floppy 1").raw();
15174 strDVDName = Bstr("DVD 1").raw();
15175 strHDName = Bstr("HDD 1").raw();
15176
15177 /* Floppy recommended? add one. */
15178 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15179 if (FAILED(rc)) return rc;
15180 if (recommendedFloppy)
15181 {
15182 rc = addStorageController(strFloppyName,
15183 StorageBus_Floppy,
15184 floppyController);
15185 if (FAILED(rc)) return rc;
15186 }
15187
15188 /* Setup one DVD storage controller. */
15189 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15190 if (FAILED(rc)) return rc;
15191
15192 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15193 if (FAILED(rc)) return rc;
15194
15195 rc = addStorageController(strDVDName,
15196 dvdStorageBusType,
15197 dvdController);
15198 if (FAILED(rc)) return rc;
15199
15200 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15201 if (FAILED(rc)) return rc;
15202
15203 /* Setup one HDD storage controller. */
15204 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15205 if (FAILED(rc)) return rc;
15206
15207 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15208 if (FAILED(rc)) return rc;
15209
15210 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15211 {
15212 rc = addStorageController(strHDName,
15213 hdStorageBusType,
15214 hdController);
15215 if (FAILED(rc)) return rc;
15216
15217 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15218 if (FAILED(rc)) return rc;
15219 }
15220 else
15221 {
15222 /* The HD controller is the same as DVD: */
15223 hdController = dvdController;
15224 strHDName = Bstr("DVD 1").raw();
15225 }
15226
15227 /* Limit the AHCI port count if it's used because windows has trouble with
15228 * too many ports and other guest (OS X in particular) may take extra long
15229 * boot: */
15230
15231 // pParent = static_cast<Medium*>(aP)
15232 IStorageController *temp = hdController;
15233 ComObjPtr<StorageController> storageController;
15234 storageController = static_cast<StorageController *>(temp);
15235
15236 // tempHDController = aHDController;
15237 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15238 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15239 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15240 storageController->COMSETTER(PortCount)(1);
15241
15242 /* USB stuff */
15243
15244 bool ohciEnabled = false;
15245
15246 ComPtr<IUSBController> usbController;
15247 BOOL recommendedUSB3;
15248 BOOL recommendedUSB;
15249 BOOL usbProxyAvailable;
15250
15251 getUSBProxyAvailable(&usbProxyAvailable);
15252 if (FAILED(rc)) return rc;
15253
15254 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15255 if (FAILED(rc)) return rc;
15256 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15257 if (FAILED(rc)) return rc;
15258
15259 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15260 {
15261#ifdef VBOX_WITH_EXTPACK
15262 /* USB 3.0 is only available if the proper ExtPack is installed. */
15263 ExtPackManager *aManager = mParent->i_getExtPackManager();
15264 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15265 {
15266 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15267 if (FAILED(rc)) return rc;
15268
15269 /* xHci includes OHCI */
15270 ohciEnabled = true;
15271 }
15272#endif
15273 }
15274 if ( !ohciEnabled
15275 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15276 {
15277 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15278 if (FAILED(rc)) return rc;
15279 ohciEnabled = true;
15280
15281#ifdef VBOX_WITH_EXTPACK
15282 /* USB 2.0 is only available if the proper ExtPack is installed.
15283 * Note. Configuring EHCI here and providing messages about
15284 * the missing extpack isn't exactly clean, but it is a
15285 * necessary evil to patch over legacy compatability issues
15286 * introduced by the new distribution model. */
15287 ExtPackManager *manager = mParent->i_getExtPackManager();
15288 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15289 {
15290 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15291 if (FAILED(rc)) return rc;
15292 }
15293#endif
15294 }
15295
15296 /* Set recommended human interface device types: */
15297 BOOL recommendedUSBHID;
15298 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15299 if (FAILED(rc)) return rc;
15300
15301 if (recommendedUSBHID)
15302 {
15303 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15304 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15305 if (!ohciEnabled && !usbDeviceFilters.isNull())
15306 {
15307 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15308 if (FAILED(rc)) return rc;
15309 }
15310 }
15311
15312 BOOL recommendedUSBTablet;
15313 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15314 if (FAILED(rc)) return rc;
15315
15316 if (recommendedUSBTablet)
15317 {
15318 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15319 if (!ohciEnabled && !usbDeviceFilters.isNull())
15320 {
15321 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15322 if (FAILED(rc)) return rc;
15323 }
15324 }
15325 return S_OK;
15326}
15327
15328/* This isn't handled entirely by the wrapper generator yet. */
15329#ifdef VBOX_WITH_XPCOM
15330NS_DECL_CLASSINFO(SessionMachine)
15331NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15332
15333NS_DECL_CLASSINFO(SnapshotMachine)
15334NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15335#endif
Note: See TracBrowser for help on using the repository browser.

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