VirtualBox

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

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

Main: bugref:7929: Added ability to change the priority of the VM process

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

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