VirtualBox

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

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

*,IPRT: Use new RTPathAbsExEx function instead of RTPathAbsEx. bugref:9172

  • 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 78090 2019-04-10 14:19:04Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mParavirtProvider = ParavirtProvider_Default;
221 mEmulatedUSBCardReaderEnabled = FALSE;
222
223 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
224 mCPUAttached[i] = false;
225
226 mIOCacheEnabled = true;
227 mIOCacheSize = 5; /* 5MB */
228}
229
230Machine::HWData::~HWData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine() :
242#ifdef VBOX_WITH_RESOURCE_USAGE_API
243 mCollectorGuest(NULL),
244#endif
245 mPeer(NULL),
246 mParent(NULL),
247 mSerialPorts(),
248 mParallelPorts(),
249 uRegistryNeedsSaving(0)
250{}
251
252Machine::~Machine()
253{}
254
255HRESULT Machine::FinalConstruct()
256{
257 LogFlowThisFunc(("\n"));
258 return BaseFinalConstruct();
259}
260
261void Machine::FinalRelease()
262{
263 LogFlowThisFunc(("\n"));
264 uninit();
265 BaseFinalRelease();
266}
267
268/**
269 * Initializes a new machine instance; this init() variant creates a new, empty machine.
270 * This gets called from VirtualBox::CreateMachine().
271 *
272 * @param aParent Associated parent object
273 * @param strConfigFile Local file system path to the VM settings file (can
274 * be relative to the VirtualBox config directory).
275 * @param strName name for the machine
276 * @param llGroups list of groups for the machine
277 * @param strOsType OS Type string (stored as is if aOsType is NULL).
278 * @param aOsType OS Type of this machine or NULL.
279 * @param aId UUID for the new machine.
280 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
281 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
282 * scheme (includes the UUID).
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 const Utf8Str &strOsType,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 if (llGroups.size())
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Let the OS type select 64-bit ness. */
345 mHWData->mLongMode = aOsType->i_is64Bit()
346 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
347
348 /* Let the OS type enable the X2APIC */
349 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
350 }
351 else if (!strOsType.isEmpty())
352 {
353 /* Store OS type */
354 mUserData->s.strOsType = strOsType;
355
356 /* No guest OS type object. Pick some plausible defaults which the
357 * host can handle. There's no way to know or validate anything. */
358 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 mHWData->mX2APIC = false;
360 }
361
362 /* Apply BIOS defaults. */
363 mBIOSSettings->i_applyDefaults(aOsType);
364
365 /* Apply record defaults. */
366 mRecordingSettings->i_applyDefaults();
367
368 /* Apply network adapters defaults */
369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
370 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
371
372 /* Apply serial port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
374 mSerialPorts[slot]->i_applyDefaults(aOsType);
375
376 /* Apply parallel port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
378 mParallelPorts[slot]->i_applyDefaults();
379
380 /* At this point the changing of the current state modification
381 * flag is allowed. */
382 i_allowStateModification();
383
384 /* commit all changes made during the initialization */
385 i_commit();
386 }
387
388 /* Confirm a successful initialization when it's the case */
389 if (SUCCEEDED(rc))
390 {
391 if (mData->mAccessible)
392 autoInitSpan.setSucceeded();
393 else
394 autoInitSpan.setLimited();
395 }
396
397 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
398 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
399 mData->mRegistered,
400 mData->mAccessible,
401 rc));
402
403 LogFlowThisFuncLeave();
404
405 return rc;
406}
407
408/**
409 * Initializes a new instance with data from machine XML (formerly Init_Registered).
410 * Gets called in two modes:
411 *
412 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
413 * UUID is specified and we mark the machine as "registered";
414 *
415 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
416 * and the machine remains unregistered until RegisterMachine() is called.
417 *
418 * @param aParent Associated parent object
419 * @param strConfigFile Local file system path to the VM settings file (can
420 * be relative to the VirtualBox config directory).
421 * @param aId UUID of the machine or NULL (see above).
422 *
423 * @return Success indicator. if not S_OK, the machine object is invalid
424 */
425HRESULT Machine::initFromSettings(VirtualBox *aParent,
426 const Utf8Str &strConfigFile,
427 const Guid *aId)
428{
429 LogFlowThisFuncEnter();
430 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 HRESULT rc = initImpl(aParent, strConfigFile);
437 if (FAILED(rc)) return rc;
438
439 if (aId)
440 {
441 // loading a registered VM:
442 unconst(mData->mUuid) = *aId;
443 mData->mRegistered = TRUE;
444 // now load the settings from XML:
445 rc = i_registeredInit();
446 // this calls initDataAndChildObjects() and loadSettings()
447 }
448 else
449 {
450 // opening an unregistered VM (VirtualBox::OpenMachine()):
451 rc = initDataAndChildObjects();
452
453 if (SUCCEEDED(rc))
454 {
455 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
456 mData->mAccessible = TRUE;
457
458 try
459 {
460 // load and parse machine XML; this will throw on XML or logic errors
461 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
462
463 // reject VM UUID duplicates, they can happen if someone
464 // tries to register an already known VM config again
465 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
466 true /* fPermitInaccessible */,
467 false /* aDoSetError */,
468 NULL) != VBOX_E_OBJECT_NOT_FOUND)
469 {
470 throw setError(E_FAIL,
471 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
472 mData->m_strConfigFile.c_str());
473 }
474
475 // use UUID from machine config
476 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
477
478 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
479 NULL /* puuidRegistry */);
480 if (FAILED(rc)) throw rc;
481
482 /* At this point the changing of the current state modification
483 * flag is allowed. */
484 i_allowStateModification();
485
486 i_commit();
487 }
488 catch (HRESULT err)
489 {
490 /* we assume that error info is set by the thrower */
491 rc = err;
492 }
493 catch (...)
494 {
495 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
496 }
497 }
498 }
499
500 /* Confirm a successful initialization when it's the case */
501 if (SUCCEEDED(rc))
502 {
503 if (mData->mAccessible)
504 autoInitSpan.setSucceeded();
505 else
506 {
507 autoInitSpan.setLimited();
508
509 // uninit media from this machine's media registry, or else
510 // reloading the settings will fail
511 mParent->i_unregisterMachineMedia(i_getId());
512 }
513 }
514
515 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
516 "rc=%08X\n",
517 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
518 mData->mRegistered, mData->mAccessible, rc));
519
520 LogFlowThisFuncLeave();
521
522 return rc;
523}
524
525/**
526 * Initializes a new instance from a machine config that is already in memory
527 * (import OVF case). Since we are importing, the UUID in the machine
528 * config is ignored and we always generate a fresh one.
529 *
530 * @param aParent Associated parent object.
531 * @param strName Name for the new machine; this overrides what is specified in config.
532 * @param strSettingsFilename File name of .vbox file.
533 * @param config Machine configuration loaded and parsed from XML.
534 *
535 * @return Success indicator. if not S_OK, the machine object is invalid
536 */
537HRESULT Machine::init(VirtualBox *aParent,
538 const Utf8Str &strName,
539 const Utf8Str &strSettingsFilename,
540 const settings::MachineConfigFile &config)
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 HRESULT rc = initImpl(aParent, strSettingsFilename);
549 if (FAILED(rc)) return rc;
550
551 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
552 if (FAILED(rc)) return rc;
553
554 rc = initDataAndChildObjects();
555
556 if (SUCCEEDED(rc))
557 {
558 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
559 mData->mAccessible = TRUE;
560
561 // create empty machine config for instance data
562 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
563
564 // generate fresh UUID, ignore machine config
565 unconst(mData->mUuid).create();
566
567 rc = i_loadMachineDataFromSettings(config,
568 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
569
570 // override VM name as well, it may be different
571 mUserData->s.strName = strName;
572
573 if (SUCCEEDED(rc))
574 {
575 /* At this point the changing of the current state modification
576 * flag is allowed. */
577 i_allowStateModification();
578
579 /* commit all changes made during the initialization */
580 i_commit();
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 if (SUCCEEDED(rc))
586 {
587 if (mData->mAccessible)
588 autoInitSpan.setSucceeded();
589 else
590 {
591 /* Ignore all errors from unregistering, they would destroy
592- * the more interesting error information we already have,
593- * pinpointing the issue with the VM config. */
594 ErrorInfoKeeper eik;
595
596 autoInitSpan.setLimited();
597
598 // uninit media from this machine's media registry, or else
599 // reloading the settings will fail
600 mParent->i_unregisterMachineMedia(i_getId());
601 }
602 }
603
604 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
605 "rc=%08X\n",
606 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
607 mData->mRegistered, mData->mAccessible, rc));
608
609 LogFlowThisFuncLeave();
610
611 return rc;
612}
613
614/**
615 * Shared code between the various init() implementations.
616 * @param aParent The VirtualBox object.
617 * @param strConfigFile Settings file.
618 * @return
619 */
620HRESULT Machine::initImpl(VirtualBox *aParent,
621 const Utf8Str &strConfigFile)
622{
623 LogFlowThisFuncEnter();
624
625 AssertReturn(aParent, E_INVALIDARG);
626 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
627
628 HRESULT rc = S_OK;
629
630 /* share the parent weakly */
631 unconst(mParent) = aParent;
632
633 /* allocate the essential machine data structure (the rest will be
634 * allocated later by initDataAndChildObjects() */
635 mData.allocate();
636
637 /* memorize the config file name (as provided) */
638 mData->m_strConfigFile = strConfigFile;
639
640 /* get the full file name */
641 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
642 if (RT_FAILURE(vrc1))
643 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
644 tr("Invalid machine settings file name '%s' (%Rrc)"),
645 strConfigFile.c_str(),
646 vrc1);
647
648 LogFlowThisFuncLeave();
649
650 return rc;
651}
652
653/**
654 * Tries to create a machine settings file in the path stored in the machine
655 * instance data. Used when a new machine is created to fail gracefully if
656 * the settings file could not be written (e.g. because machine dir is read-only).
657 * @return
658 */
659HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
660{
661 HRESULT rc = S_OK;
662
663 // when we create a new machine, we must be able to create the settings file
664 RTFILE f = NIL_RTFILE;
665 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
666 if ( RT_SUCCESS(vrc)
667 || vrc == VERR_SHARING_VIOLATION
668 )
669 {
670 if (RT_SUCCESS(vrc))
671 RTFileClose(f);
672 if (!fForceOverwrite)
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Machine settings file '%s' already exists"),
675 mData->m_strConfigFileFull.c_str());
676 else
677 {
678 /* try to delete the config file, as otherwise the creation
679 * of a new settings file will fail. */
680 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
681 if (RT_FAILURE(vrc2))
682 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
683 tr("Could not delete the existing settings file '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(), vrc2);
685 }
686 }
687 else if ( vrc != VERR_FILE_NOT_FOUND
688 && vrc != VERR_PATH_NOT_FOUND
689 )
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
691 tr("Invalid machine settings file name '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(),
693 vrc);
694 return rc;
695}
696
697/**
698 * Initializes the registered machine by loading the settings file.
699 * This method is separated from #init() in order to make it possible to
700 * retry the operation after VirtualBox startup instead of refusing to
701 * startup the whole VirtualBox server in case if the settings file of some
702 * registered VM is invalid or inaccessible.
703 *
704 * @note Must be always called from this object's write lock
705 * (unless called from #init() that doesn't need any locking).
706 * @note Locks the mUSBController method for writing.
707 * @note Subclasses must not call this method.
708 */
709HRESULT Machine::i_registeredInit()
710{
711 AssertReturn(!i_isSessionMachine(), E_FAIL);
712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
713 AssertReturn(mData->mUuid.isValid(), E_FAIL);
714 AssertReturn(!mData->mAccessible, E_FAIL);
715
716 HRESULT rc = initDataAndChildObjects();
717
718 if (SUCCEEDED(rc))
719 {
720 /* Temporarily reset the registered flag in order to let setters
721 * potentially called from loadSettings() succeed (isMutable() used in
722 * all setters will return FALSE for a Machine instance if mRegistered
723 * is TRUE). */
724 mData->mRegistered = FALSE;
725
726 try
727 {
728 // load and parse machine XML; this will throw on XML or logic errors
729 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
730
731 if (mData->mUuid != mData->pMachineConfigFile->uuid)
732 throw setError(E_FAIL,
733 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
734 mData->pMachineConfigFile->uuid.raw(),
735 mData->m_strConfigFileFull.c_str(),
736 mData->mUuid.toString().c_str(),
737 mParent->i_settingsFilePath().c_str());
738
739 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
740 NULL /* const Guid *puuidRegistry */);
741 if (FAILED(rc)) throw rc;
742 }
743 catch (HRESULT err)
744 {
745 /* we assume that error info is set by the thrower */
746 rc = err;
747 }
748 catch (...)
749 {
750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
751 }
752
753 /* Restore the registered flag (even on failure) */
754 mData->mRegistered = TRUE;
755 }
756
757 if (SUCCEEDED(rc))
758 {
759 /* Set mAccessible to TRUE only if we successfully locked and loaded
760 * the settings file */
761 mData->mAccessible = TRUE;
762
763 /* commit all changes made during loading the settings file */
764 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
765 /// @todo r=klaus for some reason the settings loading logic backs up
766 // the settings, and therefore a commit is needed. Should probably be changed.
767 }
768 else
769 {
770 /* If the machine is registered, then, instead of returning a
771 * failure, we mark it as inaccessible and set the result to
772 * success to give it a try later */
773
774 /* fetch the current error info */
775 mData->mAccessError = com::ErrorInfo();
776 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
777
778 /* rollback all changes */
779 i_rollback(false /* aNotify */);
780
781 // uninit media from this machine's media registry, or else
782 // reloading the settings will fail
783 mParent->i_unregisterMachineMedia(i_getId());
784
785 /* uninitialize the common part to make sure all data is reset to
786 * default (null) values */
787 uninitDataAndChildObjects();
788
789 rc = S_OK;
790 }
791
792 return rc;
793}
794
795/**
796 * Uninitializes the instance.
797 * Called either from FinalRelease() or by the parent when it gets destroyed.
798 *
799 * @note The caller of this method must make sure that this object
800 * a) doesn't have active callers on the current thread and b) is not locked
801 * by the current thread; otherwise uninit() will hang either a) due to
802 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
803 * a dead-lock caused by this thread waiting for all callers on the other
804 * threads are done but preventing them from doing so by holding a lock.
805 */
806void Machine::uninit()
807{
808 LogFlowThisFuncEnter();
809
810 Assert(!isWriteLockOnCurrentThread());
811
812 Assert(!uRegistryNeedsSaving);
813 if (uRegistryNeedsSaving)
814 {
815 AutoCaller autoCaller(this);
816 if (SUCCEEDED(autoCaller.rc()))
817 {
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819 i_saveSettings(NULL, Machine::SaveS_Force);
820 }
821 }
822
823 /* Enclose the state transition Ready->InUninit->NotReady */
824 AutoUninitSpan autoUninitSpan(this);
825 if (autoUninitSpan.uninitDone())
826 return;
827
828 Assert(!i_isSnapshotMachine());
829 Assert(!i_isSessionMachine());
830 Assert(!!mData);
831
832 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
833 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 if (!mData->mSession.mMachine.isNull())
838 {
839 /* Theoretically, this can only happen if the VirtualBox server has been
840 * terminated while there were clients running that owned open direct
841 * sessions. Since in this case we are definitely called by
842 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
843 * won't happen on the client watcher thread (because it has a
844 * VirtualBox caller for the duration of the
845 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
846 * cannot happen until the VirtualBox caller is released). This is
847 * important, because SessionMachine::uninit() cannot correctly operate
848 * after we return from this method (it expects the Machine instance is
849 * still valid). We'll call it ourselves below.
850 */
851 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
852 (SessionMachine*)mData->mSession.mMachine));
853
854 if (Global::IsOnlineOrTransient(mData->mMachineState))
855 {
856 Log1WarningThisFunc(("Setting state to Aborted!\n"));
857 /* set machine state using SessionMachine reimplementation */
858 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
859 }
860
861 /*
862 * Uninitialize SessionMachine using public uninit() to indicate
863 * an unexpected uninitialization.
864 */
865 mData->mSession.mMachine->uninit();
866 /* SessionMachine::uninit() must set mSession.mMachine to null */
867 Assert(mData->mSession.mMachine.isNull());
868 }
869
870 // uninit media from this machine's media registry, if they're still there
871 Guid uuidMachine(i_getId());
872
873 /* the lock is no more necessary (SessionMachine is uninitialized) */
874 alock.release();
875
876 /* XXX This will fail with
877 * "cannot be closed because it is still attached to 1 virtual machines"
878 * because at this point we did not call uninitDataAndChildObjects() yet
879 * and therefore also removeBackReference() for all these mediums was not called! */
880
881 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
882 mParent->i_unregisterMachineMedia(uuidMachine);
883
884 // has machine been modified?
885 if (mData->flModifications)
886 {
887 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
888 i_rollback(false /* aNotify */);
889 }
890
891 if (mData->mAccessible)
892 uninitDataAndChildObjects();
893
894 /* free the essential data structure last */
895 mData.free();
896
897 LogFlowThisFuncLeave();
898}
899
900// Wrapped IMachine properties
901/////////////////////////////////////////////////////////////////////////////
902HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
903{
904 /* mParent is constant during life time, no need to lock */
905 ComObjPtr<VirtualBox> pVirtualBox(mParent);
906 aParent = pVirtualBox;
907
908 return S_OK;
909}
910
911
912HRESULT Machine::getAccessible(BOOL *aAccessible)
913{
914 /* In some cases (medium registry related), it is necessary to be able to
915 * go through the list of all machines. Happens when an inaccessible VM
916 * has a sensible medium registry. */
917 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
919
920 HRESULT rc = S_OK;
921
922 if (!mData->mAccessible)
923 {
924 /* try to initialize the VM once more if not accessible */
925
926 AutoReinitSpan autoReinitSpan(this);
927 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
928
929#ifdef DEBUG
930 LogFlowThisFunc(("Dumping media backreferences\n"));
931 mParent->i_dumpAllBackRefs();
932#endif
933
934 if (mData->pMachineConfigFile)
935 {
936 // reset the XML file to force loadSettings() (called from i_registeredInit())
937 // to parse it again; the file might have changed
938 delete mData->pMachineConfigFile;
939 mData->pMachineConfigFile = NULL;
940 }
941
942 rc = i_registeredInit();
943
944 if (SUCCEEDED(rc) && mData->mAccessible)
945 {
946 autoReinitSpan.setSucceeded();
947
948 /* make sure interesting parties will notice the accessibility
949 * state change */
950 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
951 mParent->i_onMachineDataChange(mData->mUuid);
952 }
953 }
954
955 if (SUCCEEDED(rc))
956 *aAccessible = mData->mAccessible;
957
958 LogFlowThisFuncLeave();
959
960 return rc;
961}
962
963HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
964{
965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
968 {
969 /* return shortly */
970 aAccessError = NULL;
971 return S_OK;
972 }
973
974 HRESULT rc = S_OK;
975
976 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
977 rc = errorInfo.createObject();
978 if (SUCCEEDED(rc))
979 {
980 errorInfo->init(mData->mAccessError.getResultCode(),
981 mData->mAccessError.getInterfaceID().ref(),
982 Utf8Str(mData->mAccessError.getComponent()).c_str(),
983 Utf8Str(mData->mAccessError.getText()));
984 aAccessError = errorInfo;
985 }
986
987 return rc;
988}
989
990HRESULT Machine::getName(com::Utf8Str &aName)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 aName = mUserData->s.strName;
995
996 return S_OK;
997}
998
999HRESULT Machine::setName(const com::Utf8Str &aName)
1000{
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = i_checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 i_setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1021{
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 aDescription = mUserData->s.strDescription;
1025
1026 return S_OK;
1027}
1028
1029HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1030{
1031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1032
1033 // this can be done in principle in any state as it doesn't affect the VM
1034 // significantly, but play safe by not messing around while complex
1035 // activities are going on
1036 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1037 if (FAILED(rc)) return rc;
1038
1039 i_setModified(IsModified_MachineData);
1040 mUserData.backup();
1041 mUserData->s.strDescription = aDescription;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getId(com::Guid &aId)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 aId = mData->mUuid;
1051
1052 return S_OK;
1053}
1054
1055HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1056{
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058 aGroups.resize(mUserData->s.llGroups.size());
1059 size_t i = 0;
1060 for (StringsList::const_iterator
1061 it = mUserData->s.llGroups.begin();
1062 it != mUserData->s.llGroups.end();
1063 ++it, ++i)
1064 aGroups[i] = (*it);
1065
1066 return S_OK;
1067}
1068
1069HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1070{
1071 StringsList llGroups;
1072 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1073 if (FAILED(rc))
1074 return rc;
1075
1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 rc = i_checkStateDependency(MutableOrSavedStateDep);
1079 if (FAILED(rc)) return rc;
1080
1081 i_setModified(IsModified_MachineData);
1082 mUserData.backup();
1083 mUserData->s.llGroups = llGroups;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1089{
1090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 aOSTypeId = mUserData->s.strOsType;
1093
1094 return S_OK;
1095}
1096
1097HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1098{
1099 /* look up the object by Id to check it is valid */
1100 ComObjPtr<GuestOSType> pGuestOSType;
1101 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1102
1103 /* when setting, always use the "etalon" value for consistency -- lookup
1104 * by ID is case-insensitive and the input value may have different case */
1105 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 HRESULT rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComObjPtr<GuestOSType> pGuestOSType;
1298 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1299 pGuestOSType);
1300 if (FAILED(hrc2) || pGuestOSType.isNull())
1301 {
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1307 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1308
1309 switch (mHWData->mParavirtProvider)
1310 {
1311 case ParavirtProvider_Legacy:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else
1316 *aParavirtProvider = ParavirtProvider_None;
1317 break;
1318 }
1319
1320 case ParavirtProvider_Default:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else if ( mUserData->s.strOsType == "Windows10"
1325 || mUserData->s.strOsType == "Windows10_64"
1326 || mUserData->s.strOsType == "Windows81"
1327 || mUserData->s.strOsType == "Windows81_64"
1328 || mUserData->s.strOsType == "Windows8"
1329 || mUserData->s.strOsType == "Windows8_64"
1330 || mUserData->s.strOsType == "Windows7"
1331 || mUserData->s.strOsType == "Windows7_64"
1332 || mUserData->s.strOsType == "WindowsVista"
1333 || mUserData->s.strOsType == "WindowsVista_64"
1334 || mUserData->s.strOsType == "Windows2012"
1335 || mUserData->s.strOsType == "Windows2012_64"
1336 || mUserData->s.strOsType == "Windows2008"
1337 || mUserData->s.strOsType == "Windows2008_64")
1338 {
1339 *aParavirtProvider = ParavirtProvider_HyperV;
1340 }
1341 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1342 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1343 || mUserData->s.strOsType == "Linux"
1344 || mUserData->s.strOsType == "Linux_64"
1345 || mUserData->s.strOsType == "ArchLinux"
1346 || mUserData->s.strOsType == "ArchLinux_64"
1347 || mUserData->s.strOsType == "Debian"
1348 || mUserData->s.strOsType == "Debian_64"
1349 || mUserData->s.strOsType == "Fedora"
1350 || mUserData->s.strOsType == "Fedora_64"
1351 || mUserData->s.strOsType == "Gentoo"
1352 || mUserData->s.strOsType == "Gentoo_64"
1353 || mUserData->s.strOsType == "Mandriva"
1354 || mUserData->s.strOsType == "Mandriva_64"
1355 || mUserData->s.strOsType == "OpenSUSE"
1356 || mUserData->s.strOsType == "OpenSUSE_64"
1357 || mUserData->s.strOsType == "Oracle"
1358 || mUserData->s.strOsType == "Oracle_64"
1359 || mUserData->s.strOsType == "RedHat"
1360 || mUserData->s.strOsType == "RedHat_64"
1361 || mUserData->s.strOsType == "Turbolinux"
1362 || mUserData->s.strOsType == "Turbolinux_64"
1363 || mUserData->s.strOsType == "Ubuntu"
1364 || mUserData->s.strOsType == "Ubuntu_64"
1365 || mUserData->s.strOsType == "Xandros"
1366 || mUserData->s.strOsType == "Xandros_64")
1367 {
1368 *aParavirtProvider = ParavirtProvider_KVM;
1369 }
1370 else
1371 *aParavirtProvider = ParavirtProvider_None;
1372 break;
1373 }
1374
1375 default: AssertFailedBreak(); /* Shut up MSC. */
1376 }
1377 break;
1378 }
1379 }
1380
1381 Assert( *aParavirtProvider == ParavirtProvider_None
1382 || *aParavirtProvider == ParavirtProvider_Minimal
1383 || *aParavirtProvider == ParavirtProvider_HyperV
1384 || *aParavirtProvider == ParavirtProvider_KVM);
1385 return S_OK;
1386}
1387
1388HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1389{
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 aHardwareVersion = mHWData->mHWVersion;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1398{
1399 /* check known version */
1400 Utf8Str hwVersion = aHardwareVersion;
1401 if ( hwVersion.compare("1") != 0
1402 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1403 return setError(E_INVALIDARG,
1404 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1405
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mHWVersion = aHardwareVersion;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 if (!mHWData->mHardwareUUID.isZero())
1423 aHardwareUUID = mHWData->mHardwareUUID;
1424 else
1425 aHardwareUUID = mData->mUuid;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1431{
1432 if (!aHardwareUUID.isValid())
1433 return E_INVALIDARG;
1434
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 if (aHardwareUUID == mData->mUuid)
1443 mHWData->mHardwareUUID.clear();
1444 else
1445 mHWData->mHardwareUUID = aHardwareUUID;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aMemorySize = mHWData->mMemorySize;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setMemorySize(ULONG aMemorySize)
1460{
1461 /* check RAM limits */
1462 if ( aMemorySize < MM_RAM_MIN_IN_MB
1463 || aMemorySize > MM_RAM_MAX_IN_MB
1464 )
1465 return setError(E_INVALIDARG,
1466 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1467 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mMemorySize = aMemorySize;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUCount = mHWData->mCPUCount;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUCount(ULONG aCPUCount)
1491{
1492 /* check CPU limits */
1493 if ( aCPUCount < SchemaDefs::MinCPUCount
1494 || aCPUCount > SchemaDefs::MaxCPUCount
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1498 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1503 if (mHWData->mCPUHotPlugEnabled)
1504 {
1505 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1506 {
1507 if (mHWData->mCPUAttached[idx])
1508 return setError(E_INVALIDARG,
1509 tr("There is still a CPU attached to socket %lu."
1510 "Detach the CPU before removing the socket"),
1511 aCPUCount, idx+1);
1512 }
1513 }
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mCPUCount = aCPUCount;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1535{
1536 HRESULT rc = S_OK;
1537
1538 /* check throttle limits */
1539 if ( aCPUExecutionCap < 1
1540 || aCPUExecutionCap > 100
1541 )
1542 return setError(E_INVALIDARG,
1543 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1544 aCPUExecutionCap, 1, 100);
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 alock.release();
1549 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1550 alock.acquire();
1551 if (FAILED(rc)) return rc;
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1556
1557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1558 if (Global::IsOnline(mData->mMachineState))
1559 i_saveSettings(NULL);
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1574{
1575 HRESULT rc = S_OK;
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 rc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(rc)) return rc;
1581
1582 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1583 {
1584 if (aCPUHotPlugEnabled)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588
1589 /* Add the amount of CPUs currently attached */
1590 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1591 mHWData->mCPUAttached[i] = true;
1592 }
1593 else
1594 {
1595 /*
1596 * We can disable hotplug only if the amount of maximum CPUs is equal
1597 * to the amount of attached CPUs
1598 */
1599 unsigned cCpusAttached = 0;
1600 unsigned iHighestId = 0;
1601
1602 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1603 {
1604 if (mHWData->mCPUAttached[i])
1605 {
1606 cCpusAttached++;
1607 iHighestId = i;
1608 }
1609 }
1610
1611 if ( (cCpusAttached != mHWData->mCPUCount)
1612 || (iHighestId >= mHWData->mCPUCount))
1613 return setError(E_INVALIDARG,
1614 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 }
1619 }
1620
1621 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1622
1623 return rc;
1624}
1625
1626HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1636{
1637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1640 if (SUCCEEDED(hrc))
1641 {
1642 i_setModified(IsModified_MachineData);
1643 mHWData.backup();
1644 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1645 }
1646 return hrc;
1647}
1648
1649HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1650{
1651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 aCPUProfile = mHWData->mCpuProfile;
1653 return S_OK;
1654}
1655
1656HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1657{
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1660 if (SUCCEEDED(hrc))
1661 {
1662 i_setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 /* Empty equals 'host'. */
1665 if (aCPUProfile.isNotEmpty())
1666 mHWData->mCpuProfile = aCPUProfile;
1667 else
1668 mHWData->mCpuProfile = "host";
1669 }
1670 return hrc;
1671}
1672
1673HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1679
1680 return S_OK;
1681#else
1682 NOREF(aEmulatedUSBCardReaderEnabled);
1683 return E_NOTIMPL;
1684#endif
1685}
1686
1687HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1688{
1689#ifdef VBOX_WITH_USB_CARDREADER
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1698
1699 return S_OK;
1700#else
1701 NOREF(aEmulatedUSBCardReaderEnabled);
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *aHPETEnabled = mHWData->mHPETEnabled;
1711
1712 return S_OK;
1713}
1714
1715HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 rc = i_checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 i_setModified(IsModified_MachineData);
1725 mHWData.backup();
1726
1727 mHWData->mHPETEnabled = aHPETEnabled;
1728
1729 return rc;
1730}
1731
1732HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1742{
1743 switch (aGraphicsControllerType)
1744 {
1745 case GraphicsControllerType_Null:
1746 case GraphicsControllerType_VBoxVGA:
1747#ifdef VBOX_WITH_VMSVGA
1748 case GraphicsControllerType_VMSVGA:
1749 case GraphicsControllerType_VBoxSVGA:
1750#endif
1751 break;
1752 default:
1753 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1754 }
1755
1756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 HRESULT rc = i_checkStateDependency(MutableStateDep);
1759 if (FAILED(rc)) return rc;
1760
1761 i_setModified(IsModified_MachineData);
1762 mHWData.backup();
1763 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1769{
1770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 *aVRAMSize = mHWData->mVRAMSize;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1778{
1779 /* check VRAM limits */
1780 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1781 return setError(E_INVALIDARG,
1782 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1783 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 HRESULT rc = i_checkStateDependency(MutableStateDep);
1788 if (FAILED(rc)) return rc;
1789
1790 i_setModified(IsModified_MachineData);
1791 mHWData.backup();
1792 mHWData->mVRAMSize = aVRAMSize;
1793
1794 return S_OK;
1795}
1796
1797/** @todo this method should not be public */
1798HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1803
1804 return S_OK;
1805}
1806
1807/**
1808 * Set the memory balloon size.
1809 *
1810 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1811 * we have to make sure that we never call IGuest from here.
1812 */
1813HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1814{
1815 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1816#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1817 /* check limits */
1818 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1819 return setError(E_INVALIDARG,
1820 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1821 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 i_setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1828
1829 return S_OK;
1830#else
1831 NOREF(aMemoryBalloonSize);
1832 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1837{
1838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1845{
1846#ifdef VBOX_WITH_PAGE_SHARING
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1853 return S_OK;
1854#else
1855 NOREF(aPageFusionEnabled);
1856 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1857#endif
1858}
1859
1860HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 HRESULT rc = i_checkStateDependency(MutableStateDep);
1874 if (FAILED(rc)) return rc;
1875
1876 /** @todo check validity! */
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1881
1882 return S_OK;
1883}
1884
1885
1886HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1896{
1897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 HRESULT rc = i_checkStateDependency(MutableStateDep);
1900 if (FAILED(rc)) return rc;
1901
1902 /** @todo check validity! */
1903 i_setModified(IsModified_MachineData);
1904 mHWData.backup();
1905 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1906
1907 return S_OK;
1908}
1909
1910HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1911{
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 *aMonitorCount = mHWData->mMonitorCount;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1920{
1921 /* make sure monitor count is a sensible number */
1922 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1923 return setError(E_INVALIDARG,
1924 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1925 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1926
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mMonitorCount = aMonitorCount;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1940{
1941 /* mBIOSSettings is constant during life time, no need to lock */
1942 aBIOSSettings = mBIOSSettings;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 aRecordingSettings = mRecordingSettings;
1952
1953 return S_OK;
1954}
1955
1956HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1957{
1958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1959
1960 switch (aProperty)
1961 {
1962 case CPUPropertyType_PAE:
1963 *aValue = mHWData->mPAEEnabled;
1964 break;
1965
1966 case CPUPropertyType_LongMode:
1967 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1968 *aValue = TRUE;
1969 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1970 *aValue = FALSE;
1971#if HC_ARCH_BITS == 64
1972 else
1973 *aValue = TRUE;
1974#else
1975 else
1976 {
1977 *aValue = FALSE;
1978
1979 ComObjPtr<GuestOSType> pGuestOSType;
1980 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1981 pGuestOSType);
1982 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1983 {
1984 if (pGuestOSType->i_is64Bit())
1985 {
1986 ComObjPtr<Host> pHost = mParent->i_host();
1987 alock.release();
1988
1989 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1990 if (FAILED(hrc2))
1991 *aValue = FALSE;
1992 }
1993 }
1994 }
1995#endif
1996 break;
1997
1998 case CPUPropertyType_TripleFaultReset:
1999 *aValue = mHWData->mTripleFaultReset;
2000 break;
2001
2002 case CPUPropertyType_APIC:
2003 *aValue = mHWData->mAPIC;
2004 break;
2005
2006 case CPUPropertyType_X2APIC:
2007 *aValue = mHWData->mX2APIC;
2008 break;
2009
2010 case CPUPropertyType_IBPBOnVMExit:
2011 *aValue = mHWData->mIBPBOnVMExit;
2012 break;
2013
2014 case CPUPropertyType_IBPBOnVMEntry:
2015 *aValue = mHWData->mIBPBOnVMEntry;
2016 break;
2017
2018 case CPUPropertyType_SpecCtrl:
2019 *aValue = mHWData->mSpecCtrl;
2020 break;
2021
2022 case CPUPropertyType_SpecCtrlByHost:
2023 *aValue = mHWData->mSpecCtrlByHost;
2024 break;
2025
2026 case CPUPropertyType_HWVirt:
2027 *aValue = mHWData->mNestedHWVirt;
2028 break;
2029
2030 case CPUPropertyType_L1DFlushOnEMTScheduling:
2031 *aValue = mHWData->mL1DFlushOnSched;
2032 break;
2033
2034 case CPUPropertyType_L1DFlushOnVMEntry:
2035 *aValue = mHWData->mL1DFlushOnVMEntry;
2036 break;
2037
2038 default:
2039 return E_INVALIDARG;
2040 }
2041 return S_OK;
2042}
2043
2044HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2045{
2046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 HRESULT rc = i_checkStateDependency(MutableStateDep);
2049 if (FAILED(rc)) return rc;
2050
2051 switch (aProperty)
2052 {
2053 case CPUPropertyType_PAE:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mPAEEnabled = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_LongMode:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2063 break;
2064
2065 case CPUPropertyType_TripleFaultReset:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mTripleFaultReset = !!aValue;
2069 break;
2070
2071 case CPUPropertyType_APIC:
2072 if (mHWData->mX2APIC)
2073 aValue = TRUE;
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mAPIC = !!aValue;
2077 break;
2078
2079 case CPUPropertyType_X2APIC:
2080 i_setModified(IsModified_MachineData);
2081 mHWData.backup();
2082 mHWData->mX2APIC = !!aValue;
2083 if (aValue)
2084 mHWData->mAPIC = !!aValue;
2085 break;
2086
2087 case CPUPropertyType_IBPBOnVMExit:
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mIBPBOnVMExit = !!aValue;
2091 break;
2092
2093 case CPUPropertyType_IBPBOnVMEntry:
2094 i_setModified(IsModified_MachineData);
2095 mHWData.backup();
2096 mHWData->mIBPBOnVMEntry = !!aValue;
2097 break;
2098
2099 case CPUPropertyType_SpecCtrl:
2100 i_setModified(IsModified_MachineData);
2101 mHWData.backup();
2102 mHWData->mSpecCtrl = !!aValue;
2103 break;
2104
2105 case CPUPropertyType_SpecCtrlByHost:
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108 mHWData->mSpecCtrlByHost = !!aValue;
2109 break;
2110
2111 case CPUPropertyType_HWVirt:
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mNestedHWVirt = !!aValue;
2115 break;
2116
2117 case CPUPropertyType_L1DFlushOnEMTScheduling:
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mL1DFlushOnSched = !!aValue;
2121 break;
2122
2123 case CPUPropertyType_L1DFlushOnVMEntry:
2124 i_setModified(IsModified_MachineData);
2125 mHWData.backup();
2126 mHWData->mL1DFlushOnVMEntry = !!aValue;
2127 break;
2128
2129 default:
2130 return E_INVALIDARG;
2131 }
2132 return S_OK;
2133}
2134
2135HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2136 ULONG *aValEcx, ULONG *aValEdx)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2140 {
2141 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2142 it != mHWData->mCpuIdLeafList.end();
2143 ++it)
2144 {
2145 if (aOrdinal == 0)
2146 {
2147 const settings::CpuIdLeaf &rLeaf= *it;
2148 *aIdx = rLeaf.idx;
2149 *aSubIdx = rLeaf.idxSub;
2150 *aValEax = rLeaf.uEax;
2151 *aValEbx = rLeaf.uEbx;
2152 *aValEcx = rLeaf.uEcx;
2153 *aValEdx = rLeaf.uEdx;
2154 return S_OK;
2155 }
2156 aOrdinal--;
2157 }
2158 }
2159 return E_INVALIDARG;
2160}
2161
2162HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2163{
2164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2165
2166 /*
2167 * Search the list.
2168 */
2169 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2170 {
2171 const settings::CpuIdLeaf &rLeaf= *it;
2172 if ( rLeaf.idx == aIdx
2173 && ( aSubIdx == UINT32_MAX
2174 || rLeaf.idxSub == aSubIdx) )
2175 {
2176 *aValEax = rLeaf.uEax;
2177 *aValEbx = rLeaf.uEbx;
2178 *aValEcx = rLeaf.uEcx;
2179 *aValEdx = rLeaf.uEdx;
2180 return S_OK;
2181 }
2182 }
2183
2184 return E_INVALIDARG;
2185}
2186
2187
2188HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2189{
2190 /*
2191 * Validate input before taking locks and checking state.
2192 */
2193 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2194 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2195 if ( aIdx >= UINT32_C(0x20)
2196 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2197 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2198 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2199
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201 HRESULT rc = i_checkStateDependency(MutableStateDep);
2202 if (FAILED(rc)) return rc;
2203
2204 /*
2205 * Impose a maximum number of leaves.
2206 */
2207 if (mHWData->mCpuIdLeafList.size() > 256)
2208 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2209
2210 /*
2211 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2212 */
2213 i_setModified(IsModified_MachineData);
2214 mHWData.backup();
2215
2216 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2217 {
2218 settings::CpuIdLeaf &rLeaf= *it;
2219 if ( rLeaf.idx == aIdx
2220 && ( aSubIdx == UINT32_MAX
2221 || rLeaf.idxSub == aSubIdx) )
2222 it = mHWData->mCpuIdLeafList.erase(it);
2223 else
2224 ++it;
2225 }
2226
2227 settings::CpuIdLeaf NewLeaf;
2228 NewLeaf.idx = aIdx;
2229 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2230 NewLeaf.uEax = aValEax;
2231 NewLeaf.uEbx = aValEbx;
2232 NewLeaf.uEcx = aValEcx;
2233 NewLeaf.uEdx = aValEdx;
2234 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2235 return S_OK;
2236}
2237
2238HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2239{
2240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2241
2242 HRESULT rc = i_checkStateDependency(MutableStateDep);
2243 if (FAILED(rc)) return rc;
2244
2245 /*
2246 * Do the removal.
2247 */
2248 bool fModified = mHWData.isBackedUp();
2249 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2250 {
2251 settings::CpuIdLeaf &rLeaf= *it;
2252 if ( rLeaf.idx == aIdx
2253 && ( aSubIdx == UINT32_MAX
2254 || rLeaf.idxSub == aSubIdx) )
2255 {
2256 if (!fModified)
2257 {
2258 fModified = true;
2259 i_setModified(IsModified_MachineData);
2260 mHWData.backup();
2261 // Start from the beginning, since mHWData.backup() creates
2262 // a new list, causing iterator mixup. This makes sure that
2263 // the settings are not unnecessarily marked as modified,
2264 // at the price of extra list walking.
2265 it = mHWData->mCpuIdLeafList.begin();
2266 }
2267 else
2268 it = mHWData->mCpuIdLeafList.erase(it);
2269 }
2270 else
2271 ++it;
2272 }
2273
2274 return S_OK;
2275}
2276
2277HRESULT Machine::removeAllCPUIDLeaves()
2278{
2279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2280
2281 HRESULT rc = i_checkStateDependency(MutableStateDep);
2282 if (FAILED(rc)) return rc;
2283
2284 if (mHWData->mCpuIdLeafList.size() > 0)
2285 {
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288
2289 mHWData->mCpuIdLeafList.clear();
2290 }
2291
2292 return S_OK;
2293}
2294HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2295{
2296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 switch(aProperty)
2299 {
2300 case HWVirtExPropertyType_Enabled:
2301 *aValue = mHWData->mHWVirtExEnabled;
2302 break;
2303
2304 case HWVirtExPropertyType_VPID:
2305 *aValue = mHWData->mHWVirtExVPIDEnabled;
2306 break;
2307
2308 case HWVirtExPropertyType_NestedPaging:
2309 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2310 break;
2311
2312 case HWVirtExPropertyType_UnrestrictedExecution:
2313 *aValue = mHWData->mHWVirtExUXEnabled;
2314 break;
2315
2316 case HWVirtExPropertyType_LargePages:
2317 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2318#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2319 *aValue = FALSE;
2320#endif
2321 break;
2322
2323 case HWVirtExPropertyType_Force:
2324 *aValue = mHWData->mHWVirtExForceEnabled;
2325 break;
2326
2327 case HWVirtExPropertyType_UseNativeApi:
2328 *aValue = mHWData->mHWVirtExUseNativeApi;
2329 break;
2330
2331 default:
2332 return E_INVALIDARG;
2333 }
2334 return S_OK;
2335}
2336
2337HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2338{
2339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2340
2341 HRESULT rc = i_checkStateDependency(MutableStateDep);
2342 if (FAILED(rc)) return rc;
2343
2344 switch (aProperty)
2345 {
2346 case HWVirtExPropertyType_Enabled:
2347 i_setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 mHWData->mHWVirtExEnabled = !!aValue;
2350 break;
2351
2352 case HWVirtExPropertyType_VPID:
2353 i_setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2356 break;
2357
2358 case HWVirtExPropertyType_NestedPaging:
2359 i_setModified(IsModified_MachineData);
2360 mHWData.backup();
2361 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2362 break;
2363
2364 case HWVirtExPropertyType_UnrestrictedExecution:
2365 i_setModified(IsModified_MachineData);
2366 mHWData.backup();
2367 mHWData->mHWVirtExUXEnabled = !!aValue;
2368 break;
2369
2370 case HWVirtExPropertyType_LargePages:
2371 i_setModified(IsModified_MachineData);
2372 mHWData.backup();
2373 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2374 break;
2375
2376 case HWVirtExPropertyType_Force:
2377 i_setModified(IsModified_MachineData);
2378 mHWData.backup();
2379 mHWData->mHWVirtExForceEnabled = !!aValue;
2380 break;
2381
2382 case HWVirtExPropertyType_UseNativeApi:
2383 i_setModified(IsModified_MachineData);
2384 mHWData.backup();
2385 mHWData->mHWVirtExUseNativeApi = !!aValue;
2386 break;
2387
2388 default:
2389 return E_INVALIDARG;
2390 }
2391
2392 return S_OK;
2393}
2394
2395HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2396{
2397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2400
2401 return S_OK;
2402}
2403
2404HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2405{
2406 /** @todo (r=dmik):
2407 * 1. Allow to change the name of the snapshot folder containing snapshots
2408 * 2. Rename the folder on disk instead of just changing the property
2409 * value (to be smart and not to leave garbage). Note that it cannot be
2410 * done here because the change may be rolled back. Thus, the right
2411 * place is #saveSettings().
2412 */
2413
2414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2415
2416 HRESULT rc = i_checkStateDependency(MutableStateDep);
2417 if (FAILED(rc)) return rc;
2418
2419 if (!mData->mCurrentSnapshot.isNull())
2420 return setError(E_FAIL,
2421 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2422
2423 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2424
2425 if (strSnapshotFolder.isEmpty())
2426 strSnapshotFolder = "Snapshots";
2427 int vrc = i_calculateFullPath(strSnapshotFolder,
2428 strSnapshotFolder);
2429 if (RT_FAILURE(vrc))
2430 return setErrorBoth(E_FAIL, vrc,
2431 tr("Invalid snapshot folder '%s' (%Rrc)"),
2432 strSnapshotFolder.c_str(), vrc);
2433
2434 i_setModified(IsModified_MachineData);
2435 mUserData.backup();
2436
2437 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2438
2439 return S_OK;
2440}
2441
2442HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2443{
2444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2445
2446 aMediumAttachments.resize(mMediumAttachments->size());
2447 size_t i = 0;
2448 for (MediumAttachmentList::const_iterator
2449 it = mMediumAttachments->begin();
2450 it != mMediumAttachments->end();
2451 ++it, ++i)
2452 aMediumAttachments[i] = *it;
2453
2454 return S_OK;
2455}
2456
2457HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2458{
2459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 Assert(!!mVRDEServer);
2462
2463 aVRDEServer = mVRDEServer;
2464
2465 return S_OK;
2466}
2467
2468HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2469{
2470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2471
2472 aAudioAdapter = mAudioAdapter;
2473
2474 return S_OK;
2475}
2476
2477HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2478{
2479#ifdef VBOX_WITH_VUSB
2480 clearError();
2481 MultiResult rc(S_OK);
2482
2483# ifdef VBOX_WITH_USB
2484 rc = mParent->i_host()->i_checkUSBProxyService();
2485 if (FAILED(rc)) return rc;
2486# endif
2487
2488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 aUSBControllers.resize(mUSBControllers->size());
2491 size_t i = 0;
2492 for (USBControllerList::const_iterator
2493 it = mUSBControllers->begin();
2494 it != mUSBControllers->end();
2495 ++it, ++i)
2496 aUSBControllers[i] = *it;
2497
2498 return S_OK;
2499#else
2500 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2501 * extended error info to indicate that USB is simply not available
2502 * (w/o treating it as a failure), for example, as in OSE */
2503 NOREF(aUSBControllers);
2504 ReturnComNotImplemented();
2505#endif /* VBOX_WITH_VUSB */
2506}
2507
2508HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2509{
2510#ifdef VBOX_WITH_VUSB
2511 clearError();
2512 MultiResult rc(S_OK);
2513
2514# ifdef VBOX_WITH_USB
2515 rc = mParent->i_host()->i_checkUSBProxyService();
2516 if (FAILED(rc)) return rc;
2517# endif
2518
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 aUSBDeviceFilters = mUSBDeviceFilters;
2522 return rc;
2523#else
2524 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2525 * extended error info to indicate that USB is simply not available
2526 * (w/o treating it as a failure), for example, as in OSE */
2527 NOREF(aUSBDeviceFilters);
2528 ReturnComNotImplemented();
2529#endif /* VBOX_WITH_VUSB */
2530}
2531
2532HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 aSettingsFilePath = mData->m_strConfigFileFull;
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2542{
2543 RT_NOREF(aSettingsFilePath);
2544 ReturnComNotImplemented();
2545}
2546
2547HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2548{
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2552 if (FAILED(rc)) return rc;
2553
2554 if (!mData->pMachineConfigFile->fileExists())
2555 // this is a new machine, and no config file exists yet:
2556 *aSettingsModified = TRUE;
2557 else
2558 *aSettingsModified = (mData->flModifications != 0);
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2564{
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 *aSessionState = mData->mSession.mState;
2568
2569 return S_OK;
2570}
2571
2572HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2573{
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576 aSessionName = mData->mSession.mName;
2577
2578 return S_OK;
2579}
2580
2581HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2582{
2583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2584
2585 *aSessionPID = mData->mSession.mPID;
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getState(MachineState_T *aState)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 *aState = mData->mMachineState;
2595 Assert(mData->mMachineState != MachineState_Null);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2610{
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 aStateFilePath = mSSData->strStateFilePath;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 i_getLogFolder(aLogFolder);
2623
2624 return S_OK;
2625}
2626
2627HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2628{
2629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2630
2631 aCurrentSnapshot = mData->mCurrentSnapshot;
2632
2633 return S_OK;
2634}
2635
2636HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2637{
2638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2639
2640 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2641 ? 0
2642 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 /* Note: for machines with no snapshots, we always return FALSE
2652 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2653 * reasons :) */
2654
2655 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2656 ? FALSE
2657 : mData->mCurrentStateModified;
2658
2659 return S_OK;
2660}
2661
2662HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2663{
2664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2665
2666 aSharedFolders.resize(mHWData->mSharedFolders.size());
2667 size_t i = 0;
2668 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2669 it = mHWData->mSharedFolders.begin();
2670 it != mHWData->mSharedFolders.end();
2671 ++it, ++i)
2672 aSharedFolders[i] = *it;
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2678{
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 *aClipboardMode = mHWData->mClipboardMode;
2682
2683 return S_OK;
2684}
2685
2686HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2687{
2688 HRESULT rc = S_OK;
2689
2690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 alock.release();
2693 rc = i_onClipboardModeChange(aClipboardMode);
2694 alock.acquire();
2695 if (FAILED(rc)) return rc;
2696
2697 i_setModified(IsModified_MachineData);
2698 mHWData.backup();
2699 mHWData->mClipboardMode = aClipboardMode;
2700
2701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2702 if (Global::IsOnline(mData->mMachineState))
2703 i_saveSettings(NULL);
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 *aDnDMode = mHWData->mDnDMode;
2713
2714 return S_OK;
2715}
2716
2717HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2718{
2719 HRESULT rc = S_OK;
2720
2721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 alock.release();
2724 rc = i_onDnDModeChange(aDnDMode);
2725
2726 alock.acquire();
2727 if (FAILED(rc)) return rc;
2728
2729 i_setModified(IsModified_MachineData);
2730 mHWData.backup();
2731 mHWData->mDnDMode = aDnDMode;
2732
2733 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2734 if (Global::IsOnline(mData->mMachineState))
2735 i_saveSettings(NULL);
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 aStorageControllers.resize(mStorageControllers->size());
2745 size_t i = 0;
2746 for (StorageControllerList::const_iterator
2747 it = mStorageControllers->begin();
2748 it != mStorageControllers->end();
2749 ++it, ++i)
2750 aStorageControllers[i] = *it;
2751
2752 return S_OK;
2753}
2754
2755HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2756{
2757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 *aEnabled = mUserData->s.fTeleporterEnabled;
2760
2761 return S_OK;
2762}
2763
2764HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2765{
2766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 /* Only allow it to be set to true when PoweredOff or Aborted.
2769 (Clearing it is always permitted.) */
2770 if ( aTeleporterEnabled
2771 && mData->mRegistered
2772 && ( !i_isSessionMachine()
2773 || ( mData->mMachineState != MachineState_PoweredOff
2774 && mData->mMachineState != MachineState_Teleported
2775 && mData->mMachineState != MachineState_Aborted
2776 )
2777 )
2778 )
2779 return setError(VBOX_E_INVALID_VM_STATE,
2780 tr("The machine is not powered off (state is %s)"),
2781 Global::stringifyMachineState(mData->mMachineState));
2782
2783 i_setModified(IsModified_MachineData);
2784 mUserData.backup();
2785 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2791{
2792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2800{
2801 if (aTeleporterPort >= _64K)
2802 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2803
2804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2807 if (FAILED(rc)) return rc;
2808
2809 i_setModified(IsModified_MachineData);
2810 mUserData.backup();
2811 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2826{
2827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2830 if (FAILED(rc)) return rc;
2831
2832 i_setModified(IsModified_MachineData);
2833 mUserData.backup();
2834 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2848{
2849 /*
2850 * Hash the password first.
2851 */
2852 com::Utf8Str aT = aTeleporterPassword;
2853
2854 if (!aT.isEmpty())
2855 {
2856 if (VBoxIsPasswordHashed(&aT))
2857 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2858 VBoxHashPassword(&aT);
2859 }
2860
2861 /*
2862 * Do the update.
2863 */
2864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2865 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2866 if (SUCCEEDED(hrc))
2867 {
2868 i_setModified(IsModified_MachineData);
2869 mUserData.backup();
2870 mUserData->s.strTeleporterPassword = aT;
2871 }
2872
2873 return hrc;
2874}
2875
2876HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2877{
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2881 return S_OK;
2882}
2883
2884HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2885{
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 /** @todo deal with running state change. */
2889 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2890 if (FAILED(rc)) return rc;
2891
2892 i_setModified(IsModified_MachineData);
2893 mUserData.backup();
2894 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2895 return S_OK;
2896}
2897
2898HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2907{
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 /** @todo deal with running state change. */
2911 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mUserData.backup();
2916 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2917 return S_OK;
2918}
2919
2920HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2925 return S_OK;
2926}
2927
2928HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2929{
2930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2931
2932 /** @todo deal with running state change. */
2933 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2934 if (FAILED(rc)) return rc;
2935
2936 i_setModified(IsModified_MachineData);
2937 mUserData.backup();
2938 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2943{
2944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2952{
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 /** @todo deal with running state change. */
2956 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2957 if (FAILED(rc)) return rc;
2958
2959 i_setModified(IsModified_MachineData);
2960 mUserData.backup();
2961 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2962
2963 return S_OK;
2964}
2965
2966HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2967{
2968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2971 return S_OK;
2972}
2973
2974HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2975{
2976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 /** @todo deal with running state change. */
2979 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2980 if (FAILED(rc)) return rc;
2981
2982 i_setModified(IsModified_MachineData);
2983 mUserData.backup();
2984 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2985 return S_OK;
2986}
2987
2988HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2989{
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2993
2994 return S_OK;
2995}
2996
2997HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2998{
2999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 /* Only allow it to be set to true when PoweredOff or Aborted.
3002 (Clearing it is always permitted.) */
3003 if ( aRTCUseUTC
3004 && mData->mRegistered
3005 && ( !i_isSessionMachine()
3006 || ( mData->mMachineState != MachineState_PoweredOff
3007 && mData->mMachineState != MachineState_Teleported
3008 && mData->mMachineState != MachineState_Aborted
3009 )
3010 )
3011 )
3012 return setError(VBOX_E_INVALID_VM_STATE,
3013 tr("The machine is not powered off (state is %s)"),
3014 Global::stringifyMachineState(mData->mMachineState));
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3024{
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3028
3029 return S_OK;
3030}
3031
3032HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3033{
3034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3035
3036 HRESULT rc = i_checkStateDependency(MutableStateDep);
3037 if (FAILED(rc)) return rc;
3038
3039 i_setModified(IsModified_MachineData);
3040 mHWData.backup();
3041 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3042
3043 return S_OK;
3044}
3045
3046HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3047{
3048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3049
3050 *aIOCacheSize = mHWData->mIOCacheSize;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3056{
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 HRESULT rc = i_checkStateDependency(MutableStateDep);
3060 if (FAILED(rc)) return rc;
3061
3062 i_setModified(IsModified_MachineData);
3063 mHWData.backup();
3064 mHWData->mIOCacheSize = aIOCacheSize;
3065
3066 return S_OK;
3067}
3068
3069
3070/**
3071 * @note Locks objects!
3072 */
3073HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3074 LockType_T aLockType)
3075{
3076 /* check the session state */
3077 SessionState_T state;
3078 HRESULT rc = aSession->COMGETTER(State)(&state);
3079 if (FAILED(rc)) return rc;
3080
3081 if (state != SessionState_Unlocked)
3082 return setError(VBOX_E_INVALID_OBJECT_STATE,
3083 tr("The given session is busy"));
3084
3085 // get the client's IInternalSessionControl interface
3086 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3087 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3088 E_INVALIDARG);
3089
3090 // session name (only used in some code paths)
3091 Utf8Str strSessionName;
3092
3093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 if (!mData->mRegistered)
3096 return setError(E_UNEXPECTED,
3097 tr("The machine '%s' is not registered"),
3098 mUserData->s.strName.c_str());
3099
3100 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3101
3102 SessionState_T oldState = mData->mSession.mState;
3103 /* Hack: in case the session is closing and there is a progress object
3104 * which allows waiting for the session to be closed, take the opportunity
3105 * and do a limited wait (max. 1 second). This helps a lot when the system
3106 * is busy and thus session closing can take a little while. */
3107 if ( mData->mSession.mState == SessionState_Unlocking
3108 && mData->mSession.mProgress)
3109 {
3110 alock.release();
3111 mData->mSession.mProgress->WaitForCompletion(1000);
3112 alock.acquire();
3113 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3114 }
3115
3116 // try again now
3117 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3118 // (i.e. session machine exists)
3119 && (aLockType == LockType_Shared) // caller wants a shared link to the
3120 // existing session that holds the write lock:
3121 )
3122 {
3123 // OK, share the session... we are now dealing with three processes:
3124 // 1) VBoxSVC (where this code runs);
3125 // 2) process C: the caller's client process (who wants a shared session);
3126 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3127
3128 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3129 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3130 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3131 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3132 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3133
3134 /*
3135 * Release the lock before calling the client process. It's safe here
3136 * since the only thing to do after we get the lock again is to add
3137 * the remote control to the list (which doesn't directly influence
3138 * anything).
3139 */
3140 alock.release();
3141
3142 // get the console of the session holding the write lock (this is a remote call)
3143 ComPtr<IConsole> pConsoleW;
3144 if (mData->mSession.mLockType == LockType_VM)
3145 {
3146 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3147 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3148 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3149 if (FAILED(rc))
3150 // the failure may occur w/o any error info (from RPC), so provide one
3151 return setError(VBOX_E_VM_ERROR,
3152 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3153 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3154 }
3155
3156 // share the session machine and W's console with the caller's session
3157 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3158 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3159 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3160
3161 if (FAILED(rc))
3162 // the failure may occur w/o any error info (from RPC), so provide one
3163 return setError(VBOX_E_VM_ERROR,
3164 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3165 alock.acquire();
3166
3167 // need to revalidate the state after acquiring the lock again
3168 if (mData->mSession.mState != SessionState_Locked)
3169 {
3170 pSessionControl->Uninitialize();
3171 return setError(VBOX_E_INVALID_SESSION_STATE,
3172 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3173 mUserData->s.strName.c_str());
3174 }
3175
3176 // add the caller's session to the list
3177 mData->mSession.mRemoteControls.push_back(pSessionControl);
3178 }
3179 else if ( mData->mSession.mState == SessionState_Locked
3180 || mData->mSession.mState == SessionState_Unlocking
3181 )
3182 {
3183 // sharing not permitted, or machine still unlocking:
3184 return setError(VBOX_E_INVALID_OBJECT_STATE,
3185 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3186 mUserData->s.strName.c_str());
3187 }
3188 else
3189 {
3190 // machine is not locked: then write-lock the machine (create the session machine)
3191
3192 // must not be busy
3193 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3194
3195 // get the caller's session PID
3196 RTPROCESS pid = NIL_RTPROCESS;
3197 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3198 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3199 Assert(pid != NIL_RTPROCESS);
3200
3201 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3202
3203 if (fLaunchingVMProcess)
3204 {
3205 if (mData->mSession.mPID == NIL_RTPROCESS)
3206 {
3207 // two or more clients racing for a lock, the one which set the
3208 // session state to Spawning will win, the others will get an
3209 // error as we can't decide here if waiting a little would help
3210 // (only for shared locks this would avoid an error)
3211 return setError(VBOX_E_INVALID_OBJECT_STATE,
3212 tr("The machine '%s' already has a lock request pending"),
3213 mUserData->s.strName.c_str());
3214 }
3215
3216 // this machine is awaiting for a spawning session to be opened:
3217 // then the calling process must be the one that got started by
3218 // LaunchVMProcess()
3219
3220 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3221 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3222
3223#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3224 /* Hardened windows builds spawns three processes when a VM is
3225 launched, the 3rd one is the one that will end up here. */
3226 RTPROCESS ppid;
3227 int rc = RTProcQueryParent(pid, &ppid);
3228 if (RT_SUCCESS(rc))
3229 rc = RTProcQueryParent(ppid, &ppid);
3230 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3231 || rc == VERR_ACCESS_DENIED)
3232 {
3233 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3234 mData->mSession.mPID = pid;
3235 }
3236#endif
3237
3238 if (mData->mSession.mPID != pid)
3239 return setError(E_ACCESSDENIED,
3240 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3241 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3242 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3243 }
3244
3245 // create the mutable SessionMachine from the current machine
3246 ComObjPtr<SessionMachine> sessionMachine;
3247 sessionMachine.createObject();
3248 rc = sessionMachine->init(this);
3249 AssertComRC(rc);
3250
3251 /* NOTE: doing return from this function after this point but
3252 * before the end is forbidden since it may call SessionMachine::uninit()
3253 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3254 * lock while still holding the Machine lock in alock so that a deadlock
3255 * is possible due to the wrong lock order. */
3256
3257 if (SUCCEEDED(rc))
3258 {
3259 /*
3260 * Set the session state to Spawning to protect against subsequent
3261 * attempts to open a session and to unregister the machine after
3262 * we release the lock.
3263 */
3264 SessionState_T origState = mData->mSession.mState;
3265 mData->mSession.mState = SessionState_Spawning;
3266
3267#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3268 /* Get the client token ID to be passed to the client process */
3269 Utf8Str strTokenId;
3270 sessionMachine->i_getTokenId(strTokenId);
3271 Assert(!strTokenId.isEmpty());
3272#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3273 /* Get the client token to be passed to the client process */
3274 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3275 /* The token is now "owned" by pToken, fix refcount */
3276 if (!pToken.isNull())
3277 pToken->Release();
3278#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3279
3280 /*
3281 * Release the lock before calling the client process -- it will call
3282 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3283 * because the state is Spawning, so that LaunchVMProcess() and
3284 * LockMachine() calls will fail. This method, called before we
3285 * acquire the lock again, will fail because of the wrong PID.
3286 *
3287 * Note that mData->mSession.mRemoteControls accessed outside
3288 * the lock may not be modified when state is Spawning, so it's safe.
3289 */
3290 alock.release();
3291
3292 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3293#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3294 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3295#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3296 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3297 /* Now the token is owned by the client process. */
3298 pToken.setNull();
3299#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3300 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3301
3302 /* The failure may occur w/o any error info (from RPC), so provide one */
3303 if (FAILED(rc))
3304 setError(VBOX_E_VM_ERROR,
3305 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3306
3307 // get session name, either to remember or to compare against
3308 // the already known session name.
3309 {
3310 Bstr bstrSessionName;
3311 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3312 if (SUCCEEDED(rc2))
3313 strSessionName = bstrSessionName;
3314 }
3315
3316 if ( SUCCEEDED(rc)
3317 && fLaunchingVMProcess
3318 )
3319 {
3320 /* complete the remote session initialization */
3321
3322 /* get the console from the direct session */
3323 ComPtr<IConsole> console;
3324 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3325 ComAssertComRC(rc);
3326
3327 if (SUCCEEDED(rc) && !console)
3328 {
3329 ComAssert(!!console);
3330 rc = E_FAIL;
3331 }
3332
3333 /* assign machine & console to the remote session */
3334 if (SUCCEEDED(rc))
3335 {
3336 /*
3337 * after LaunchVMProcess(), the first and the only
3338 * entry in remoteControls is that remote session
3339 */
3340 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3341 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3342 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3343
3344 /* The failure may occur w/o any error info (from RPC), so provide one */
3345 if (FAILED(rc))
3346 setError(VBOX_E_VM_ERROR,
3347 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3348 }
3349
3350 if (FAILED(rc))
3351 pSessionControl->Uninitialize();
3352 }
3353
3354 /* acquire the lock again */
3355 alock.acquire();
3356
3357 /* Restore the session state */
3358 mData->mSession.mState = origState;
3359 }
3360
3361 // finalize spawning anyway (this is why we don't return on errors above)
3362 if (fLaunchingVMProcess)
3363 {
3364 Assert(mData->mSession.mName == strSessionName);
3365 /* Note that the progress object is finalized later */
3366 /** @todo Consider checking mData->mSession.mProgress for cancellation
3367 * around here. */
3368
3369 /* We don't reset mSession.mPID here because it is necessary for
3370 * SessionMachine::uninit() to reap the child process later. */
3371
3372 if (FAILED(rc))
3373 {
3374 /* Close the remote session, remove the remote control from the list
3375 * and reset session state to Closed (@note keep the code in sync
3376 * with the relevant part in checkForSpawnFailure()). */
3377
3378 Assert(mData->mSession.mRemoteControls.size() == 1);
3379 if (mData->mSession.mRemoteControls.size() == 1)
3380 {
3381 ErrorInfoKeeper eik;
3382 mData->mSession.mRemoteControls.front()->Uninitialize();
3383 }
3384
3385 mData->mSession.mRemoteControls.clear();
3386 mData->mSession.mState = SessionState_Unlocked;
3387 }
3388 }
3389 else
3390 {
3391 /* memorize PID of the directly opened session */
3392 if (SUCCEEDED(rc))
3393 mData->mSession.mPID = pid;
3394 }
3395
3396 if (SUCCEEDED(rc))
3397 {
3398 mData->mSession.mLockType = aLockType;
3399 /* memorize the direct session control and cache IUnknown for it */
3400 mData->mSession.mDirectControl = pSessionControl;
3401 mData->mSession.mState = SessionState_Locked;
3402 if (!fLaunchingVMProcess)
3403 mData->mSession.mName = strSessionName;
3404 /* associate the SessionMachine with this Machine */
3405 mData->mSession.mMachine = sessionMachine;
3406
3407 /* request an IUnknown pointer early from the remote party for later
3408 * identity checks (it will be internally cached within mDirectControl
3409 * at least on XPCOM) */
3410 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3411 NOREF(unk);
3412 }
3413
3414 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3415 * would break the lock order */
3416 alock.release();
3417
3418 /* uninitialize the created session machine on failure */
3419 if (FAILED(rc))
3420 sessionMachine->uninit();
3421 }
3422
3423 if (SUCCEEDED(rc))
3424 {
3425 /*
3426 * tell the client watcher thread to update the set of
3427 * machines that have open sessions
3428 */
3429 mParent->i_updateClientWatcher();
3430
3431 if (oldState != SessionState_Locked)
3432 /* fire an event */
3433 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3434 }
3435
3436 return rc;
3437}
3438
3439/**
3440 * @note Locks objects!
3441 */
3442HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3443 const com::Utf8Str &aName,
3444 const com::Utf8Str &aEnvironment,
3445 ComPtr<IProgress> &aProgress)
3446{
3447 Utf8Str strFrontend(aName);
3448 /* "emergencystop" doesn't need the session, so skip the checks/interface
3449 * retrieval. This code doesn't quite fit in here, but introducing a
3450 * special API method would be even more effort, and would require explicit
3451 * support by every API client. It's better to hide the feature a bit. */
3452 if (strFrontend != "emergencystop")
3453 CheckComArgNotNull(aSession);
3454
3455 HRESULT rc = S_OK;
3456 if (strFrontend.isEmpty())
3457 {
3458 Bstr bstrFrontend;
3459 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3460 if (FAILED(rc))
3461 return rc;
3462 strFrontend = bstrFrontend;
3463 if (strFrontend.isEmpty())
3464 {
3465 ComPtr<ISystemProperties> systemProperties;
3466 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3467 if (FAILED(rc))
3468 return rc;
3469 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3470 if (FAILED(rc))
3471 return rc;
3472 strFrontend = bstrFrontend;
3473 }
3474 /* paranoia - emergencystop is not a valid default */
3475 if (strFrontend == "emergencystop")
3476 strFrontend = Utf8Str::Empty;
3477 }
3478 /* default frontend: Qt GUI */
3479 if (strFrontend.isEmpty())
3480 strFrontend = "GUI/Qt";
3481
3482 if (strFrontend != "emergencystop")
3483 {
3484 /* check the session state */
3485 SessionState_T state;
3486 rc = aSession->COMGETTER(State)(&state);
3487 if (FAILED(rc))
3488 return rc;
3489
3490 if (state != SessionState_Unlocked)
3491 return setError(VBOX_E_INVALID_OBJECT_STATE,
3492 tr("The given session is busy"));
3493
3494 /* get the IInternalSessionControl interface */
3495 ComPtr<IInternalSessionControl> control(aSession);
3496 ComAssertMsgRet(!control.isNull(),
3497 ("No IInternalSessionControl interface"),
3498 E_INVALIDARG);
3499
3500 /* get the teleporter enable state for the progress object init. */
3501 BOOL fTeleporterEnabled;
3502 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3503 if (FAILED(rc))
3504 return rc;
3505
3506 /* create a progress object */
3507 ComObjPtr<ProgressProxy> progress;
3508 progress.createObject();
3509 rc = progress->init(mParent,
3510 static_cast<IMachine*>(this),
3511 Bstr(tr("Starting VM")).raw(),
3512 TRUE /* aCancelable */,
3513 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3514 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3515 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3516 2 /* uFirstOperationWeight */,
3517 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3518
3519 if (SUCCEEDED(rc))
3520 {
3521 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3522 if (SUCCEEDED(rc))
3523 {
3524 aProgress = progress;
3525
3526 /* signal the client watcher thread */
3527 mParent->i_updateClientWatcher();
3528
3529 /* fire an event */
3530 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3531 }
3532 }
3533 }
3534 else
3535 {
3536 /* no progress object - either instant success or failure */
3537 aProgress = NULL;
3538
3539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 if (mData->mSession.mState != SessionState_Locked)
3542 return setError(VBOX_E_INVALID_OBJECT_STATE,
3543 tr("The machine '%s' is not locked by a session"),
3544 mUserData->s.strName.c_str());
3545
3546 /* must have a VM process associated - do not kill normal API clients
3547 * with an open session */
3548 if (!Global::IsOnline(mData->mMachineState))
3549 return setError(VBOX_E_INVALID_OBJECT_STATE,
3550 tr("The machine '%s' does not have a VM process"),
3551 mUserData->s.strName.c_str());
3552
3553 /* forcibly terminate the VM process */
3554 if (mData->mSession.mPID != NIL_RTPROCESS)
3555 RTProcTerminate(mData->mSession.mPID);
3556
3557 /* signal the client watcher thread, as most likely the client has
3558 * been terminated */
3559 mParent->i_updateClientWatcher();
3560 }
3561
3562 return rc;
3563}
3564
3565HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3566{
3567 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3568 return setError(E_INVALIDARG,
3569 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3570 aPosition, SchemaDefs::MaxBootPosition);
3571
3572 if (aDevice == DeviceType_USB)
3573 return setError(E_NOTIMPL,
3574 tr("Booting from USB device is currently not supported"));
3575
3576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3577
3578 HRESULT rc = i_checkStateDependency(MutableStateDep);
3579 if (FAILED(rc)) return rc;
3580
3581 i_setModified(IsModified_MachineData);
3582 mHWData.backup();
3583 mHWData->mBootOrder[aPosition - 1] = aDevice;
3584
3585 return S_OK;
3586}
3587
3588HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3589{
3590 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3591 return setError(E_INVALIDARG,
3592 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3593 aPosition, SchemaDefs::MaxBootPosition);
3594
3595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3596
3597 *aDevice = mHWData->mBootOrder[aPosition - 1];
3598
3599 return S_OK;
3600}
3601
3602HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3603 LONG aControllerPort,
3604 LONG aDevice,
3605 DeviceType_T aType,
3606 const ComPtr<IMedium> &aMedium)
3607{
3608 IMedium *aM = aMedium;
3609 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3610 aName.c_str(), aControllerPort, aDevice, aType, aM));
3611
3612 // request the host lock first, since might be calling Host methods for getting host drives;
3613 // next, protect the media tree all the while we're in here, as well as our member variables
3614 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3615 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3616
3617 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3618 if (FAILED(rc)) return rc;
3619
3620 /// @todo NEWMEDIA implicit machine registration
3621 if (!mData->mRegistered)
3622 return setError(VBOX_E_INVALID_OBJECT_STATE,
3623 tr("Cannot attach storage devices to an unregistered machine"));
3624
3625 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3626
3627 /* Check for an existing controller. */
3628 ComObjPtr<StorageController> ctl;
3629 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3630 if (FAILED(rc)) return rc;
3631
3632 StorageControllerType_T ctrlType;
3633 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3634 if (FAILED(rc))
3635 return setError(E_FAIL,
3636 tr("Could not get type of controller '%s'"),
3637 aName.c_str());
3638
3639 bool fSilent = false;
3640 Utf8Str strReconfig;
3641
3642 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3643 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3644 if ( mData->mMachineState == MachineState_Paused
3645 && strReconfig == "1")
3646 fSilent = true;
3647
3648 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3649 bool fHotplug = false;
3650 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3651 fHotplug = true;
3652
3653 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3654 return setError(VBOX_E_INVALID_VM_STATE,
3655 tr("Controller '%s' does not support hotplugging"),
3656 aName.c_str());
3657
3658 // check that the port and device are not out of range
3659 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3660 if (FAILED(rc)) return rc;
3661
3662 /* check if the device slot is already busy */
3663 MediumAttachment *pAttachTemp;
3664 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3665 aName,
3666 aControllerPort,
3667 aDevice)))
3668 {
3669 Medium *pMedium = pAttachTemp->i_getMedium();
3670 if (pMedium)
3671 {
3672 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3673 return setError(VBOX_E_OBJECT_IN_USE,
3674 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3675 pMedium->i_getLocationFull().c_str(),
3676 aControllerPort,
3677 aDevice,
3678 aName.c_str());
3679 }
3680 else
3681 return setError(VBOX_E_OBJECT_IN_USE,
3682 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3683 aControllerPort, aDevice, aName.c_str());
3684 }
3685
3686 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3687 if (aMedium && medium.isNull())
3688 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3689
3690 AutoCaller mediumCaller(medium);
3691 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3692
3693 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3694
3695 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3696 && !medium.isNull()
3697 )
3698 return setError(VBOX_E_OBJECT_IN_USE,
3699 tr("Medium '%s' is already attached to this virtual machine"),
3700 medium->i_getLocationFull().c_str());
3701
3702 if (!medium.isNull())
3703 {
3704 MediumType_T mtype = medium->i_getType();
3705 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3706 // For DVDs it's not written to the config file, so needs no global config
3707 // version bump. For floppies it's a new attribute "type", which is ignored
3708 // by older VirtualBox version, so needs no global config version bump either.
3709 // For hard disks this type is not accepted.
3710 if (mtype == MediumType_MultiAttach)
3711 {
3712 // This type is new with VirtualBox 4.0 and therefore requires settings
3713 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3714 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3715 // two reasons: The medium type is a property of the media registry tree, which
3716 // can reside in the global config file (for pre-4.0 media); we would therefore
3717 // possibly need to bump the global config version. We don't want to do that though
3718 // because that might make downgrading to pre-4.0 impossible.
3719 // As a result, we can only use these two new types if the medium is NOT in the
3720 // global registry:
3721 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3722 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3723 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3724 )
3725 return setError(VBOX_E_INVALID_OBJECT_STATE,
3726 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3727 "to machines that were created with VirtualBox 4.0 or later"),
3728 medium->i_getLocationFull().c_str());
3729 }
3730 }
3731
3732 bool fIndirect = false;
3733 if (!medium.isNull())
3734 fIndirect = medium->i_isReadOnly();
3735 bool associate = true;
3736
3737 do
3738 {
3739 if ( aType == DeviceType_HardDisk
3740 && mMediumAttachments.isBackedUp())
3741 {
3742 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3743
3744 /* check if the medium was attached to the VM before we started
3745 * changing attachments in which case the attachment just needs to
3746 * be restored */
3747 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3748 {
3749 AssertReturn(!fIndirect, E_FAIL);
3750
3751 /* see if it's the same bus/channel/device */
3752 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3753 {
3754 /* the simplest case: restore the whole attachment
3755 * and return, nothing else to do */
3756 mMediumAttachments->push_back(pAttachTemp);
3757
3758 /* Reattach the medium to the VM. */
3759 if (fHotplug || fSilent)
3760 {
3761 mediumLock.release();
3762 treeLock.release();
3763 alock.release();
3764
3765 MediumLockList *pMediumLockList(new MediumLockList());
3766
3767 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3768 medium /* pToLockWrite */,
3769 false /* fMediumLockWriteAll */,
3770 NULL,
3771 *pMediumLockList);
3772 alock.acquire();
3773 if (FAILED(rc))
3774 delete pMediumLockList;
3775 else
3776 {
3777 mData->mSession.mLockedMedia.Unlock();
3778 alock.release();
3779 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3780 mData->mSession.mLockedMedia.Lock();
3781 alock.acquire();
3782 }
3783 alock.release();
3784
3785 if (SUCCEEDED(rc))
3786 {
3787 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3788 /* Remove lock list in case of error. */
3789 if (FAILED(rc))
3790 {
3791 mData->mSession.mLockedMedia.Unlock();
3792 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3793 mData->mSession.mLockedMedia.Lock();
3794 }
3795 }
3796 }
3797
3798 return S_OK;
3799 }
3800
3801 /* bus/channel/device differ; we need a new attachment object,
3802 * but don't try to associate it again */
3803 associate = false;
3804 break;
3805 }
3806 }
3807
3808 /* go further only if the attachment is to be indirect */
3809 if (!fIndirect)
3810 break;
3811
3812 /* perform the so called smart attachment logic for indirect
3813 * attachments. Note that smart attachment is only applicable to base
3814 * hard disks. */
3815
3816 if (medium->i_getParent().isNull())
3817 {
3818 /* first, investigate the backup copy of the current hard disk
3819 * attachments to make it possible to re-attach existing diffs to
3820 * another device slot w/o losing their contents */
3821 if (mMediumAttachments.isBackedUp())
3822 {
3823 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3824
3825 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3826 uint32_t foundLevel = 0;
3827
3828 for (MediumAttachmentList::const_iterator
3829 it = oldAtts.begin();
3830 it != oldAtts.end();
3831 ++it)
3832 {
3833 uint32_t level = 0;
3834 MediumAttachment *pAttach = *it;
3835 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3836 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3837 if (pMedium.isNull())
3838 continue;
3839
3840 if (pMedium->i_getBase(&level) == medium)
3841 {
3842 /* skip the hard disk if its currently attached (we
3843 * cannot attach the same hard disk twice) */
3844 if (i_findAttachment(*mMediumAttachments.data(),
3845 pMedium))
3846 continue;
3847
3848 /* matched device, channel and bus (i.e. attached to the
3849 * same place) will win and immediately stop the search;
3850 * otherwise the attachment that has the youngest
3851 * descendant of medium will be used
3852 */
3853 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3854 {
3855 /* the simplest case: restore the whole attachment
3856 * and return, nothing else to do */
3857 mMediumAttachments->push_back(*it);
3858
3859 /* Reattach the medium to the VM. */
3860 if (fHotplug || fSilent)
3861 {
3862 mediumLock.release();
3863 treeLock.release();
3864 alock.release();
3865
3866 MediumLockList *pMediumLockList(new MediumLockList());
3867
3868 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3869 medium /* pToLockWrite */,
3870 false /* fMediumLockWriteAll */,
3871 NULL,
3872 *pMediumLockList);
3873 alock.acquire();
3874 if (FAILED(rc))
3875 delete pMediumLockList;
3876 else
3877 {
3878 mData->mSession.mLockedMedia.Unlock();
3879 alock.release();
3880 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3881 mData->mSession.mLockedMedia.Lock();
3882 alock.acquire();
3883 }
3884 alock.release();
3885
3886 if (SUCCEEDED(rc))
3887 {
3888 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3889 /* Remove lock list in case of error. */
3890 if (FAILED(rc))
3891 {
3892 mData->mSession.mLockedMedia.Unlock();
3893 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3894 mData->mSession.mLockedMedia.Lock();
3895 }
3896 }
3897 }
3898
3899 return S_OK;
3900 }
3901 else if ( foundIt == oldAtts.end()
3902 || level > foundLevel /* prefer younger */
3903 )
3904 {
3905 foundIt = it;
3906 foundLevel = level;
3907 }
3908 }
3909 }
3910
3911 if (foundIt != oldAtts.end())
3912 {
3913 /* use the previously attached hard disk */
3914 medium = (*foundIt)->i_getMedium();
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 /* not implicit, doesn't require association with this VM */
3919 fIndirect = false;
3920 associate = false;
3921 /* go right to the MediumAttachment creation */
3922 break;
3923 }
3924 }
3925
3926 /* must give up the medium lock and medium tree lock as below we
3927 * go over snapshots, which needs a lock with higher lock order. */
3928 mediumLock.release();
3929 treeLock.release();
3930
3931 /* then, search through snapshots for the best diff in the given
3932 * hard disk's chain to base the new diff on */
3933
3934 ComObjPtr<Medium> base;
3935 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3936 while (snap)
3937 {
3938 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3939
3940 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3941
3942 MediumAttachment *pAttachFound = NULL;
3943 uint32_t foundLevel = 0;
3944
3945 for (MediumAttachmentList::const_iterator
3946 it = snapAtts.begin();
3947 it != snapAtts.end();
3948 ++it)
3949 {
3950 MediumAttachment *pAttach = *it;
3951 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3952 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3953 if (pMedium.isNull())
3954 continue;
3955
3956 uint32_t level = 0;
3957 if (pMedium->i_getBase(&level) == medium)
3958 {
3959 /* matched device, channel and bus (i.e. attached to the
3960 * same place) will win and immediately stop the search;
3961 * otherwise the attachment that has the youngest
3962 * descendant of medium will be used
3963 */
3964 if ( pAttach->i_getDevice() == aDevice
3965 && pAttach->i_getPort() == aControllerPort
3966 && pAttach->i_getControllerName() == aName
3967 )
3968 {
3969 pAttachFound = pAttach;
3970 break;
3971 }
3972 else if ( !pAttachFound
3973 || level > foundLevel /* prefer younger */
3974 )
3975 {
3976 pAttachFound = pAttach;
3977 foundLevel = level;
3978 }
3979 }
3980 }
3981
3982 if (pAttachFound)
3983 {
3984 base = pAttachFound->i_getMedium();
3985 break;
3986 }
3987
3988 snap = snap->i_getParent();
3989 }
3990
3991 /* re-lock medium tree and the medium, as we need it below */
3992 treeLock.acquire();
3993 mediumLock.acquire();
3994
3995 /* found a suitable diff, use it as a base */
3996 if (!base.isNull())
3997 {
3998 medium = base;
3999 mediumCaller.attach(medium);
4000 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4001 mediumLock.attach(medium);
4002 }
4003 }
4004
4005 Utf8Str strFullSnapshotFolder;
4006 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4007
4008 ComObjPtr<Medium> diff;
4009 diff.createObject();
4010 // store this diff in the same registry as the parent
4011 Guid uuidRegistryParent;
4012 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4013 {
4014 // parent image has no registry: this can happen if we're attaching a new immutable
4015 // image that has not yet been attached (medium then points to the base and we're
4016 // creating the diff image for the immutable, and the parent is not yet registered);
4017 // put the parent in the machine registry then
4018 mediumLock.release();
4019 treeLock.release();
4020 alock.release();
4021 i_addMediumToRegistry(medium);
4022 alock.acquire();
4023 treeLock.acquire();
4024 mediumLock.acquire();
4025 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4026 }
4027 rc = diff->init(mParent,
4028 medium->i_getPreferredDiffFormat(),
4029 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4030 uuidRegistryParent,
4031 DeviceType_HardDisk);
4032 if (FAILED(rc)) return rc;
4033
4034 /* Apply the normal locking logic to the entire chain. */
4035 MediumLockList *pMediumLockList(new MediumLockList());
4036 mediumLock.release();
4037 treeLock.release();
4038 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4039 diff /* pToLockWrite */,
4040 false /* fMediumLockWriteAll */,
4041 medium,
4042 *pMediumLockList);
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045 if (SUCCEEDED(rc))
4046 {
4047 mediumLock.release();
4048 treeLock.release();
4049 rc = pMediumLockList->Lock();
4050 treeLock.acquire();
4051 mediumLock.acquire();
4052 if (FAILED(rc))
4053 setError(rc,
4054 tr("Could not lock medium when creating diff '%s'"),
4055 diff->i_getLocationFull().c_str());
4056 else
4057 {
4058 /* will release the lock before the potentially lengthy
4059 * operation, so protect with the special state */
4060 MachineState_T oldState = mData->mMachineState;
4061 i_setMachineState(MachineState_SettingUp);
4062
4063 mediumLock.release();
4064 treeLock.release();
4065 alock.release();
4066
4067 rc = medium->i_createDiffStorage(diff,
4068 medium->i_getPreferredDiffVariant(),
4069 pMediumLockList,
4070 NULL /* aProgress */,
4071 true /* aWait */,
4072 false /* aNotify */);
4073
4074 alock.acquire();
4075 treeLock.acquire();
4076 mediumLock.acquire();
4077
4078 i_setMachineState(oldState);
4079 }
4080 }
4081
4082 /* Unlock the media and free the associated memory. */
4083 delete pMediumLockList;
4084
4085 if (FAILED(rc)) return rc;
4086
4087 /* use the created diff for the actual attachment */
4088 medium = diff;
4089 mediumCaller.attach(medium);
4090 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4091 mediumLock.attach(medium);
4092 }
4093 while (0);
4094
4095 ComObjPtr<MediumAttachment> attachment;
4096 attachment.createObject();
4097 rc = attachment->init(this,
4098 medium,
4099 aName,
4100 aControllerPort,
4101 aDevice,
4102 aType,
4103 fIndirect,
4104 false /* fPassthrough */,
4105 false /* fTempEject */,
4106 false /* fNonRotational */,
4107 false /* fDiscard */,
4108 fHotplug /* fHotPluggable */,
4109 Utf8Str::Empty);
4110 if (FAILED(rc)) return rc;
4111
4112 if (associate && !medium.isNull())
4113 {
4114 // as the last step, associate the medium to the VM
4115 rc = medium->i_addBackReference(mData->mUuid);
4116 // here we can fail because of Deleting, or being in process of creating a Diff
4117 if (FAILED(rc)) return rc;
4118
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 }
4127
4128 /* success: finally remember the attachment */
4129 i_setModified(IsModified_Storage);
4130 mMediumAttachments.backup();
4131 mMediumAttachments->push_back(attachment);
4132
4133 mediumLock.release();
4134 treeLock.release();
4135 alock.release();
4136
4137 if (fHotplug || fSilent)
4138 {
4139 if (!medium.isNull())
4140 {
4141 MediumLockList *pMediumLockList(new MediumLockList());
4142
4143 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4144 medium /* pToLockWrite */,
4145 false /* fMediumLockWriteAll */,
4146 NULL,
4147 *pMediumLockList);
4148 alock.acquire();
4149 if (FAILED(rc))
4150 delete pMediumLockList;
4151 else
4152 {
4153 mData->mSession.mLockedMedia.Unlock();
4154 alock.release();
4155 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4156 mData->mSession.mLockedMedia.Lock();
4157 alock.acquire();
4158 }
4159 alock.release();
4160 }
4161
4162 if (SUCCEEDED(rc))
4163 {
4164 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4165 /* Remove lock list in case of error. */
4166 if (FAILED(rc))
4167 {
4168 mData->mSession.mLockedMedia.Unlock();
4169 mData->mSession.mLockedMedia.Remove(attachment);
4170 mData->mSession.mLockedMedia.Lock();
4171 }
4172 }
4173 }
4174
4175 /* Save modified registries, but skip this machine as it's the caller's
4176 * job to save its settings like all other settings changes. */
4177 mParent->i_unmarkRegistryModified(i_getId());
4178 mParent->i_saveModifiedRegistries();
4179
4180 if (aM)
4181 mParent->i_onMediumConfigChanged(aM);
4182 return rc;
4183}
4184
4185HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4186 LONG aDevice)
4187{
4188 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4189 aName.c_str(), aControllerPort, aDevice));
4190
4191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4192
4193 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4194 if (FAILED(rc)) return rc;
4195
4196 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4197
4198 /* Check for an existing controller. */
4199 ComObjPtr<StorageController> ctl;
4200 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4201 if (FAILED(rc)) return rc;
4202
4203 StorageControllerType_T ctrlType;
4204 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4205 if (FAILED(rc))
4206 return setError(E_FAIL,
4207 tr("Could not get type of controller '%s'"),
4208 aName.c_str());
4209
4210 bool fSilent = false;
4211 Utf8Str strReconfig;
4212
4213 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4214 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4215 if ( mData->mMachineState == MachineState_Paused
4216 && strReconfig == "1")
4217 fSilent = true;
4218
4219 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4220 bool fHotplug = false;
4221 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4222 fHotplug = true;
4223
4224 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4225 return setError(VBOX_E_INVALID_VM_STATE,
4226 tr("Controller '%s' does not support hotplugging"),
4227 aName.c_str());
4228
4229 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4230 aName,
4231 aControllerPort,
4232 aDevice);
4233 if (!pAttach)
4234 return setError(VBOX_E_OBJECT_NOT_FOUND,
4235 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4236 aDevice, aControllerPort, aName.c_str());
4237
4238 if (fHotplug && !pAttach->i_getHotPluggable())
4239 return setError(VBOX_E_NOT_SUPPORTED,
4240 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4241 aDevice, aControllerPort, aName.c_str());
4242
4243 /*
4244 * The VM has to detach the device before we delete any implicit diffs.
4245 * If this fails we can roll back without loosing data.
4246 */
4247 if (fHotplug || fSilent)
4248 {
4249 alock.release();
4250 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4251 alock.acquire();
4252 }
4253 if (FAILED(rc)) return rc;
4254
4255 /* If we are here everything went well and we can delete the implicit now. */
4256 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4257
4258 alock.release();
4259
4260 /* Save modified registries, but skip this machine as it's the caller's
4261 * job to save its settings like all other settings changes. */
4262 mParent->i_unmarkRegistryModified(i_getId());
4263 mParent->i_saveModifiedRegistries();
4264
4265 return rc;
4266}
4267
4268HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4269 LONG aDevice, BOOL aPassthrough)
4270{
4271 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4272 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4273
4274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4275
4276 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4277 if (FAILED(rc)) return rc;
4278
4279 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4280
4281 /* Check for an existing controller. */
4282 ComObjPtr<StorageController> ctl;
4283 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4284 if (FAILED(rc)) return rc;
4285
4286 StorageControllerType_T ctrlType;
4287 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4288 if (FAILED(rc))
4289 return setError(E_FAIL,
4290 tr("Could not get type of controller '%s'"),
4291 aName.c_str());
4292
4293 bool fSilent = false;
4294 Utf8Str strReconfig;
4295
4296 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4297 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4298 if ( mData->mMachineState == MachineState_Paused
4299 && strReconfig == "1")
4300 fSilent = true;
4301
4302 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4303 bool fHotplug = false;
4304 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4305 fHotplug = true;
4306
4307 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4308 return setError(VBOX_E_INVALID_VM_STATE,
4309 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4310 aName.c_str());
4311
4312 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4313 aName,
4314 aControllerPort,
4315 aDevice);
4316 if (!pAttach)
4317 return setError(VBOX_E_OBJECT_NOT_FOUND,
4318 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4319 aDevice, aControllerPort, aName.c_str());
4320
4321
4322 i_setModified(IsModified_Storage);
4323 mMediumAttachments.backup();
4324
4325 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4326
4327 if (pAttach->i_getType() != DeviceType_DVD)
4328 return setError(E_INVALIDARG,
4329 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4330 aDevice, aControllerPort, aName.c_str());
4331 pAttach->i_updatePassthrough(!!aPassthrough);
4332
4333 attLock.release();
4334 alock.release();
4335 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4336
4337 return rc;
4338}
4339
4340HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4341 LONG aDevice, BOOL aTemporaryEject)
4342{
4343
4344 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4345 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4346
4347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4348
4349 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4350 if (FAILED(rc)) return rc;
4351
4352 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4353 aName,
4354 aControllerPort,
4355 aDevice);
4356 if (!pAttach)
4357 return setError(VBOX_E_OBJECT_NOT_FOUND,
4358 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4359 aDevice, aControllerPort, aName.c_str());
4360
4361
4362 i_setModified(IsModified_Storage);
4363 mMediumAttachments.backup();
4364
4365 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4366
4367 if (pAttach->i_getType() != DeviceType_DVD)
4368 return setError(E_INVALIDARG,
4369 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4370 aDevice, aControllerPort, aName.c_str());
4371 pAttach->i_updateTempEject(!!aTemporaryEject);
4372
4373 return S_OK;
4374}
4375
4376HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4377 LONG aDevice, BOOL aNonRotational)
4378{
4379
4380 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4381 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4382
4383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4384
4385 HRESULT rc = i_checkStateDependency(MutableStateDep);
4386 if (FAILED(rc)) return rc;
4387
4388 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4389
4390 if (Global::IsOnlineOrTransient(mData->mMachineState))
4391 return setError(VBOX_E_INVALID_VM_STATE,
4392 tr("Invalid machine state: %s"),
4393 Global::stringifyMachineState(mData->mMachineState));
4394
4395 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4396 aName,
4397 aControllerPort,
4398 aDevice);
4399 if (!pAttach)
4400 return setError(VBOX_E_OBJECT_NOT_FOUND,
4401 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4402 aDevice, aControllerPort, aName.c_str());
4403
4404
4405 i_setModified(IsModified_Storage);
4406 mMediumAttachments.backup();
4407
4408 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4409
4410 if (pAttach->i_getType() != DeviceType_HardDisk)
4411 return setError(E_INVALIDARG,
4412 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4413 aDevice, aControllerPort, aName.c_str());
4414 pAttach->i_updateNonRotational(!!aNonRotational);
4415
4416 return S_OK;
4417}
4418
4419HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4420 LONG aDevice, BOOL aDiscard)
4421{
4422
4423 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4424 aName.c_str(), aControllerPort, aDevice, aDiscard));
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = i_checkStateDependency(MutableStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4432
4433 if (Global::IsOnlineOrTransient(mData->mMachineState))
4434 return setError(VBOX_E_INVALID_VM_STATE,
4435 tr("Invalid machine state: %s"),
4436 Global::stringifyMachineState(mData->mMachineState));
4437
4438 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4439 aName,
4440 aControllerPort,
4441 aDevice);
4442 if (!pAttach)
4443 return setError(VBOX_E_OBJECT_NOT_FOUND,
4444 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4445 aDevice, aControllerPort, aName.c_str());
4446
4447
4448 i_setModified(IsModified_Storage);
4449 mMediumAttachments.backup();
4450
4451 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4452
4453 if (pAttach->i_getType() != DeviceType_HardDisk)
4454 return setError(E_INVALIDARG,
4455 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4456 aDevice, aControllerPort, aName.c_str());
4457 pAttach->i_updateDiscard(!!aDiscard);
4458
4459 return S_OK;
4460}
4461
4462HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4463 LONG aDevice, BOOL aHotPluggable)
4464{
4465 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4466 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4467
4468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4469
4470 HRESULT rc = i_checkStateDependency(MutableStateDep);
4471 if (FAILED(rc)) return rc;
4472
4473 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4474
4475 if (Global::IsOnlineOrTransient(mData->mMachineState))
4476 return setError(VBOX_E_INVALID_VM_STATE,
4477 tr("Invalid machine state: %s"),
4478 Global::stringifyMachineState(mData->mMachineState));
4479
4480 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4481 aName,
4482 aControllerPort,
4483 aDevice);
4484 if (!pAttach)
4485 return setError(VBOX_E_OBJECT_NOT_FOUND,
4486 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489 /* Check for an existing controller. */
4490 ComObjPtr<StorageController> ctl;
4491 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4492 if (FAILED(rc)) return rc;
4493
4494 StorageControllerType_T ctrlType;
4495 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4496 if (FAILED(rc))
4497 return setError(E_FAIL,
4498 tr("Could not get type of controller '%s'"),
4499 aName.c_str());
4500
4501 if (!i_isControllerHotplugCapable(ctrlType))
4502 return setError(VBOX_E_NOT_SUPPORTED,
4503 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4504 aName.c_str());
4505
4506 i_setModified(IsModified_Storage);
4507 mMediumAttachments.backup();
4508
4509 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4510
4511 if (pAttach->i_getType() == DeviceType_Floppy)
4512 return setError(E_INVALIDARG,
4513 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4514 aDevice, aControllerPort, aName.c_str());
4515 pAttach->i_updateHotPluggable(!!aHotPluggable);
4516
4517 return S_OK;
4518}
4519
4520HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4521 LONG aDevice)
4522{
4523 int rc = S_OK;
4524 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4525 aName.c_str(), aControllerPort, aDevice));
4526
4527 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4528
4529 return rc;
4530}
4531
4532HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4533 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4534{
4535 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4536 aName.c_str(), aControllerPort, aDevice));
4537
4538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4539
4540 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4541 if (FAILED(rc)) return rc;
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4549 aName,
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557
4558 i_setModified(IsModified_Storage);
4559 mMediumAttachments.backup();
4560
4561 IBandwidthGroup *iB = aBandwidthGroup;
4562 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4563 if (aBandwidthGroup && group.isNull())
4564 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4565
4566 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4567
4568 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4569 if (strBandwidthGroupOld.isNotEmpty())
4570 {
4571 /* Get the bandwidth group object and release it - this must not fail. */
4572 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4573 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4574 Assert(SUCCEEDED(rc));
4575
4576 pBandwidthGroupOld->i_release();
4577 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4578 }
4579
4580 if (!group.isNull())
4581 {
4582 group->i_reference();
4583 pAttach->i_updateBandwidthGroup(group->i_getName());
4584 }
4585
4586 return S_OK;
4587}
4588
4589HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4590 LONG aControllerPort,
4591 LONG aDevice,
4592 DeviceType_T aType)
4593{
4594 HRESULT rc = S_OK;
4595
4596 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4597 aName.c_str(), aControllerPort, aDevice, aType));
4598
4599 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4600
4601 return rc;
4602}
4603
4604
4605HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4606 LONG aControllerPort,
4607 LONG aDevice,
4608 BOOL aForce)
4609{
4610 int rc = S_OK;
4611 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4612 aName.c_str(), aControllerPort, aForce));
4613
4614 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4615
4616 return rc;
4617}
4618
4619HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 const ComPtr<IMedium> &aMedium,
4623 BOOL aForce)
4624{
4625 int rc = S_OK;
4626 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4627 aName.c_str(), aControllerPort, aDevice, aForce));
4628
4629 // request the host lock first, since might be calling Host methods for getting host drives;
4630 // next, protect the media tree all the while we're in here, as well as our member variables
4631 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4632 this->lockHandle(),
4633 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4634
4635 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4636 aName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4642 aDevice, aControllerPort, aName.c_str());
4643
4644 /* Remember previously mounted medium. The medium before taking the
4645 * backup is not necessarily the same thing. */
4646 ComObjPtr<Medium> oldmedium;
4647 oldmedium = pAttach->i_getMedium();
4648
4649 IMedium *iM = aMedium;
4650 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4651 if (aMedium && pMedium.isNull())
4652 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4653
4654 AutoCaller mediumCaller(pMedium);
4655 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4656
4657 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4658 if (pMedium)
4659 {
4660 DeviceType_T mediumType = pAttach->i_getType();
4661 switch (mediumType)
4662 {
4663 case DeviceType_DVD:
4664 case DeviceType_Floppy:
4665 break;
4666
4667 default:
4668 return setError(VBOX_E_INVALID_OBJECT_STATE,
4669 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4670 aControllerPort,
4671 aDevice,
4672 aName.c_str());
4673 }
4674 }
4675
4676 i_setModified(IsModified_Storage);
4677 mMediumAttachments.backup();
4678
4679 {
4680 // The backup operation makes the pAttach reference point to the
4681 // old settings. Re-get the correct reference.
4682 pAttach = i_findAttachment(*mMediumAttachments.data(),
4683 aName,
4684 aControllerPort,
4685 aDevice);
4686 if (!oldmedium.isNull())
4687 oldmedium->i_removeBackReference(mData->mUuid);
4688 if (!pMedium.isNull())
4689 {
4690 pMedium->i_addBackReference(mData->mUuid);
4691
4692 mediumLock.release();
4693 multiLock.release();
4694 i_addMediumToRegistry(pMedium);
4695 multiLock.acquire();
4696 mediumLock.acquire();
4697 }
4698
4699 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4700 pAttach->i_updateMedium(pMedium);
4701 }
4702
4703 i_setModified(IsModified_Storage);
4704
4705 mediumLock.release();
4706 multiLock.release();
4707 rc = i_onMediumChange(pAttach, aForce);
4708 multiLock.acquire();
4709 mediumLock.acquire();
4710
4711 /* On error roll back this change only. */
4712 if (FAILED(rc))
4713 {
4714 if (!pMedium.isNull())
4715 pMedium->i_removeBackReference(mData->mUuid);
4716 pAttach = i_findAttachment(*mMediumAttachments.data(),
4717 aName,
4718 aControllerPort,
4719 aDevice);
4720 /* If the attachment is gone in the meantime, bail out. */
4721 if (pAttach.isNull())
4722 return rc;
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724 if (!oldmedium.isNull())
4725 oldmedium->i_addBackReference(mData->mUuid);
4726 pAttach->i_updateMedium(oldmedium);
4727 }
4728
4729 mediumLock.release();
4730 multiLock.release();
4731
4732 /* Save modified registries, but skip this machine as it's the caller's
4733 * job to save its settings like all other settings changes. */
4734 mParent->i_unmarkRegistryModified(i_getId());
4735 mParent->i_saveModifiedRegistries();
4736
4737 return rc;
4738}
4739HRESULT Machine::getMedium(const com::Utf8Str &aName,
4740 LONG aControllerPort,
4741 LONG aDevice,
4742 ComPtr<IMedium> &aMedium)
4743{
4744 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4745 aName.c_str(), aControllerPort, aDevice));
4746
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4748
4749 aMedium = NULL;
4750
4751 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4752 aName,
4753 aControllerPort,
4754 aDevice);
4755 if (pAttach.isNull())
4756 return setError(VBOX_E_OBJECT_NOT_FOUND,
4757 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4758 aDevice, aControllerPort, aName.c_str());
4759
4760 aMedium = pAttach->i_getMedium();
4761
4762 return S_OK;
4763}
4764
4765HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4766{
4767
4768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4771
4772 return S_OK;
4773}
4774
4775HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4776{
4777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4778
4779 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4780
4781 return S_OK;
4782}
4783
4784HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4785{
4786 /* Do not assert if slot is out of range, just return the advertised
4787 status. testdriver/vbox.py triggers this in logVmInfo. */
4788 if (aSlot >= mNetworkAdapters.size())
4789 return setError(E_INVALIDARG,
4790 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4791 aSlot, mNetworkAdapters.size());
4792
4793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4796
4797 return S_OK;
4798}
4799
4800HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4801{
4802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4803
4804 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4805 size_t i = 0;
4806 for (settings::StringsMap::const_iterator
4807 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4808 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4809 ++it, ++i)
4810 aKeys[i] = it->first;
4811
4812 return S_OK;
4813}
4814
4815 /**
4816 * @note Locks this object for reading.
4817 */
4818HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4819 com::Utf8Str &aValue)
4820{
4821 /* start with nothing found */
4822 aValue = "";
4823
4824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4827 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4828 // found:
4829 aValue = it->second; // source is a Utf8Str
4830
4831 /* return the result to caller (may be empty) */
4832 return S_OK;
4833}
4834
4835 /**
4836 * @note Locks mParent for writing + this object for writing.
4837 */
4838HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4839{
4840 /* Because non-ASCII characters in aKey have caused problems in the settings
4841 * they are rejected unless the key should be deleted. */
4842 if (!aValue.isEmpty())
4843 {
4844 for (size_t i = 0; i < aKey.length(); ++i)
4845 {
4846 char ch = aKey[i];
4847 if (!RTLocCIsPrint(ch))
4848 return E_INVALIDARG;
4849 }
4850 }
4851
4852 Utf8Str strOldValue; // empty
4853
4854 // locking note: we only hold the read lock briefly to look up the old value,
4855 // then release it and call the onExtraCanChange callbacks. There is a small
4856 // chance of a race insofar as the callback might be called twice if two callers
4857 // change the same key at the same time, but that's a much better solution
4858 // than the deadlock we had here before. The actual changing of the extradata
4859 // is then performed under the write lock and race-free.
4860
4861 // look up the old value first; if nothing has changed then we need not do anything
4862 {
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4864
4865 // For snapshots don't even think about allowing changes, extradata
4866 // is global for a machine, so there is nothing snapshot specific.
4867 if (i_isSnapshotMachine())
4868 return setError(VBOX_E_INVALID_VM_STATE,
4869 tr("Cannot set extradata for a snapshot"));
4870
4871 // check if the right IMachine instance is used
4872 if (mData->mRegistered && !i_isSessionMachine())
4873 return setError(VBOX_E_INVALID_VM_STATE,
4874 tr("Cannot set extradata for an immutable machine"));
4875
4876 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4877 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4878 strOldValue = it->second;
4879 }
4880
4881 bool fChanged;
4882 if ((fChanged = (strOldValue != aValue)))
4883 {
4884 // ask for permission from all listeners outside the locks;
4885 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4886 // lock to copy the list of callbacks to invoke
4887 Bstr error;
4888 Bstr bstrValue(aValue);
4889
4890 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4891 {
4892 const char *sep = error.isEmpty() ? "" : ": ";
4893 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4894 return setError(E_ACCESSDENIED,
4895 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4896 aKey.c_str(),
4897 aValue.c_str(),
4898 sep,
4899 error.raw());
4900 }
4901
4902 // data is changing and change not vetoed: then write it out under the lock
4903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4904
4905 if (aValue.isEmpty())
4906 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4907 else
4908 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4909 // creates a new key if needed
4910
4911 bool fNeedsGlobalSaveSettings = false;
4912 // This saving of settings is tricky: there is no "old state" for the
4913 // extradata items at all (unlike all other settings), so the old/new
4914 // settings comparison would give a wrong result!
4915 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4916
4917 if (fNeedsGlobalSaveSettings)
4918 {
4919 // save the global settings; for that we should hold only the VirtualBox lock
4920 alock.release();
4921 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4922 mParent->i_saveSettings();
4923 }
4924 }
4925
4926 // fire notification outside the lock
4927 if (fChanged)
4928 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4929
4930 return S_OK;
4931}
4932
4933HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4934{
4935 aProgress = NULL;
4936 NOREF(aSettingsFilePath);
4937 ReturnComNotImplemented();
4938}
4939
4940HRESULT Machine::saveSettings()
4941{
4942 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4945 if (FAILED(rc)) return rc;
4946
4947 /* the settings file path may never be null */
4948 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4949
4950 /* save all VM data excluding snapshots */
4951 bool fNeedsGlobalSaveSettings = false;
4952 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4953 mlock.release();
4954
4955 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4956 {
4957 // save the global settings; for that we should hold only the VirtualBox lock
4958 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4959 rc = mParent->i_saveSettings();
4960 }
4961
4962 return rc;
4963}
4964
4965
4966HRESULT Machine::discardSettings()
4967{
4968 /*
4969 * We need to take the machine list lock here as well as the machine one
4970 * or we'll get into trouble should any media stuff require rolling back.
4971 *
4972 * Details:
4973 *
4974 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4975 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4976 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4977 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4978 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4979 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4980 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4981 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4982 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4983 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4984 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4985 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4986 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4987 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4988 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4989 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4990 * 0:005> k
4991 * # Child-SP RetAddr Call Site
4992 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4993 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4994 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4995 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4996 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4997 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4998 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4999 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5000 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5001 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5002 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5003 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5004 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5005 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5006 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5007 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5008 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5009 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5010 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5011 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5012 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5013 *
5014 */
5015 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5017
5018 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5019 if (FAILED(rc)) return rc;
5020
5021 /*
5022 * during this rollback, the session will be notified if data has
5023 * been actually changed
5024 */
5025 i_rollback(true /* aNotify */);
5026
5027 return S_OK;
5028}
5029
5030/** @note Locks objects! */
5031HRESULT Machine::unregister(AutoCaller &autoCaller,
5032 CleanupMode_T aCleanupMode,
5033 std::vector<ComPtr<IMedium> > &aMedia)
5034{
5035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5036
5037 Guid id(i_getId());
5038
5039 if (mData->mSession.mState != SessionState_Unlocked)
5040 return setError(VBOX_E_INVALID_OBJECT_STATE,
5041 tr("Cannot unregister the machine '%s' while it is locked"),
5042 mUserData->s.strName.c_str());
5043
5044 // wait for state dependents to drop to zero
5045 i_ensureNoStateDependencies();
5046
5047 if (!mData->mAccessible)
5048 {
5049 // inaccessible maschines can only be unregistered; uninitialize ourselves
5050 // here because currently there may be no unregistered that are inaccessible
5051 // (this state combination is not supported). Note releasing the caller and
5052 // leaving the lock before calling uninit()
5053 alock.release();
5054 autoCaller.release();
5055
5056 uninit();
5057
5058 mParent->i_unregisterMachine(this, id);
5059 // calls VirtualBox::i_saveSettings()
5060
5061 return S_OK;
5062 }
5063
5064 HRESULT rc = S_OK;
5065
5066 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5067 // discard saved state
5068 if (mData->mMachineState == MachineState_Saved)
5069 {
5070 // add the saved state file to the list of files the caller should delete
5071 Assert(!mSSData->strStateFilePath.isEmpty());
5072 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5073
5074 mSSData->strStateFilePath.setNull();
5075
5076 // unconditionally set the machine state to powered off, we now
5077 // know no session has locked the machine
5078 mData->mMachineState = MachineState_PoweredOff;
5079 }
5080
5081 size_t cSnapshots = 0;
5082 if (mData->mFirstSnapshot)
5083 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5084 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5085 // fail now before we start detaching media
5086 return setError(VBOX_E_INVALID_OBJECT_STATE,
5087 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5088 mUserData->s.strName.c_str(), cSnapshots);
5089
5090 // This list collects the medium objects from all medium attachments
5091 // which we will detach from the machine and its snapshots, in a specific
5092 // order which allows for closing all media without getting "media in use"
5093 // errors, simply by going through the list from the front to the back:
5094 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5095 // and must be closed before the parent media from the snapshots, or closing the parents
5096 // will fail because they still have children);
5097 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5098 // the root ("first") snapshot of the machine.
5099 MediaList llMedia;
5100
5101 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5102 && mMediumAttachments->size()
5103 )
5104 {
5105 // we have media attachments: detach them all and add the Medium objects to our list
5106 if (aCleanupMode != CleanupMode_UnregisterOnly)
5107 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5108 else
5109 return setError(VBOX_E_INVALID_OBJECT_STATE,
5110 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5111 mUserData->s.strName.c_str(), mMediumAttachments->size());
5112 }
5113
5114 if (cSnapshots)
5115 {
5116 // add the media from the medium attachments of the snapshots to llMedia
5117 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5118 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5119 // into the children first
5120
5121 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5122 MachineState_T oldState = mData->mMachineState;
5123 mData->mMachineState = MachineState_DeletingSnapshot;
5124
5125 // make a copy of the first snapshot so the refcount does not drop to 0
5126 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5127 // because of the AutoCaller voodoo)
5128 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5129
5130 // GO!
5131 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5132
5133 mData->mMachineState = oldState;
5134 }
5135
5136 if (FAILED(rc))
5137 {
5138 i_rollbackMedia();
5139 return rc;
5140 }
5141
5142 // commit all the media changes made above
5143 i_commitMedia();
5144
5145 mData->mRegistered = false;
5146
5147 // machine lock no longer needed
5148 alock.release();
5149
5150 // return media to caller
5151 aMedia.resize(llMedia.size());
5152 size_t i = 0;
5153 for (MediaList::const_iterator
5154 it = llMedia.begin();
5155 it != llMedia.end();
5156 ++it, ++i)
5157 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5158
5159 mParent->i_unregisterMachine(this, id);
5160 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5161
5162 return S_OK;
5163}
5164
5165/**
5166 * Task record for deleting a machine config.
5167 */
5168class Machine::DeleteConfigTask
5169 : public Machine::Task
5170{
5171public:
5172 DeleteConfigTask(Machine *m,
5173 Progress *p,
5174 const Utf8Str &t,
5175 const RTCList<ComPtr<IMedium> > &llMediums,
5176 const StringsList &llFilesToDelete)
5177 : Task(m, p, t),
5178 m_llMediums(llMediums),
5179 m_llFilesToDelete(llFilesToDelete)
5180 {}
5181
5182private:
5183 void handler()
5184 {
5185 try
5186 {
5187 m_pMachine->i_deleteConfigHandler(*this);
5188 }
5189 catch (...)
5190 {
5191 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5192 }
5193 }
5194
5195 RTCList<ComPtr<IMedium> > m_llMediums;
5196 StringsList m_llFilesToDelete;
5197
5198 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5199};
5200
5201/**
5202 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5203 * SessionMachine::taskHandler().
5204 *
5205 * @note Locks this object for writing.
5206 *
5207 * @param task
5208 * @return
5209 */
5210void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5211{
5212 LogFlowThisFuncEnter();
5213
5214 AutoCaller autoCaller(this);
5215 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5216 if (FAILED(autoCaller.rc()))
5217 {
5218 /* we might have been uninitialized because the session was accidentally
5219 * closed by the client, so don't assert */
5220 HRESULT rc = setError(E_FAIL,
5221 tr("The session has been accidentally closed"));
5222 task.m_pProgress->i_notifyComplete(rc);
5223 LogFlowThisFuncLeave();
5224 return;
5225 }
5226
5227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5228
5229 HRESULT rc = S_OK;
5230
5231 try
5232 {
5233 ULONG uLogHistoryCount = 3;
5234 ComPtr<ISystemProperties> systemProperties;
5235 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5236 if (FAILED(rc)) throw rc;
5237
5238 if (!systemProperties.isNull())
5239 {
5240 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5241 if (FAILED(rc)) throw rc;
5242 }
5243
5244 MachineState_T oldState = mData->mMachineState;
5245 i_setMachineState(MachineState_SettingUp);
5246 alock.release();
5247 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5248 {
5249 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5250 {
5251 AutoCaller mac(pMedium);
5252 if (FAILED(mac.rc())) throw mac.rc();
5253 Utf8Str strLocation = pMedium->i_getLocationFull();
5254 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5255 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5256 if (FAILED(rc)) throw rc;
5257 }
5258 if (pMedium->i_isMediumFormatFile())
5259 {
5260 ComPtr<IProgress> pProgress2;
5261 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5262 if (FAILED(rc)) throw rc;
5263 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5264 if (FAILED(rc)) throw rc;
5265 }
5266
5267 /* Close the medium, deliberately without checking the return
5268 * code, and without leaving any trace in the error info, as
5269 * a failure here is a very minor issue, which shouldn't happen
5270 * as above we even managed to delete the medium. */
5271 {
5272 ErrorInfoKeeper eik;
5273 pMedium->Close();
5274 }
5275 }
5276 i_setMachineState(oldState);
5277 alock.acquire();
5278
5279 // delete the files pushed on the task list by Machine::Delete()
5280 // (this includes saved states of the machine and snapshots and
5281 // medium storage files from the IMedium list passed in, and the
5282 // machine XML file)
5283 for (StringsList::const_iterator
5284 it = task.m_llFilesToDelete.begin();
5285 it != task.m_llFilesToDelete.end();
5286 ++it)
5287 {
5288 const Utf8Str &strFile = *it;
5289 LogFunc(("Deleting file %s\n", strFile.c_str()));
5290 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5291 if (FAILED(rc)) throw rc;
5292
5293 int vrc = RTFileDelete(strFile.c_str());
5294 if (RT_FAILURE(vrc))
5295 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5296 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5297 }
5298
5299 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5300 if (FAILED(rc)) throw rc;
5301
5302 /* delete the settings only when the file actually exists */
5303 if (mData->pMachineConfigFile->fileExists())
5304 {
5305 /* Delete any backup or uncommitted XML files. Ignore failures.
5306 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5307 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5308 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5309 RTFileDelete(otherXml.c_str());
5310 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5311 RTFileDelete(otherXml.c_str());
5312
5313 /* delete the Logs folder, nothing important should be left
5314 * there (we don't check for errors because the user might have
5315 * some private files there that we don't want to delete) */
5316 Utf8Str logFolder;
5317 getLogFolder(logFolder);
5318 Assert(logFolder.length());
5319 if (RTDirExists(logFolder.c_str()))
5320 {
5321 /* Delete all VBox.log[.N] files from the Logs folder
5322 * (this must be in sync with the rotation logic in
5323 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5324 * files that may have been created by the GUI. */
5325 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5326 logFolder.c_str(), RTPATH_DELIMITER);
5327 RTFileDelete(log.c_str());
5328 log = Utf8StrFmt("%s%cVBox.png",
5329 logFolder.c_str(), RTPATH_DELIMITER);
5330 RTFileDelete(log.c_str());
5331 for (int i = uLogHistoryCount; i > 0; i--)
5332 {
5333 log = Utf8StrFmt("%s%cVBox.log.%d",
5334 logFolder.c_str(), RTPATH_DELIMITER, i);
5335 RTFileDelete(log.c_str());
5336 log = Utf8StrFmt("%s%cVBox.png.%d",
5337 logFolder.c_str(), RTPATH_DELIMITER, i);
5338 RTFileDelete(log.c_str());
5339 }
5340#if defined(RT_OS_WINDOWS)
5341 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5342 RTFileDelete(log.c_str());
5343 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345#endif
5346
5347 RTDirRemove(logFolder.c_str());
5348 }
5349
5350 /* delete the Snapshots folder, nothing important should be left
5351 * there (we don't check for errors because the user might have
5352 * some private files there that we don't want to delete) */
5353 Utf8Str strFullSnapshotFolder;
5354 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5355 Assert(!strFullSnapshotFolder.isEmpty());
5356 if (RTDirExists(strFullSnapshotFolder.c_str()))
5357 RTDirRemove(strFullSnapshotFolder.c_str());
5358
5359 // delete the directory that contains the settings file, but only
5360 // if it matches the VM name
5361 Utf8Str settingsDir;
5362 if (i_isInOwnDir(&settingsDir))
5363 RTDirRemove(settingsDir.c_str());
5364 }
5365
5366 alock.release();
5367
5368 mParent->i_saveModifiedRegistries();
5369 }
5370 catch (HRESULT aRC) { rc = aRC; }
5371
5372 task.m_pProgress->i_notifyComplete(rc);
5373
5374 LogFlowThisFuncLeave();
5375}
5376
5377HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5378{
5379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5380
5381 HRESULT rc = i_checkStateDependency(MutableStateDep);
5382 if (FAILED(rc)) return rc;
5383
5384 if (mData->mRegistered)
5385 return setError(VBOX_E_INVALID_VM_STATE,
5386 tr("Cannot delete settings of a registered machine"));
5387
5388 // collect files to delete
5389 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5390 if (mData->pMachineConfigFile->fileExists())
5391 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5392
5393 RTCList<ComPtr<IMedium> > llMediums;
5394 for (size_t i = 0; i < aMedia.size(); ++i)
5395 {
5396 IMedium *pIMedium(aMedia[i]);
5397 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5398 if (pMedium.isNull())
5399 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5400 SafeArray<BSTR> ids;
5401 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5402 if (FAILED(rc)) return rc;
5403 /* At this point the medium should not have any back references
5404 * anymore. If it has it is attached to another VM and *must* not
5405 * deleted. */
5406 if (ids.size() < 1)
5407 llMediums.append(pMedium);
5408 }
5409
5410 ComObjPtr<Progress> pProgress;
5411 pProgress.createObject();
5412 rc = pProgress->init(i_getVirtualBox(),
5413 static_cast<IMachine*>(this) /* aInitiator */,
5414 tr("Deleting files"),
5415 true /* fCancellable */,
5416 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5417 tr("Collecting file inventory"));
5418 if (FAILED(rc))
5419 return rc;
5420
5421 /* create and start the task on a separate thread (note that it will not
5422 * start working until we release alock) */
5423 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5424 rc = pTask->createThread();
5425 if (FAILED(rc))
5426 return rc;
5427
5428 pProgress.queryInterfaceTo(aProgress.asOutParam());
5429
5430 LogFlowFuncLeave();
5431
5432 return S_OK;
5433}
5434
5435HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5436{
5437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5438
5439 ComObjPtr<Snapshot> pSnapshot;
5440 HRESULT rc;
5441
5442 if (aNameOrId.isEmpty())
5443 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5444 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5445 else
5446 {
5447 Guid uuid(aNameOrId);
5448 if (uuid.isValid())
5449 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5450 else
5451 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5452 }
5453 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5454
5455 return rc;
5456}
5457
5458HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5459 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5460{
5461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5462
5463 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5464 if (FAILED(rc)) return rc;
5465
5466 ComObjPtr<SharedFolder> sharedFolder;
5467 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5468 if (SUCCEEDED(rc))
5469 return setError(VBOX_E_OBJECT_IN_USE,
5470 tr("Shared folder named '%s' already exists"),
5471 aName.c_str());
5472
5473 sharedFolder.createObject();
5474 rc = sharedFolder->init(i_getMachine(),
5475 aName,
5476 aHostPath,
5477 !!aWritable,
5478 !!aAutomount,
5479 aAutoMountPoint,
5480 true /* fFailOnError */);
5481 if (FAILED(rc)) return rc;
5482
5483 i_setModified(IsModified_SharedFolders);
5484 mHWData.backup();
5485 mHWData->mSharedFolders.push_back(sharedFolder);
5486
5487 /* inform the direct session if any */
5488 alock.release();
5489 i_onSharedFolderChange();
5490
5491 return S_OK;
5492}
5493
5494HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5495{
5496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5497
5498 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5499 if (FAILED(rc)) return rc;
5500
5501 ComObjPtr<SharedFolder> sharedFolder;
5502 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5503 if (FAILED(rc)) return rc;
5504
5505 i_setModified(IsModified_SharedFolders);
5506 mHWData.backup();
5507 mHWData->mSharedFolders.remove(sharedFolder);
5508
5509 /* inform the direct session if any */
5510 alock.release();
5511 i_onSharedFolderChange();
5512
5513 return S_OK;
5514}
5515
5516HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5517{
5518 /* start with No */
5519 *aCanShow = FALSE;
5520
5521 ComPtr<IInternalSessionControl> directControl;
5522 {
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 if (mData->mSession.mState != SessionState_Locked)
5526 return setError(VBOX_E_INVALID_VM_STATE,
5527 tr("Machine is not locked for session (session state: %s)"),
5528 Global::stringifySessionState(mData->mSession.mState));
5529
5530 if (mData->mSession.mLockType == LockType_VM)
5531 directControl = mData->mSession.mDirectControl;
5532 }
5533
5534 /* ignore calls made after #OnSessionEnd() is called */
5535 if (!directControl)
5536 return S_OK;
5537
5538 LONG64 dummy;
5539 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5540}
5541
5542HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5543{
5544 ComPtr<IInternalSessionControl> directControl;
5545 {
5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5547
5548 if (mData->mSession.mState != SessionState_Locked)
5549 return setError(E_FAIL,
5550 tr("Machine is not locked for session (session state: %s)"),
5551 Global::stringifySessionState(mData->mSession.mState));
5552
5553 if (mData->mSession.mLockType == LockType_VM)
5554 directControl = mData->mSession.mDirectControl;
5555 }
5556
5557 /* ignore calls made after #OnSessionEnd() is called */
5558 if (!directControl)
5559 return S_OK;
5560
5561 BOOL dummy;
5562 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5563}
5564
5565#ifdef VBOX_WITH_GUEST_PROPS
5566/**
5567 * Look up a guest property in VBoxSVC's internal structures.
5568 */
5569HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5570 com::Utf8Str &aValue,
5571 LONG64 *aTimestamp,
5572 com::Utf8Str &aFlags) const
5573{
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5577 if (it != mHWData->mGuestProperties.end())
5578 {
5579 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5580 aValue = it->second.strValue;
5581 *aTimestamp = it->second.mTimestamp;
5582 GuestPropWriteFlags(it->second.mFlags, szFlags);
5583 aFlags = Utf8Str(szFlags);
5584 }
5585
5586 return S_OK;
5587}
5588
5589/**
5590 * Query the VM that a guest property belongs to for the property.
5591 * @returns E_ACCESSDENIED if the VM process is not available or not
5592 * currently handling queries and the lookup should then be done in
5593 * VBoxSVC.
5594 */
5595HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags) const
5599{
5600 HRESULT rc = S_OK;
5601 Bstr bstrValue;
5602 Bstr bstrFlags;
5603
5604 ComPtr<IInternalSessionControl> directControl;
5605 {
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 /* ignore calls made after #OnSessionEnd() is called */
5612 if (!directControl)
5613 rc = E_ACCESSDENIED;
5614 else
5615 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5616 0 /* accessMode */,
5617 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5618
5619 aValue = bstrValue;
5620 aFlags = bstrFlags;
5621
5622 return rc;
5623}
5624#endif // VBOX_WITH_GUEST_PROPS
5625
5626HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5627 com::Utf8Str &aValue,
5628 LONG64 *aTimestamp,
5629 com::Utf8Str &aFlags)
5630{
5631#ifndef VBOX_WITH_GUEST_PROPS
5632 ReturnComNotImplemented();
5633#else // VBOX_WITH_GUEST_PROPS
5634
5635 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5636
5637 if (rc == E_ACCESSDENIED)
5638 /* The VM is not running or the service is not (yet) accessible */
5639 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5640 return rc;
5641#endif // VBOX_WITH_GUEST_PROPS
5642}
5643
5644HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5645{
5646 LONG64 dummyTimestamp;
5647 com::Utf8Str dummyFlags;
5648 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5649 return rc;
5650
5651}
5652HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5653{
5654 com::Utf8Str dummyFlags;
5655 com::Utf8Str dummyValue;
5656 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5657 return rc;
5658}
5659
5660#ifdef VBOX_WITH_GUEST_PROPS
5661/**
5662 * Set a guest property in VBoxSVC's internal structures.
5663 */
5664HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5665 const com::Utf8Str &aFlags, bool fDelete)
5666{
5667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5668 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5669 if (FAILED(rc)) return rc;
5670
5671 try
5672 {
5673 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5674 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5675 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5676
5677 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5678 if (it == mHWData->mGuestProperties.end())
5679 {
5680 if (!fDelete)
5681 {
5682 i_setModified(IsModified_MachineData);
5683 mHWData.backupEx();
5684
5685 RTTIMESPEC time;
5686 HWData::GuestProperty prop;
5687 prop.strValue = Bstr(aValue).raw();
5688 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5689 prop.mFlags = fFlags;
5690 mHWData->mGuestProperties[aName] = prop;
5691 }
5692 }
5693 else
5694 {
5695 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5696 {
5697 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5698 }
5699 else
5700 {
5701 i_setModified(IsModified_MachineData);
5702 mHWData.backupEx();
5703
5704 /* The backupEx() operation invalidates our iterator,
5705 * so get a new one. */
5706 it = mHWData->mGuestProperties.find(aName);
5707 Assert(it != mHWData->mGuestProperties.end());
5708
5709 if (!fDelete)
5710 {
5711 RTTIMESPEC time;
5712 it->second.strValue = aValue;
5713 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5714 it->second.mFlags = fFlags;
5715 }
5716 else
5717 mHWData->mGuestProperties.erase(it);
5718 }
5719 }
5720
5721 if (SUCCEEDED(rc))
5722 {
5723 alock.release();
5724
5725 mParent->i_onGuestPropertyChange(mData->mUuid,
5726 Bstr(aName).raw(),
5727 Bstr(aValue).raw(),
5728 Bstr(aFlags).raw());
5729 }
5730 }
5731 catch (std::bad_alloc &)
5732 {
5733 rc = E_OUTOFMEMORY;
5734 }
5735
5736 return rc;
5737}
5738
5739/**
5740 * Set a property on the VM that that property belongs to.
5741 * @returns E_ACCESSDENIED if the VM process is not available or not
5742 * currently handling queries and the setting should then be done in
5743 * VBoxSVC.
5744 */
5745HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5746 const com::Utf8Str &aFlags, bool fDelete)
5747{
5748 HRESULT rc;
5749
5750 try
5751 {
5752 ComPtr<IInternalSessionControl> directControl;
5753 {
5754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5755 if (mData->mSession.mLockType == LockType_VM)
5756 directControl = mData->mSession.mDirectControl;
5757 }
5758
5759 Bstr dummy1; /* will not be changed (setter) */
5760 Bstr dummy2; /* will not be changed (setter) */
5761 LONG64 dummy64;
5762 if (!directControl)
5763 rc = E_ACCESSDENIED;
5764 else
5765 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5766 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5767 fDelete ? 2 : 1 /* accessMode */,
5768 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5769 }
5770 catch (std::bad_alloc &)
5771 {
5772 rc = E_OUTOFMEMORY;
5773 }
5774
5775 return rc;
5776}
5777#endif // VBOX_WITH_GUEST_PROPS
5778
5779HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5780 const com::Utf8Str &aFlags)
5781{
5782#ifndef VBOX_WITH_GUEST_PROPS
5783 ReturnComNotImplemented();
5784#else // VBOX_WITH_GUEST_PROPS
5785 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5786 if (rc == E_ACCESSDENIED)
5787 /* The VM is not running or the service is not (yet) accessible */
5788 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5789 return rc;
5790#endif // VBOX_WITH_GUEST_PROPS
5791}
5792
5793HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5794{
5795 return setGuestProperty(aProperty, aValue, "");
5796}
5797
5798HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5799{
5800#ifndef VBOX_WITH_GUEST_PROPS
5801 ReturnComNotImplemented();
5802#else // VBOX_WITH_GUEST_PROPS
5803 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5804 if (rc == E_ACCESSDENIED)
5805 /* The VM is not running or the service is not (yet) accessible */
5806 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5807 return rc;
5808#endif // VBOX_WITH_GUEST_PROPS
5809}
5810
5811#ifdef VBOX_WITH_GUEST_PROPS
5812/**
5813 * Enumerate the guest properties in VBoxSVC's internal structures.
5814 */
5815HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5816 std::vector<com::Utf8Str> &aNames,
5817 std::vector<com::Utf8Str> &aValues,
5818 std::vector<LONG64> &aTimestamps,
5819 std::vector<com::Utf8Str> &aFlags)
5820{
5821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5822 Utf8Str strPatterns(aPatterns);
5823
5824 /*
5825 * Look for matching patterns and build up a list.
5826 */
5827 HWData::GuestPropertyMap propMap;
5828 for (HWData::GuestPropertyMap::const_iterator
5829 it = mHWData->mGuestProperties.begin();
5830 it != mHWData->mGuestProperties.end();
5831 ++it)
5832 {
5833 if ( strPatterns.isEmpty()
5834 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5835 RTSTR_MAX,
5836 it->first.c_str(),
5837 RTSTR_MAX,
5838 NULL)
5839 )
5840 propMap.insert(*it);
5841 }
5842
5843 alock.release();
5844
5845 /*
5846 * And build up the arrays for returning the property information.
5847 */
5848 size_t cEntries = propMap.size();
5849
5850 aNames.resize(cEntries);
5851 aValues.resize(cEntries);
5852 aTimestamps.resize(cEntries);
5853 aFlags.resize(cEntries);
5854
5855 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5856 size_t i = 0;
5857 for (HWData::GuestPropertyMap::const_iterator
5858 it = propMap.begin();
5859 it != propMap.end();
5860 ++it, ++i)
5861 {
5862 aNames[i] = it->first;
5863 aValues[i] = it->second.strValue;
5864 aTimestamps[i] = it->second.mTimestamp;
5865 GuestPropWriteFlags(it->second.mFlags, szFlags);
5866 aFlags[i] = Utf8Str(szFlags);
5867 }
5868
5869 return S_OK;
5870}
5871
5872/**
5873 * Enumerate the properties managed by a VM.
5874 * @returns E_ACCESSDENIED if the VM process is not available or not
5875 * currently handling queries and the setting should then be done in
5876 * VBoxSVC.
5877 */
5878HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5879 std::vector<com::Utf8Str> &aNames,
5880 std::vector<com::Utf8Str> &aValues,
5881 std::vector<LONG64> &aTimestamps,
5882 std::vector<com::Utf8Str> &aFlags)
5883{
5884 HRESULT rc;
5885 ComPtr<IInternalSessionControl> directControl;
5886 {
5887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5888 if (mData->mSession.mLockType == LockType_VM)
5889 directControl = mData->mSession.mDirectControl;
5890 }
5891
5892 com::SafeArray<BSTR> bNames;
5893 com::SafeArray<BSTR> bValues;
5894 com::SafeArray<LONG64> bTimestamps;
5895 com::SafeArray<BSTR> bFlags;
5896
5897 if (!directControl)
5898 rc = E_ACCESSDENIED;
5899 else
5900 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5901 ComSafeArrayAsOutParam(bNames),
5902 ComSafeArrayAsOutParam(bValues),
5903 ComSafeArrayAsOutParam(bTimestamps),
5904 ComSafeArrayAsOutParam(bFlags));
5905 size_t i;
5906 aNames.resize(bNames.size());
5907 for (i = 0; i < bNames.size(); ++i)
5908 aNames[i] = Utf8Str(bNames[i]);
5909 aValues.resize(bValues.size());
5910 for (i = 0; i < bValues.size(); ++i)
5911 aValues[i] = Utf8Str(bValues[i]);
5912 aTimestamps.resize(bTimestamps.size());
5913 for (i = 0; i < bTimestamps.size(); ++i)
5914 aTimestamps[i] = bTimestamps[i];
5915 aFlags.resize(bFlags.size());
5916 for (i = 0; i < bFlags.size(); ++i)
5917 aFlags[i] = Utf8Str(bFlags[i]);
5918
5919 return rc;
5920}
5921#endif // VBOX_WITH_GUEST_PROPS
5922HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5923 std::vector<com::Utf8Str> &aNames,
5924 std::vector<com::Utf8Str> &aValues,
5925 std::vector<LONG64> &aTimestamps,
5926 std::vector<com::Utf8Str> &aFlags)
5927{
5928#ifndef VBOX_WITH_GUEST_PROPS
5929 ReturnComNotImplemented();
5930#else // VBOX_WITH_GUEST_PROPS
5931
5932 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5933
5934 if (rc == E_ACCESSDENIED)
5935 /* The VM is not running or the service is not (yet) accessible */
5936 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5937 return rc;
5938#endif // VBOX_WITH_GUEST_PROPS
5939}
5940
5941HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5942 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5943{
5944 MediumAttachmentList atts;
5945
5946 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5947 if (FAILED(rc)) return rc;
5948
5949 aMediumAttachments.resize(atts.size());
5950 size_t i = 0;
5951 for (MediumAttachmentList::const_iterator
5952 it = atts.begin();
5953 it != atts.end();
5954 ++it, ++i)
5955 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5956
5957 return S_OK;
5958}
5959
5960HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5961 LONG aControllerPort,
5962 LONG aDevice,
5963 ComPtr<IMediumAttachment> &aAttachment)
5964{
5965 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5966 aName.c_str(), aControllerPort, aDevice));
5967
5968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5969
5970 aAttachment = NULL;
5971
5972 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5973 aName,
5974 aControllerPort,
5975 aDevice);
5976 if (pAttach.isNull())
5977 return setError(VBOX_E_OBJECT_NOT_FOUND,
5978 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5979 aDevice, aControllerPort, aName.c_str());
5980
5981 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5982
5983 return S_OK;
5984}
5985
5986
5987HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5988 StorageBus_T aConnectionType,
5989 ComPtr<IStorageController> &aController)
5990{
5991 if ( (aConnectionType <= StorageBus_Null)
5992 || (aConnectionType > StorageBus_PCIe))
5993 return setError(E_INVALIDARG,
5994 tr("Invalid connection type: %d"),
5995 aConnectionType);
5996
5997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5998
5999 HRESULT rc = i_checkStateDependency(MutableStateDep);
6000 if (FAILED(rc)) return rc;
6001
6002 /* try to find one with the name first. */
6003 ComObjPtr<StorageController> ctrl;
6004
6005 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6006 if (SUCCEEDED(rc))
6007 return setError(VBOX_E_OBJECT_IN_USE,
6008 tr("Storage controller named '%s' already exists"),
6009 aName.c_str());
6010
6011 ctrl.createObject();
6012
6013 /* get a new instance number for the storage controller */
6014 ULONG ulInstance = 0;
6015 bool fBootable = true;
6016 for (StorageControllerList::const_iterator
6017 it = mStorageControllers->begin();
6018 it != mStorageControllers->end();
6019 ++it)
6020 {
6021 if ((*it)->i_getStorageBus() == aConnectionType)
6022 {
6023 ULONG ulCurInst = (*it)->i_getInstance();
6024
6025 if (ulCurInst >= ulInstance)
6026 ulInstance = ulCurInst + 1;
6027
6028 /* Only one controller of each type can be marked as bootable. */
6029 if ((*it)->i_getBootable())
6030 fBootable = false;
6031 }
6032 }
6033
6034 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6035 if (FAILED(rc)) return rc;
6036
6037 i_setModified(IsModified_Storage);
6038 mStorageControllers.backup();
6039 mStorageControllers->push_back(ctrl);
6040
6041 ctrl.queryInterfaceTo(aController.asOutParam());
6042
6043 /* inform the direct session if any */
6044 alock.release();
6045 i_onStorageControllerChange();
6046
6047 return S_OK;
6048}
6049
6050HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6051 ComPtr<IStorageController> &aStorageController)
6052{
6053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6054
6055 ComObjPtr<StorageController> ctrl;
6056
6057 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6058 if (SUCCEEDED(rc))
6059 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6060
6061 return rc;
6062}
6063
6064HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6065 ULONG aInstance,
6066 ComPtr<IStorageController> &aStorageController)
6067{
6068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6069
6070 for (StorageControllerList::const_iterator
6071 it = mStorageControllers->begin();
6072 it != mStorageControllers->end();
6073 ++it)
6074 {
6075 if ( (*it)->i_getStorageBus() == aConnectionType
6076 && (*it)->i_getInstance() == aInstance)
6077 {
6078 (*it).queryInterfaceTo(aStorageController.asOutParam());
6079 return S_OK;
6080 }
6081 }
6082
6083 return setError(VBOX_E_OBJECT_NOT_FOUND,
6084 tr("Could not find a storage controller with instance number '%lu'"),
6085 aInstance);
6086}
6087
6088HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6089{
6090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6091
6092 HRESULT rc = i_checkStateDependency(MutableStateDep);
6093 if (FAILED(rc)) return rc;
6094
6095 ComObjPtr<StorageController> ctrl;
6096
6097 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6098 if (SUCCEEDED(rc))
6099 {
6100 /* Ensure that only one controller of each type is marked as bootable. */
6101 if (aBootable == TRUE)
6102 {
6103 for (StorageControllerList::const_iterator
6104 it = mStorageControllers->begin();
6105 it != mStorageControllers->end();
6106 ++it)
6107 {
6108 ComObjPtr<StorageController> aCtrl = (*it);
6109
6110 if ( (aCtrl->i_getName() != aName)
6111 && aCtrl->i_getBootable() == TRUE
6112 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6113 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6114 {
6115 aCtrl->i_setBootable(FALSE);
6116 break;
6117 }
6118 }
6119 }
6120
6121 if (SUCCEEDED(rc))
6122 {
6123 ctrl->i_setBootable(aBootable);
6124 i_setModified(IsModified_Storage);
6125 }
6126 }
6127
6128 if (SUCCEEDED(rc))
6129 {
6130 /* inform the direct session if any */
6131 alock.release();
6132 i_onStorageControllerChange();
6133 }
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6139{
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 ComObjPtr<StorageController> ctrl;
6146 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6147 if (FAILED(rc)) return rc;
6148
6149 {
6150 /* find all attached devices to the appropriate storage controller and detach them all */
6151 // make a temporary list because detachDevice invalidates iterators into
6152 // mMediumAttachments
6153 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6154
6155 for (MediumAttachmentList::const_iterator
6156 it = llAttachments2.begin();
6157 it != llAttachments2.end();
6158 ++it)
6159 {
6160 MediumAttachment *pAttachTemp = *it;
6161
6162 AutoCaller localAutoCaller(pAttachTemp);
6163 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6164
6165 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6166
6167 if (pAttachTemp->i_getControllerName() == aName)
6168 {
6169 rc = i_detachDevice(pAttachTemp, alock, NULL);
6170 if (FAILED(rc)) return rc;
6171 }
6172 }
6173 }
6174
6175 /* We can remove it now. */
6176 i_setModified(IsModified_Storage);
6177 mStorageControllers.backup();
6178
6179 ctrl->i_unshare();
6180
6181 mStorageControllers->remove(ctrl);
6182
6183 /* inform the direct session if any */
6184 alock.release();
6185 i_onStorageControllerChange();
6186
6187 return S_OK;
6188}
6189
6190HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6191 ComPtr<IUSBController> &aController)
6192{
6193 if ( (aType <= USBControllerType_Null)
6194 || (aType >= USBControllerType_Last))
6195 return setError(E_INVALIDARG,
6196 tr("Invalid USB controller type: %d"),
6197 aType);
6198
6199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6200
6201 HRESULT rc = i_checkStateDependency(MutableStateDep);
6202 if (FAILED(rc)) return rc;
6203
6204 /* try to find one with the same type first. */
6205 ComObjPtr<USBController> ctrl;
6206
6207 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6208 if (SUCCEEDED(rc))
6209 return setError(VBOX_E_OBJECT_IN_USE,
6210 tr("USB controller named '%s' already exists"),
6211 aName.c_str());
6212
6213 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6214 ULONG maxInstances;
6215 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6216 if (FAILED(rc))
6217 return rc;
6218
6219 ULONG cInstances = i_getUSBControllerCountByType(aType);
6220 if (cInstances >= maxInstances)
6221 return setError(E_INVALIDARG,
6222 tr("Too many USB controllers of this type"));
6223
6224 ctrl.createObject();
6225
6226 rc = ctrl->init(this, aName, aType);
6227 if (FAILED(rc)) return rc;
6228
6229 i_setModified(IsModified_USB);
6230 mUSBControllers.backup();
6231 mUSBControllers->push_back(ctrl);
6232
6233 ctrl.queryInterfaceTo(aController.asOutParam());
6234
6235 /* inform the direct session if any */
6236 alock.release();
6237 i_onUSBControllerChange();
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6243{
6244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 ComObjPtr<USBController> ctrl;
6247
6248 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6249 if (SUCCEEDED(rc))
6250 ctrl.queryInterfaceTo(aController.asOutParam());
6251
6252 return rc;
6253}
6254
6255HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6256 ULONG *aControllers)
6257{
6258 if ( (aType <= USBControllerType_Null)
6259 || (aType >= USBControllerType_Last))
6260 return setError(E_INVALIDARG,
6261 tr("Invalid USB controller type: %d"),
6262 aType);
6263
6264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6265
6266 ComObjPtr<USBController> ctrl;
6267
6268 *aControllers = i_getUSBControllerCountByType(aType);
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6274{
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 HRESULT rc = i_checkStateDependency(MutableStateDep);
6279 if (FAILED(rc)) return rc;
6280
6281 ComObjPtr<USBController> ctrl;
6282 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6283 if (FAILED(rc)) return rc;
6284
6285 i_setModified(IsModified_USB);
6286 mUSBControllers.backup();
6287
6288 ctrl->i_unshare();
6289
6290 mUSBControllers->remove(ctrl);
6291
6292 /* inform the direct session if any */
6293 alock.release();
6294 i_onUSBControllerChange();
6295
6296 return S_OK;
6297}
6298
6299HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6300 ULONG *aOriginX,
6301 ULONG *aOriginY,
6302 ULONG *aWidth,
6303 ULONG *aHeight,
6304 BOOL *aEnabled)
6305{
6306 uint32_t u32OriginX= 0;
6307 uint32_t u32OriginY= 0;
6308 uint32_t u32Width = 0;
6309 uint32_t u32Height = 0;
6310 uint16_t u16Flags = 0;
6311
6312 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6313 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6314 if (RT_FAILURE(vrc))
6315 {
6316#ifdef RT_OS_WINDOWS
6317 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6318 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6319 * So just assign fEnable to TRUE again.
6320 * The right fix would be to change GUI API wrappers to make sure that parameters
6321 * are changed only if API succeeds.
6322 */
6323 *aEnabled = TRUE;
6324#endif
6325 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6326 tr("Saved guest size is not available (%Rrc)"),
6327 vrc);
6328 }
6329
6330 *aOriginX = u32OriginX;
6331 *aOriginY = u32OriginY;
6332 *aWidth = u32Width;
6333 *aHeight = u32Height;
6334 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6340 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6341{
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 if ( aBitmapFormat != BitmapFormat_BGR0
6346 && aBitmapFormat != BitmapFormat_BGRA
6347 && aBitmapFormat != BitmapFormat_RGBA
6348 && aBitmapFormat != BitmapFormat_PNG)
6349 return setError(E_NOTIMPL,
6350 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6351
6352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6353
6354 uint8_t *pu8Data = NULL;
6355 uint32_t cbData = 0;
6356 uint32_t u32Width = 0;
6357 uint32_t u32Height = 0;
6358
6359 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6360
6361 if (RT_FAILURE(vrc))
6362 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6363 tr("Saved thumbnail data is not available (%Rrc)"),
6364 vrc);
6365
6366 HRESULT hr = S_OK;
6367
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370
6371 if (cbData > 0)
6372 {
6373 /* Convert pixels to the format expected by the API caller. */
6374 if (aBitmapFormat == BitmapFormat_BGR0)
6375 {
6376 /* [0] B, [1] G, [2] R, [3] 0. */
6377 aData.resize(cbData);
6378 memcpy(&aData.front(), pu8Data, cbData);
6379 }
6380 else if (aBitmapFormat == BitmapFormat_BGRA)
6381 {
6382 /* [0] B, [1] G, [2] R, [3] A. */
6383 aData.resize(cbData);
6384 for (uint32_t i = 0; i < cbData; i += 4)
6385 {
6386 aData[i] = pu8Data[i];
6387 aData[i + 1] = pu8Data[i + 1];
6388 aData[i + 2] = pu8Data[i + 2];
6389 aData[i + 3] = 0xff;
6390 }
6391 }
6392 else if (aBitmapFormat == BitmapFormat_RGBA)
6393 {
6394 /* [0] R, [1] G, [2] B, [3] A. */
6395 aData.resize(cbData);
6396 for (uint32_t i = 0; i < cbData; i += 4)
6397 {
6398 aData[i] = pu8Data[i + 2];
6399 aData[i + 1] = pu8Data[i + 1];
6400 aData[i + 2] = pu8Data[i];
6401 aData[i + 3] = 0xff;
6402 }
6403 }
6404 else if (aBitmapFormat == BitmapFormat_PNG)
6405 {
6406 uint8_t *pu8PNG = NULL;
6407 uint32_t cbPNG = 0;
6408 uint32_t cxPNG = 0;
6409 uint32_t cyPNG = 0;
6410
6411 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6412
6413 if (RT_SUCCESS(vrc))
6414 {
6415 aData.resize(cbPNG);
6416 if (cbPNG)
6417 memcpy(&aData.front(), pu8PNG, cbPNG);
6418 }
6419 else
6420 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6421 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6422 vrc);
6423
6424 RTMemFree(pu8PNG);
6425 }
6426 }
6427
6428 freeSavedDisplayScreenshot(pu8Data);
6429
6430 return hr;
6431}
6432
6433HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6434 ULONG *aWidth,
6435 ULONG *aHeight,
6436 std::vector<BitmapFormat_T> &aBitmapFormats)
6437{
6438 if (aScreenId != 0)
6439 return E_NOTIMPL;
6440
6441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6442
6443 uint8_t *pu8Data = NULL;
6444 uint32_t cbData = 0;
6445 uint32_t u32Width = 0;
6446 uint32_t u32Height = 0;
6447
6448 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6449
6450 if (RT_FAILURE(vrc))
6451 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6452 tr("Saved screenshot data is not available (%Rrc)"),
6453 vrc);
6454
6455 *aWidth = u32Width;
6456 *aHeight = u32Height;
6457 aBitmapFormats.resize(1);
6458 aBitmapFormats[0] = BitmapFormat_PNG;
6459
6460 freeSavedDisplayScreenshot(pu8Data);
6461
6462 return S_OK;
6463}
6464
6465HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6466 BitmapFormat_T aBitmapFormat,
6467 ULONG *aWidth,
6468 ULONG *aHeight,
6469 std::vector<BYTE> &aData)
6470{
6471 if (aScreenId != 0)
6472 return E_NOTIMPL;
6473
6474 if (aBitmapFormat != BitmapFormat_PNG)
6475 return E_NOTIMPL;
6476
6477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6478
6479 uint8_t *pu8Data = NULL;
6480 uint32_t cbData = 0;
6481 uint32_t u32Width = 0;
6482 uint32_t u32Height = 0;
6483
6484 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6485
6486 if (RT_FAILURE(vrc))
6487 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6488 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6489 vrc);
6490
6491 *aWidth = u32Width;
6492 *aHeight = u32Height;
6493
6494 aData.resize(cbData);
6495 if (cbData)
6496 memcpy(&aData.front(), pu8Data, cbData);
6497
6498 freeSavedDisplayScreenshot(pu8Data);
6499
6500 return S_OK;
6501}
6502
6503HRESULT Machine::hotPlugCPU(ULONG aCpu)
6504{
6505 HRESULT rc = S_OK;
6506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6507
6508 if (!mHWData->mCPUHotPlugEnabled)
6509 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6510
6511 if (aCpu >= mHWData->mCPUCount)
6512 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6513
6514 if (mHWData->mCPUAttached[aCpu])
6515 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6516
6517 alock.release();
6518 rc = i_onCPUChange(aCpu, false);
6519 alock.acquire();
6520 if (FAILED(rc)) return rc;
6521
6522 i_setModified(IsModified_MachineData);
6523 mHWData.backup();
6524 mHWData->mCPUAttached[aCpu] = true;
6525
6526 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6527 if (Global::IsOnline(mData->mMachineState))
6528 i_saveSettings(NULL);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6534{
6535 HRESULT rc = S_OK;
6536
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 if (!mHWData->mCPUHotPlugEnabled)
6540 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6541
6542 if (aCpu >= SchemaDefs::MaxCPUCount)
6543 return setError(E_INVALIDARG,
6544 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6545 SchemaDefs::MaxCPUCount);
6546
6547 if (!mHWData->mCPUAttached[aCpu])
6548 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6549
6550 /* CPU 0 can't be detached */
6551 if (aCpu == 0)
6552 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6553
6554 alock.release();
6555 rc = i_onCPUChange(aCpu, true);
6556 alock.acquire();
6557 if (FAILED(rc)) return rc;
6558
6559 i_setModified(IsModified_MachineData);
6560 mHWData.backup();
6561 mHWData->mCPUAttached[aCpu] = false;
6562
6563 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6564 if (Global::IsOnline(mData->mMachineState))
6565 i_saveSettings(NULL);
6566
6567 return S_OK;
6568}
6569
6570HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6571{
6572 *aAttached = false;
6573
6574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6575
6576 /* If hotplug is enabled the CPU is always enabled. */
6577 if (!mHWData->mCPUHotPlugEnabled)
6578 {
6579 if (aCpu < mHWData->mCPUCount)
6580 *aAttached = true;
6581 }
6582 else
6583 {
6584 if (aCpu < SchemaDefs::MaxCPUCount)
6585 *aAttached = mHWData->mCPUAttached[aCpu];
6586 }
6587
6588 return S_OK;
6589}
6590
6591HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6592{
6593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 Utf8Str log = i_getLogFilename(aIdx);
6596 if (!RTFileExists(log.c_str()))
6597 log.setNull();
6598 aFilename = log;
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6604{
6605 if (aSize < 0)
6606 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6607
6608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6609
6610 HRESULT rc = S_OK;
6611 Utf8Str log = i_getLogFilename(aIdx);
6612
6613 /* do not unnecessarily hold the lock while doing something which does
6614 * not need the lock and potentially takes a long time. */
6615 alock.release();
6616
6617 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6618 * keeps the SOAP reply size under 1M for the webservice (we're using
6619 * base64 encoded strings for binary data for years now, avoiding the
6620 * expansion of each byte array element to approx. 25 bytes of XML. */
6621 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6622 aData.resize(cbData);
6623
6624 RTFILE LogFile;
6625 int vrc = RTFileOpen(&LogFile, log.c_str(),
6626 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6627 if (RT_SUCCESS(vrc))
6628 {
6629 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6630 if (RT_SUCCESS(vrc))
6631 aData.resize(cbData);
6632 else
6633 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6634 tr("Could not read log file '%s' (%Rrc)"),
6635 log.c_str(), vrc);
6636 RTFileClose(LogFile);
6637 }
6638 else
6639 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6640 tr("Could not open log file '%s' (%Rrc)"),
6641 log.c_str(), vrc);
6642
6643 if (FAILED(rc))
6644 aData.resize(0);
6645
6646 return rc;
6647}
6648
6649
6650/**
6651 * Currently this method doesn't attach device to the running VM,
6652 * just makes sure it's plugged on next VM start.
6653 */
6654HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6655{
6656 // lock scope
6657 {
6658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 HRESULT rc = i_checkStateDependency(MutableStateDep);
6661 if (FAILED(rc)) return rc;
6662
6663 ChipsetType_T aChipset = ChipsetType_PIIX3;
6664 COMGETTER(ChipsetType)(&aChipset);
6665
6666 if (aChipset != ChipsetType_ICH9)
6667 {
6668 return setError(E_INVALIDARG,
6669 tr("Host PCI attachment only supported with ICH9 chipset"));
6670 }
6671
6672 // check if device with this host PCI address already attached
6673 for (HWData::PCIDeviceAssignmentList::const_iterator
6674 it = mHWData->mPCIDeviceAssignments.begin();
6675 it != mHWData->mPCIDeviceAssignments.end();
6676 ++it)
6677 {
6678 LONG iHostAddress = -1;
6679 ComPtr<PCIDeviceAttachment> pAttach;
6680 pAttach = *it;
6681 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6682 if (iHostAddress == aHostAddress)
6683 return setError(E_INVALIDARG,
6684 tr("Device with host PCI address already attached to this VM"));
6685 }
6686
6687 ComObjPtr<PCIDeviceAttachment> pda;
6688 char name[32];
6689
6690 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6691 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6692 pda.createObject();
6693 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6694 i_setModified(IsModified_MachineData);
6695 mHWData.backup();
6696 mHWData->mPCIDeviceAssignments.push_back(pda);
6697 }
6698
6699 return S_OK;
6700}
6701
6702/**
6703 * Currently this method doesn't detach device from the running VM,
6704 * just makes sure it's not plugged on next VM start.
6705 */
6706HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6707{
6708 ComObjPtr<PCIDeviceAttachment> pAttach;
6709 bool fRemoved = false;
6710 HRESULT rc;
6711
6712 // lock scope
6713 {
6714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6715
6716 rc = i_checkStateDependency(MutableStateDep);
6717 if (FAILED(rc)) return rc;
6718
6719 for (HWData::PCIDeviceAssignmentList::const_iterator
6720 it = mHWData->mPCIDeviceAssignments.begin();
6721 it != mHWData->mPCIDeviceAssignments.end();
6722 ++it)
6723 {
6724 LONG iHostAddress = -1;
6725 pAttach = *it;
6726 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6727 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6728 {
6729 i_setModified(IsModified_MachineData);
6730 mHWData.backup();
6731 mHWData->mPCIDeviceAssignments.remove(pAttach);
6732 fRemoved = true;
6733 break;
6734 }
6735 }
6736 }
6737
6738
6739 /* Fire event outside of the lock */
6740 if (fRemoved)
6741 {
6742 Assert(!pAttach.isNull());
6743 ComPtr<IEventSource> es;
6744 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6745 Assert(SUCCEEDED(rc));
6746 Bstr mid;
6747 rc = this->COMGETTER(Id)(mid.asOutParam());
6748 Assert(SUCCEEDED(rc));
6749 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6750 }
6751
6752 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6753 tr("No host PCI device %08x attached"),
6754 aHostAddress
6755 );
6756}
6757
6758HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6759{
6760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6763 size_t i = 0;
6764 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6765 it = mHWData->mPCIDeviceAssignments.begin();
6766 it != mHWData->mPCIDeviceAssignments.end();
6767 ++it, ++i)
6768 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6774{
6775 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6776
6777 return S_OK;
6778}
6779
6780HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6781{
6782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6783
6784 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6785
6786 return S_OK;
6787}
6788
6789HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6790{
6791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6792 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6793 if (SUCCEEDED(hrc))
6794 {
6795 hrc = mHWData.backupEx();
6796 if (SUCCEEDED(hrc))
6797 {
6798 i_setModified(IsModified_MachineData);
6799 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6800 }
6801 }
6802 return hrc;
6803}
6804
6805HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6806{
6807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6809 return S_OK;
6810}
6811
6812HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6813{
6814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6815 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6816 if (SUCCEEDED(hrc))
6817 {
6818 hrc = mHWData.backupEx();
6819 if (SUCCEEDED(hrc))
6820 {
6821 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6822 if (SUCCEEDED(hrc))
6823 i_setModified(IsModified_MachineData);
6824 }
6825 }
6826 return hrc;
6827}
6828
6829HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6830{
6831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6834
6835 return S_OK;
6836}
6837
6838HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6839{
6840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6841 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6842 if (SUCCEEDED(hrc))
6843 {
6844 hrc = mHWData.backupEx();
6845 if (SUCCEEDED(hrc))
6846 {
6847 i_setModified(IsModified_MachineData);
6848 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6849 }
6850 }
6851 return hrc;
6852}
6853
6854HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6855{
6856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6857
6858 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6859
6860 return S_OK;
6861}
6862
6863HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6864{
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6868 if ( SUCCEEDED(hrc)
6869 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6870 {
6871 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6872 int vrc;
6873
6874 if (aAutostartEnabled)
6875 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6876 else
6877 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6878
6879 if (RT_SUCCESS(vrc))
6880 {
6881 hrc = mHWData.backupEx();
6882 if (SUCCEEDED(hrc))
6883 {
6884 i_setModified(IsModified_MachineData);
6885 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6886 }
6887 }
6888 else if (vrc == VERR_NOT_SUPPORTED)
6889 hrc = setError(VBOX_E_NOT_SUPPORTED,
6890 tr("The VM autostart feature is not supported on this platform"));
6891 else if (vrc == VERR_PATH_NOT_FOUND)
6892 hrc = setError(E_FAIL,
6893 tr("The path to the autostart database is not set"));
6894 else
6895 hrc = setError(E_UNEXPECTED,
6896 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6897 aAutostartEnabled ? "Adding" : "Removing",
6898 mUserData->s.strName.c_str(), vrc);
6899 }
6900 return hrc;
6901}
6902
6903HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6908
6909 return S_OK;
6910}
6911
6912HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6913{
6914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6915 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6916 if (SUCCEEDED(hrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 i_setModified(IsModified_MachineData);
6922 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6923 }
6924 }
6925 return hrc;
6926}
6927
6928HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6929{
6930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6931
6932 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6933
6934 return S_OK;
6935}
6936
6937HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6938{
6939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6940 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6941 if ( SUCCEEDED(hrc)
6942 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6943 {
6944 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6945 int vrc;
6946
6947 if (aAutostopType != AutostopType_Disabled)
6948 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6949 else
6950 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6951
6952 if (RT_SUCCESS(vrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData->mAutostart.enmAutostopType = aAutostopType;
6959 }
6960 }
6961 else if (vrc == VERR_NOT_SUPPORTED)
6962 hrc = setError(VBOX_E_NOT_SUPPORTED,
6963 tr("The VM autostop feature is not supported on this platform"));
6964 else if (vrc == VERR_PATH_NOT_FOUND)
6965 hrc = setError(E_FAIL,
6966 tr("The path to the autostart database is not set"));
6967 else
6968 hrc = setError(E_UNEXPECTED,
6969 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6970 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6971 mUserData->s.strName.c_str(), vrc);
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 aDefaultFrontend = mHWData->mDefaultFrontend;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6989 if (SUCCEEDED(hrc))
6990 {
6991 hrc = mHWData.backupEx();
6992 if (SUCCEEDED(hrc))
6993 {
6994 i_setModified(IsModified_MachineData);
6995 mHWData->mDefaultFrontend = aDefaultFrontend;
6996 }
6997 }
6998 return hrc;
6999}
7000
7001HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7002{
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 size_t cbIcon = mUserData->s.ovIcon.size();
7005 aIcon.resize(cbIcon);
7006 if (cbIcon)
7007 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7008 return S_OK;
7009}
7010
7011HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7012{
7013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7014 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7015 if (SUCCEEDED(hrc))
7016 {
7017 i_setModified(IsModified_MachineData);
7018 mUserData.backup();
7019 size_t cbIcon = aIcon.size();
7020 mUserData->s.ovIcon.resize(cbIcon);
7021 if (cbIcon)
7022 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7023 }
7024 return hrc;
7025}
7026
7027HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7028{
7029#ifdef VBOX_WITH_USB
7030 *aUSBProxyAvailable = true;
7031#else
7032 *aUSBProxyAvailable = false;
7033#endif
7034 return S_OK;
7035}
7036
7037HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7038{
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 *aVMProcessPriority = mUserData->s.enmVMPriority;
7042
7043 return S_OK;
7044}
7045
7046HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7047{
7048 RT_NOREF(aVMProcessPriority);
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7051 if (SUCCEEDED(hrc))
7052 {
7053 hrc = mUserData.backupEx();
7054 if (SUCCEEDED(hrc))
7055 {
7056 i_setModified(IsModified_MachineData);
7057 mUserData->s.enmVMPriority = aVMProcessPriority;
7058 }
7059 }
7060 alock.release();
7061 if (SUCCEEDED(hrc))
7062 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7063 return hrc;
7064}
7065
7066HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7067 ComPtr<IProgress> &aProgress)
7068{
7069 ComObjPtr<Progress> pP;
7070 Progress *ppP = pP;
7071 IProgress *iP = static_cast<IProgress *>(ppP);
7072 IProgress **pProgress = &iP;
7073
7074 IMachine *pTarget = aTarget;
7075
7076 /* Convert the options. */
7077 RTCList<CloneOptions_T> optList;
7078 if (aOptions.size())
7079 for (size_t i = 0; i < aOptions.size(); ++i)
7080 optList.append(aOptions[i]);
7081
7082 if (optList.contains(CloneOptions_Link))
7083 {
7084 if (!i_isSnapshotMachine())
7085 return setError(E_INVALIDARG,
7086 tr("Linked clone can only be created from a snapshot"));
7087 if (aMode != CloneMode_MachineState)
7088 return setError(E_INVALIDARG,
7089 tr("Linked clone can only be created for a single machine state"));
7090 }
7091 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7092
7093 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7094
7095 HRESULT rc = pWorker->start(pProgress);
7096
7097 pP = static_cast<Progress *>(*pProgress);
7098 pP.queryInterfaceTo(aProgress.asOutParam());
7099
7100 return rc;
7101
7102}
7103
7104HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7105 const com::Utf8Str &aType,
7106 ComPtr<IProgress> &aProgress)
7107{
7108 LogFlowThisFuncEnter();
7109
7110 ComObjPtr<Progress> progress;
7111
7112 progress.createObject();
7113
7114 HRESULT rc = S_OK;
7115 Utf8Str targetPath = aTargetPath;
7116 Utf8Str type = aType;
7117
7118 /* Initialize our worker task */
7119 MachineMoveVM* task = NULL;
7120 try
7121 {
7122 task = new MachineMoveVM(this, targetPath, type, progress);
7123 }
7124 catch(...)
7125 {
7126 delete task;
7127 return rc;
7128 }
7129
7130 /*
7131 * task pointer will be owned by the ThreadTask class.
7132 * There is no need to call operator "delete" in the end.
7133 */
7134 rc = task->init();
7135 if (SUCCEEDED(rc))
7136 {
7137 rc = task->createThread();
7138 if (FAILED(rc))
7139 {
7140 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7141 }
7142
7143 /* Return progress to the caller */
7144 progress.queryInterfaceTo(aProgress.asOutParam());
7145 }
7146
7147 LogFlowThisFuncLeave();
7148 return rc;
7149
7150}
7151
7152HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7153{
7154 NOREF(aProgress);
7155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7156
7157 // This check should always fail.
7158 HRESULT rc = i_checkStateDependency(MutableStateDep);
7159 if (FAILED(rc)) return rc;
7160
7161 AssertFailedReturn(E_NOTIMPL);
7162}
7163
7164HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7165{
7166 NOREF(aSavedStateFile);
7167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 // This check should always fail.
7170 HRESULT rc = i_checkStateDependency(MutableStateDep);
7171 if (FAILED(rc)) return rc;
7172
7173 AssertFailedReturn(E_NOTIMPL);
7174}
7175
7176HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7177{
7178 NOREF(aFRemoveFile);
7179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 // This check should always fail.
7182 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7183 if (FAILED(rc)) return rc;
7184
7185 AssertFailedReturn(E_NOTIMPL);
7186}
7187
7188// public methods for internal purposes
7189/////////////////////////////////////////////////////////////////////////////
7190
7191/**
7192 * Adds the given IsModified_* flag to the dirty flags of the machine.
7193 * This must be called either during i_loadSettings or under the machine write lock.
7194 * @param fl Flag
7195 * @param fAllowStateModification If state modifications are allowed.
7196 */
7197void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7198{
7199 mData->flModifications |= fl;
7200 if (fAllowStateModification && i_isStateModificationAllowed())
7201 mData->mCurrentStateModified = true;
7202}
7203
7204/**
7205 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7206 * care of the write locking.
7207 *
7208 * @param fModification The flag to add.
7209 * @param fAllowStateModification If state modifications are allowed.
7210 */
7211void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7212{
7213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7214 i_setModified(fModification, fAllowStateModification);
7215}
7216
7217/**
7218 * Saves the registry entry of this machine to the given configuration node.
7219 *
7220 * @param data Machine registry data.
7221 *
7222 * @note locks this object for reading.
7223 */
7224HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7225{
7226 AutoLimitedCaller autoCaller(this);
7227 AssertComRCReturnRC(autoCaller.rc());
7228
7229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7230
7231 data.uuid = mData->mUuid;
7232 data.strSettingsFile = mData->m_strConfigFile;
7233
7234 return S_OK;
7235}
7236
7237/**
7238 * Calculates the absolute path of the given path taking the directory of the
7239 * machine settings file as the current directory.
7240 *
7241 * @param strPath Path to calculate the absolute path for.
7242 * @param aResult Where to put the result (used only on success, can be the
7243 * same Utf8Str instance as passed in @a aPath).
7244 * @return IPRT result.
7245 *
7246 * @note Locks this object for reading.
7247 */
7248int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7249{
7250 AutoCaller autoCaller(this);
7251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7252
7253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7254
7255 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7256
7257 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7258
7259 strSettingsDir.stripFilename();
7260 char szFolder[RTPATH_MAX];
7261 size_t cbFolder = sizeof(szFolder);
7262 int vrc = RTPathAbsExEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7263 if (RT_SUCCESS(vrc))
7264 aResult = szFolder;
7265
7266 return vrc;
7267}
7268
7269/**
7270 * Copies strSource to strTarget, making it relative to the machine folder
7271 * if it is a subdirectory thereof, or simply copying it otherwise.
7272 *
7273 * @param strSource Path to evaluate and copy.
7274 * @param strTarget Buffer to receive target path.
7275 *
7276 * @note Locks this object for reading.
7277 */
7278void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7279 Utf8Str &strTarget)
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturn(autoCaller.rc(), (void)0);
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7287 // use strTarget as a temporary buffer to hold the machine settings dir
7288 strTarget = mData->m_strConfigFileFull;
7289 strTarget.stripFilename();
7290 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7291 {
7292 // is relative: then append what's left
7293 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7294 // for empty paths (only possible for subdirs) use "." to avoid
7295 // triggering default settings for not present config attributes.
7296 if (strTarget.isEmpty())
7297 strTarget = ".";
7298 }
7299 else
7300 // is not relative: then overwrite
7301 strTarget = strSource;
7302}
7303
7304/**
7305 * Returns the full path to the machine's log folder in the
7306 * \a aLogFolder argument.
7307 */
7308void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7309{
7310 AutoCaller autoCaller(this);
7311 AssertComRCReturnVoid(autoCaller.rc());
7312
7313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7314
7315 char szTmp[RTPATH_MAX];
7316 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7317 if (RT_SUCCESS(vrc))
7318 {
7319 if (szTmp[0] && !mUserData.isNull())
7320 {
7321 char szTmp2[RTPATH_MAX];
7322 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7323 if (RT_SUCCESS(vrc))
7324 aLogFolder = Utf8StrFmt("%s%c%s",
7325 szTmp2,
7326 RTPATH_DELIMITER,
7327 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7328 }
7329 else
7330 vrc = VERR_PATH_IS_RELATIVE;
7331 }
7332
7333 if (RT_FAILURE(vrc))
7334 {
7335 // fallback if VBOX_USER_LOGHOME is not set or invalid
7336 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7337 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7338 aLogFolder.append(RTPATH_DELIMITER);
7339 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7340 }
7341}
7342
7343/**
7344 * Returns the full path to the machine's log file for an given index.
7345 */
7346Utf8Str Machine::i_getLogFilename(ULONG idx)
7347{
7348 Utf8Str logFolder;
7349 getLogFolder(logFolder);
7350 Assert(logFolder.length());
7351
7352 Utf8Str log;
7353 if (idx == 0)
7354 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7355#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7356 else if (idx == 1)
7357 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7358 else
7359 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7360#else
7361 else
7362 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7363#endif
7364 return log;
7365}
7366
7367/**
7368 * Returns the full path to the machine's hardened log file.
7369 */
7370Utf8Str Machine::i_getHardeningLogFilename(void)
7371{
7372 Utf8Str strFilename;
7373 getLogFolder(strFilename);
7374 Assert(strFilename.length());
7375 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7376 return strFilename;
7377}
7378
7379
7380/**
7381 * Composes a unique saved state filename based on the current system time. The filename is
7382 * granular to the second so this will work so long as no more than one snapshot is taken on
7383 * a machine per second.
7384 *
7385 * Before version 4.1, we used this formula for saved state files:
7386 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7387 * which no longer works because saved state files can now be shared between the saved state of the
7388 * "saved" machine and an online snapshot, and the following would cause problems:
7389 * 1) save machine
7390 * 2) create online snapshot from that machine state --> reusing saved state file
7391 * 3) save machine again --> filename would be reused, breaking the online snapshot
7392 *
7393 * So instead we now use a timestamp.
7394 *
7395 * @param strStateFilePath
7396 */
7397
7398void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7399{
7400 AutoCaller autoCaller(this);
7401 AssertComRCReturnVoid(autoCaller.rc());
7402
7403 {
7404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7405 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7406 }
7407
7408 RTTIMESPEC ts;
7409 RTTimeNow(&ts);
7410 RTTIME time;
7411 RTTimeExplode(&time, &ts);
7412
7413 strStateFilePath += RTPATH_DELIMITER;
7414 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7415 time.i32Year, time.u8Month, time.u8MonthDay,
7416 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7417}
7418
7419/**
7420 * Returns whether at least one USB controller is present for the VM.
7421 */
7422bool Machine::i_isUSBControllerPresent()
7423{
7424 AutoCaller autoCaller(this);
7425 AssertComRCReturn(autoCaller.rc(), false);
7426
7427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7428
7429 return (mUSBControllers->size() > 0);
7430}
7431
7432/**
7433 * @note Locks this object for writing, calls the client process
7434 * (inside the lock).
7435 */
7436HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7437 const Utf8Str &strFrontend,
7438 const Utf8Str &strEnvironment,
7439 ProgressProxy *aProgress)
7440{
7441 LogFlowThisFuncEnter();
7442
7443 AssertReturn(aControl, E_FAIL);
7444 AssertReturn(aProgress, E_FAIL);
7445 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7446
7447 AutoCaller autoCaller(this);
7448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7449
7450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7451
7452 if (!mData->mRegistered)
7453 return setError(E_UNEXPECTED,
7454 tr("The machine '%s' is not registered"),
7455 mUserData->s.strName.c_str());
7456
7457 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7458
7459 /* The process started when launching a VM with separate UI/VM processes is always
7460 * the UI process, i.e. needs special handling as it won't claim the session. */
7461 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7462
7463 if (fSeparate)
7464 {
7465 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7466 return setError(VBOX_E_INVALID_OBJECT_STATE,
7467 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7468 mUserData->s.strName.c_str());
7469 }
7470 else
7471 {
7472 if ( mData->mSession.mState == SessionState_Locked
7473 || mData->mSession.mState == SessionState_Spawning
7474 || mData->mSession.mState == SessionState_Unlocking)
7475 return setError(VBOX_E_INVALID_OBJECT_STATE,
7476 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7477 mUserData->s.strName.c_str());
7478
7479 /* may not be busy */
7480 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7481 }
7482
7483 /* get the path to the executable */
7484 char szPath[RTPATH_MAX];
7485 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7486 size_t cchBufLeft = strlen(szPath);
7487 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7488 szPath[cchBufLeft] = 0;
7489 char *pszNamePart = szPath + cchBufLeft;
7490 cchBufLeft = sizeof(szPath) - cchBufLeft;
7491
7492 int vrc = VINF_SUCCESS;
7493 RTPROCESS pid = NIL_RTPROCESS;
7494
7495 RTENV env = RTENV_DEFAULT;
7496
7497 if (!strEnvironment.isEmpty())
7498 {
7499 char *newEnvStr = NULL;
7500
7501 do
7502 {
7503 /* clone the current environment */
7504 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7505 AssertRCBreakStmt(vrc2, vrc = vrc2);
7506
7507 newEnvStr = RTStrDup(strEnvironment.c_str());
7508 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7509
7510 /* put new variables to the environment
7511 * (ignore empty variable names here since RTEnv API
7512 * intentionally doesn't do that) */
7513 char *var = newEnvStr;
7514 for (char *p = newEnvStr; *p; ++p)
7515 {
7516 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7517 {
7518 *p = '\0';
7519 if (*var)
7520 {
7521 char *val = strchr(var, '=');
7522 if (val)
7523 {
7524 *val++ = '\0';
7525 vrc2 = RTEnvSetEx(env, var, val);
7526 }
7527 else
7528 vrc2 = RTEnvUnsetEx(env, var);
7529 if (RT_FAILURE(vrc2))
7530 break;
7531 }
7532 var = p + 1;
7533 }
7534 }
7535 if (RT_SUCCESS(vrc2) && *var)
7536 vrc2 = RTEnvPutEx(env, var);
7537
7538 AssertRCBreakStmt(vrc2, vrc = vrc2);
7539 }
7540 while (0);
7541
7542 if (newEnvStr != NULL)
7543 RTStrFree(newEnvStr);
7544 }
7545
7546 /* Hardening logging */
7547#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7548 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7549 {
7550 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7551 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7552 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7553 {
7554 Utf8Str strStartupLogDir = strHardeningLogFile;
7555 strStartupLogDir.stripFilename();
7556 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7557 file without stripping the file. */
7558 }
7559 strSupHardeningLogArg.append(strHardeningLogFile);
7560
7561 /* Remove legacy log filename to avoid confusion. */
7562 Utf8Str strOldStartupLogFile;
7563 getLogFolder(strOldStartupLogFile);
7564 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7565 RTFileDelete(strOldStartupLogFile.c_str());
7566 }
7567 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7568#else
7569 const char *pszSupHardeningLogArg = NULL;
7570#endif
7571
7572 Utf8Str strCanonicalName;
7573
7574#ifdef VBOX_WITH_QTGUI
7575 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7576 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7577 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7578 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7579 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7580 {
7581 strCanonicalName = "GUI/Qt";
7582# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7583 /* Modify the base path so that we don't need to use ".." below. */
7584 RTPathStripTrailingSlash(szPath);
7585 RTPathStripFilename(szPath);
7586 cchBufLeft = strlen(szPath);
7587 pszNamePart = szPath + cchBufLeft;
7588 cchBufLeft = sizeof(szPath) - cchBufLeft;
7589
7590# define OSX_APP_NAME "VirtualBoxVM"
7591# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7592
7593 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7594 if ( strAppOverride.contains(".")
7595 || strAppOverride.contains("/")
7596 || strAppOverride.contains("\\")
7597 || strAppOverride.contains(":"))
7598 strAppOverride.setNull();
7599 Utf8Str strAppPath;
7600 if (!strAppOverride.isEmpty())
7601 {
7602 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7603 Utf8Str strFullPath(szPath);
7604 strFullPath.append(strAppPath);
7605 /* there is a race, but people using this deserve the failure */
7606 if (!RTFileExists(strFullPath.c_str()))
7607 strAppOverride.setNull();
7608 }
7609 if (strAppOverride.isEmpty())
7610 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7611 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7612 strcpy(pszNamePart, strAppPath.c_str());
7613# else
7614 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7615 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7616 strcpy(pszNamePart, s_szVirtualBox_exe);
7617# endif
7618
7619 Utf8Str idStr = mData->mUuid.toString();
7620 const char *apszArgs[] =
7621 {
7622 szPath,
7623 "--comment", mUserData->s.strName.c_str(),
7624 "--startvm", idStr.c_str(),
7625 "--no-startvm-errormsgbox",
7626 NULL, /* For "--separate". */
7627 NULL, /* For "--sup-startup-log". */
7628 NULL
7629 };
7630 unsigned iArg = 6;
7631 if (fSeparate)
7632 apszArgs[iArg++] = "--separate";
7633 apszArgs[iArg++] = pszSupHardeningLogArg;
7634
7635 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7636 }
7637#else /* !VBOX_WITH_QTGUI */
7638 if (0)
7639 ;
7640#endif /* VBOX_WITH_QTGUI */
7641
7642 else
7643
7644#ifdef VBOX_WITH_VBOXSDL
7645 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7646 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7647 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7648 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7649 {
7650 strCanonicalName = "GUI/SDL";
7651 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7652 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7653 strcpy(pszNamePart, s_szVBoxSDL_exe);
7654
7655 Utf8Str idStr = mData->mUuid.toString();
7656 const char *apszArgs[] =
7657 {
7658 szPath,
7659 "--comment", mUserData->s.strName.c_str(),
7660 "--startvm", idStr.c_str(),
7661 NULL, /* For "--separate". */
7662 NULL, /* For "--sup-startup-log". */
7663 NULL
7664 };
7665 unsigned iArg = 5;
7666 if (fSeparate)
7667 apszArgs[iArg++] = "--separate";
7668 apszArgs[iArg++] = pszSupHardeningLogArg;
7669
7670 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7671 }
7672#else /* !VBOX_WITH_VBOXSDL */
7673 if (0)
7674 ;
7675#endif /* !VBOX_WITH_VBOXSDL */
7676
7677 else
7678
7679#ifdef VBOX_WITH_HEADLESS
7680 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7681 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7682 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7683 )
7684 {
7685 strCanonicalName = "headless";
7686 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7687 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7688 * and a VM works even if the server has not been installed.
7689 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7690 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7691 * differently in 4.0 and 3.x.
7692 */
7693 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7694 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7695 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7696
7697 Utf8Str idStr = mData->mUuid.toString();
7698 const char *apszArgs[] =
7699 {
7700 szPath,
7701 "--comment", mUserData->s.strName.c_str(),
7702 "--startvm", idStr.c_str(),
7703 "--vrde", "config",
7704 NULL, /* For "--capture". */
7705 NULL, /* For "--sup-startup-log". */
7706 NULL
7707 };
7708 unsigned iArg = 7;
7709 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7710 apszArgs[iArg++] = "--capture";
7711 apszArgs[iArg++] = pszSupHardeningLogArg;
7712
7713# ifdef RT_OS_WINDOWS
7714 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7715# else
7716 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7717# endif
7718 }
7719#else /* !VBOX_WITH_HEADLESS */
7720 if (0)
7721 ;
7722#endif /* !VBOX_WITH_HEADLESS */
7723 else
7724 {
7725 RTEnvDestroy(env);
7726 return setError(E_INVALIDARG,
7727 tr("Invalid frontend name: '%s'"),
7728 strFrontend.c_str());
7729 }
7730
7731 RTEnvDestroy(env);
7732
7733 if (RT_FAILURE(vrc))
7734 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7735 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7736 mUserData->s.strName.c_str(), vrc);
7737
7738 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7739
7740 if (!fSeparate)
7741 {
7742 /*
7743 * Note that we don't release the lock here before calling the client,
7744 * because it doesn't need to call us back if called with a NULL argument.
7745 * Releasing the lock here is dangerous because we didn't prepare the
7746 * launch data yet, but the client we've just started may happen to be
7747 * too fast and call LockMachine() that will fail (because of PID, etc.),
7748 * so that the Machine will never get out of the Spawning session state.
7749 */
7750
7751 /* inform the session that it will be a remote one */
7752 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7753#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7754 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7755#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7756 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7757#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7758 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7759
7760 if (FAILED(rc))
7761 {
7762 /* restore the session state */
7763 mData->mSession.mState = SessionState_Unlocked;
7764 alock.release();
7765 mParent->i_addProcessToReap(pid);
7766 /* The failure may occur w/o any error info (from RPC), so provide one */
7767 return setError(VBOX_E_VM_ERROR,
7768 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7769 }
7770
7771 /* attach launch data to the machine */
7772 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7773 mData->mSession.mRemoteControls.push_back(aControl);
7774 mData->mSession.mProgress = aProgress;
7775 mData->mSession.mPID = pid;
7776 mData->mSession.mState = SessionState_Spawning;
7777 Assert(strCanonicalName.isNotEmpty());
7778 mData->mSession.mName = strCanonicalName;
7779 }
7780 else
7781 {
7782 /* For separate UI process we declare the launch as completed instantly, as the
7783 * actual headless VM start may or may not come. No point in remembering anything
7784 * yet, as what matters for us is when the headless VM gets started. */
7785 aProgress->i_notifyComplete(S_OK);
7786 }
7787
7788 alock.release();
7789 mParent->i_addProcessToReap(pid);
7790
7791 LogFlowThisFuncLeave();
7792 return S_OK;
7793}
7794
7795/**
7796 * Returns @c true if the given session machine instance has an open direct
7797 * session (and optionally also for direct sessions which are closing) and
7798 * returns the session control machine instance if so.
7799 *
7800 * Note that when the method returns @c false, the arguments remain unchanged.
7801 *
7802 * @param aMachine Session machine object.
7803 * @param aControl Direct session control object (optional).
7804 * @param aRequireVM If true then only allow VM sessions.
7805 * @param aAllowClosing If true then additionally a session which is currently
7806 * being closed will also be allowed.
7807 *
7808 * @note locks this object for reading.
7809 */
7810bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7811 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7812 bool aRequireVM /*= false*/,
7813 bool aAllowClosing /*= false*/)
7814{
7815 AutoLimitedCaller autoCaller(this);
7816 AssertComRCReturn(autoCaller.rc(), false);
7817
7818 /* just return false for inaccessible machines */
7819 if (getObjectState().getState() != ObjectState::Ready)
7820 return false;
7821
7822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7823
7824 if ( ( mData->mSession.mState == SessionState_Locked
7825 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7826 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7827 )
7828 {
7829 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7830
7831 aMachine = mData->mSession.mMachine;
7832
7833 if (aControl != NULL)
7834 *aControl = mData->mSession.mDirectControl;
7835
7836 return true;
7837 }
7838
7839 return false;
7840}
7841
7842/**
7843 * Returns @c true if the given machine has an spawning direct session.
7844 *
7845 * @note locks this object for reading.
7846 */
7847bool Machine::i_isSessionSpawning()
7848{
7849 AutoLimitedCaller autoCaller(this);
7850 AssertComRCReturn(autoCaller.rc(), false);
7851
7852 /* just return false for inaccessible machines */
7853 if (getObjectState().getState() != ObjectState::Ready)
7854 return false;
7855
7856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7857
7858 if (mData->mSession.mState == SessionState_Spawning)
7859 return true;
7860
7861 return false;
7862}
7863
7864/**
7865 * Called from the client watcher thread to check for unexpected client process
7866 * death during Session_Spawning state (e.g. before it successfully opened a
7867 * direct session).
7868 *
7869 * On Win32 and on OS/2, this method is called only when we've got the
7870 * direct client's process termination notification, so it always returns @c
7871 * true.
7872 *
7873 * On other platforms, this method returns @c true if the client process is
7874 * terminated and @c false if it's still alive.
7875 *
7876 * @note Locks this object for writing.
7877 */
7878bool Machine::i_checkForSpawnFailure()
7879{
7880 AutoCaller autoCaller(this);
7881 if (!autoCaller.isOk())
7882 {
7883 /* nothing to do */
7884 LogFlowThisFunc(("Already uninitialized!\n"));
7885 return true;
7886 }
7887
7888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7889
7890 if (mData->mSession.mState != SessionState_Spawning)
7891 {
7892 /* nothing to do */
7893 LogFlowThisFunc(("Not spawning any more!\n"));
7894 return true;
7895 }
7896
7897 HRESULT rc = S_OK;
7898
7899 /* PID not yet initialized, skip check. */
7900 if (mData->mSession.mPID == NIL_RTPROCESS)
7901 return false;
7902
7903 RTPROCSTATUS status;
7904 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7905
7906 if (vrc != VERR_PROCESS_RUNNING)
7907 {
7908 Utf8Str strExtraInfo;
7909
7910#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7911 /* If the startup logfile exists and is of non-zero length, tell the
7912 user to look there for more details to encourage them to attach it
7913 when reporting startup issues. */
7914 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7915 uint64_t cbStartupLogFile = 0;
7916 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7917 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7918 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7919#endif
7920
7921 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7922 rc = setError(E_FAIL,
7923 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7924 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7925 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7926 rc = setError(E_FAIL,
7927 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7928 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7929 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7930 rc = setError(E_FAIL,
7931 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7932 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7933 else
7934 rc = setErrorBoth(E_FAIL, vrc,
7935 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7936 i_getName().c_str(), vrc, strExtraInfo.c_str());
7937 }
7938
7939 if (FAILED(rc))
7940 {
7941 /* Close the remote session, remove the remote control from the list
7942 * and reset session state to Closed (@note keep the code in sync with
7943 * the relevant part in LockMachine()). */
7944
7945 Assert(mData->mSession.mRemoteControls.size() == 1);
7946 if (mData->mSession.mRemoteControls.size() == 1)
7947 {
7948 ErrorInfoKeeper eik;
7949 mData->mSession.mRemoteControls.front()->Uninitialize();
7950 }
7951
7952 mData->mSession.mRemoteControls.clear();
7953 mData->mSession.mState = SessionState_Unlocked;
7954
7955 /* finalize the progress after setting the state */
7956 if (!mData->mSession.mProgress.isNull())
7957 {
7958 mData->mSession.mProgress->notifyComplete(rc);
7959 mData->mSession.mProgress.setNull();
7960 }
7961
7962 mData->mSession.mPID = NIL_RTPROCESS;
7963
7964 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7965 return true;
7966 }
7967
7968 return false;
7969}
7970
7971/**
7972 * Checks whether the machine can be registered. If so, commits and saves
7973 * all settings.
7974 *
7975 * @note Must be called from mParent's write lock. Locks this object and
7976 * children for writing.
7977 */
7978HRESULT Machine::i_prepareRegister()
7979{
7980 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7981
7982 AutoLimitedCaller autoCaller(this);
7983 AssertComRCReturnRC(autoCaller.rc());
7984
7985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7986
7987 /* wait for state dependents to drop to zero */
7988 i_ensureNoStateDependencies();
7989
7990 if (!mData->mAccessible)
7991 return setError(VBOX_E_INVALID_OBJECT_STATE,
7992 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7993 mUserData->s.strName.c_str(),
7994 mData->mUuid.toString().c_str());
7995
7996 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7997
7998 if (mData->mRegistered)
7999 return setError(VBOX_E_INVALID_OBJECT_STATE,
8000 tr("The machine '%s' with UUID {%s} is already registered"),
8001 mUserData->s.strName.c_str(),
8002 mData->mUuid.toString().c_str());
8003
8004 HRESULT rc = S_OK;
8005
8006 // Ensure the settings are saved. If we are going to be registered and
8007 // no config file exists yet, create it by calling i_saveSettings() too.
8008 if ( (mData->flModifications)
8009 || (!mData->pMachineConfigFile->fileExists())
8010 )
8011 {
8012 rc = i_saveSettings(NULL);
8013 // no need to check whether VirtualBox.xml needs saving too since
8014 // we can't have a machine XML file rename pending
8015 if (FAILED(rc)) return rc;
8016 }
8017
8018 /* more config checking goes here */
8019
8020 if (SUCCEEDED(rc))
8021 {
8022 /* we may have had implicit modifications we want to fix on success */
8023 i_commit();
8024
8025 mData->mRegistered = true;
8026 }
8027 else
8028 {
8029 /* we may have had implicit modifications we want to cancel on failure*/
8030 i_rollback(false /* aNotify */);
8031 }
8032
8033 return rc;
8034}
8035
8036/**
8037 * Increases the number of objects dependent on the machine state or on the
8038 * registered state. Guarantees that these two states will not change at least
8039 * until #i_releaseStateDependency() is called.
8040 *
8041 * Depending on the @a aDepType value, additional state checks may be made.
8042 * These checks will set extended error info on failure. See
8043 * #i_checkStateDependency() for more info.
8044 *
8045 * If this method returns a failure, the dependency is not added and the caller
8046 * is not allowed to rely on any particular machine state or registration state
8047 * value and may return the failed result code to the upper level.
8048 *
8049 * @param aDepType Dependency type to add.
8050 * @param aState Current machine state (NULL if not interested).
8051 * @param aRegistered Current registered state (NULL if not interested).
8052 *
8053 * @note Locks this object for writing.
8054 */
8055HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8056 MachineState_T *aState /* = NULL */,
8057 BOOL *aRegistered /* = NULL */)
8058{
8059 AutoCaller autoCaller(this);
8060 AssertComRCReturnRC(autoCaller.rc());
8061
8062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8063
8064 HRESULT rc = i_checkStateDependency(aDepType);
8065 if (FAILED(rc)) return rc;
8066
8067 {
8068 if (mData->mMachineStateChangePending != 0)
8069 {
8070 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8071 * drop to zero so don't add more. It may make sense to wait a bit
8072 * and retry before reporting an error (since the pending state
8073 * transition should be really quick) but let's just assert for
8074 * now to see if it ever happens on practice. */
8075
8076 AssertFailed();
8077
8078 return setError(E_ACCESSDENIED,
8079 tr("Machine state change is in progress. Please retry the operation later."));
8080 }
8081
8082 ++mData->mMachineStateDeps;
8083 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8084 }
8085
8086 if (aState)
8087 *aState = mData->mMachineState;
8088 if (aRegistered)
8089 *aRegistered = mData->mRegistered;
8090
8091 return S_OK;
8092}
8093
8094/**
8095 * Decreases the number of objects dependent on the machine state.
8096 * Must always complete the #i_addStateDependency() call after the state
8097 * dependency is no more necessary.
8098 */
8099void Machine::i_releaseStateDependency()
8100{
8101 AutoCaller autoCaller(this);
8102 AssertComRCReturnVoid(autoCaller.rc());
8103
8104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8105
8106 /* releaseStateDependency() w/o addStateDependency()? */
8107 AssertReturnVoid(mData->mMachineStateDeps != 0);
8108 -- mData->mMachineStateDeps;
8109
8110 if (mData->mMachineStateDeps == 0)
8111 {
8112 /* inform i_ensureNoStateDependencies() that there are no more deps */
8113 if (mData->mMachineStateChangePending != 0)
8114 {
8115 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8116 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8117 }
8118 }
8119}
8120
8121Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8122{
8123 /* start with nothing found */
8124 Utf8Str strResult("");
8125
8126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8127
8128 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8129 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8130 // found:
8131 strResult = it->second; // source is a Utf8Str
8132
8133 return strResult;
8134}
8135
8136// protected methods
8137/////////////////////////////////////////////////////////////////////////////
8138
8139/**
8140 * Performs machine state checks based on the @a aDepType value. If a check
8141 * fails, this method will set extended error info, otherwise it will return
8142 * S_OK. It is supposed, that on failure, the caller will immediately return
8143 * the return value of this method to the upper level.
8144 *
8145 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8146 *
8147 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8148 * current state of this machine object allows to change settings of the
8149 * machine (i.e. the machine is not registered, or registered but not running
8150 * and not saved). It is useful to call this method from Machine setters
8151 * before performing any change.
8152 *
8153 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8154 * as for MutableStateDep except that if the machine is saved, S_OK is also
8155 * returned. This is useful in setters which allow changing machine
8156 * properties when it is in the saved state.
8157 *
8158 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8159 * if the current state of this machine object allows to change runtime
8160 * changeable settings of the machine (i.e. the machine is not registered, or
8161 * registered but either running or not running and not saved). It is useful
8162 * to call this method from Machine setters before performing any changes to
8163 * runtime changeable settings.
8164 *
8165 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8166 * the same as for MutableOrRunningStateDep except that if the machine is
8167 * saved, S_OK is also returned. This is useful in setters which allow
8168 * changing runtime and saved state changeable machine properties.
8169 *
8170 * @param aDepType Dependency type to check.
8171 *
8172 * @note Non Machine based classes should use #i_addStateDependency() and
8173 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8174 * template.
8175 *
8176 * @note This method must be called from under this object's read or write
8177 * lock.
8178 */
8179HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8180{
8181 switch (aDepType)
8182 {
8183 case AnyStateDep:
8184 {
8185 break;
8186 }
8187 case MutableStateDep:
8188 {
8189 if ( mData->mRegistered
8190 && ( !i_isSessionMachine()
8191 || ( mData->mMachineState != MachineState_Aborted
8192 && mData->mMachineState != MachineState_Teleported
8193 && mData->mMachineState != MachineState_PoweredOff
8194 )
8195 )
8196 )
8197 return setError(VBOX_E_INVALID_VM_STATE,
8198 tr("The machine is not mutable (state is %s)"),
8199 Global::stringifyMachineState(mData->mMachineState));
8200 break;
8201 }
8202 case MutableOrSavedStateDep:
8203 {
8204 if ( mData->mRegistered
8205 && ( !i_isSessionMachine()
8206 || ( mData->mMachineState != MachineState_Aborted
8207 && mData->mMachineState != MachineState_Teleported
8208 && mData->mMachineState != MachineState_Saved
8209 && mData->mMachineState != MachineState_PoweredOff
8210 )
8211 )
8212 )
8213 return setError(VBOX_E_INVALID_VM_STATE,
8214 tr("The machine is not mutable or saved (state is %s)"),
8215 Global::stringifyMachineState(mData->mMachineState));
8216 break;
8217 }
8218 case MutableOrRunningStateDep:
8219 {
8220 if ( mData->mRegistered
8221 && ( !i_isSessionMachine()
8222 || ( mData->mMachineState != MachineState_Aborted
8223 && mData->mMachineState != MachineState_Teleported
8224 && mData->mMachineState != MachineState_PoweredOff
8225 && !Global::IsOnline(mData->mMachineState)
8226 )
8227 )
8228 )
8229 return setError(VBOX_E_INVALID_VM_STATE,
8230 tr("The machine is not mutable or running (state is %s)"),
8231 Global::stringifyMachineState(mData->mMachineState));
8232 break;
8233 }
8234 case MutableOrSavedOrRunningStateDep:
8235 {
8236 if ( mData->mRegistered
8237 && ( !i_isSessionMachine()
8238 || ( mData->mMachineState != MachineState_Aborted
8239 && mData->mMachineState != MachineState_Teleported
8240 && mData->mMachineState != MachineState_Saved
8241 && mData->mMachineState != MachineState_PoweredOff
8242 && !Global::IsOnline(mData->mMachineState)
8243 )
8244 )
8245 )
8246 return setError(VBOX_E_INVALID_VM_STATE,
8247 tr("The machine is not mutable, saved or running (state is %s)"),
8248 Global::stringifyMachineState(mData->mMachineState));
8249 break;
8250 }
8251 }
8252
8253 return S_OK;
8254}
8255
8256/**
8257 * Helper to initialize all associated child objects and allocate data
8258 * structures.
8259 *
8260 * This method must be called as a part of the object's initialization procedure
8261 * (usually done in the #init() method).
8262 *
8263 * @note Must be called only from #init() or from #i_registeredInit().
8264 */
8265HRESULT Machine::initDataAndChildObjects()
8266{
8267 AutoCaller autoCaller(this);
8268 AssertComRCReturnRC(autoCaller.rc());
8269 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8270 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8271
8272 AssertReturn(!mData->mAccessible, E_FAIL);
8273
8274 /* allocate data structures */
8275 mSSData.allocate();
8276 mUserData.allocate();
8277 mHWData.allocate();
8278 mMediumAttachments.allocate();
8279 mStorageControllers.allocate();
8280 mUSBControllers.allocate();
8281
8282 /* initialize mOSTypeId */
8283 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8284
8285/** @todo r=bird: init() methods never fails, right? Why don't we make them
8286 * return void then! */
8287
8288 /* create associated BIOS settings object */
8289 unconst(mBIOSSettings).createObject();
8290 mBIOSSettings->init(this);
8291
8292 /* create associated record settings object */
8293 unconst(mRecordingSettings).createObject();
8294 mRecordingSettings->init(this);
8295
8296 /* create an associated VRDE object (default is disabled) */
8297 unconst(mVRDEServer).createObject();
8298 mVRDEServer->init(this);
8299
8300 /* create associated serial port objects */
8301 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8302 {
8303 unconst(mSerialPorts[slot]).createObject();
8304 mSerialPorts[slot]->init(this, slot);
8305 }
8306
8307 /* create associated parallel port objects */
8308 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8309 {
8310 unconst(mParallelPorts[slot]).createObject();
8311 mParallelPorts[slot]->init(this, slot);
8312 }
8313
8314 /* create the audio adapter object (always present, default is disabled) */
8315 unconst(mAudioAdapter).createObject();
8316 mAudioAdapter->init(this);
8317
8318 /* create the USB device filters object (always present) */
8319 unconst(mUSBDeviceFilters).createObject();
8320 mUSBDeviceFilters->init(this);
8321
8322 /* create associated network adapter objects */
8323 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8324 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8325 {
8326 unconst(mNetworkAdapters[slot]).createObject();
8327 mNetworkAdapters[slot]->init(this, slot);
8328 }
8329
8330 /* create the bandwidth control */
8331 unconst(mBandwidthControl).createObject();
8332 mBandwidthControl->init(this);
8333
8334 return S_OK;
8335}
8336
8337/**
8338 * Helper to uninitialize all associated child objects and to free all data
8339 * structures.
8340 *
8341 * This method must be called as a part of the object's uninitialization
8342 * procedure (usually done in the #uninit() method).
8343 *
8344 * @note Must be called only from #uninit() or from #i_registeredInit().
8345 */
8346void Machine::uninitDataAndChildObjects()
8347{
8348 AutoCaller autoCaller(this);
8349 AssertComRCReturnVoid(autoCaller.rc());
8350 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8351 || getObjectState().getState() == ObjectState::Limited);
8352
8353 /* tell all our other child objects we've been uninitialized */
8354 if (mBandwidthControl)
8355 {
8356 mBandwidthControl->uninit();
8357 unconst(mBandwidthControl).setNull();
8358 }
8359
8360 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8361 {
8362 if (mNetworkAdapters[slot])
8363 {
8364 mNetworkAdapters[slot]->uninit();
8365 unconst(mNetworkAdapters[slot]).setNull();
8366 }
8367 }
8368
8369 if (mUSBDeviceFilters)
8370 {
8371 mUSBDeviceFilters->uninit();
8372 unconst(mUSBDeviceFilters).setNull();
8373 }
8374
8375 if (mAudioAdapter)
8376 {
8377 mAudioAdapter->uninit();
8378 unconst(mAudioAdapter).setNull();
8379 }
8380
8381 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8382 {
8383 if (mParallelPorts[slot])
8384 {
8385 mParallelPorts[slot]->uninit();
8386 unconst(mParallelPorts[slot]).setNull();
8387 }
8388 }
8389
8390 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8391 {
8392 if (mSerialPorts[slot])
8393 {
8394 mSerialPorts[slot]->uninit();
8395 unconst(mSerialPorts[slot]).setNull();
8396 }
8397 }
8398
8399 if (mVRDEServer)
8400 {
8401 mVRDEServer->uninit();
8402 unconst(mVRDEServer).setNull();
8403 }
8404
8405 if (mBIOSSettings)
8406 {
8407 mBIOSSettings->uninit();
8408 unconst(mBIOSSettings).setNull();
8409 }
8410
8411 if (mRecordingSettings)
8412 {
8413 mRecordingSettings->uninit();
8414 unconst(mRecordingSettings).setNull();
8415 }
8416
8417 /* Deassociate media (only when a real Machine or a SnapshotMachine
8418 * instance is uninitialized; SessionMachine instances refer to real
8419 * Machine media). This is necessary for a clean re-initialization of
8420 * the VM after successfully re-checking the accessibility state. Note
8421 * that in case of normal Machine or SnapshotMachine uninitialization (as
8422 * a result of unregistering or deleting the snapshot), outdated media
8423 * attachments will already be uninitialized and deleted, so this
8424 * code will not affect them. */
8425 if ( !mMediumAttachments.isNull()
8426 && !i_isSessionMachine()
8427 )
8428 {
8429 for (MediumAttachmentList::const_iterator
8430 it = mMediumAttachments->begin();
8431 it != mMediumAttachments->end();
8432 ++it)
8433 {
8434 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8435 if (pMedium.isNull())
8436 continue;
8437 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8438 AssertComRC(rc);
8439 }
8440 }
8441
8442 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8443 {
8444 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8445 if (mData->mFirstSnapshot)
8446 {
8447 // snapshots tree is protected by machine write lock; strictly
8448 // this isn't necessary here since we're deleting the entire
8449 // machine, but otherwise we assert in Snapshot::uninit()
8450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8451 mData->mFirstSnapshot->uninit();
8452 mData->mFirstSnapshot.setNull();
8453 }
8454
8455 mData->mCurrentSnapshot.setNull();
8456 }
8457
8458 /* free data structures (the essential mData structure is not freed here
8459 * since it may be still in use) */
8460 mMediumAttachments.free();
8461 mStorageControllers.free();
8462 mUSBControllers.free();
8463 mHWData.free();
8464 mUserData.free();
8465 mSSData.free();
8466}
8467
8468/**
8469 * Returns a pointer to the Machine object for this machine that acts like a
8470 * parent for complex machine data objects such as shared folders, etc.
8471 *
8472 * For primary Machine objects and for SnapshotMachine objects, returns this
8473 * object's pointer itself. For SessionMachine objects, returns the peer
8474 * (primary) machine pointer.
8475 */
8476Machine *Machine::i_getMachine()
8477{
8478 if (i_isSessionMachine())
8479 return (Machine*)mPeer;
8480 return this;
8481}
8482
8483/**
8484 * Makes sure that there are no machine state dependents. If necessary, waits
8485 * for the number of dependents to drop to zero.
8486 *
8487 * Make sure this method is called from under this object's write lock to
8488 * guarantee that no new dependents may be added when this method returns
8489 * control to the caller.
8490 *
8491 * @note Locks this object for writing. The lock will be released while waiting
8492 * (if necessary).
8493 *
8494 * @warning To be used only in methods that change the machine state!
8495 */
8496void Machine::i_ensureNoStateDependencies()
8497{
8498 AssertReturnVoid(isWriteLockOnCurrentThread());
8499
8500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8501
8502 /* Wait for all state dependents if necessary */
8503 if (mData->mMachineStateDeps != 0)
8504 {
8505 /* lazy semaphore creation */
8506 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8507 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8508
8509 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8510 mData->mMachineStateDeps));
8511
8512 ++mData->mMachineStateChangePending;
8513
8514 /* reset the semaphore before waiting, the last dependent will signal
8515 * it */
8516 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8517
8518 alock.release();
8519
8520 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8521
8522 alock.acquire();
8523
8524 -- mData->mMachineStateChangePending;
8525 }
8526}
8527
8528/**
8529 * Changes the machine state and informs callbacks.
8530 *
8531 * This method is not intended to fail so it either returns S_OK or asserts (and
8532 * returns a failure).
8533 *
8534 * @note Locks this object for writing.
8535 */
8536HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8537{
8538 LogFlowThisFuncEnter();
8539 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8540 Assert(aMachineState != MachineState_Null);
8541
8542 AutoCaller autoCaller(this);
8543 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8544
8545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8546
8547 /* wait for state dependents to drop to zero */
8548 i_ensureNoStateDependencies();
8549
8550 MachineState_T const enmOldState = mData->mMachineState;
8551 if (enmOldState != aMachineState)
8552 {
8553 mData->mMachineState = aMachineState;
8554 RTTimeNow(&mData->mLastStateChange);
8555
8556#ifdef VBOX_WITH_DTRACE_R3_MAIN
8557 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8558#endif
8559 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8560 }
8561
8562 LogFlowThisFuncLeave();
8563 return S_OK;
8564}
8565
8566/**
8567 * Searches for a shared folder with the given logical name
8568 * in the collection of shared folders.
8569 *
8570 * @param aName logical name of the shared folder
8571 * @param aSharedFolder where to return the found object
8572 * @param aSetError whether to set the error info if the folder is
8573 * not found
8574 * @return
8575 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8576 *
8577 * @note
8578 * must be called from under the object's lock!
8579 */
8580HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8581 ComObjPtr<SharedFolder> &aSharedFolder,
8582 bool aSetError /* = false */)
8583{
8584 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8585 for (HWData::SharedFolderList::const_iterator
8586 it = mHWData->mSharedFolders.begin();
8587 it != mHWData->mSharedFolders.end();
8588 ++it)
8589 {
8590 SharedFolder *pSF = *it;
8591 AutoCaller autoCaller(pSF);
8592 if (pSF->i_getName() == aName)
8593 {
8594 aSharedFolder = pSF;
8595 rc = S_OK;
8596 break;
8597 }
8598 }
8599
8600 if (aSetError && FAILED(rc))
8601 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8602
8603 return rc;
8604}
8605
8606/**
8607 * Initializes all machine instance data from the given settings structures
8608 * from XML. The exception is the machine UUID which needs special handling
8609 * depending on the caller's use case, so the caller needs to set that herself.
8610 *
8611 * This gets called in several contexts during machine initialization:
8612 *
8613 * -- When machine XML exists on disk already and needs to be loaded into memory,
8614 * for example, from #i_registeredInit() to load all registered machines on
8615 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8616 * attached to the machine should be part of some media registry already.
8617 *
8618 * -- During OVF import, when a machine config has been constructed from an
8619 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8620 * ensure that the media listed as attachments in the config (which have
8621 * been imported from the OVF) receive the correct registry ID.
8622 *
8623 * -- During VM cloning.
8624 *
8625 * @param config Machine settings from XML.
8626 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8627 * for each attached medium in the config.
8628 * @return
8629 */
8630HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8631 const Guid *puuidRegistry)
8632{
8633 // copy name, description, OS type, teleporter, UTC etc.
8634 mUserData->s = config.machineUserData;
8635
8636 // look up the object by Id to check it is valid
8637 ComObjPtr<GuestOSType> pGuestOSType;
8638 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8639 if (!pGuestOSType.isNull())
8640 mUserData->s.strOsType = pGuestOSType->i_id();
8641
8642 // stateFile (optional)
8643 if (config.strStateFile.isEmpty())
8644 mSSData->strStateFilePath.setNull();
8645 else
8646 {
8647 Utf8Str stateFilePathFull(config.strStateFile);
8648 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8649 if (RT_FAILURE(vrc))
8650 return setErrorBoth(E_FAIL, vrc,
8651 tr("Invalid saved state file path '%s' (%Rrc)"),
8652 config.strStateFile.c_str(),
8653 vrc);
8654 mSSData->strStateFilePath = stateFilePathFull;
8655 }
8656
8657 // snapshot folder needs special processing so set it again
8658 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8659 if (FAILED(rc)) return rc;
8660
8661 /* Copy the extra data items (config may or may not be the same as
8662 * mData->pMachineConfigFile) if necessary. When loading the XML files
8663 * from disk they are the same, but not for OVF import. */
8664 if (mData->pMachineConfigFile != &config)
8665 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8666
8667 /* currentStateModified (optional, default is true) */
8668 mData->mCurrentStateModified = config.fCurrentStateModified;
8669
8670 mData->mLastStateChange = config.timeLastStateChange;
8671
8672 /*
8673 * note: all mUserData members must be assigned prior this point because
8674 * we need to commit changes in order to let mUserData be shared by all
8675 * snapshot machine instances.
8676 */
8677 mUserData.commitCopy();
8678
8679 // machine registry, if present (must be loaded before snapshots)
8680 if (config.canHaveOwnMediaRegistry())
8681 {
8682 // determine machine folder
8683 Utf8Str strMachineFolder = i_getSettingsFileFull();
8684 strMachineFolder.stripFilename();
8685 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8686 config.mediaRegistry,
8687 strMachineFolder);
8688 if (FAILED(rc)) return rc;
8689 }
8690
8691 /* Snapshot node (optional) */
8692 size_t cRootSnapshots;
8693 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8694 {
8695 // there must be only one root snapshot
8696 Assert(cRootSnapshots == 1);
8697
8698 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8699
8700 rc = i_loadSnapshot(snap,
8701 config.uuidCurrentSnapshot,
8702 NULL); // no parent == first snapshot
8703 if (FAILED(rc)) return rc;
8704 }
8705
8706 // hardware data
8707 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8708 if (FAILED(rc)) return rc;
8709
8710 /*
8711 * NOTE: the assignment below must be the last thing to do,
8712 * otherwise it will be not possible to change the settings
8713 * somewhere in the code above because all setters will be
8714 * blocked by i_checkStateDependency(MutableStateDep).
8715 */
8716
8717 /* set the machine state to Aborted or Saved when appropriate */
8718 if (config.fAborted)
8719 {
8720 mSSData->strStateFilePath.setNull();
8721
8722 /* no need to use i_setMachineState() during init() */
8723 mData->mMachineState = MachineState_Aborted;
8724 }
8725 else if (!mSSData->strStateFilePath.isEmpty())
8726 {
8727 /* no need to use i_setMachineState() during init() */
8728 mData->mMachineState = MachineState_Saved;
8729 }
8730
8731 // after loading settings, we are no longer different from the XML on disk
8732 mData->flModifications = 0;
8733
8734 return S_OK;
8735}
8736
8737/**
8738 * Recursively loads all snapshots starting from the given.
8739 *
8740 * @param data snapshot settings.
8741 * @param aCurSnapshotId Current snapshot ID from the settings file.
8742 * @param aParentSnapshot Parent snapshot.
8743 */
8744HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8745 const Guid &aCurSnapshotId,
8746 Snapshot *aParentSnapshot)
8747{
8748 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8749 AssertReturn(!i_isSessionMachine(), E_FAIL);
8750
8751 HRESULT rc = S_OK;
8752
8753 Utf8Str strStateFile;
8754 if (!data.strStateFile.isEmpty())
8755 {
8756 /* optional */
8757 strStateFile = data.strStateFile;
8758 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8759 if (RT_FAILURE(vrc))
8760 return setErrorBoth(E_FAIL, vrc,
8761 tr("Invalid saved state file path '%s' (%Rrc)"),
8762 strStateFile.c_str(),
8763 vrc);
8764 }
8765
8766 /* create a snapshot machine object */
8767 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8768 pSnapshotMachine.createObject();
8769 rc = pSnapshotMachine->initFromSettings(this,
8770 data.hardware,
8771 &data.debugging,
8772 &data.autostart,
8773 data.uuid.ref(),
8774 strStateFile);
8775 if (FAILED(rc)) return rc;
8776
8777 /* create a snapshot object */
8778 ComObjPtr<Snapshot> pSnapshot;
8779 pSnapshot.createObject();
8780 /* initialize the snapshot */
8781 rc = pSnapshot->init(mParent, // VirtualBox object
8782 data.uuid,
8783 data.strName,
8784 data.strDescription,
8785 data.timestamp,
8786 pSnapshotMachine,
8787 aParentSnapshot);
8788 if (FAILED(rc)) return rc;
8789
8790 /* memorize the first snapshot if necessary */
8791 if (!mData->mFirstSnapshot)
8792 mData->mFirstSnapshot = pSnapshot;
8793
8794 /* memorize the current snapshot when appropriate */
8795 if ( !mData->mCurrentSnapshot
8796 && pSnapshot->i_getId() == aCurSnapshotId
8797 )
8798 mData->mCurrentSnapshot = pSnapshot;
8799
8800 // now create the children
8801 for (settings::SnapshotsList::const_iterator
8802 it = data.llChildSnapshots.begin();
8803 it != data.llChildSnapshots.end();
8804 ++it)
8805 {
8806 const settings::Snapshot &childData = *it;
8807 // recurse
8808 rc = i_loadSnapshot(childData,
8809 aCurSnapshotId,
8810 pSnapshot); // parent = the one we created above
8811 if (FAILED(rc)) return rc;
8812 }
8813
8814 return rc;
8815}
8816
8817/**
8818 * Loads settings into mHWData.
8819 *
8820 * @param puuidRegistry Registry ID.
8821 * @param puuidSnapshot Snapshot ID
8822 * @param data Reference to the hardware settings.
8823 * @param pDbg Pointer to the debugging settings.
8824 * @param pAutostart Pointer to the autostart settings.
8825 */
8826HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8827 const Guid *puuidSnapshot,
8828 const settings::Hardware &data,
8829 const settings::Debugging *pDbg,
8830 const settings::Autostart *pAutostart)
8831{
8832 AssertReturn(!i_isSessionMachine(), E_FAIL);
8833
8834 HRESULT rc = S_OK;
8835
8836 try
8837 {
8838 ComObjPtr<GuestOSType> pGuestOSType;
8839 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8840
8841 /* The hardware version attribute (optional). */
8842 mHWData->mHWVersion = data.strVersion;
8843 mHWData->mHardwareUUID = data.uuid;
8844
8845 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8846 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8847 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8848 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8849 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8850 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8851 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8852 mHWData->mPAEEnabled = data.fPAE;
8853 mHWData->mLongMode = data.enmLongMode;
8854 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8855 mHWData->mAPIC = data.fAPIC;
8856 mHWData->mX2APIC = data.fX2APIC;
8857 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8858 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8859 mHWData->mSpecCtrl = data.fSpecCtrl;
8860 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8861 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8862 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8863 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8864 mHWData->mCPUCount = data.cCPUs;
8865 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8866 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8867 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8868 mHWData->mCpuProfile = data.strCpuProfile;
8869
8870 // cpu
8871 if (mHWData->mCPUHotPlugEnabled)
8872 {
8873 for (settings::CpuList::const_iterator
8874 it = data.llCpus.begin();
8875 it != data.llCpus.end();
8876 ++it)
8877 {
8878 const settings::Cpu &cpu = *it;
8879
8880 mHWData->mCPUAttached[cpu.ulId] = true;
8881 }
8882 }
8883
8884 // cpuid leafs
8885 for (settings::CpuIdLeafsList::const_iterator
8886 it = data.llCpuIdLeafs.begin();
8887 it != data.llCpuIdLeafs.end();
8888 ++it)
8889 {
8890 const settings::CpuIdLeaf &rLeaf= *it;
8891 if ( rLeaf.idx < UINT32_C(0x20)
8892 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8893 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8894 mHWData->mCpuIdLeafList.push_back(rLeaf);
8895 /* else: just ignore */
8896 }
8897
8898 mHWData->mMemorySize = data.ulMemorySizeMB;
8899 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8900
8901 // boot order
8902 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8903 {
8904 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8905 if (it == data.mapBootOrder.end())
8906 mHWData->mBootOrder[i] = DeviceType_Null;
8907 else
8908 mHWData->mBootOrder[i] = it->second;
8909 }
8910
8911 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8912 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8913 mHWData->mMonitorCount = data.cMonitors;
8914 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8915 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8916 mHWData->mFirmwareType = data.firmwareType;
8917 mHWData->mPointingHIDType = data.pointingHIDType;
8918 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8919 mHWData->mChipsetType = data.chipsetType;
8920 mHWData->mParavirtProvider = data.paravirtProvider;
8921 mHWData->mParavirtDebug = data.strParavirtDebug;
8922 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8923 mHWData->mHPETEnabled = data.fHPETEnabled;
8924
8925 /* VRDEServer */
8926 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8927 if (FAILED(rc)) return rc;
8928
8929 /* BIOS */
8930 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8931 if (FAILED(rc)) return rc;
8932
8933 /* Recording settings */
8934 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8935 if (FAILED(rc)) return rc;
8936
8937 // Bandwidth control (must come before network adapters)
8938 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8939 if (FAILED(rc)) return rc;
8940
8941 /* USB controllers */
8942 for (settings::USBControllerList::const_iterator
8943 it = data.usbSettings.llUSBControllers.begin();
8944 it != data.usbSettings.llUSBControllers.end();
8945 ++it)
8946 {
8947 const settings::USBController &settingsCtrl = *it;
8948 ComObjPtr<USBController> newCtrl;
8949
8950 newCtrl.createObject();
8951 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8952 mUSBControllers->push_back(newCtrl);
8953 }
8954
8955 /* USB device filters */
8956 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8957 if (FAILED(rc)) return rc;
8958
8959 // network adapters (establish array size first and apply defaults, to
8960 // ensure reading the same settings as we saved, since the list skips
8961 // adapters having defaults)
8962 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8963 size_t oldCount = mNetworkAdapters.size();
8964 if (newCount > oldCount)
8965 {
8966 mNetworkAdapters.resize(newCount);
8967 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8968 {
8969 unconst(mNetworkAdapters[slot]).createObject();
8970 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8971 }
8972 }
8973 else if (newCount < oldCount)
8974 mNetworkAdapters.resize(newCount);
8975 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8976 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8977 for (settings::NetworkAdaptersList::const_iterator
8978 it = data.llNetworkAdapters.begin();
8979 it != data.llNetworkAdapters.end();
8980 ++it)
8981 {
8982 const settings::NetworkAdapter &nic = *it;
8983
8984 /* slot uniqueness is guaranteed by XML Schema */
8985 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8986 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8987 if (FAILED(rc)) return rc;
8988 }
8989
8990 // serial ports (establish defaults first, to ensure reading the same
8991 // settings as we saved, since the list skips ports having defaults)
8992 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8993 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8994 for (settings::SerialPortsList::const_iterator
8995 it = data.llSerialPorts.begin();
8996 it != data.llSerialPorts.end();
8997 ++it)
8998 {
8999 const settings::SerialPort &s = *it;
9000
9001 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9002 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9003 if (FAILED(rc)) return rc;
9004 }
9005
9006 // parallel ports (establish defaults first, to ensure reading the same
9007 // settings as we saved, since the list skips ports having defaults)
9008 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9009 mParallelPorts[i]->i_applyDefaults();
9010 for (settings::ParallelPortsList::const_iterator
9011 it = data.llParallelPorts.begin();
9012 it != data.llParallelPorts.end();
9013 ++it)
9014 {
9015 const settings::ParallelPort &p = *it;
9016
9017 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9018 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9019 if (FAILED(rc)) return rc;
9020 }
9021
9022 /* AudioAdapter */
9023 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9024 if (FAILED(rc)) return rc;
9025
9026 /* storage controllers */
9027 rc = i_loadStorageControllers(data.storage,
9028 puuidRegistry,
9029 puuidSnapshot);
9030 if (FAILED(rc)) return rc;
9031
9032 /* Shared folders */
9033 for (settings::SharedFoldersList::const_iterator
9034 it = data.llSharedFolders.begin();
9035 it != data.llSharedFolders.end();
9036 ++it)
9037 {
9038 const settings::SharedFolder &sf = *it;
9039
9040 ComObjPtr<SharedFolder> sharedFolder;
9041 /* Check for double entries. Not allowed! */
9042 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9043 if (SUCCEEDED(rc))
9044 return setError(VBOX_E_OBJECT_IN_USE,
9045 tr("Shared folder named '%s' already exists"),
9046 sf.strName.c_str());
9047
9048 /* Create the new shared folder. Don't break on error. This will be
9049 * reported when the machine starts. */
9050 sharedFolder.createObject();
9051 rc = sharedFolder->init(i_getMachine(),
9052 sf.strName,
9053 sf.strHostPath,
9054 RT_BOOL(sf.fWritable),
9055 RT_BOOL(sf.fAutoMount),
9056 sf.strAutoMountPoint,
9057 false /* fFailOnError */);
9058 if (FAILED(rc)) return rc;
9059 mHWData->mSharedFolders.push_back(sharedFolder);
9060 }
9061
9062 // Clipboard
9063 mHWData->mClipboardMode = data.clipboardMode;
9064
9065 // drag'n'drop
9066 mHWData->mDnDMode = data.dndMode;
9067
9068 // guest settings
9069 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9070
9071 // IO settings
9072 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9073 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9074
9075 // Host PCI devices
9076 for (settings::HostPCIDeviceAttachmentList::const_iterator
9077 it = data.pciAttachments.begin();
9078 it != data.pciAttachments.end();
9079 ++it)
9080 {
9081 const settings::HostPCIDeviceAttachment &hpda = *it;
9082 ComObjPtr<PCIDeviceAttachment> pda;
9083
9084 pda.createObject();
9085 pda->i_loadSettings(this, hpda);
9086 mHWData->mPCIDeviceAssignments.push_back(pda);
9087 }
9088
9089 /*
9090 * (The following isn't really real hardware, but it lives in HWData
9091 * for reasons of convenience.)
9092 */
9093
9094#ifdef VBOX_WITH_GUEST_PROPS
9095 /* Guest properties (optional) */
9096
9097 /* Only load transient guest properties for configs which have saved
9098 * state, because there shouldn't be any for powered off VMs. The same
9099 * logic applies for snapshots, as offline snapshots shouldn't have
9100 * any such properties. They confuse the code in various places.
9101 * Note: can't rely on the machine state, as it isn't set yet. */
9102 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9103 /* apologies for the hacky unconst() usage, but this needs hacking
9104 * actually inconsistent settings into consistency, otherwise there
9105 * will be some corner cases where the inconsistency survives
9106 * surprisingly long without getting fixed, especially for snapshots
9107 * as there are no config changes. */
9108 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9109 for (settings::GuestPropertiesList::iterator
9110 it = llGuestProperties.begin();
9111 it != llGuestProperties.end();
9112 /*nothing*/)
9113 {
9114 const settings::GuestProperty &prop = *it;
9115 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9116 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9117 if ( fSkipTransientGuestProperties
9118 && ( fFlags & GUEST_PROP_F_TRANSIENT
9119 || fFlags & GUEST_PROP_F_TRANSRESET))
9120 {
9121 it = llGuestProperties.erase(it);
9122 continue;
9123 }
9124 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9125 mHWData->mGuestProperties[prop.strName] = property;
9126 ++it;
9127 }
9128#endif /* VBOX_WITH_GUEST_PROPS defined */
9129
9130 rc = i_loadDebugging(pDbg);
9131 if (FAILED(rc))
9132 return rc;
9133
9134 mHWData->mAutostart = *pAutostart;
9135
9136 /* default frontend */
9137 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9138 }
9139 catch (std::bad_alloc &)
9140 {
9141 return E_OUTOFMEMORY;
9142 }
9143
9144 AssertComRC(rc);
9145 return rc;
9146}
9147
9148/**
9149 * Called from i_loadHardware() to load the debugging settings of the
9150 * machine.
9151 *
9152 * @param pDbg Pointer to the settings.
9153 */
9154HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9155{
9156 mHWData->mDebugging = *pDbg;
9157 /* no more processing currently required, this will probably change. */
9158 return S_OK;
9159}
9160
9161/**
9162 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9163 *
9164 * @param data storage settings.
9165 * @param puuidRegistry media registry ID to set media to or NULL;
9166 * see Machine::i_loadMachineDataFromSettings()
9167 * @param puuidSnapshot snapshot ID
9168 * @return
9169 */
9170HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9171 const Guid *puuidRegistry,
9172 const Guid *puuidSnapshot)
9173{
9174 AssertReturn(!i_isSessionMachine(), E_FAIL);
9175
9176 HRESULT rc = S_OK;
9177
9178 for (settings::StorageControllersList::const_iterator
9179 it = data.llStorageControllers.begin();
9180 it != data.llStorageControllers.end();
9181 ++it)
9182 {
9183 const settings::StorageController &ctlData = *it;
9184
9185 ComObjPtr<StorageController> pCtl;
9186 /* Try to find one with the name first. */
9187 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9188 if (SUCCEEDED(rc))
9189 return setError(VBOX_E_OBJECT_IN_USE,
9190 tr("Storage controller named '%s' already exists"),
9191 ctlData.strName.c_str());
9192
9193 pCtl.createObject();
9194 rc = pCtl->init(this,
9195 ctlData.strName,
9196 ctlData.storageBus,
9197 ctlData.ulInstance,
9198 ctlData.fBootable);
9199 if (FAILED(rc)) return rc;
9200
9201 mStorageControllers->push_back(pCtl);
9202
9203 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9204 if (FAILED(rc)) return rc;
9205
9206 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9207 if (FAILED(rc)) return rc;
9208
9209 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9210 if (FAILED(rc)) return rc;
9211
9212 /* Load the attached devices now. */
9213 rc = i_loadStorageDevices(pCtl,
9214 ctlData,
9215 puuidRegistry,
9216 puuidSnapshot);
9217 if (FAILED(rc)) return rc;
9218 }
9219
9220 return S_OK;
9221}
9222
9223/**
9224 * Called from i_loadStorageControllers for a controller's devices.
9225 *
9226 * @param aStorageController
9227 * @param data
9228 * @param puuidRegistry media registry ID to set media to or NULL; see
9229 * Machine::i_loadMachineDataFromSettings()
9230 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9231 * @return
9232 */
9233HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9234 const settings::StorageController &data,
9235 const Guid *puuidRegistry,
9236 const Guid *puuidSnapshot)
9237{
9238 HRESULT rc = S_OK;
9239
9240 /* paranoia: detect duplicate attachments */
9241 for (settings::AttachedDevicesList::const_iterator
9242 it = data.llAttachedDevices.begin();
9243 it != data.llAttachedDevices.end();
9244 ++it)
9245 {
9246 const settings::AttachedDevice &ad = *it;
9247
9248 for (settings::AttachedDevicesList::const_iterator it2 = it;
9249 it2 != data.llAttachedDevices.end();
9250 ++it2)
9251 {
9252 if (it == it2)
9253 continue;
9254
9255 const settings::AttachedDevice &ad2 = *it2;
9256
9257 if ( ad.lPort == ad2.lPort
9258 && ad.lDevice == ad2.lDevice)
9259 {
9260 return setError(E_FAIL,
9261 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9262 aStorageController->i_getName().c_str(),
9263 ad.lPort,
9264 ad.lDevice,
9265 mUserData->s.strName.c_str());
9266 }
9267 }
9268 }
9269
9270 for (settings::AttachedDevicesList::const_iterator
9271 it = data.llAttachedDevices.begin();
9272 it != data.llAttachedDevices.end();
9273 ++it)
9274 {
9275 const settings::AttachedDevice &dev = *it;
9276 ComObjPtr<Medium> medium;
9277
9278 switch (dev.deviceType)
9279 {
9280 case DeviceType_Floppy:
9281 case DeviceType_DVD:
9282 if (dev.strHostDriveSrc.isNotEmpty())
9283 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9284 false /* fRefresh */, medium);
9285 else
9286 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9287 dev.uuid,
9288 false /* fRefresh */,
9289 false /* aSetError */,
9290 medium);
9291 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9292 // This is not an error. The host drive or UUID might have vanished, so just go
9293 // ahead without this removeable medium attachment
9294 rc = S_OK;
9295 break;
9296
9297 case DeviceType_HardDisk:
9298 {
9299 /* find a hard disk by UUID */
9300 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9301 if (FAILED(rc))
9302 {
9303 if (i_isSnapshotMachine())
9304 {
9305 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9306 // so the user knows that the bad disk is in a snapshot somewhere
9307 com::ErrorInfo info;
9308 return setError(E_FAIL,
9309 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9310 puuidSnapshot->raw(),
9311 info.getText().raw());
9312 }
9313 else
9314 return rc;
9315 }
9316
9317 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9318
9319 if (medium->i_getType() == MediumType_Immutable)
9320 {
9321 if (i_isSnapshotMachine())
9322 return setError(E_FAIL,
9323 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9324 "of the virtual machine '%s' ('%s')"),
9325 medium->i_getLocationFull().c_str(),
9326 dev.uuid.raw(),
9327 puuidSnapshot->raw(),
9328 mUserData->s.strName.c_str(),
9329 mData->m_strConfigFileFull.c_str());
9330
9331 return setError(E_FAIL,
9332 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9333 medium->i_getLocationFull().c_str(),
9334 dev.uuid.raw(),
9335 mUserData->s.strName.c_str(),
9336 mData->m_strConfigFileFull.c_str());
9337 }
9338
9339 if (medium->i_getType() == MediumType_MultiAttach)
9340 {
9341 if (i_isSnapshotMachine())
9342 return setError(E_FAIL,
9343 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9344 "of the virtual machine '%s' ('%s')"),
9345 medium->i_getLocationFull().c_str(),
9346 dev.uuid.raw(),
9347 puuidSnapshot->raw(),
9348 mUserData->s.strName.c_str(),
9349 mData->m_strConfigFileFull.c_str());
9350
9351 return setError(E_FAIL,
9352 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9353 medium->i_getLocationFull().c_str(),
9354 dev.uuid.raw(),
9355 mUserData->s.strName.c_str(),
9356 mData->m_strConfigFileFull.c_str());
9357 }
9358
9359 if ( !i_isSnapshotMachine()
9360 && medium->i_getChildren().size() != 0
9361 )
9362 return setError(E_FAIL,
9363 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9364 "because it has %d differencing child hard disks"),
9365 medium->i_getLocationFull().c_str(),
9366 dev.uuid.raw(),
9367 mUserData->s.strName.c_str(),
9368 mData->m_strConfigFileFull.c_str(),
9369 medium->i_getChildren().size());
9370
9371 if (i_findAttachment(*mMediumAttachments.data(),
9372 medium))
9373 return setError(E_FAIL,
9374 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9375 medium->i_getLocationFull().c_str(),
9376 dev.uuid.raw(),
9377 mUserData->s.strName.c_str(),
9378 mData->m_strConfigFileFull.c_str());
9379
9380 break;
9381 }
9382
9383 default:
9384 return setError(E_FAIL,
9385 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9386 medium->i_getLocationFull().c_str(),
9387 mUserData->s.strName.c_str(),
9388 mData->m_strConfigFileFull.c_str());
9389 }
9390
9391 if (FAILED(rc))
9392 break;
9393
9394 /* Bandwidth groups are loaded at this point. */
9395 ComObjPtr<BandwidthGroup> pBwGroup;
9396
9397 if (!dev.strBwGroup.isEmpty())
9398 {
9399 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9400 if (FAILED(rc))
9401 return setError(E_FAIL,
9402 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9403 medium->i_getLocationFull().c_str(),
9404 dev.strBwGroup.c_str(),
9405 mUserData->s.strName.c_str(),
9406 mData->m_strConfigFileFull.c_str());
9407 pBwGroup->i_reference();
9408 }
9409
9410 const Utf8Str controllerName = aStorageController->i_getName();
9411 ComObjPtr<MediumAttachment> pAttachment;
9412 pAttachment.createObject();
9413 rc = pAttachment->init(this,
9414 medium,
9415 controllerName,
9416 dev.lPort,
9417 dev.lDevice,
9418 dev.deviceType,
9419 false,
9420 dev.fPassThrough,
9421 dev.fTempEject,
9422 dev.fNonRotational,
9423 dev.fDiscard,
9424 dev.fHotPluggable,
9425 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9426 if (FAILED(rc)) break;
9427
9428 /* associate the medium with this machine and snapshot */
9429 if (!medium.isNull())
9430 {
9431 AutoCaller medCaller(medium);
9432 if (FAILED(medCaller.rc())) return medCaller.rc();
9433 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9434
9435 if (i_isSnapshotMachine())
9436 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9437 else
9438 rc = medium->i_addBackReference(mData->mUuid);
9439 /* If the medium->addBackReference fails it sets an appropriate
9440 * error message, so no need to do any guesswork here. */
9441
9442 if (puuidRegistry)
9443 // caller wants registry ID to be set on all attached media (OVF import case)
9444 medium->i_addRegistry(*puuidRegistry);
9445 }
9446
9447 if (FAILED(rc))
9448 break;
9449
9450 /* back up mMediumAttachments to let registeredInit() properly rollback
9451 * on failure (= limited accessibility) */
9452 i_setModified(IsModified_Storage);
9453 mMediumAttachments.backup();
9454 mMediumAttachments->push_back(pAttachment);
9455 }
9456
9457 return rc;
9458}
9459
9460/**
9461 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9462 *
9463 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9464 * @param aSnapshot where to return the found snapshot
9465 * @param aSetError true to set extended error info on failure
9466 */
9467HRESULT Machine::i_findSnapshotById(const Guid &aId,
9468 ComObjPtr<Snapshot> &aSnapshot,
9469 bool aSetError /* = false */)
9470{
9471 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9472
9473 if (!mData->mFirstSnapshot)
9474 {
9475 if (aSetError)
9476 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9477 return E_FAIL;
9478 }
9479
9480 if (aId.isZero())
9481 aSnapshot = mData->mFirstSnapshot;
9482 else
9483 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9484
9485 if (!aSnapshot)
9486 {
9487 if (aSetError)
9488 return setError(E_FAIL,
9489 tr("Could not find a snapshot with UUID {%s}"),
9490 aId.toString().c_str());
9491 return E_FAIL;
9492 }
9493
9494 return S_OK;
9495}
9496
9497/**
9498 * Returns the snapshot with the given name or fails of no such snapshot.
9499 *
9500 * @param strName snapshot name to find
9501 * @param aSnapshot where to return the found snapshot
9502 * @param aSetError true to set extended error info on failure
9503 */
9504HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9505 ComObjPtr<Snapshot> &aSnapshot,
9506 bool aSetError /* = false */)
9507{
9508 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9509
9510 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9511
9512 if (!mData->mFirstSnapshot)
9513 {
9514 if (aSetError)
9515 return setError(VBOX_E_OBJECT_NOT_FOUND,
9516 tr("This machine does not have any snapshots"));
9517 return VBOX_E_OBJECT_NOT_FOUND;
9518 }
9519
9520 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9521
9522 if (!aSnapshot)
9523 {
9524 if (aSetError)
9525 return setError(VBOX_E_OBJECT_NOT_FOUND,
9526 tr("Could not find a snapshot named '%s'"), strName.c_str());
9527 return VBOX_E_OBJECT_NOT_FOUND;
9528 }
9529
9530 return S_OK;
9531}
9532
9533/**
9534 * Returns a storage controller object with the given name.
9535 *
9536 * @param aName storage controller name to find
9537 * @param aStorageController where to return the found storage controller
9538 * @param aSetError true to set extended error info on failure
9539 */
9540HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9541 ComObjPtr<StorageController> &aStorageController,
9542 bool aSetError /* = false */)
9543{
9544 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9545
9546 for (StorageControllerList::const_iterator
9547 it = mStorageControllers->begin();
9548 it != mStorageControllers->end();
9549 ++it)
9550 {
9551 if ((*it)->i_getName() == aName)
9552 {
9553 aStorageController = (*it);
9554 return S_OK;
9555 }
9556 }
9557
9558 if (aSetError)
9559 return setError(VBOX_E_OBJECT_NOT_FOUND,
9560 tr("Could not find a storage controller named '%s'"),
9561 aName.c_str());
9562 return VBOX_E_OBJECT_NOT_FOUND;
9563}
9564
9565/**
9566 * Returns a USB controller object with the given name.
9567 *
9568 * @param aName USB controller name to find
9569 * @param aUSBController where to return the found USB controller
9570 * @param aSetError true to set extended error info on failure
9571 */
9572HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9573 ComObjPtr<USBController> &aUSBController,
9574 bool aSetError /* = false */)
9575{
9576 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9577
9578 for (USBControllerList::const_iterator
9579 it = mUSBControllers->begin();
9580 it != mUSBControllers->end();
9581 ++it)
9582 {
9583 if ((*it)->i_getName() == aName)
9584 {
9585 aUSBController = (*it);
9586 return S_OK;
9587 }
9588 }
9589
9590 if (aSetError)
9591 return setError(VBOX_E_OBJECT_NOT_FOUND,
9592 tr("Could not find a storage controller named '%s'"),
9593 aName.c_str());
9594 return VBOX_E_OBJECT_NOT_FOUND;
9595}
9596
9597/**
9598 * Returns the number of USB controller instance of the given type.
9599 *
9600 * @param enmType USB controller type.
9601 */
9602ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9603{
9604 ULONG cCtrls = 0;
9605
9606 for (USBControllerList::const_iterator
9607 it = mUSBControllers->begin();
9608 it != mUSBControllers->end();
9609 ++it)
9610 {
9611 if ((*it)->i_getControllerType() == enmType)
9612 cCtrls++;
9613 }
9614
9615 return cCtrls;
9616}
9617
9618HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9619 MediumAttachmentList &atts)
9620{
9621 AutoCaller autoCaller(this);
9622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9623
9624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9625
9626 for (MediumAttachmentList::const_iterator
9627 it = mMediumAttachments->begin();
9628 it != mMediumAttachments->end();
9629 ++it)
9630 {
9631 const ComObjPtr<MediumAttachment> &pAtt = *it;
9632 // should never happen, but deal with NULL pointers in the list.
9633 AssertContinue(!pAtt.isNull());
9634
9635 // getControllerName() needs caller+read lock
9636 AutoCaller autoAttCaller(pAtt);
9637 if (FAILED(autoAttCaller.rc()))
9638 {
9639 atts.clear();
9640 return autoAttCaller.rc();
9641 }
9642 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9643
9644 if (pAtt->i_getControllerName() == aName)
9645 atts.push_back(pAtt);
9646 }
9647
9648 return S_OK;
9649}
9650
9651
9652/**
9653 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9654 * file if the machine name was changed and about creating a new settings file
9655 * if this is a new machine.
9656 *
9657 * @note Must be never called directly but only from #saveSettings().
9658 */
9659HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9660{
9661 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9662
9663 HRESULT rc = S_OK;
9664
9665 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9666
9667 /// @todo need to handle primary group change, too
9668
9669 /* attempt to rename the settings file if machine name is changed */
9670 if ( mUserData->s.fNameSync
9671 && mUserData.isBackedUp()
9672 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9673 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9674 )
9675 {
9676 bool dirRenamed = false;
9677 bool fileRenamed = false;
9678
9679 Utf8Str configFile, newConfigFile;
9680 Utf8Str configFilePrev, newConfigFilePrev;
9681 Utf8Str configDir, newConfigDir;
9682
9683 do
9684 {
9685 int vrc = VINF_SUCCESS;
9686
9687 Utf8Str name = mUserData.backedUpData()->s.strName;
9688 Utf8Str newName = mUserData->s.strName;
9689 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9690 if (group == "/")
9691 group.setNull();
9692 Utf8Str newGroup = mUserData->s.llGroups.front();
9693 if (newGroup == "/")
9694 newGroup.setNull();
9695
9696 configFile = mData->m_strConfigFileFull;
9697
9698 /* first, rename the directory if it matches the group and machine name */
9699 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9700 group.c_str(), RTPATH_DELIMITER, name.c_str());
9701 /** @todo hack, make somehow use of ComposeMachineFilename */
9702 if (mUserData->s.fDirectoryIncludesUUID)
9703 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9704 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9705 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9706 /** @todo hack, make somehow use of ComposeMachineFilename */
9707 if (mUserData->s.fDirectoryIncludesUUID)
9708 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9709 configDir = configFile;
9710 configDir.stripFilename();
9711 newConfigDir = configDir;
9712 if ( configDir.length() >= groupPlusName.length()
9713 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9714 groupPlusName.c_str()))
9715 {
9716 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9717 Utf8Str newConfigBaseDir(newConfigDir);
9718 newConfigDir.append(newGroupPlusName);
9719 /* consistency: use \ if appropriate on the platform */
9720 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9721 /* new dir and old dir cannot be equal here because of 'if'
9722 * above and because name != newName */
9723 Assert(configDir != newConfigDir);
9724 if (!fSettingsFileIsNew)
9725 {
9726 /* perform real rename only if the machine is not new */
9727 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9728 if ( vrc == VERR_FILE_NOT_FOUND
9729 || vrc == VERR_PATH_NOT_FOUND)
9730 {
9731 /* create the parent directory, then retry renaming */
9732 Utf8Str parent(newConfigDir);
9733 parent.stripFilename();
9734 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9735 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9736 }
9737 if (RT_FAILURE(vrc))
9738 {
9739 rc = setErrorBoth(E_FAIL, vrc,
9740 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9741 configDir.c_str(),
9742 newConfigDir.c_str(),
9743 vrc);
9744 break;
9745 }
9746 /* delete subdirectories which are no longer needed */
9747 Utf8Str dir(configDir);
9748 dir.stripFilename();
9749 while (dir != newConfigBaseDir && dir != ".")
9750 {
9751 vrc = RTDirRemove(dir.c_str());
9752 if (RT_FAILURE(vrc))
9753 break;
9754 dir.stripFilename();
9755 }
9756 dirRenamed = true;
9757 }
9758 }
9759
9760 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9761 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9762
9763 /* then try to rename the settings file itself */
9764 if (newConfigFile != configFile)
9765 {
9766 /* get the path to old settings file in renamed directory */
9767 configFile = Utf8StrFmt("%s%c%s",
9768 newConfigDir.c_str(),
9769 RTPATH_DELIMITER,
9770 RTPathFilename(configFile.c_str()));
9771 if (!fSettingsFileIsNew)
9772 {
9773 /* perform real rename only if the machine is not new */
9774 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9775 if (RT_FAILURE(vrc))
9776 {
9777 rc = setErrorBoth(E_FAIL, vrc,
9778 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9779 configFile.c_str(),
9780 newConfigFile.c_str(),
9781 vrc);
9782 break;
9783 }
9784 fileRenamed = true;
9785 configFilePrev = configFile;
9786 configFilePrev += "-prev";
9787 newConfigFilePrev = newConfigFile;
9788 newConfigFilePrev += "-prev";
9789 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9790 }
9791 }
9792
9793 // update m_strConfigFileFull amd mConfigFile
9794 mData->m_strConfigFileFull = newConfigFile;
9795 // compute the relative path too
9796 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9797
9798 // store the old and new so that VirtualBox::i_saveSettings() can update
9799 // the media registry
9800 if ( mData->mRegistered
9801 && (configDir != newConfigDir || configFile != newConfigFile))
9802 {
9803 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9804
9805 if (pfNeedsGlobalSaveSettings)
9806 *pfNeedsGlobalSaveSettings = true;
9807 }
9808
9809 // in the saved state file path, replace the old directory with the new directory
9810 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9811 {
9812 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9813 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9814 }
9815
9816 // and do the same thing for the saved state file paths of all the online snapshots
9817 if (mData->mFirstSnapshot)
9818 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9819 newConfigDir.c_str());
9820 }
9821 while (0);
9822
9823 if (FAILED(rc))
9824 {
9825 /* silently try to rename everything back */
9826 if (fileRenamed)
9827 {
9828 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9829 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9830 }
9831 if (dirRenamed)
9832 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9833 }
9834
9835 if (FAILED(rc)) return rc;
9836 }
9837
9838 if (fSettingsFileIsNew)
9839 {
9840 /* create a virgin config file */
9841 int vrc = VINF_SUCCESS;
9842
9843 /* ensure the settings directory exists */
9844 Utf8Str path(mData->m_strConfigFileFull);
9845 path.stripFilename();
9846 if (!RTDirExists(path.c_str()))
9847 {
9848 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9849 if (RT_FAILURE(vrc))
9850 {
9851 return setErrorBoth(E_FAIL, vrc,
9852 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9853 path.c_str(),
9854 vrc);
9855 }
9856 }
9857
9858 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9859 path = Utf8Str(mData->m_strConfigFileFull);
9860 RTFILE f = NIL_RTFILE;
9861 vrc = RTFileOpen(&f, path.c_str(),
9862 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9863 if (RT_FAILURE(vrc))
9864 return setErrorBoth(E_FAIL, vrc,
9865 tr("Could not create the settings file '%s' (%Rrc)"),
9866 path.c_str(),
9867 vrc);
9868 RTFileClose(f);
9869 }
9870
9871 return rc;
9872}
9873
9874/**
9875 * Saves and commits machine data, user data and hardware data.
9876 *
9877 * Note that on failure, the data remains uncommitted.
9878 *
9879 * @a aFlags may combine the following flags:
9880 *
9881 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9882 * Used when saving settings after an operation that makes them 100%
9883 * correspond to the settings from the current snapshot.
9884 * - SaveS_Force: settings will be saved without doing a deep compare of the
9885 * settings structures. This is used when this is called because snapshots
9886 * have changed to avoid the overhead of the deep compare.
9887 *
9888 * @note Must be called from under this object's write lock. Locks children for
9889 * writing.
9890 *
9891 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9892 * initialized to false and that will be set to true by this function if
9893 * the caller must invoke VirtualBox::i_saveSettings() because the global
9894 * settings have changed. This will happen if a machine rename has been
9895 * saved and the global machine and media registries will therefore need
9896 * updating.
9897 * @param aFlags Flags.
9898 */
9899HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9900 int aFlags /*= 0*/)
9901{
9902 LogFlowThisFuncEnter();
9903
9904 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9905
9906 /* make sure child objects are unable to modify the settings while we are
9907 * saving them */
9908 i_ensureNoStateDependencies();
9909
9910 AssertReturn(!i_isSnapshotMachine(),
9911 E_FAIL);
9912
9913 HRESULT rc = S_OK;
9914 bool fNeedsWrite = false;
9915
9916 /* First, prepare to save settings. It will care about renaming the
9917 * settings directory and file if the machine name was changed and about
9918 * creating a new settings file if this is a new machine. */
9919 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9920 if (FAILED(rc)) return rc;
9921
9922 // keep a pointer to the current settings structures
9923 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9924 settings::MachineConfigFile *pNewConfig = NULL;
9925
9926 try
9927 {
9928 // make a fresh one to have everyone write stuff into
9929 pNewConfig = new settings::MachineConfigFile(NULL);
9930 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9931
9932 // now go and copy all the settings data from COM to the settings structures
9933 // (this calls i_saveSettings() on all the COM objects in the machine)
9934 i_copyMachineDataToSettings(*pNewConfig);
9935
9936 if (aFlags & SaveS_ResetCurStateModified)
9937 {
9938 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9939 mData->mCurrentStateModified = FALSE;
9940 fNeedsWrite = true; // always, no need to compare
9941 }
9942 else if (aFlags & SaveS_Force)
9943 {
9944 fNeedsWrite = true; // always, no need to compare
9945 }
9946 else
9947 {
9948 if (!mData->mCurrentStateModified)
9949 {
9950 // do a deep compare of the settings that we just saved with the settings
9951 // previously stored in the config file; this invokes MachineConfigFile::operator==
9952 // which does a deep compare of all the settings, which is expensive but less expensive
9953 // than writing out XML in vain
9954 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9955
9956 // could still be modified if any settings changed
9957 mData->mCurrentStateModified = fAnySettingsChanged;
9958
9959 fNeedsWrite = fAnySettingsChanged;
9960 }
9961 else
9962 fNeedsWrite = true;
9963 }
9964
9965 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9966
9967 if (fNeedsWrite)
9968 // now spit it all out!
9969 pNewConfig->write(mData->m_strConfigFileFull);
9970
9971 mData->pMachineConfigFile = pNewConfig;
9972 delete pOldConfig;
9973 i_commit();
9974
9975 // after saving settings, we are no longer different from the XML on disk
9976 mData->flModifications = 0;
9977 }
9978 catch (HRESULT err)
9979 {
9980 // we assume that error info is set by the thrower
9981 rc = err;
9982
9983 // restore old config
9984 delete pNewConfig;
9985 mData->pMachineConfigFile = pOldConfig;
9986 }
9987 catch (...)
9988 {
9989 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9990 }
9991
9992 if (fNeedsWrite)
9993 {
9994 /* Fire the data change event, even on failure (since we've already
9995 * committed all data). This is done only for SessionMachines because
9996 * mutable Machine instances are always not registered (i.e. private
9997 * to the client process that creates them) and thus don't need to
9998 * inform callbacks. */
9999 if (i_isSessionMachine())
10000 mParent->i_onMachineDataChange(mData->mUuid);
10001 }
10002
10003 LogFlowThisFunc(("rc=%08X\n", rc));
10004 LogFlowThisFuncLeave();
10005 return rc;
10006}
10007
10008/**
10009 * Implementation for saving the machine settings into the given
10010 * settings::MachineConfigFile instance. This copies machine extradata
10011 * from the previous machine config file in the instance data, if any.
10012 *
10013 * This gets called from two locations:
10014 *
10015 * -- Machine::i_saveSettings(), during the regular XML writing;
10016 *
10017 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10018 * exported to OVF and we write the VirtualBox proprietary XML
10019 * into a <vbox:Machine> tag.
10020 *
10021 * This routine fills all the fields in there, including snapshots, *except*
10022 * for the following:
10023 *
10024 * -- fCurrentStateModified. There is some special logic associated with that.
10025 *
10026 * The caller can then call MachineConfigFile::write() or do something else
10027 * with it.
10028 *
10029 * Caller must hold the machine lock!
10030 *
10031 * This throws XML errors and HRESULT, so the caller must have a catch block!
10032 */
10033void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10034{
10035 // deep copy extradata, being extra careful with self assignment (the STL
10036 // map assignment on Mac OS X clang based Xcode isn't checking)
10037 if (&config != mData->pMachineConfigFile)
10038 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10039
10040 config.uuid = mData->mUuid;
10041
10042 // copy name, description, OS type, teleport, UTC etc.
10043 config.machineUserData = mUserData->s;
10044
10045 if ( mData->mMachineState == MachineState_Saved
10046 || mData->mMachineState == MachineState_Restoring
10047 // when doing certain snapshot operations we may or may not have
10048 // a saved state in the current state, so keep everything as is
10049 || ( ( mData->mMachineState == MachineState_Snapshotting
10050 || mData->mMachineState == MachineState_DeletingSnapshot
10051 || mData->mMachineState == MachineState_RestoringSnapshot)
10052 && (!mSSData->strStateFilePath.isEmpty())
10053 )
10054 )
10055 {
10056 Assert(!mSSData->strStateFilePath.isEmpty());
10057 /* try to make the file name relative to the settings file dir */
10058 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10059 }
10060 else
10061 {
10062 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10063 config.strStateFile.setNull();
10064 }
10065
10066 if (mData->mCurrentSnapshot)
10067 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10068 else
10069 config.uuidCurrentSnapshot.clear();
10070
10071 config.timeLastStateChange = mData->mLastStateChange;
10072 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10073 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10074
10075 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10076 if (FAILED(rc)) throw rc;
10077
10078 // save machine's media registry if this is VirtualBox 4.0 or later
10079 if (config.canHaveOwnMediaRegistry())
10080 {
10081 // determine machine folder
10082 Utf8Str strMachineFolder = i_getSettingsFileFull();
10083 strMachineFolder.stripFilename();
10084 mParent->i_saveMediaRegistry(config.mediaRegistry,
10085 i_getId(), // only media with registry ID == machine UUID
10086 strMachineFolder);
10087 // this throws HRESULT
10088 }
10089
10090 // save snapshots
10091 rc = i_saveAllSnapshots(config);
10092 if (FAILED(rc)) throw rc;
10093}
10094
10095/**
10096 * Saves all snapshots of the machine into the given machine config file. Called
10097 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10098 * @param config
10099 * @return
10100 */
10101HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10102{
10103 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10104
10105 HRESULT rc = S_OK;
10106
10107 try
10108 {
10109 config.llFirstSnapshot.clear();
10110
10111 if (mData->mFirstSnapshot)
10112 {
10113 // the settings use a list for "the first snapshot"
10114 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10115
10116 // get reference to the snapshot on the list and work on that
10117 // element straight in the list to avoid excessive copying later
10118 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10119 if (FAILED(rc)) throw rc;
10120 }
10121
10122// if (mType == IsSessionMachine)
10123// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10124
10125 }
10126 catch (HRESULT err)
10127 {
10128 /* we assume that error info is set by the thrower */
10129 rc = err;
10130 }
10131 catch (...)
10132 {
10133 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10134 }
10135
10136 return rc;
10137}
10138
10139/**
10140 * Saves the VM hardware configuration. It is assumed that the
10141 * given node is empty.
10142 *
10143 * @param data Reference to the settings object for the hardware config.
10144 * @param pDbg Pointer to the settings object for the debugging config
10145 * which happens to live in mHWData.
10146 * @param pAutostart Pointer to the settings object for the autostart config
10147 * which happens to live in mHWData.
10148 */
10149HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10150 settings::Autostart *pAutostart)
10151{
10152 HRESULT rc = S_OK;
10153
10154 try
10155 {
10156 /* The hardware version attribute (optional).
10157 Automatically upgrade from 1 to current default hardware version
10158 when there is no saved state. (ugly!) */
10159 if ( mHWData->mHWVersion == "1"
10160 && mSSData->strStateFilePath.isEmpty()
10161 )
10162 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10163
10164 data.strVersion = mHWData->mHWVersion;
10165 data.uuid = mHWData->mHardwareUUID;
10166
10167 // CPU
10168 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10169 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10170 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10171 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10172 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10173 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10174 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10175 data.fPAE = !!mHWData->mPAEEnabled;
10176 data.enmLongMode = mHWData->mLongMode;
10177 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10178 data.fAPIC = !!mHWData->mAPIC;
10179 data.fX2APIC = !!mHWData->mX2APIC;
10180 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10181 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10182 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10183 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10184 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10185 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10186 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10187 data.cCPUs = mHWData->mCPUCount;
10188 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10189 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10190 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10191 data.strCpuProfile = mHWData->mCpuProfile;
10192
10193 data.llCpus.clear();
10194 if (data.fCpuHotPlug)
10195 {
10196 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10197 {
10198 if (mHWData->mCPUAttached[idx])
10199 {
10200 settings::Cpu cpu;
10201 cpu.ulId = idx;
10202 data.llCpus.push_back(cpu);
10203 }
10204 }
10205 }
10206
10207 /* Standard and Extended CPUID leafs. */
10208 data.llCpuIdLeafs.clear();
10209 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10210
10211 // memory
10212 data.ulMemorySizeMB = mHWData->mMemorySize;
10213 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10214
10215 // firmware
10216 data.firmwareType = mHWData->mFirmwareType;
10217
10218 // HID
10219 data.pointingHIDType = mHWData->mPointingHIDType;
10220 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10221
10222 // chipset
10223 data.chipsetType = mHWData->mChipsetType;
10224
10225 // paravirt
10226 data.paravirtProvider = mHWData->mParavirtProvider;
10227 data.strParavirtDebug = mHWData->mParavirtDebug;
10228
10229 // emulated USB card reader
10230 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10231
10232 // HPET
10233 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10234
10235 // boot order
10236 data.mapBootOrder.clear();
10237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10238 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10239
10240 // display
10241 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10242 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10243 data.cMonitors = mHWData->mMonitorCount;
10244 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10245 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10246
10247 /* VRDEServer settings (optional) */
10248 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10249 if (FAILED(rc)) throw rc;
10250
10251 /* BIOS settings (required) */
10252 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10253 if (FAILED(rc)) throw rc;
10254
10255 /* Recording settings (required) */
10256 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10257 if (FAILED(rc)) throw rc;
10258
10259 /* USB Controller (required) */
10260 data.usbSettings.llUSBControllers.clear();
10261 for (USBControllerList::const_iterator
10262 it = mUSBControllers->begin();
10263 it != mUSBControllers->end();
10264 ++it)
10265 {
10266 ComObjPtr<USBController> ctrl = *it;
10267 settings::USBController settingsCtrl;
10268
10269 settingsCtrl.strName = ctrl->i_getName();
10270 settingsCtrl.enmType = ctrl->i_getControllerType();
10271
10272 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10273 }
10274
10275 /* USB device filters (required) */
10276 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10277 if (FAILED(rc)) throw rc;
10278
10279 /* Network adapters (required) */
10280 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10281 data.llNetworkAdapters.clear();
10282 /* Write out only the nominal number of network adapters for this
10283 * chipset type. Since Machine::commit() hasn't been called there
10284 * may be extra NIC settings in the vector. */
10285 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10286 {
10287 settings::NetworkAdapter nic;
10288 nic.ulSlot = (uint32_t)slot;
10289 /* paranoia check... must not be NULL, but must not crash either. */
10290 if (mNetworkAdapters[slot])
10291 {
10292 if (mNetworkAdapters[slot]->i_hasDefaults())
10293 continue;
10294
10295 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10296 if (FAILED(rc)) throw rc;
10297
10298 data.llNetworkAdapters.push_back(nic);
10299 }
10300 }
10301
10302 /* Serial ports */
10303 data.llSerialPorts.clear();
10304 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10305 {
10306 if (mSerialPorts[slot]->i_hasDefaults())
10307 continue;
10308
10309 settings::SerialPort s;
10310 s.ulSlot = slot;
10311 rc = mSerialPorts[slot]->i_saveSettings(s);
10312 if (FAILED(rc)) return rc;
10313
10314 data.llSerialPorts.push_back(s);
10315 }
10316
10317 /* Parallel ports */
10318 data.llParallelPorts.clear();
10319 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10320 {
10321 if (mParallelPorts[slot]->i_hasDefaults())
10322 continue;
10323
10324 settings::ParallelPort p;
10325 p.ulSlot = slot;
10326 rc = mParallelPorts[slot]->i_saveSettings(p);
10327 if (FAILED(rc)) return rc;
10328
10329 data.llParallelPorts.push_back(p);
10330 }
10331
10332 /* Audio adapter */
10333 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10334 if (FAILED(rc)) return rc;
10335
10336 rc = i_saveStorageControllers(data.storage);
10337 if (FAILED(rc)) return rc;
10338
10339 /* Shared folders */
10340 data.llSharedFolders.clear();
10341 for (HWData::SharedFolderList::const_iterator
10342 it = mHWData->mSharedFolders.begin();
10343 it != mHWData->mSharedFolders.end();
10344 ++it)
10345 {
10346 SharedFolder *pSF = *it;
10347 AutoCaller sfCaller(pSF);
10348 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10349 settings::SharedFolder sf;
10350 sf.strName = pSF->i_getName();
10351 sf.strHostPath = pSF->i_getHostPath();
10352 sf.fWritable = !!pSF->i_isWritable();
10353 sf.fAutoMount = !!pSF->i_isAutoMounted();
10354 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10355
10356 data.llSharedFolders.push_back(sf);
10357 }
10358
10359 // clipboard
10360 data.clipboardMode = mHWData->mClipboardMode;
10361
10362 // drag'n'drop
10363 data.dndMode = mHWData->mDnDMode;
10364
10365 /* Guest */
10366 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10367
10368 // IO settings
10369 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10370 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10371
10372 /* BandwidthControl (required) */
10373 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10374 if (FAILED(rc)) throw rc;
10375
10376 /* Host PCI devices */
10377 data.pciAttachments.clear();
10378 for (HWData::PCIDeviceAssignmentList::const_iterator
10379 it = mHWData->mPCIDeviceAssignments.begin();
10380 it != mHWData->mPCIDeviceAssignments.end();
10381 ++it)
10382 {
10383 ComObjPtr<PCIDeviceAttachment> pda = *it;
10384 settings::HostPCIDeviceAttachment hpda;
10385
10386 rc = pda->i_saveSettings(hpda);
10387 if (FAILED(rc)) throw rc;
10388
10389 data.pciAttachments.push_back(hpda);
10390 }
10391
10392 // guest properties
10393 data.llGuestProperties.clear();
10394#ifdef VBOX_WITH_GUEST_PROPS
10395 for (HWData::GuestPropertyMap::const_iterator
10396 it = mHWData->mGuestProperties.begin();
10397 it != mHWData->mGuestProperties.end();
10398 ++it)
10399 {
10400 HWData::GuestProperty property = it->second;
10401
10402 /* Remove transient guest properties at shutdown unless we
10403 * are saving state. Note that restoring snapshot intentionally
10404 * keeps them, they will be removed if appropriate once the final
10405 * machine state is set (as crashes etc. need to work). */
10406 if ( ( mData->mMachineState == MachineState_PoweredOff
10407 || mData->mMachineState == MachineState_Aborted
10408 || mData->mMachineState == MachineState_Teleported)
10409 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10410 continue;
10411 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10412 prop.strName = it->first;
10413 prop.strValue = property.strValue;
10414 prop.timestamp = property.mTimestamp;
10415 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10416 GuestPropWriteFlags(property.mFlags, szFlags);
10417 prop.strFlags = szFlags;
10418
10419 data.llGuestProperties.push_back(prop);
10420 }
10421
10422 /* I presume this doesn't require a backup(). */
10423 mData->mGuestPropertiesModified = FALSE;
10424#endif /* VBOX_WITH_GUEST_PROPS defined */
10425
10426 *pDbg = mHWData->mDebugging;
10427 *pAutostart = mHWData->mAutostart;
10428
10429 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10430 }
10431 catch (std::bad_alloc &)
10432 {
10433 return E_OUTOFMEMORY;
10434 }
10435
10436 AssertComRC(rc);
10437 return rc;
10438}
10439
10440/**
10441 * Saves the storage controller configuration.
10442 *
10443 * @param data storage settings.
10444 */
10445HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10446{
10447 data.llStorageControllers.clear();
10448
10449 for (StorageControllerList::const_iterator
10450 it = mStorageControllers->begin();
10451 it != mStorageControllers->end();
10452 ++it)
10453 {
10454 HRESULT rc;
10455 ComObjPtr<StorageController> pCtl = *it;
10456
10457 settings::StorageController ctl;
10458 ctl.strName = pCtl->i_getName();
10459 ctl.controllerType = pCtl->i_getControllerType();
10460 ctl.storageBus = pCtl->i_getStorageBus();
10461 ctl.ulInstance = pCtl->i_getInstance();
10462 ctl.fBootable = pCtl->i_getBootable();
10463
10464 /* Save the port count. */
10465 ULONG portCount;
10466 rc = pCtl->COMGETTER(PortCount)(&portCount);
10467 ComAssertComRCRet(rc, rc);
10468 ctl.ulPortCount = portCount;
10469
10470 /* Save fUseHostIOCache */
10471 BOOL fUseHostIOCache;
10472 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10473 ComAssertComRCRet(rc, rc);
10474 ctl.fUseHostIOCache = !!fUseHostIOCache;
10475
10476 /* save the devices now. */
10477 rc = i_saveStorageDevices(pCtl, ctl);
10478 ComAssertComRCRet(rc, rc);
10479
10480 data.llStorageControllers.push_back(ctl);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Saves the hard disk configuration.
10488 */
10489HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10490 settings::StorageController &data)
10491{
10492 MediumAttachmentList atts;
10493
10494 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10495 if (FAILED(rc)) return rc;
10496
10497 data.llAttachedDevices.clear();
10498 for (MediumAttachmentList::const_iterator
10499 it = atts.begin();
10500 it != atts.end();
10501 ++it)
10502 {
10503 settings::AttachedDevice dev;
10504 IMediumAttachment *iA = *it;
10505 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10506 Medium *pMedium = pAttach->i_getMedium();
10507
10508 dev.deviceType = pAttach->i_getType();
10509 dev.lPort = pAttach->i_getPort();
10510 dev.lDevice = pAttach->i_getDevice();
10511 dev.fPassThrough = pAttach->i_getPassthrough();
10512 dev.fHotPluggable = pAttach->i_getHotPluggable();
10513 if (pMedium)
10514 {
10515 if (pMedium->i_isHostDrive())
10516 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10517 else
10518 dev.uuid = pMedium->i_getId();
10519 dev.fTempEject = pAttach->i_getTempEject();
10520 dev.fNonRotational = pAttach->i_getNonRotational();
10521 dev.fDiscard = pAttach->i_getDiscard();
10522 }
10523
10524 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10525
10526 data.llAttachedDevices.push_back(dev);
10527 }
10528
10529 return S_OK;
10530}
10531
10532/**
10533 * Saves machine state settings as defined by aFlags
10534 * (SaveSTS_* values).
10535 *
10536 * @param aFlags Combination of SaveSTS_* flags.
10537 *
10538 * @note Locks objects for writing.
10539 */
10540HRESULT Machine::i_saveStateSettings(int aFlags)
10541{
10542 if (aFlags == 0)
10543 return S_OK;
10544
10545 AutoCaller autoCaller(this);
10546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10547
10548 /* This object's write lock is also necessary to serialize file access
10549 * (prevent concurrent reads and writes) */
10550 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10551
10552 HRESULT rc = S_OK;
10553
10554 Assert(mData->pMachineConfigFile);
10555
10556 try
10557 {
10558 if (aFlags & SaveSTS_CurStateModified)
10559 mData->pMachineConfigFile->fCurrentStateModified = true;
10560
10561 if (aFlags & SaveSTS_StateFilePath)
10562 {
10563 if (!mSSData->strStateFilePath.isEmpty())
10564 /* try to make the file name relative to the settings file dir */
10565 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10566 else
10567 mData->pMachineConfigFile->strStateFile.setNull();
10568 }
10569
10570 if (aFlags & SaveSTS_StateTimeStamp)
10571 {
10572 Assert( mData->mMachineState != MachineState_Aborted
10573 || mSSData->strStateFilePath.isEmpty());
10574
10575 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10576
10577 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10578/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10579 }
10580
10581 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10582 }
10583 catch (...)
10584 {
10585 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10586 }
10587
10588 return rc;
10589}
10590
10591/**
10592 * Ensures that the given medium is added to a media registry. If this machine
10593 * was created with 4.0 or later, then the machine registry is used. Otherwise
10594 * the global VirtualBox media registry is used.
10595 *
10596 * Caller must NOT hold machine lock, media tree or any medium locks!
10597 *
10598 * @param pMedium
10599 */
10600void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10601{
10602 /* Paranoia checks: do not hold machine or media tree locks. */
10603 AssertReturnVoid(!isWriteLockOnCurrentThread());
10604 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10605
10606 ComObjPtr<Medium> pBase;
10607 {
10608 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10609 pBase = pMedium->i_getBase();
10610 }
10611
10612 /* Paranoia checks: do not hold medium locks. */
10613 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10614 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10615
10616 // decide which medium registry to use now that the medium is attached:
10617 Guid uuid;
10618 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10619 if (fCanHaveOwnMediaRegistry)
10620 // machine XML is VirtualBox 4.0 or higher:
10621 uuid = i_getId(); // machine UUID
10622 else
10623 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10624
10625 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10626 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10627 if (pMedium->i_addRegistry(uuid))
10628 mParent->i_markRegistryModified(uuid);
10629
10630 /* For more complex hard disk structures it can happen that the base
10631 * medium isn't yet associated with any medium registry. Do that now. */
10632 if (pMedium != pBase)
10633 {
10634 /* Tree lock needed by Medium::addRegistry when recursing. */
10635 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10636 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10637 {
10638 treeLock.release();
10639 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10640 treeLock.acquire();
10641 }
10642 if (pBase->i_addRegistryRecursive(uuid))
10643 {
10644 treeLock.release();
10645 mParent->i_markRegistryModified(uuid);
10646 }
10647 }
10648}
10649
10650/**
10651 * Creates differencing hard disks for all normal hard disks attached to this
10652 * machine and a new set of attachments to refer to created disks.
10653 *
10654 * Used when taking a snapshot or when deleting the current state. Gets called
10655 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10656 *
10657 * This method assumes that mMediumAttachments contains the original hard disk
10658 * attachments it needs to create diffs for. On success, these attachments will
10659 * be replaced with the created diffs.
10660 *
10661 * Attachments with non-normal hard disks are left as is.
10662 *
10663 * If @a aOnline is @c false then the original hard disks that require implicit
10664 * diffs will be locked for reading. Otherwise it is assumed that they are
10665 * already locked for writing (when the VM was started). Note that in the latter
10666 * case it is responsibility of the caller to lock the newly created diffs for
10667 * writing if this method succeeds.
10668 *
10669 * @param aProgress Progress object to run (must contain at least as
10670 * many operations left as the number of hard disks
10671 * attached).
10672 * @param aWeight Weight of this operation.
10673 * @param aOnline Whether the VM was online prior to this operation.
10674 *
10675 * @note The progress object is not marked as completed, neither on success nor
10676 * on failure. This is a responsibility of the caller.
10677 *
10678 * @note Locks this object and the media tree for writing.
10679 */
10680HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10681 ULONG aWeight,
10682 bool aOnline)
10683{
10684 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10685
10686 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10687 AssertReturn(!!pProgressControl, E_INVALIDARG);
10688
10689 AutoCaller autoCaller(this);
10690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10691
10692 AutoMultiWriteLock2 alock(this->lockHandle(),
10693 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10694
10695 /* must be in a protective state because we release the lock below */
10696 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10697 || mData->mMachineState == MachineState_OnlineSnapshotting
10698 || mData->mMachineState == MachineState_LiveSnapshotting
10699 || mData->mMachineState == MachineState_RestoringSnapshot
10700 || mData->mMachineState == MachineState_DeletingSnapshot
10701 , E_FAIL);
10702
10703 HRESULT rc = S_OK;
10704
10705 // use appropriate locked media map (online or offline)
10706 MediumLockListMap lockedMediaOffline;
10707 MediumLockListMap *lockedMediaMap;
10708 if (aOnline)
10709 lockedMediaMap = &mData->mSession.mLockedMedia;
10710 else
10711 lockedMediaMap = &lockedMediaOffline;
10712
10713 try
10714 {
10715 if (!aOnline)
10716 {
10717 /* lock all attached hard disks early to detect "in use"
10718 * situations before creating actual diffs */
10719 for (MediumAttachmentList::const_iterator
10720 it = mMediumAttachments->begin();
10721 it != mMediumAttachments->end();
10722 ++it)
10723 {
10724 MediumAttachment *pAtt = *it;
10725 if (pAtt->i_getType() == DeviceType_HardDisk)
10726 {
10727 Medium *pMedium = pAtt->i_getMedium();
10728 Assert(pMedium);
10729
10730 MediumLockList *pMediumLockList(new MediumLockList());
10731 alock.release();
10732 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10733 NULL /* pToLockWrite */,
10734 false /* fMediumLockWriteAll */,
10735 NULL,
10736 *pMediumLockList);
10737 alock.acquire();
10738 if (FAILED(rc))
10739 {
10740 delete pMediumLockList;
10741 throw rc;
10742 }
10743 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10744 if (FAILED(rc))
10745 {
10746 throw setError(rc,
10747 tr("Collecting locking information for all attached media failed"));
10748 }
10749 }
10750 }
10751
10752 /* Now lock all media. If this fails, nothing is locked. */
10753 alock.release();
10754 rc = lockedMediaMap->Lock();
10755 alock.acquire();
10756 if (FAILED(rc))
10757 {
10758 throw setError(rc,
10759 tr("Locking of attached media failed"));
10760 }
10761 }
10762
10763 /* remember the current list (note that we don't use backup() since
10764 * mMediumAttachments may be already backed up) */
10765 MediumAttachmentList atts = *mMediumAttachments.data();
10766
10767 /* start from scratch */
10768 mMediumAttachments->clear();
10769
10770 /* go through remembered attachments and create diffs for normal hard
10771 * disks and attach them */
10772 for (MediumAttachmentList::const_iterator
10773 it = atts.begin();
10774 it != atts.end();
10775 ++it)
10776 {
10777 MediumAttachment *pAtt = *it;
10778
10779 DeviceType_T devType = pAtt->i_getType();
10780 Medium *pMedium = pAtt->i_getMedium();
10781
10782 if ( devType != DeviceType_HardDisk
10783 || pMedium == NULL
10784 || pMedium->i_getType() != MediumType_Normal)
10785 {
10786 /* copy the attachment as is */
10787
10788 /** @todo the progress object created in SessionMachine::TakeSnaphot
10789 * only expects operations for hard disks. Later other
10790 * device types need to show up in the progress as well. */
10791 if (devType == DeviceType_HardDisk)
10792 {
10793 if (pMedium == NULL)
10794 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10795 aWeight); // weight
10796 else
10797 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10798 pMedium->i_getBase()->i_getName().c_str()).raw(),
10799 aWeight); // weight
10800 }
10801
10802 mMediumAttachments->push_back(pAtt);
10803 continue;
10804 }
10805
10806 /* need a diff */
10807 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10808 pMedium->i_getBase()->i_getName().c_str()).raw(),
10809 aWeight); // weight
10810
10811 Utf8Str strFullSnapshotFolder;
10812 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10813
10814 ComObjPtr<Medium> diff;
10815 diff.createObject();
10816 // store the diff in the same registry as the parent
10817 // (this cannot fail here because we can't create implicit diffs for
10818 // unregistered images)
10819 Guid uuidRegistryParent;
10820 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10821 Assert(fInRegistry); NOREF(fInRegistry);
10822 rc = diff->init(mParent,
10823 pMedium->i_getPreferredDiffFormat(),
10824 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10825 uuidRegistryParent,
10826 DeviceType_HardDisk);
10827 if (FAILED(rc)) throw rc;
10828
10829 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10830 * the push_back? Looks like we're going to release medium with the
10831 * wrong kind of lock (general issue with if we fail anywhere at all)
10832 * and an orphaned VDI in the snapshots folder. */
10833
10834 /* update the appropriate lock list */
10835 MediumLockList *pMediumLockList;
10836 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10837 AssertComRCThrowRC(rc);
10838 if (aOnline)
10839 {
10840 alock.release();
10841 /* The currently attached medium will be read-only, change
10842 * the lock type to read. */
10843 rc = pMediumLockList->Update(pMedium, false);
10844 alock.acquire();
10845 AssertComRCThrowRC(rc);
10846 }
10847
10848 /* release the locks before the potentially lengthy operation */
10849 alock.release();
10850 rc = pMedium->i_createDiffStorage(diff,
10851 pMedium->i_getPreferredDiffVariant(),
10852 pMediumLockList,
10853 NULL /* aProgress */,
10854 true /* aWait */,
10855 false /* aNotify */);
10856 alock.acquire();
10857 if (FAILED(rc)) throw rc;
10858
10859 /* actual lock list update is done in Machine::i_commitMedia */
10860
10861 rc = diff->i_addBackReference(mData->mUuid);
10862 AssertComRCThrowRC(rc);
10863
10864 /* add a new attachment */
10865 ComObjPtr<MediumAttachment> attachment;
10866 attachment.createObject();
10867 rc = attachment->init(this,
10868 diff,
10869 pAtt->i_getControllerName(),
10870 pAtt->i_getPort(),
10871 pAtt->i_getDevice(),
10872 DeviceType_HardDisk,
10873 true /* aImplicit */,
10874 false /* aPassthrough */,
10875 false /* aTempEject */,
10876 pAtt->i_getNonRotational(),
10877 pAtt->i_getDiscard(),
10878 pAtt->i_getHotPluggable(),
10879 pAtt->i_getBandwidthGroup());
10880 if (FAILED(rc)) throw rc;
10881
10882 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10883 AssertComRCThrowRC(rc);
10884 mMediumAttachments->push_back(attachment);
10885 }
10886 }
10887 catch (HRESULT aRC) { rc = aRC; }
10888
10889 /* unlock all hard disks we locked when there is no VM */
10890 if (!aOnline)
10891 {
10892 ErrorInfoKeeper eik;
10893
10894 HRESULT rc1 = lockedMediaMap->Clear();
10895 AssertComRC(rc1);
10896 }
10897
10898 return rc;
10899}
10900
10901/**
10902 * Deletes implicit differencing hard disks created either by
10903 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10904 * mMediumAttachments.
10905 *
10906 * Note that to delete hard disks created by #attachDevice() this method is
10907 * called from #i_rollbackMedia() when the changes are rolled back.
10908 *
10909 * @note Locks this object and the media tree for writing.
10910 */
10911HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10912{
10913 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10914
10915 AutoCaller autoCaller(this);
10916 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10917
10918 AutoMultiWriteLock2 alock(this->lockHandle(),
10919 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10920
10921 /* We absolutely must have backed up state. */
10922 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10923
10924 /* Check if there are any implicitly created diff images. */
10925 bool fImplicitDiffs = false;
10926 for (MediumAttachmentList::const_iterator
10927 it = mMediumAttachments->begin();
10928 it != mMediumAttachments->end();
10929 ++it)
10930 {
10931 const ComObjPtr<MediumAttachment> &pAtt = *it;
10932 if (pAtt->i_isImplicit())
10933 {
10934 fImplicitDiffs = true;
10935 break;
10936 }
10937 }
10938 /* If there is nothing to do, leave early. This saves lots of image locking
10939 * effort. It also avoids a MachineStateChanged event without real reason.
10940 * This is important e.g. when loading a VM config, because there should be
10941 * no events. Otherwise API clients can become thoroughly confused for
10942 * inaccessible VMs (the code for loading VM configs uses this method for
10943 * cleanup if the config makes no sense), as they take such events as an
10944 * indication that the VM is alive, and they would force the VM config to
10945 * be reread, leading to an endless loop. */
10946 if (!fImplicitDiffs)
10947 return S_OK;
10948
10949 HRESULT rc = S_OK;
10950 MachineState_T oldState = mData->mMachineState;
10951
10952 /* will release the lock before the potentially lengthy operation,
10953 * so protect with the special state (unless already protected) */
10954 if ( oldState != MachineState_Snapshotting
10955 && oldState != MachineState_OnlineSnapshotting
10956 && oldState != MachineState_LiveSnapshotting
10957 && oldState != MachineState_RestoringSnapshot
10958 && oldState != MachineState_DeletingSnapshot
10959 && oldState != MachineState_DeletingSnapshotOnline
10960 && oldState != MachineState_DeletingSnapshotPaused
10961 )
10962 i_setMachineState(MachineState_SettingUp);
10963
10964 // use appropriate locked media map (online or offline)
10965 MediumLockListMap lockedMediaOffline;
10966 MediumLockListMap *lockedMediaMap;
10967 if (aOnline)
10968 lockedMediaMap = &mData->mSession.mLockedMedia;
10969 else
10970 lockedMediaMap = &lockedMediaOffline;
10971
10972 try
10973 {
10974 if (!aOnline)
10975 {
10976 /* lock all attached hard disks early to detect "in use"
10977 * situations before deleting actual diffs */
10978 for (MediumAttachmentList::const_iterator
10979 it = mMediumAttachments->begin();
10980 it != mMediumAttachments->end();
10981 ++it)
10982 {
10983 MediumAttachment *pAtt = *it;
10984 if (pAtt->i_getType() == DeviceType_HardDisk)
10985 {
10986 Medium *pMedium = pAtt->i_getMedium();
10987 Assert(pMedium);
10988
10989 MediumLockList *pMediumLockList(new MediumLockList());
10990 alock.release();
10991 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10992 NULL /* pToLockWrite */,
10993 false /* fMediumLockWriteAll */,
10994 NULL,
10995 *pMediumLockList);
10996 alock.acquire();
10997
10998 if (FAILED(rc))
10999 {
11000 delete pMediumLockList;
11001 throw rc;
11002 }
11003
11004 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11005 if (FAILED(rc))
11006 throw rc;
11007 }
11008 }
11009
11010 if (FAILED(rc))
11011 throw rc;
11012 } // end of offline
11013
11014 /* Lock lists are now up to date and include implicitly created media */
11015
11016 /* Go through remembered attachments and delete all implicitly created
11017 * diffs and fix up the attachment information */
11018 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11019 MediumAttachmentList implicitAtts;
11020 for (MediumAttachmentList::const_iterator
11021 it = mMediumAttachments->begin();
11022 it != mMediumAttachments->end();
11023 ++it)
11024 {
11025 ComObjPtr<MediumAttachment> pAtt = *it;
11026 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11027 if (pMedium.isNull())
11028 continue;
11029
11030 // Implicit attachments go on the list for deletion and back references are removed.
11031 if (pAtt->i_isImplicit())
11032 {
11033 /* Deassociate and mark for deletion */
11034 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11035 rc = pMedium->i_removeBackReference(mData->mUuid);
11036 if (FAILED(rc))
11037 throw rc;
11038 implicitAtts.push_back(pAtt);
11039 continue;
11040 }
11041
11042 /* Was this medium attached before? */
11043 if (!i_findAttachment(oldAtts, pMedium))
11044 {
11045 /* no: de-associate */
11046 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11047 rc = pMedium->i_removeBackReference(mData->mUuid);
11048 if (FAILED(rc))
11049 throw rc;
11050 continue;
11051 }
11052 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11053 }
11054
11055 /* If there are implicit attachments to delete, throw away the lock
11056 * map contents (which will unlock all media) since the medium
11057 * attachments will be rolled back. Below we need to completely
11058 * recreate the lock map anyway since it is infinitely complex to
11059 * do this incrementally (would need reconstructing each attachment
11060 * change, which would be extremely hairy). */
11061 if (implicitAtts.size() != 0)
11062 {
11063 ErrorInfoKeeper eik;
11064
11065 HRESULT rc1 = lockedMediaMap->Clear();
11066 AssertComRC(rc1);
11067 }
11068
11069 /* rollback hard disk changes */
11070 mMediumAttachments.rollback();
11071
11072 MultiResult mrc(S_OK);
11073
11074 // Delete unused implicit diffs.
11075 if (implicitAtts.size() != 0)
11076 {
11077 alock.release();
11078
11079 for (MediumAttachmentList::const_iterator
11080 it = implicitAtts.begin();
11081 it != implicitAtts.end();
11082 ++it)
11083 {
11084 // Remove medium associated with this attachment.
11085 ComObjPtr<MediumAttachment> pAtt = *it;
11086 Assert(pAtt);
11087 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11088 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11089 Assert(pMedium);
11090
11091 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11092 // continue on delete failure, just collect error messages
11093 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11094 pMedium->i_getLocationFull().c_str() ));
11095 mrc = rc;
11096 }
11097 // Clear the list of deleted implicit attachments now, while not
11098 // holding the lock, as it will ultimately trigger Medium::uninit()
11099 // calls which assume that the media tree lock isn't held.
11100 implicitAtts.clear();
11101
11102 alock.acquire();
11103
11104 /* if there is a VM recreate media lock map as mentioned above,
11105 * otherwise it is a waste of time and we leave things unlocked */
11106 if (aOnline)
11107 {
11108 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11109 /* must never be NULL, but better safe than sorry */
11110 if (!pMachine.isNull())
11111 {
11112 alock.release();
11113 rc = mData->mSession.mMachine->i_lockMedia();
11114 alock.acquire();
11115 if (FAILED(rc))
11116 throw rc;
11117 }
11118 }
11119 }
11120 }
11121 catch (HRESULT aRC) {rc = aRC;}
11122
11123 if (mData->mMachineState == MachineState_SettingUp)
11124 i_setMachineState(oldState);
11125
11126 /* unlock all hard disks we locked when there is no VM */
11127 if (!aOnline)
11128 {
11129 ErrorInfoKeeper eik;
11130
11131 HRESULT rc1 = lockedMediaMap->Clear();
11132 AssertComRC(rc1);
11133 }
11134
11135 return rc;
11136}
11137
11138
11139/**
11140 * Looks through the given list of media attachments for one with the given parameters
11141 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11142 * can be searched as well if needed.
11143 *
11144 * @param ll
11145 * @param aControllerName
11146 * @param aControllerPort
11147 * @param aDevice
11148 * @return
11149 */
11150MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11151 const Utf8Str &aControllerName,
11152 LONG aControllerPort,
11153 LONG aDevice)
11154{
11155 for (MediumAttachmentList::const_iterator
11156 it = ll.begin();
11157 it != ll.end();
11158 ++it)
11159 {
11160 MediumAttachment *pAttach = *it;
11161 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11162 return pAttach;
11163 }
11164
11165 return NULL;
11166}
11167
11168/**
11169 * Looks through the given list of media attachments for one with the given parameters
11170 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11171 * can be searched as well if needed.
11172 *
11173 * @param ll
11174 * @param pMedium
11175 * @return
11176 */
11177MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11178 ComObjPtr<Medium> pMedium)
11179{
11180 for (MediumAttachmentList::const_iterator
11181 it = ll.begin();
11182 it != ll.end();
11183 ++it)
11184 {
11185 MediumAttachment *pAttach = *it;
11186 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11187 if (pMediumThis == pMedium)
11188 return pAttach;
11189 }
11190
11191 return NULL;
11192}
11193
11194/**
11195 * Looks through the given list of media attachments for one with the given parameters
11196 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11197 * can be searched as well if needed.
11198 *
11199 * @param ll
11200 * @param id
11201 * @return
11202 */
11203MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11204 Guid &id)
11205{
11206 for (MediumAttachmentList::const_iterator
11207 it = ll.begin();
11208 it != ll.end();
11209 ++it)
11210 {
11211 MediumAttachment *pAttach = *it;
11212 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11213 if (pMediumThis->i_getId() == id)
11214 return pAttach;
11215 }
11216
11217 return NULL;
11218}
11219
11220/**
11221 * Main implementation for Machine::DetachDevice. This also gets called
11222 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11223 *
11224 * @param pAttach Medium attachment to detach.
11225 * @param writeLock Machine write lock which the caller must have locked once.
11226 * This may be released temporarily in here.
11227 * @param pSnapshot If NULL, then the detachment is for the current machine.
11228 * Otherwise this is for a SnapshotMachine, and this must be
11229 * its snapshot.
11230 * @return
11231 */
11232HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11233 AutoWriteLock &writeLock,
11234 Snapshot *pSnapshot)
11235{
11236 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11237 DeviceType_T mediumType = pAttach->i_getType();
11238
11239 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11240
11241 if (pAttach->i_isImplicit())
11242 {
11243 /* attempt to implicitly delete the implicitly created diff */
11244
11245 /// @todo move the implicit flag from MediumAttachment to Medium
11246 /// and forbid any hard disk operation when it is implicit. Or maybe
11247 /// a special media state for it to make it even more simple.
11248
11249 Assert(mMediumAttachments.isBackedUp());
11250
11251 /* will release the lock before the potentially lengthy operation, so
11252 * protect with the special state */
11253 MachineState_T oldState = mData->mMachineState;
11254 i_setMachineState(MachineState_SettingUp);
11255
11256 writeLock.release();
11257
11258 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11259 true /*aWait*/,
11260 false /*aNotify*/);
11261
11262 writeLock.acquire();
11263
11264 i_setMachineState(oldState);
11265
11266 if (FAILED(rc)) return rc;
11267 }
11268
11269 i_setModified(IsModified_Storage);
11270 mMediumAttachments.backup();
11271 mMediumAttachments->remove(pAttach);
11272
11273 if (!oldmedium.isNull())
11274 {
11275 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11276 if (pSnapshot)
11277 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11278 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11279 else if (mediumType != DeviceType_HardDisk)
11280 oldmedium->i_removeBackReference(mData->mUuid);
11281 }
11282
11283 return S_OK;
11284}
11285
11286/**
11287 * Goes thru all media of the given list and
11288 *
11289 * 1) calls i_detachDevice() on each of them for this machine and
11290 * 2) adds all Medium objects found in the process to the given list,
11291 * depending on cleanupMode.
11292 *
11293 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11294 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11295 * media to the list.
11296 *
11297 * This gets called from Machine::Unregister, both for the actual Machine and
11298 * the SnapshotMachine objects that might be found in the snapshots.
11299 *
11300 * Requires caller and locking. The machine lock must be passed in because it
11301 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11302 *
11303 * @param writeLock Machine lock from top-level caller; this gets passed to
11304 * i_detachDevice.
11305 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11306 * object if called for a SnapshotMachine.
11307 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11308 * added to llMedia; if Full, then all media get added;
11309 * otherwise no media get added.
11310 * @param llMedia Caller's list to receive Medium objects which got detached so
11311 * caller can close() them, depending on cleanupMode.
11312 * @return
11313 */
11314HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11315 Snapshot *pSnapshot,
11316 CleanupMode_T cleanupMode,
11317 MediaList &llMedia)
11318{
11319 Assert(isWriteLockOnCurrentThread());
11320
11321 HRESULT rc;
11322
11323 // make a temporary list because i_detachDevice invalidates iterators into
11324 // mMediumAttachments
11325 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11326
11327 for (MediumAttachmentList::iterator
11328 it = llAttachments2.begin();
11329 it != llAttachments2.end();
11330 ++it)
11331 {
11332 ComObjPtr<MediumAttachment> &pAttach = *it;
11333 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11334
11335 if (!pMedium.isNull())
11336 {
11337 AutoCaller mac(pMedium);
11338 if (FAILED(mac.rc())) return mac.rc();
11339 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11340 DeviceType_T devType = pMedium->i_getDeviceType();
11341 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11342 && devType == DeviceType_HardDisk)
11343 || (cleanupMode == CleanupMode_Full)
11344 )
11345 {
11346 llMedia.push_back(pMedium);
11347 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11348 /* Not allowed to keep this lock as below we need the parent
11349 * medium lock, and the lock order is parent to child. */
11350 lock.release();
11351 /*
11352 * Search for medias which are not attached to any machine, but
11353 * in the chain to an attached disk. Mediums are only consided
11354 * if they are:
11355 * - have only one child
11356 * - no references to any machines
11357 * - are of normal medium type
11358 */
11359 while (!pParent.isNull())
11360 {
11361 AutoCaller mac1(pParent);
11362 if (FAILED(mac1.rc())) return mac1.rc();
11363 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11364 if (pParent->i_getChildren().size() == 1)
11365 {
11366 if ( pParent->i_getMachineBackRefCount() == 0
11367 && pParent->i_getType() == MediumType_Normal
11368 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11369 llMedia.push_back(pParent);
11370 }
11371 else
11372 break;
11373 pParent = pParent->i_getParent();
11374 }
11375 }
11376 }
11377
11378 // real machine: then we need to use the proper method
11379 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11380
11381 if (FAILED(rc))
11382 return rc;
11383 }
11384
11385 return S_OK;
11386}
11387
11388/**
11389 * Perform deferred hard disk detachments.
11390 *
11391 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11392 * changed (not backed up).
11393 *
11394 * If @a aOnline is @c true then this method will also unlock the old hard
11395 * disks for which the new implicit diffs were created and will lock these new
11396 * diffs for writing.
11397 *
11398 * @param aOnline Whether the VM was online prior to this operation.
11399 *
11400 * @note Locks this object for writing!
11401 */
11402void Machine::i_commitMedia(bool aOnline /*= false*/)
11403{
11404 AutoCaller autoCaller(this);
11405 AssertComRCReturnVoid(autoCaller.rc());
11406
11407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11408
11409 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11410
11411 HRESULT rc = S_OK;
11412
11413 /* no attach/detach operations -- nothing to do */
11414 if (!mMediumAttachments.isBackedUp())
11415 return;
11416
11417 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11418 bool fMediaNeedsLocking = false;
11419
11420 /* enumerate new attachments */
11421 for (MediumAttachmentList::const_iterator
11422 it = mMediumAttachments->begin();
11423 it != mMediumAttachments->end();
11424 ++it)
11425 {
11426 MediumAttachment *pAttach = *it;
11427
11428 pAttach->i_commit();
11429
11430 Medium *pMedium = pAttach->i_getMedium();
11431 bool fImplicit = pAttach->i_isImplicit();
11432
11433 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11434 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11435 fImplicit));
11436
11437 /** @todo convert all this Machine-based voodoo to MediumAttachment
11438 * based commit logic. */
11439 if (fImplicit)
11440 {
11441 /* convert implicit attachment to normal */
11442 pAttach->i_setImplicit(false);
11443
11444 if ( aOnline
11445 && pMedium
11446 && pAttach->i_getType() == DeviceType_HardDisk
11447 )
11448 {
11449 /* update the appropriate lock list */
11450 MediumLockList *pMediumLockList;
11451 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11452 AssertComRC(rc);
11453 if (pMediumLockList)
11454 {
11455 /* unlock if there's a need to change the locking */
11456 if (!fMediaNeedsLocking)
11457 {
11458 rc = mData->mSession.mLockedMedia.Unlock();
11459 AssertComRC(rc);
11460 fMediaNeedsLocking = true;
11461 }
11462 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11463 AssertComRC(rc);
11464 rc = pMediumLockList->Append(pMedium, true);
11465 AssertComRC(rc);
11466 }
11467 }
11468
11469 continue;
11470 }
11471
11472 if (pMedium)
11473 {
11474 /* was this medium attached before? */
11475 for (MediumAttachmentList::iterator
11476 oldIt = oldAtts.begin();
11477 oldIt != oldAtts.end();
11478 ++oldIt)
11479 {
11480 MediumAttachment *pOldAttach = *oldIt;
11481 if (pOldAttach->i_getMedium() == pMedium)
11482 {
11483 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11484
11485 /* yes: remove from old to avoid de-association */
11486 oldAtts.erase(oldIt);
11487 break;
11488 }
11489 }
11490 }
11491 }
11492
11493 /* enumerate remaining old attachments and de-associate from the
11494 * current machine state */
11495 for (MediumAttachmentList::const_iterator
11496 it = oldAtts.begin();
11497 it != oldAtts.end();
11498 ++it)
11499 {
11500 MediumAttachment *pAttach = *it;
11501 Medium *pMedium = pAttach->i_getMedium();
11502
11503 /* Detach only hard disks, since DVD/floppy media is detached
11504 * instantly in MountMedium. */
11505 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11506 {
11507 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11508
11509 /* now de-associate from the current machine state */
11510 rc = pMedium->i_removeBackReference(mData->mUuid);
11511 AssertComRC(rc);
11512
11513 if (aOnline)
11514 {
11515 /* unlock since medium is not used anymore */
11516 MediumLockList *pMediumLockList;
11517 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11518 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11519 {
11520 /* this happens for online snapshots, there the attachment
11521 * is changing, but only to a diff image created under
11522 * the old one, so there is no separate lock list */
11523 Assert(!pMediumLockList);
11524 }
11525 else
11526 {
11527 AssertComRC(rc);
11528 if (pMediumLockList)
11529 {
11530 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11531 AssertComRC(rc);
11532 }
11533 }
11534 }
11535 }
11536 }
11537
11538 /* take media locks again so that the locking state is consistent */
11539 if (fMediaNeedsLocking)
11540 {
11541 Assert(aOnline);
11542 rc = mData->mSession.mLockedMedia.Lock();
11543 AssertComRC(rc);
11544 }
11545
11546 /* commit the hard disk changes */
11547 mMediumAttachments.commit();
11548
11549 if (i_isSessionMachine())
11550 {
11551 /*
11552 * Update the parent machine to point to the new owner.
11553 * This is necessary because the stored parent will point to the
11554 * session machine otherwise and cause crashes or errors later
11555 * when the session machine gets invalid.
11556 */
11557 /** @todo Change the MediumAttachment class to behave like any other
11558 * class in this regard by creating peer MediumAttachment
11559 * objects for session machines and share the data with the peer
11560 * machine.
11561 */
11562 for (MediumAttachmentList::const_iterator
11563 it = mMediumAttachments->begin();
11564 it != mMediumAttachments->end();
11565 ++it)
11566 (*it)->i_updateParentMachine(mPeer);
11567
11568 /* attach new data to the primary machine and reshare it */
11569 mPeer->mMediumAttachments.attach(mMediumAttachments);
11570 }
11571
11572 return;
11573}
11574
11575/**
11576 * Perform deferred deletion of implicitly created diffs.
11577 *
11578 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11579 * changed (not backed up).
11580 *
11581 * @note Locks this object for writing!
11582 */
11583void Machine::i_rollbackMedia()
11584{
11585 AutoCaller autoCaller(this);
11586 AssertComRCReturnVoid(autoCaller.rc());
11587
11588 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11589 LogFlowThisFunc(("Entering rollbackMedia\n"));
11590
11591 HRESULT rc = S_OK;
11592
11593 /* no attach/detach operations -- nothing to do */
11594 if (!mMediumAttachments.isBackedUp())
11595 return;
11596
11597 /* enumerate new attachments */
11598 for (MediumAttachmentList::const_iterator
11599 it = mMediumAttachments->begin();
11600 it != mMediumAttachments->end();
11601 ++it)
11602 {
11603 MediumAttachment *pAttach = *it;
11604 /* Fix up the backrefs for DVD/floppy media. */
11605 if (pAttach->i_getType() != DeviceType_HardDisk)
11606 {
11607 Medium *pMedium = pAttach->i_getMedium();
11608 if (pMedium)
11609 {
11610 rc = pMedium->i_removeBackReference(mData->mUuid);
11611 AssertComRC(rc);
11612 }
11613 }
11614
11615 (*it)->i_rollback();
11616
11617 pAttach = *it;
11618 /* Fix up the backrefs for DVD/floppy media. */
11619 if (pAttach->i_getType() != DeviceType_HardDisk)
11620 {
11621 Medium *pMedium = pAttach->i_getMedium();
11622 if (pMedium)
11623 {
11624 rc = pMedium->i_addBackReference(mData->mUuid);
11625 AssertComRC(rc);
11626 }
11627 }
11628 }
11629
11630 /** @todo convert all this Machine-based voodoo to MediumAttachment
11631 * based rollback logic. */
11632 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11633
11634 return;
11635}
11636
11637/**
11638 * Returns true if the settings file is located in the directory named exactly
11639 * as the machine; this means, among other things, that the machine directory
11640 * should be auto-renamed.
11641 *
11642 * @param aSettingsDir if not NULL, the full machine settings file directory
11643 * name will be assigned there.
11644 *
11645 * @note Doesn't lock anything.
11646 * @note Not thread safe (must be called from this object's lock).
11647 */
11648bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11649{
11650 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11651 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11652 if (aSettingsDir)
11653 *aSettingsDir = strMachineDirName;
11654 strMachineDirName.stripPath(); // vmname
11655 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11656 strConfigFileOnly.stripPath() // vmname.vbox
11657 .stripSuffix(); // vmname
11658 /** @todo hack, make somehow use of ComposeMachineFilename */
11659 if (mUserData->s.fDirectoryIncludesUUID)
11660 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11661
11662 AssertReturn(!strMachineDirName.isEmpty(), false);
11663 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11664
11665 return strMachineDirName == strConfigFileOnly;
11666}
11667
11668/**
11669 * Discards all changes to machine settings.
11670 *
11671 * @param aNotify Whether to notify the direct session about changes or not.
11672 *
11673 * @note Locks objects for writing!
11674 */
11675void Machine::i_rollback(bool aNotify)
11676{
11677 AutoCaller autoCaller(this);
11678 AssertComRCReturn(autoCaller.rc(), (void)0);
11679
11680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11681
11682 if (!mStorageControllers.isNull())
11683 {
11684 if (mStorageControllers.isBackedUp())
11685 {
11686 /* unitialize all new devices (absent in the backed up list). */
11687 StorageControllerList *backedList = mStorageControllers.backedUpData();
11688 for (StorageControllerList::const_iterator
11689 it = mStorageControllers->begin();
11690 it != mStorageControllers->end();
11691 ++it)
11692 {
11693 if ( std::find(backedList->begin(), backedList->end(), *it)
11694 == backedList->end()
11695 )
11696 {
11697 (*it)->uninit();
11698 }
11699 }
11700
11701 /* restore the list */
11702 mStorageControllers.rollback();
11703 }
11704
11705 /* rollback any changes to devices after restoring the list */
11706 if (mData->flModifications & IsModified_Storage)
11707 {
11708 for (StorageControllerList::const_iterator
11709 it = mStorageControllers->begin();
11710 it != mStorageControllers->end();
11711 ++it)
11712 {
11713 (*it)->i_rollback();
11714 }
11715 }
11716 }
11717
11718 if (!mUSBControllers.isNull())
11719 {
11720 if (mUSBControllers.isBackedUp())
11721 {
11722 /* unitialize all new devices (absent in the backed up list). */
11723 USBControllerList *backedList = mUSBControllers.backedUpData();
11724 for (USBControllerList::const_iterator
11725 it = mUSBControllers->begin();
11726 it != mUSBControllers->end();
11727 ++it)
11728 {
11729 if ( std::find(backedList->begin(), backedList->end(), *it)
11730 == backedList->end()
11731 )
11732 {
11733 (*it)->uninit();
11734 }
11735 }
11736
11737 /* restore the list */
11738 mUSBControllers.rollback();
11739 }
11740
11741 /* rollback any changes to devices after restoring the list */
11742 if (mData->flModifications & IsModified_USB)
11743 {
11744 for (USBControllerList::const_iterator
11745 it = mUSBControllers->begin();
11746 it != mUSBControllers->end();
11747 ++it)
11748 {
11749 (*it)->i_rollback();
11750 }
11751 }
11752 }
11753
11754 mUserData.rollback();
11755
11756 mHWData.rollback();
11757
11758 if (mData->flModifications & IsModified_Storage)
11759 i_rollbackMedia();
11760
11761 if (mBIOSSettings)
11762 mBIOSSettings->i_rollback();
11763
11764 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11765 mRecordingSettings->i_rollback();
11766
11767 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11768 mVRDEServer->i_rollback();
11769
11770 if (mAudioAdapter)
11771 mAudioAdapter->i_rollback();
11772
11773 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11774 mUSBDeviceFilters->i_rollback();
11775
11776 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11777 mBandwidthControl->i_rollback();
11778
11779 if (!mHWData.isNull())
11780 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11781 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11782 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11783 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11784
11785 if (mData->flModifications & IsModified_NetworkAdapters)
11786 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11787 if ( mNetworkAdapters[slot]
11788 && mNetworkAdapters[slot]->i_isModified())
11789 {
11790 mNetworkAdapters[slot]->i_rollback();
11791 networkAdapters[slot] = mNetworkAdapters[slot];
11792 }
11793
11794 if (mData->flModifications & IsModified_SerialPorts)
11795 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11796 if ( mSerialPorts[slot]
11797 && mSerialPorts[slot]->i_isModified())
11798 {
11799 mSerialPorts[slot]->i_rollback();
11800 serialPorts[slot] = mSerialPorts[slot];
11801 }
11802
11803 if (mData->flModifications & IsModified_ParallelPorts)
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11805 if ( mParallelPorts[slot]
11806 && mParallelPorts[slot]->i_isModified())
11807 {
11808 mParallelPorts[slot]->i_rollback();
11809 parallelPorts[slot] = mParallelPorts[slot];
11810 }
11811
11812 if (aNotify)
11813 {
11814 /* inform the direct session about changes */
11815
11816 ComObjPtr<Machine> that = this;
11817 uint32_t flModifications = mData->flModifications;
11818 alock.release();
11819
11820 if (flModifications & IsModified_SharedFolders)
11821 that->i_onSharedFolderChange();
11822
11823 if (flModifications & IsModified_VRDEServer)
11824 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11825 if (flModifications & IsModified_USB)
11826 that->i_onUSBControllerChange();
11827
11828 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11829 if (networkAdapters[slot])
11830 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11831 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11832 if (serialPorts[slot])
11833 that->i_onSerialPortChange(serialPorts[slot]);
11834 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11835 if (parallelPorts[slot])
11836 that->i_onParallelPortChange(parallelPorts[slot]);
11837
11838 if (flModifications & IsModified_Storage)
11839 that->i_onStorageControllerChange();
11840
11841#if 0
11842 if (flModifications & IsModified_BandwidthControl)
11843 that->onBandwidthControlChange();
11844#endif
11845 }
11846}
11847
11848/**
11849 * Commits all the changes to machine settings.
11850 *
11851 * Note that this operation is supposed to never fail.
11852 *
11853 * @note Locks this object and children for writing.
11854 */
11855void Machine::i_commit()
11856{
11857 AutoCaller autoCaller(this);
11858 AssertComRCReturnVoid(autoCaller.rc());
11859
11860 AutoCaller peerCaller(mPeer);
11861 AssertComRCReturnVoid(peerCaller.rc());
11862
11863 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11864
11865 /*
11866 * use safe commit to ensure Snapshot machines (that share mUserData)
11867 * will still refer to a valid memory location
11868 */
11869 mUserData.commitCopy();
11870
11871 mHWData.commit();
11872
11873 if (mMediumAttachments.isBackedUp())
11874 i_commitMedia(Global::IsOnline(mData->mMachineState));
11875
11876 mBIOSSettings->i_commit();
11877 mRecordingSettings->i_commit();
11878 mVRDEServer->i_commit();
11879 mAudioAdapter->i_commit();
11880 mUSBDeviceFilters->i_commit();
11881 mBandwidthControl->i_commit();
11882
11883 /* Since mNetworkAdapters is a list which might have been changed (resized)
11884 * without using the Backupable<> template we need to handle the copying
11885 * of the list entries manually, including the creation of peers for the
11886 * new objects. */
11887 bool commitNetworkAdapters = false;
11888 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11889 if (mPeer)
11890 {
11891 /* commit everything, even the ones which will go away */
11892 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11893 mNetworkAdapters[slot]->i_commit();
11894 /* copy over the new entries, creating a peer and uninit the original */
11895 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11896 for (size_t slot = 0; slot < newSize; slot++)
11897 {
11898 /* look if this adapter has a peer device */
11899 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11900 if (!peer)
11901 {
11902 /* no peer means the adapter is a newly created one;
11903 * create a peer owning data this data share it with */
11904 peer.createObject();
11905 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11906 }
11907 mPeer->mNetworkAdapters[slot] = peer;
11908 }
11909 /* uninit any no longer needed network adapters */
11910 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11911 mNetworkAdapters[slot]->uninit();
11912 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11913 {
11914 if (mPeer->mNetworkAdapters[slot])
11915 mPeer->mNetworkAdapters[slot]->uninit();
11916 }
11917 /* Keep the original network adapter count until this point, so that
11918 * discarding a chipset type change will not lose settings. */
11919 mNetworkAdapters.resize(newSize);
11920 mPeer->mNetworkAdapters.resize(newSize);
11921 }
11922 else
11923 {
11924 /* we have no peer (our parent is the newly created machine);
11925 * just commit changes to the network adapters */
11926 commitNetworkAdapters = true;
11927 }
11928 if (commitNetworkAdapters)
11929 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11930 mNetworkAdapters[slot]->i_commit();
11931
11932 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11933 mSerialPorts[slot]->i_commit();
11934 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11935 mParallelPorts[slot]->i_commit();
11936
11937 bool commitStorageControllers = false;
11938
11939 if (mStorageControllers.isBackedUp())
11940 {
11941 mStorageControllers.commit();
11942
11943 if (mPeer)
11944 {
11945 /* Commit all changes to new controllers (this will reshare data with
11946 * peers for those who have peers) */
11947 StorageControllerList *newList = new StorageControllerList();
11948 for (StorageControllerList::const_iterator
11949 it = mStorageControllers->begin();
11950 it != mStorageControllers->end();
11951 ++it)
11952 {
11953 (*it)->i_commit();
11954
11955 /* look if this controller has a peer device */
11956 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11957 if (!peer)
11958 {
11959 /* no peer means the device is a newly created one;
11960 * create a peer owning data this device share it with */
11961 peer.createObject();
11962 peer->init(mPeer, *it, true /* aReshare */);
11963 }
11964 else
11965 {
11966 /* remove peer from the old list */
11967 mPeer->mStorageControllers->remove(peer);
11968 }
11969 /* and add it to the new list */
11970 newList->push_back(peer);
11971 }
11972
11973 /* uninit old peer's controllers that are left */
11974 for (StorageControllerList::const_iterator
11975 it = mPeer->mStorageControllers->begin();
11976 it != mPeer->mStorageControllers->end();
11977 ++it)
11978 {
11979 (*it)->uninit();
11980 }
11981
11982 /* attach new list of controllers to our peer */
11983 mPeer->mStorageControllers.attach(newList);
11984 }
11985 else
11986 {
11987 /* we have no peer (our parent is the newly created machine);
11988 * just commit changes to devices */
11989 commitStorageControllers = true;
11990 }
11991 }
11992 else
11993 {
11994 /* the list of controllers itself is not changed,
11995 * just commit changes to controllers themselves */
11996 commitStorageControllers = true;
11997 }
11998
11999 if (commitStorageControllers)
12000 {
12001 for (StorageControllerList::const_iterator
12002 it = mStorageControllers->begin();
12003 it != mStorageControllers->end();
12004 ++it)
12005 {
12006 (*it)->i_commit();
12007 }
12008 }
12009
12010 bool commitUSBControllers = false;
12011
12012 if (mUSBControllers.isBackedUp())
12013 {
12014 mUSBControllers.commit();
12015
12016 if (mPeer)
12017 {
12018 /* Commit all changes to new controllers (this will reshare data with
12019 * peers for those who have peers) */
12020 USBControllerList *newList = new USBControllerList();
12021 for (USBControllerList::const_iterator
12022 it = mUSBControllers->begin();
12023 it != mUSBControllers->end();
12024 ++it)
12025 {
12026 (*it)->i_commit();
12027
12028 /* look if this controller has a peer device */
12029 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12030 if (!peer)
12031 {
12032 /* no peer means the device is a newly created one;
12033 * create a peer owning data this device share it with */
12034 peer.createObject();
12035 peer->init(mPeer, *it, true /* aReshare */);
12036 }
12037 else
12038 {
12039 /* remove peer from the old list */
12040 mPeer->mUSBControllers->remove(peer);
12041 }
12042 /* and add it to the new list */
12043 newList->push_back(peer);
12044 }
12045
12046 /* uninit old peer's controllers that are left */
12047 for (USBControllerList::const_iterator
12048 it = mPeer->mUSBControllers->begin();
12049 it != mPeer->mUSBControllers->end();
12050 ++it)
12051 {
12052 (*it)->uninit();
12053 }
12054
12055 /* attach new list of controllers to our peer */
12056 mPeer->mUSBControllers.attach(newList);
12057 }
12058 else
12059 {
12060 /* we have no peer (our parent is the newly created machine);
12061 * just commit changes to devices */
12062 commitUSBControllers = true;
12063 }
12064 }
12065 else
12066 {
12067 /* the list of controllers itself is not changed,
12068 * just commit changes to controllers themselves */
12069 commitUSBControllers = true;
12070 }
12071
12072 if (commitUSBControllers)
12073 {
12074 for (USBControllerList::const_iterator
12075 it = mUSBControllers->begin();
12076 it != mUSBControllers->end();
12077 ++it)
12078 {
12079 (*it)->i_commit();
12080 }
12081 }
12082
12083 if (i_isSessionMachine())
12084 {
12085 /* attach new data to the primary machine and reshare it */
12086 mPeer->mUserData.attach(mUserData);
12087 mPeer->mHWData.attach(mHWData);
12088 /* mmMediumAttachments is reshared by fixupMedia */
12089 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12090 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12091 }
12092}
12093
12094/**
12095 * Copies all the hardware data from the given machine.
12096 *
12097 * Currently, only called when the VM is being restored from a snapshot. In
12098 * particular, this implies that the VM is not running during this method's
12099 * call.
12100 *
12101 * @note This method must be called from under this object's lock.
12102 *
12103 * @note This method doesn't call #i_commit(), so all data remains backed up and
12104 * unsaved.
12105 */
12106void Machine::i_copyFrom(Machine *aThat)
12107{
12108 AssertReturnVoid(!i_isSnapshotMachine());
12109 AssertReturnVoid(aThat->i_isSnapshotMachine());
12110
12111 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12112
12113 mHWData.assignCopy(aThat->mHWData);
12114
12115 // create copies of all shared folders (mHWData after attaching a copy
12116 // contains just references to original objects)
12117 for (HWData::SharedFolderList::iterator
12118 it = mHWData->mSharedFolders.begin();
12119 it != mHWData->mSharedFolders.end();
12120 ++it)
12121 {
12122 ComObjPtr<SharedFolder> folder;
12123 folder.createObject();
12124 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12125 AssertComRC(rc);
12126 *it = folder;
12127 }
12128
12129 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12130 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12131 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12132 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12133 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12134 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12135
12136 /* create private copies of all controllers */
12137 mStorageControllers.backup();
12138 mStorageControllers->clear();
12139 for (StorageControllerList::const_iterator
12140 it = aThat->mStorageControllers->begin();
12141 it != aThat->mStorageControllers->end();
12142 ++it)
12143 {
12144 ComObjPtr<StorageController> ctrl;
12145 ctrl.createObject();
12146 ctrl->initCopy(this, *it);
12147 mStorageControllers->push_back(ctrl);
12148 }
12149
12150 /* create private copies of all USB controllers */
12151 mUSBControllers.backup();
12152 mUSBControllers->clear();
12153 for (USBControllerList::const_iterator
12154 it = aThat->mUSBControllers->begin();
12155 it != aThat->mUSBControllers->end();
12156 ++it)
12157 {
12158 ComObjPtr<USBController> ctrl;
12159 ctrl.createObject();
12160 ctrl->initCopy(this, *it);
12161 mUSBControllers->push_back(ctrl);
12162 }
12163
12164 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12165 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12166 {
12167 if (mNetworkAdapters[slot].isNotNull())
12168 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12169 else
12170 {
12171 unconst(mNetworkAdapters[slot]).createObject();
12172 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12173 }
12174 }
12175 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12176 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12177 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12178 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12179}
12180
12181/**
12182 * Returns whether the given storage controller is hotplug capable.
12183 *
12184 * @returns true if the controller supports hotplugging
12185 * false otherwise.
12186 * @param enmCtrlType The controller type to check for.
12187 */
12188bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12189{
12190 ComPtr<ISystemProperties> systemProperties;
12191 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12192 if (FAILED(rc))
12193 return false;
12194
12195 BOOL aHotplugCapable = FALSE;
12196 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12197
12198 return RT_BOOL(aHotplugCapable);
12199}
12200
12201#ifdef VBOX_WITH_RESOURCE_USAGE_API
12202
12203void Machine::i_getDiskList(MediaList &list)
12204{
12205 for (MediumAttachmentList::const_iterator
12206 it = mMediumAttachments->begin();
12207 it != mMediumAttachments->end();
12208 ++it)
12209 {
12210 MediumAttachment *pAttach = *it;
12211 /* just in case */
12212 AssertContinue(pAttach);
12213
12214 AutoCaller localAutoCallerA(pAttach);
12215 if (FAILED(localAutoCallerA.rc())) continue;
12216
12217 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12218
12219 if (pAttach->i_getType() == DeviceType_HardDisk)
12220 list.push_back(pAttach->i_getMedium());
12221 }
12222}
12223
12224void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12225{
12226 AssertReturnVoid(isWriteLockOnCurrentThread());
12227 AssertPtrReturnVoid(aCollector);
12228
12229 pm::CollectorHAL *hal = aCollector->getHAL();
12230 /* Create sub metrics */
12231 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12232 "Percentage of processor time spent in user mode by the VM process.");
12233 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12234 "Percentage of processor time spent in kernel mode by the VM process.");
12235 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12236 "Size of resident portion of VM process in memory.");
12237 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12238 "Actual size of all VM disks combined.");
12239 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12240 "Network receive rate.");
12241 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12242 "Network transmit rate.");
12243 /* Create and register base metrics */
12244 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12245 cpuLoadUser, cpuLoadKernel);
12246 aCollector->registerBaseMetric(cpuLoad);
12247 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12248 ramUsageUsed);
12249 aCollector->registerBaseMetric(ramUsage);
12250 MediaList disks;
12251 i_getDiskList(disks);
12252 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12253 diskUsageUsed);
12254 aCollector->registerBaseMetric(diskUsage);
12255
12256 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12257 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12258 new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12260 new pm::AggregateMin()));
12261 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12262 new pm::AggregateMax()));
12263 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12265 new pm::AggregateAvg()));
12266 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12267 new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12269 new pm::AggregateMax()));
12270
12271 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12272 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12273 new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12275 new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12277 new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12280 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12281 new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12283 new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12285 new pm::AggregateMax()));
12286
12287
12288 /* Guest metrics collector */
12289 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12290 aCollector->registerGuest(mCollectorGuest);
12291 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12292
12293 /* Create sub metrics */
12294 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12295 "Percentage of processor time spent in user mode as seen by the guest.");
12296 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12297 "Percentage of processor time spent in kernel mode as seen by the guest.");
12298 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12299 "Percentage of processor time spent idling as seen by the guest.");
12300
12301 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12302 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12303 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12304 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12305 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12306 pm::SubMetric *guestMemCache = new pm::SubMetric(
12307 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12308
12309 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12310 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12311
12312 /* Create and register base metrics */
12313 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12314 machineNetRx, machineNetTx);
12315 aCollector->registerBaseMetric(machineNetRate);
12316
12317 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12318 guestLoadUser, guestLoadKernel, guestLoadIdle);
12319 aCollector->registerBaseMetric(guestCpuLoad);
12320
12321 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12322 guestMemTotal, guestMemFree,
12323 guestMemBalloon, guestMemShared,
12324 guestMemCache, guestPagedTotal);
12325 aCollector->registerBaseMetric(guestCpuMem);
12326
12327 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12328 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12329 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12331
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12336
12337 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12341
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12351
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12376
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12381}
12382
12383void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12384{
12385 AssertReturnVoid(isWriteLockOnCurrentThread());
12386
12387 if (aCollector)
12388 {
12389 aCollector->unregisterMetricsFor(aMachine);
12390 aCollector->unregisterBaseMetricsFor(aMachine);
12391 }
12392}
12393
12394#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12395
12396
12397////////////////////////////////////////////////////////////////////////////////
12398
12399DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12400
12401HRESULT SessionMachine::FinalConstruct()
12402{
12403 LogFlowThisFunc(("\n"));
12404
12405 mClientToken = NULL;
12406
12407 return BaseFinalConstruct();
12408}
12409
12410void SessionMachine::FinalRelease()
12411{
12412 LogFlowThisFunc(("\n"));
12413
12414 Assert(!mClientToken);
12415 /* paranoia, should not hang around any more */
12416 if (mClientToken)
12417 {
12418 delete mClientToken;
12419 mClientToken = NULL;
12420 }
12421
12422 uninit(Uninit::Unexpected);
12423
12424 BaseFinalRelease();
12425}
12426
12427/**
12428 * @note Must be called only by Machine::LockMachine() from its own write lock.
12429 */
12430HRESULT SessionMachine::init(Machine *aMachine)
12431{
12432 LogFlowThisFuncEnter();
12433 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12434
12435 AssertReturn(aMachine, E_INVALIDARG);
12436
12437 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12438
12439 /* Enclose the state transition NotReady->InInit->Ready */
12440 AutoInitSpan autoInitSpan(this);
12441 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12442
12443 HRESULT rc = S_OK;
12444
12445 RT_ZERO(mAuthLibCtx);
12446
12447 /* create the machine client token */
12448 try
12449 {
12450 mClientToken = new ClientToken(aMachine, this);
12451 if (!mClientToken->isReady())
12452 {
12453 delete mClientToken;
12454 mClientToken = NULL;
12455 rc = E_FAIL;
12456 }
12457 }
12458 catch (std::bad_alloc &)
12459 {
12460 rc = E_OUTOFMEMORY;
12461 }
12462 if (FAILED(rc))
12463 return rc;
12464
12465 /* memorize the peer Machine */
12466 unconst(mPeer) = aMachine;
12467 /* share the parent pointer */
12468 unconst(mParent) = aMachine->mParent;
12469
12470 /* take the pointers to data to share */
12471 mData.share(aMachine->mData);
12472 mSSData.share(aMachine->mSSData);
12473
12474 mUserData.share(aMachine->mUserData);
12475 mHWData.share(aMachine->mHWData);
12476 mMediumAttachments.share(aMachine->mMediumAttachments);
12477
12478 mStorageControllers.allocate();
12479 for (StorageControllerList::const_iterator
12480 it = aMachine->mStorageControllers->begin();
12481 it != aMachine->mStorageControllers->end();
12482 ++it)
12483 {
12484 ComObjPtr<StorageController> ctl;
12485 ctl.createObject();
12486 ctl->init(this, *it);
12487 mStorageControllers->push_back(ctl);
12488 }
12489
12490 mUSBControllers.allocate();
12491 for (USBControllerList::const_iterator
12492 it = aMachine->mUSBControllers->begin();
12493 it != aMachine->mUSBControllers->end();
12494 ++it)
12495 {
12496 ComObjPtr<USBController> ctl;
12497 ctl.createObject();
12498 ctl->init(this, *it);
12499 mUSBControllers->push_back(ctl);
12500 }
12501
12502 unconst(mBIOSSettings).createObject();
12503 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12504 unconst(mRecordingSettings).createObject();
12505 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12506 /* create another VRDEServer object that will be mutable */
12507 unconst(mVRDEServer).createObject();
12508 mVRDEServer->init(this, aMachine->mVRDEServer);
12509 /* create another audio adapter object that will be mutable */
12510 unconst(mAudioAdapter).createObject();
12511 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12512 /* create a list of serial ports that will be mutable */
12513 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12514 {
12515 unconst(mSerialPorts[slot]).createObject();
12516 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12517 }
12518 /* create a list of parallel ports that will be mutable */
12519 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12520 {
12521 unconst(mParallelPorts[slot]).createObject();
12522 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12523 }
12524
12525 /* create another USB device filters object that will be mutable */
12526 unconst(mUSBDeviceFilters).createObject();
12527 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12528
12529 /* create a list of network adapters that will be mutable */
12530 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12531 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12532 {
12533 unconst(mNetworkAdapters[slot]).createObject();
12534 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12535 }
12536
12537 /* create another bandwidth control object that will be mutable */
12538 unconst(mBandwidthControl).createObject();
12539 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12540
12541 /* default is to delete saved state on Saved -> PoweredOff transition */
12542 mRemoveSavedState = true;
12543
12544 /* Confirm a successful initialization when it's the case */
12545 autoInitSpan.setSucceeded();
12546
12547 miNATNetworksStarted = 0;
12548
12549 LogFlowThisFuncLeave();
12550 return rc;
12551}
12552
12553/**
12554 * Uninitializes this session object. If the reason is other than
12555 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12556 * or the client watcher code.
12557 *
12558 * @param aReason uninitialization reason
12559 *
12560 * @note Locks mParent + this object for writing.
12561 */
12562void SessionMachine::uninit(Uninit::Reason aReason)
12563{
12564 LogFlowThisFuncEnter();
12565 LogFlowThisFunc(("reason=%d\n", aReason));
12566
12567 /*
12568 * Strongly reference ourselves to prevent this object deletion after
12569 * mData->mSession.mMachine.setNull() below (which can release the last
12570 * reference and call the destructor). Important: this must be done before
12571 * accessing any members (and before AutoUninitSpan that does it as well).
12572 * This self reference will be released as the very last step on return.
12573 */
12574 ComObjPtr<SessionMachine> selfRef;
12575 if (aReason != Uninit::Unexpected)
12576 selfRef = this;
12577
12578 /* Enclose the state transition Ready->InUninit->NotReady */
12579 AutoUninitSpan autoUninitSpan(this);
12580 if (autoUninitSpan.uninitDone())
12581 {
12582 LogFlowThisFunc(("Already uninitialized\n"));
12583 LogFlowThisFuncLeave();
12584 return;
12585 }
12586
12587 if (autoUninitSpan.initFailed())
12588 {
12589 /* We've been called by init() because it's failed. It's not really
12590 * necessary (nor it's safe) to perform the regular uninit sequence
12591 * below, the following is enough.
12592 */
12593 LogFlowThisFunc(("Initialization failed.\n"));
12594 /* destroy the machine client token */
12595 if (mClientToken)
12596 {
12597 delete mClientToken;
12598 mClientToken = NULL;
12599 }
12600 uninitDataAndChildObjects();
12601 mData.free();
12602 unconst(mParent) = NULL;
12603 unconst(mPeer) = NULL;
12604 LogFlowThisFuncLeave();
12605 return;
12606 }
12607
12608 MachineState_T lastState;
12609 {
12610 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12611 lastState = mData->mMachineState;
12612 }
12613 NOREF(lastState);
12614
12615#ifdef VBOX_WITH_USB
12616 // release all captured USB devices, but do this before requesting the locks below
12617 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12618 {
12619 /* Console::captureUSBDevices() is called in the VM process only after
12620 * setting the machine state to Starting or Restoring.
12621 * Console::detachAllUSBDevices() will be called upon successful
12622 * termination. So, we need to release USB devices only if there was
12623 * an abnormal termination of a running VM.
12624 *
12625 * This is identical to SessionMachine::DetachAllUSBDevices except
12626 * for the aAbnormal argument. */
12627 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12628 AssertComRC(rc);
12629 NOREF(rc);
12630
12631 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12632 if (service)
12633 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12634 }
12635#endif /* VBOX_WITH_USB */
12636
12637 // we need to lock this object in uninit() because the lock is shared
12638 // with mPeer (as well as data we modify below). mParent lock is needed
12639 // by several calls to it.
12640 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12641
12642#ifdef VBOX_WITH_RESOURCE_USAGE_API
12643 /*
12644 * It is safe to call Machine::i_unregisterMetrics() here because
12645 * PerformanceCollector::samplerCallback no longer accesses guest methods
12646 * holding the lock.
12647 */
12648 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12649 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12650 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12651 if (mCollectorGuest)
12652 {
12653 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12654 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12655 mCollectorGuest = NULL;
12656 }
12657#endif
12658
12659 if (aReason == Uninit::Abnormal)
12660 {
12661 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12662
12663 /* reset the state to Aborted */
12664 if (mData->mMachineState != MachineState_Aborted)
12665 i_setMachineState(MachineState_Aborted);
12666 }
12667
12668 // any machine settings modified?
12669 if (mData->flModifications)
12670 {
12671 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12672 i_rollback(false /* aNotify */);
12673 }
12674
12675 mData->mSession.mPID = NIL_RTPROCESS;
12676
12677 if (aReason == Uninit::Unexpected)
12678 {
12679 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12680 * client watcher thread to update the set of machines that have open
12681 * sessions. */
12682 mParent->i_updateClientWatcher();
12683 }
12684
12685 /* uninitialize all remote controls */
12686 if (mData->mSession.mRemoteControls.size())
12687 {
12688 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12689 mData->mSession.mRemoteControls.size()));
12690
12691 /* Always restart a the beginning, since the iterator is invalidated
12692 * by using erase(). */
12693 for (Data::Session::RemoteControlList::iterator
12694 it = mData->mSession.mRemoteControls.begin();
12695 it != mData->mSession.mRemoteControls.end();
12696 it = mData->mSession.mRemoteControls.begin())
12697 {
12698 ComPtr<IInternalSessionControl> pControl = *it;
12699 mData->mSession.mRemoteControls.erase(it);
12700 multilock.release();
12701 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12702 HRESULT rc = pControl->Uninitialize();
12703 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12704 if (FAILED(rc))
12705 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12706 multilock.acquire();
12707 }
12708 mData->mSession.mRemoteControls.clear();
12709 }
12710
12711 /* Remove all references to the NAT network service. The service will stop
12712 * if all references (also from other VMs) are removed. */
12713 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12714 {
12715 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12716 {
12717 BOOL enabled;
12718 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12719 if ( FAILED(hrc)
12720 || !enabled)
12721 continue;
12722
12723 NetworkAttachmentType_T type;
12724 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12725 if ( SUCCEEDED(hrc)
12726 && type == NetworkAttachmentType_NATNetwork)
12727 {
12728 Bstr name;
12729 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12730 if (SUCCEEDED(hrc))
12731 {
12732 multilock.release();
12733 Utf8Str strName(name);
12734 LogRel(("VM '%s' stops using NAT network '%s'\n",
12735 mUserData->s.strName.c_str(), strName.c_str()));
12736 mParent->i_natNetworkRefDec(strName);
12737 multilock.acquire();
12738 }
12739 }
12740 }
12741 }
12742
12743 /*
12744 * An expected uninitialization can come only from #i_checkForDeath().
12745 * Otherwise it means that something's gone really wrong (for example,
12746 * the Session implementation has released the VirtualBox reference
12747 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12748 * etc). However, it's also possible, that the client releases the IPC
12749 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12750 * but the VirtualBox release event comes first to the server process.
12751 * This case is practically possible, so we should not assert on an
12752 * unexpected uninit, just log a warning.
12753 */
12754
12755 if (aReason == Uninit::Unexpected)
12756 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12757
12758 if (aReason != Uninit::Normal)
12759 {
12760 mData->mSession.mDirectControl.setNull();
12761 }
12762 else
12763 {
12764 /* this must be null here (see #OnSessionEnd()) */
12765 Assert(mData->mSession.mDirectControl.isNull());
12766 Assert(mData->mSession.mState == SessionState_Unlocking);
12767 Assert(!mData->mSession.mProgress.isNull());
12768 }
12769 if (mData->mSession.mProgress)
12770 {
12771 if (aReason == Uninit::Normal)
12772 mData->mSession.mProgress->i_notifyComplete(S_OK);
12773 else
12774 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12775 COM_IIDOF(ISession),
12776 getComponentName(),
12777 tr("The VM session was aborted"));
12778 mData->mSession.mProgress.setNull();
12779 }
12780
12781 if (mConsoleTaskData.mProgress)
12782 {
12783 Assert(aReason == Uninit::Abnormal);
12784 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12785 COM_IIDOF(ISession),
12786 getComponentName(),
12787 tr("The VM session was aborted"));
12788 mConsoleTaskData.mProgress.setNull();
12789 }
12790
12791 /* remove the association between the peer machine and this session machine */
12792 Assert( (SessionMachine*)mData->mSession.mMachine == this
12793 || aReason == Uninit::Unexpected);
12794
12795 /* reset the rest of session data */
12796 mData->mSession.mLockType = LockType_Null;
12797 mData->mSession.mMachine.setNull();
12798 mData->mSession.mState = SessionState_Unlocked;
12799 mData->mSession.mName.setNull();
12800
12801 /* destroy the machine client token before leaving the exclusive lock */
12802 if (mClientToken)
12803 {
12804 delete mClientToken;
12805 mClientToken = NULL;
12806 }
12807
12808 /* fire an event */
12809 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12810
12811 uninitDataAndChildObjects();
12812
12813 /* free the essential data structure last */
12814 mData.free();
12815
12816 /* release the exclusive lock before setting the below two to NULL */
12817 multilock.release();
12818
12819 unconst(mParent) = NULL;
12820 unconst(mPeer) = NULL;
12821
12822 AuthLibUnload(&mAuthLibCtx);
12823
12824 LogFlowThisFuncLeave();
12825}
12826
12827// util::Lockable interface
12828////////////////////////////////////////////////////////////////////////////////
12829
12830/**
12831 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12832 * with the primary Machine instance (mPeer).
12833 */
12834RWLockHandle *SessionMachine::lockHandle() const
12835{
12836 AssertReturn(mPeer != NULL, NULL);
12837 return mPeer->lockHandle();
12838}
12839
12840// IInternalMachineControl methods
12841////////////////////////////////////////////////////////////////////////////////
12842
12843/**
12844 * Passes collected guest statistics to performance collector object
12845 */
12846HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12847 ULONG aCpuKernel, ULONG aCpuIdle,
12848 ULONG aMemTotal, ULONG aMemFree,
12849 ULONG aMemBalloon, ULONG aMemShared,
12850 ULONG aMemCache, ULONG aPageTotal,
12851 ULONG aAllocVMM, ULONG aFreeVMM,
12852 ULONG aBalloonedVMM, ULONG aSharedVMM,
12853 ULONG aVmNetRx, ULONG aVmNetTx)
12854{
12855#ifdef VBOX_WITH_RESOURCE_USAGE_API
12856 if (mCollectorGuest)
12857 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12858 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12859 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12860 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12861
12862 return S_OK;
12863#else
12864 NOREF(aValidStats);
12865 NOREF(aCpuUser);
12866 NOREF(aCpuKernel);
12867 NOREF(aCpuIdle);
12868 NOREF(aMemTotal);
12869 NOREF(aMemFree);
12870 NOREF(aMemBalloon);
12871 NOREF(aMemShared);
12872 NOREF(aMemCache);
12873 NOREF(aPageTotal);
12874 NOREF(aAllocVMM);
12875 NOREF(aFreeVMM);
12876 NOREF(aBalloonedVMM);
12877 NOREF(aSharedVMM);
12878 NOREF(aVmNetRx);
12879 NOREF(aVmNetTx);
12880 return E_NOTIMPL;
12881#endif
12882}
12883
12884////////////////////////////////////////////////////////////////////////////////
12885//
12886// SessionMachine task records
12887//
12888////////////////////////////////////////////////////////////////////////////////
12889
12890/**
12891 * Task record for saving the machine state.
12892 */
12893class SessionMachine::SaveStateTask
12894 : public Machine::Task
12895{
12896public:
12897 SaveStateTask(SessionMachine *m,
12898 Progress *p,
12899 const Utf8Str &t,
12900 Reason_T enmReason,
12901 const Utf8Str &strStateFilePath)
12902 : Task(m, p, t),
12903 m_enmReason(enmReason),
12904 m_strStateFilePath(strStateFilePath)
12905 {}
12906
12907private:
12908 void handler()
12909 {
12910 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12911 }
12912
12913 Reason_T m_enmReason;
12914 Utf8Str m_strStateFilePath;
12915
12916 friend class SessionMachine;
12917};
12918
12919/**
12920 * Task thread implementation for SessionMachine::SaveState(), called from
12921 * SessionMachine::taskHandler().
12922 *
12923 * @note Locks this object for writing.
12924 *
12925 * @param task
12926 * @return
12927 */
12928void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12929{
12930 LogFlowThisFuncEnter();
12931
12932 AutoCaller autoCaller(this);
12933 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12934 if (FAILED(autoCaller.rc()))
12935 {
12936 /* we might have been uninitialized because the session was accidentally
12937 * closed by the client, so don't assert */
12938 HRESULT rc = setError(E_FAIL,
12939 tr("The session has been accidentally closed"));
12940 task.m_pProgress->i_notifyComplete(rc);
12941 LogFlowThisFuncLeave();
12942 return;
12943 }
12944
12945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12946
12947 HRESULT rc = S_OK;
12948
12949 try
12950 {
12951 ComPtr<IInternalSessionControl> directControl;
12952 if (mData->mSession.mLockType == LockType_VM)
12953 directControl = mData->mSession.mDirectControl;
12954 if (directControl.isNull())
12955 throw setError(VBOX_E_INVALID_VM_STATE,
12956 tr("Trying to save state without a running VM"));
12957 alock.release();
12958 BOOL fSuspendedBySave;
12959 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12960 Assert(!fSuspendedBySave);
12961 alock.acquire();
12962
12963 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12964 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12965 throw E_FAIL);
12966
12967 if (SUCCEEDED(rc))
12968 {
12969 mSSData->strStateFilePath = task.m_strStateFilePath;
12970
12971 /* save all VM settings */
12972 rc = i_saveSettings(NULL);
12973 // no need to check whether VirtualBox.xml needs saving also since
12974 // we can't have a name change pending at this point
12975 }
12976 else
12977 {
12978 // On failure, set the state to the state we had at the beginning.
12979 i_setMachineState(task.m_machineStateBackup);
12980 i_updateMachineStateOnClient();
12981
12982 // Delete the saved state file (might have been already created).
12983 // No need to check whether this is shared with a snapshot here
12984 // because we certainly created a fresh saved state file here.
12985 RTFileDelete(task.m_strStateFilePath.c_str());
12986 }
12987 }
12988 catch (HRESULT aRC) { rc = aRC; }
12989
12990 task.m_pProgress->i_notifyComplete(rc);
12991
12992 LogFlowThisFuncLeave();
12993}
12994
12995/**
12996 * @note Locks this object for writing.
12997 */
12998HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12999{
13000 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13001}
13002
13003HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13004{
13005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13006
13007 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13008 if (FAILED(rc)) return rc;
13009
13010 if ( mData->mMachineState != MachineState_Running
13011 && mData->mMachineState != MachineState_Paused
13012 )
13013 return setError(VBOX_E_INVALID_VM_STATE,
13014 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13015 Global::stringifyMachineState(mData->mMachineState));
13016
13017 ComObjPtr<Progress> pProgress;
13018 pProgress.createObject();
13019 rc = pProgress->init(i_getVirtualBox(),
13020 static_cast<IMachine *>(this) /* aInitiator */,
13021 tr("Saving the execution state of the virtual machine"),
13022 FALSE /* aCancelable */);
13023 if (FAILED(rc))
13024 return rc;
13025
13026 Utf8Str strStateFilePath;
13027 i_composeSavedStateFilename(strStateFilePath);
13028
13029 /* create and start the task on a separate thread (note that it will not
13030 * start working until we release alock) */
13031 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13032 rc = pTask->createThread();
13033 if (FAILED(rc))
13034 return rc;
13035
13036 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13037 i_setMachineState(MachineState_Saving);
13038 i_updateMachineStateOnClient();
13039
13040 pProgress.queryInterfaceTo(aProgress.asOutParam());
13041
13042 return S_OK;
13043}
13044
13045/**
13046 * @note Locks this object for writing.
13047 */
13048HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13049{
13050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13051
13052 HRESULT rc = i_checkStateDependency(MutableStateDep);
13053 if (FAILED(rc)) return rc;
13054
13055 if ( mData->mMachineState != MachineState_PoweredOff
13056 && mData->mMachineState != MachineState_Teleported
13057 && mData->mMachineState != MachineState_Aborted
13058 )
13059 return setError(VBOX_E_INVALID_VM_STATE,
13060 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13061 Global::stringifyMachineState(mData->mMachineState));
13062
13063 com::Utf8Str stateFilePathFull;
13064 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13065 if (RT_FAILURE(vrc))
13066 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13067 tr("Invalid saved state file path '%s' (%Rrc)"),
13068 aSavedStateFile.c_str(),
13069 vrc);
13070
13071 mSSData->strStateFilePath = stateFilePathFull;
13072
13073 /* The below i_setMachineState() will detect the state transition and will
13074 * update the settings file */
13075
13076 return i_setMachineState(MachineState_Saved);
13077}
13078
13079/**
13080 * @note Locks this object for writing.
13081 */
13082HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13083{
13084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13085
13086 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13087 if (FAILED(rc)) return rc;
13088
13089 if (mData->mMachineState != MachineState_Saved)
13090 return setError(VBOX_E_INVALID_VM_STATE,
13091 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13092 Global::stringifyMachineState(mData->mMachineState));
13093
13094 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13095
13096 /*
13097 * Saved -> PoweredOff transition will be detected in the SessionMachine
13098 * and properly handled.
13099 */
13100 rc = i_setMachineState(MachineState_PoweredOff);
13101 return rc;
13102}
13103
13104
13105/**
13106 * @note Locks the same as #i_setMachineState() does.
13107 */
13108HRESULT SessionMachine::updateState(MachineState_T aState)
13109{
13110 return i_setMachineState(aState);
13111}
13112
13113/**
13114 * @note Locks this object for writing.
13115 */
13116HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13117{
13118 IProgress *pProgress(aProgress);
13119
13120 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13121
13122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13123
13124 if (mData->mSession.mState != SessionState_Locked)
13125 return VBOX_E_INVALID_OBJECT_STATE;
13126
13127 if (!mData->mSession.mProgress.isNull())
13128 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13129
13130 /* If we didn't reference the NAT network service yet, add a reference to
13131 * force a start */
13132 if (miNATNetworksStarted < 1)
13133 {
13134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13135 {
13136 BOOL enabled;
13137 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13138 if ( FAILED(hrc)
13139 || !enabled)
13140 continue;
13141
13142 NetworkAttachmentType_T type;
13143 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13144 if ( SUCCEEDED(hrc)
13145 && type == NetworkAttachmentType_NATNetwork)
13146 {
13147 Bstr name;
13148 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13149 if (SUCCEEDED(hrc))
13150 {
13151 Utf8Str strName(name);
13152 LogRel(("VM '%s' starts using NAT network '%s'\n",
13153 mUserData->s.strName.c_str(), strName.c_str()));
13154 mPeer->lockHandle()->unlockWrite();
13155 mParent->i_natNetworkRefInc(strName);
13156#ifdef RT_LOCK_STRICT
13157 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13158#else
13159 mPeer->lockHandle()->lockWrite();
13160#endif
13161 }
13162 }
13163 }
13164 miNATNetworksStarted++;
13165 }
13166
13167 LogFlowThisFunc(("returns S_OK.\n"));
13168 return S_OK;
13169}
13170
13171/**
13172 * @note Locks this object for writing.
13173 */
13174HRESULT SessionMachine::endPowerUp(LONG aResult)
13175{
13176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13177
13178 if (mData->mSession.mState != SessionState_Locked)
13179 return VBOX_E_INVALID_OBJECT_STATE;
13180
13181 /* Finalize the LaunchVMProcess progress object. */
13182 if (mData->mSession.mProgress)
13183 {
13184 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13185 mData->mSession.mProgress.setNull();
13186 }
13187
13188 if (SUCCEEDED((HRESULT)aResult))
13189 {
13190#ifdef VBOX_WITH_RESOURCE_USAGE_API
13191 /* The VM has been powered up successfully, so it makes sense
13192 * now to offer the performance metrics for a running machine
13193 * object. Doing it earlier wouldn't be safe. */
13194 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13195 mData->mSession.mPID);
13196#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13197 }
13198
13199 return S_OK;
13200}
13201
13202/**
13203 * @note Locks this object for writing.
13204 */
13205HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13206{
13207 LogFlowThisFuncEnter();
13208
13209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13210
13211 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13212 E_FAIL);
13213
13214 /* create a progress object to track operation completion */
13215 ComObjPtr<Progress> pProgress;
13216 pProgress.createObject();
13217 pProgress->init(i_getVirtualBox(),
13218 static_cast<IMachine *>(this) /* aInitiator */,
13219 tr("Stopping the virtual machine"),
13220 FALSE /* aCancelable */);
13221
13222 /* fill in the console task data */
13223 mConsoleTaskData.mLastState = mData->mMachineState;
13224 mConsoleTaskData.mProgress = pProgress;
13225
13226 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13227 i_setMachineState(MachineState_Stopping);
13228
13229 pProgress.queryInterfaceTo(aProgress.asOutParam());
13230
13231 return S_OK;
13232}
13233
13234/**
13235 * @note Locks this object for writing.
13236 */
13237HRESULT SessionMachine::endPoweringDown(LONG aResult,
13238 const com::Utf8Str &aErrMsg)
13239{
13240 LogFlowThisFuncEnter();
13241
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13245 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13246 && mConsoleTaskData.mLastState != MachineState_Null,
13247 E_FAIL);
13248
13249 /*
13250 * On failure, set the state to the state we had when BeginPoweringDown()
13251 * was called (this is expected by Console::PowerDown() and the associated
13252 * task). On success the VM process already changed the state to
13253 * MachineState_PoweredOff, so no need to do anything.
13254 */
13255 if (FAILED(aResult))
13256 i_setMachineState(mConsoleTaskData.mLastState);
13257
13258 /* notify the progress object about operation completion */
13259 Assert(mConsoleTaskData.mProgress);
13260 if (SUCCEEDED(aResult))
13261 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13262 else
13263 {
13264 if (aErrMsg.length())
13265 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13266 COM_IIDOF(ISession),
13267 getComponentName(),
13268 aErrMsg.c_str());
13269 else
13270 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13271 }
13272
13273 /* clear out the temporary saved state data */
13274 mConsoleTaskData.mLastState = MachineState_Null;
13275 mConsoleTaskData.mProgress.setNull();
13276
13277 LogFlowThisFuncLeave();
13278 return S_OK;
13279}
13280
13281
13282/**
13283 * Goes through the USB filters of the given machine to see if the given
13284 * device matches any filter or not.
13285 *
13286 * @note Locks the same as USBController::hasMatchingFilter() does.
13287 */
13288HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13289 BOOL *aMatched,
13290 ULONG *aMaskedInterfaces)
13291{
13292 LogFlowThisFunc(("\n"));
13293
13294#ifdef VBOX_WITH_USB
13295 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13296#else
13297 NOREF(aDevice);
13298 NOREF(aMaskedInterfaces);
13299 *aMatched = FALSE;
13300#endif
13301
13302 return S_OK;
13303}
13304
13305/**
13306 * @note Locks the same as Host::captureUSBDevice() does.
13307 */
13308HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13309{
13310 LogFlowThisFunc(("\n"));
13311
13312#ifdef VBOX_WITH_USB
13313 /* if captureDeviceForVM() fails, it must have set extended error info */
13314 clearError();
13315 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13316 if (FAILED(rc)) return rc;
13317
13318 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13319 AssertReturn(service, E_FAIL);
13320 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13321#else
13322 NOREF(aId);
13323 return E_NOTIMPL;
13324#endif
13325}
13326
13327/**
13328 * @note Locks the same as Host::detachUSBDevice() does.
13329 */
13330HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13331 BOOL aDone)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_USB
13336 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13337 AssertReturn(service, E_FAIL);
13338 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13339#else
13340 NOREF(aId);
13341 NOREF(aDone);
13342 return E_NOTIMPL;
13343#endif
13344}
13345
13346/**
13347 * Inserts all machine filters to the USB proxy service and then calls
13348 * Host::autoCaptureUSBDevices().
13349 *
13350 * Called by Console from the VM process upon VM startup.
13351 *
13352 * @note Locks what called methods lock.
13353 */
13354HRESULT SessionMachine::autoCaptureUSBDevices()
13355{
13356 LogFlowThisFunc(("\n"));
13357
13358#ifdef VBOX_WITH_USB
13359 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13360 AssertComRC(rc);
13361 NOREF(rc);
13362
13363 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13364 AssertReturn(service, E_FAIL);
13365 return service->autoCaptureDevicesForVM(this);
13366#else
13367 return S_OK;
13368#endif
13369}
13370
13371/**
13372 * Removes all machine filters from the USB proxy service and then calls
13373 * Host::detachAllUSBDevices().
13374 *
13375 * Called by Console from the VM process upon normal VM termination or by
13376 * SessionMachine::uninit() upon abnormal VM termination (from under the
13377 * Machine/SessionMachine lock).
13378 *
13379 * @note Locks what called methods lock.
13380 */
13381HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13382{
13383 LogFlowThisFunc(("\n"));
13384
13385#ifdef VBOX_WITH_USB
13386 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13387 AssertComRC(rc);
13388 NOREF(rc);
13389
13390 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13391 AssertReturn(service, E_FAIL);
13392 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13393#else
13394 NOREF(aDone);
13395 return S_OK;
13396#endif
13397}
13398
13399/**
13400 * @note Locks this object for writing.
13401 */
13402HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13403 ComPtr<IProgress> &aProgress)
13404{
13405 LogFlowThisFuncEnter();
13406
13407 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13408 /*
13409 * We don't assert below because it might happen that a non-direct session
13410 * informs us it is closed right after we've been uninitialized -- it's ok.
13411 */
13412
13413 /* get IInternalSessionControl interface */
13414 ComPtr<IInternalSessionControl> control(aSession);
13415
13416 ComAssertRet(!control.isNull(), E_INVALIDARG);
13417
13418 /* Creating a Progress object requires the VirtualBox lock, and
13419 * thus locking it here is required by the lock order rules. */
13420 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13421
13422 if (control == mData->mSession.mDirectControl)
13423 {
13424 /* The direct session is being normally closed by the client process
13425 * ----------------------------------------------------------------- */
13426
13427 /* go to the closing state (essential for all open*Session() calls and
13428 * for #i_checkForDeath()) */
13429 Assert(mData->mSession.mState == SessionState_Locked);
13430 mData->mSession.mState = SessionState_Unlocking;
13431
13432 /* set direct control to NULL to release the remote instance */
13433 mData->mSession.mDirectControl.setNull();
13434 LogFlowThisFunc(("Direct control is set to NULL\n"));
13435
13436 if (mData->mSession.mProgress)
13437 {
13438 /* finalize the progress, someone might wait if a frontend
13439 * closes the session before powering on the VM. */
13440 mData->mSession.mProgress->notifyComplete(E_FAIL,
13441 COM_IIDOF(ISession),
13442 getComponentName(),
13443 tr("The VM session was closed before any attempt to power it on"));
13444 mData->mSession.mProgress.setNull();
13445 }
13446
13447 /* Create the progress object the client will use to wait until
13448 * #i_checkForDeath() is called to uninitialize this session object after
13449 * it releases the IPC semaphore.
13450 * Note! Because we're "reusing" mProgress here, this must be a proxy
13451 * object just like for LaunchVMProcess. */
13452 Assert(mData->mSession.mProgress.isNull());
13453 ComObjPtr<ProgressProxy> progress;
13454 progress.createObject();
13455 ComPtr<IUnknown> pPeer(mPeer);
13456 progress->init(mParent, pPeer,
13457 Bstr(tr("Closing session")).raw(),
13458 FALSE /* aCancelable */);
13459 progress.queryInterfaceTo(aProgress.asOutParam());
13460 mData->mSession.mProgress = progress;
13461 }
13462 else
13463 {
13464 /* the remote session is being normally closed */
13465 bool found = false;
13466 for (Data::Session::RemoteControlList::iterator
13467 it = mData->mSession.mRemoteControls.begin();
13468 it != mData->mSession.mRemoteControls.end();
13469 ++it)
13470 {
13471 if (control == *it)
13472 {
13473 found = true;
13474 // This MUST be erase(it), not remove(*it) as the latter
13475 // triggers a very nasty use after free due to the place where
13476 // the value "lives".
13477 mData->mSession.mRemoteControls.erase(it);
13478 break;
13479 }
13480 }
13481 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13482 E_INVALIDARG);
13483 }
13484
13485 /* signal the client watcher thread, because the client is going away */
13486 mParent->i_updateClientWatcher();
13487
13488 LogFlowThisFuncLeave();
13489 return S_OK;
13490}
13491
13492HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13493 std::vector<com::Utf8Str> &aValues,
13494 std::vector<LONG64> &aTimestamps,
13495 std::vector<com::Utf8Str> &aFlags)
13496{
13497 LogFlowThisFunc(("\n"));
13498
13499#ifdef VBOX_WITH_GUEST_PROPS
13500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13501
13502 size_t cEntries = mHWData->mGuestProperties.size();
13503 aNames.resize(cEntries);
13504 aValues.resize(cEntries);
13505 aTimestamps.resize(cEntries);
13506 aFlags.resize(cEntries);
13507
13508 size_t i = 0;
13509 for (HWData::GuestPropertyMap::const_iterator
13510 it = mHWData->mGuestProperties.begin();
13511 it != mHWData->mGuestProperties.end();
13512 ++it, ++i)
13513 {
13514 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13515 aNames[i] = it->first;
13516 aValues[i] = it->second.strValue;
13517 aTimestamps[i] = it->second.mTimestamp;
13518
13519 /* If it is NULL, keep it NULL. */
13520 if (it->second.mFlags)
13521 {
13522 GuestPropWriteFlags(it->second.mFlags, szFlags);
13523 aFlags[i] = szFlags;
13524 }
13525 else
13526 aFlags[i] = "";
13527 }
13528 return S_OK;
13529#else
13530 ReturnComNotImplemented();
13531#endif
13532}
13533
13534HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13535 const com::Utf8Str &aValue,
13536 LONG64 aTimestamp,
13537 const com::Utf8Str &aFlags)
13538{
13539 LogFlowThisFunc(("\n"));
13540
13541#ifdef VBOX_WITH_GUEST_PROPS
13542 try
13543 {
13544 /*
13545 * Convert input up front.
13546 */
13547 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13548 if (aFlags.length())
13549 {
13550 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13551 AssertRCReturn(vrc, E_INVALIDARG);
13552 }
13553
13554 /*
13555 * Now grab the object lock, validate the state and do the update.
13556 */
13557
13558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13559
13560 if (!Global::IsOnline(mData->mMachineState))
13561 {
13562 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13563 VBOX_E_INVALID_VM_STATE);
13564 }
13565
13566 i_setModified(IsModified_MachineData);
13567 mHWData.backup();
13568
13569 bool fDelete = !aValue.length();
13570 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13571 if (it != mHWData->mGuestProperties.end())
13572 {
13573 if (!fDelete)
13574 {
13575 it->second.strValue = aValue;
13576 it->second.mTimestamp = aTimestamp;
13577 it->second.mFlags = fFlags;
13578 }
13579 else
13580 mHWData->mGuestProperties.erase(it);
13581
13582 mData->mGuestPropertiesModified = TRUE;
13583 }
13584 else if (!fDelete)
13585 {
13586 HWData::GuestProperty prop;
13587 prop.strValue = aValue;
13588 prop.mTimestamp = aTimestamp;
13589 prop.mFlags = fFlags;
13590
13591 mHWData->mGuestProperties[aName] = prop;
13592 mData->mGuestPropertiesModified = TRUE;
13593 }
13594
13595 alock.release();
13596
13597 mParent->i_onGuestPropertyChange(mData->mUuid,
13598 Bstr(aName).raw(),
13599 Bstr(aValue).raw(),
13600 Bstr(aFlags).raw());
13601 }
13602 catch (...)
13603 {
13604 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13605 }
13606 return S_OK;
13607#else
13608 ReturnComNotImplemented();
13609#endif
13610}
13611
13612
13613HRESULT SessionMachine::lockMedia()
13614{
13615 AutoMultiWriteLock2 alock(this->lockHandle(),
13616 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13617
13618 AssertReturn( mData->mMachineState == MachineState_Starting
13619 || mData->mMachineState == MachineState_Restoring
13620 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13621
13622 clearError();
13623 alock.release();
13624 return i_lockMedia();
13625}
13626
13627HRESULT SessionMachine::unlockMedia()
13628{
13629 HRESULT hrc = i_unlockMedia();
13630 return hrc;
13631}
13632
13633HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13634 ComPtr<IMediumAttachment> &aNewAttachment)
13635{
13636 // request the host lock first, since might be calling Host methods for getting host drives;
13637 // next, protect the media tree all the while we're in here, as well as our member variables
13638 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13639 this->lockHandle(),
13640 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13641
13642 IMediumAttachment *iAttach = aAttachment;
13643 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13644
13645 Utf8Str ctrlName;
13646 LONG lPort;
13647 LONG lDevice;
13648 bool fTempEject;
13649 {
13650 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13651
13652 /* Need to query the details first, as the IMediumAttachment reference
13653 * might be to the original settings, which we are going to change. */
13654 ctrlName = pAttach->i_getControllerName();
13655 lPort = pAttach->i_getPort();
13656 lDevice = pAttach->i_getDevice();
13657 fTempEject = pAttach->i_getTempEject();
13658 }
13659
13660 if (!fTempEject)
13661 {
13662 /* Remember previously mounted medium. The medium before taking the
13663 * backup is not necessarily the same thing. */
13664 ComObjPtr<Medium> oldmedium;
13665 oldmedium = pAttach->i_getMedium();
13666
13667 i_setModified(IsModified_Storage);
13668 mMediumAttachments.backup();
13669
13670 // The backup operation makes the pAttach reference point to the
13671 // old settings. Re-get the correct reference.
13672 pAttach = i_findAttachment(*mMediumAttachments.data(),
13673 ctrlName,
13674 lPort,
13675 lDevice);
13676
13677 {
13678 AutoCaller autoAttachCaller(this);
13679 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13680
13681 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13682 if (!oldmedium.isNull())
13683 oldmedium->i_removeBackReference(mData->mUuid);
13684
13685 pAttach->i_updateMedium(NULL);
13686 pAttach->i_updateEjected();
13687 }
13688
13689 i_setModified(IsModified_Storage);
13690 }
13691 else
13692 {
13693 {
13694 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13695 pAttach->i_updateEjected();
13696 }
13697 }
13698
13699 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13700
13701 return S_OK;
13702}
13703
13704HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13705 com::Utf8Str &aResult)
13706{
13707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13708
13709 HRESULT hr = S_OK;
13710
13711 if (!mAuthLibCtx.hAuthLibrary)
13712 {
13713 /* Load the external authentication library. */
13714 Bstr authLibrary;
13715 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13716
13717 Utf8Str filename = authLibrary;
13718
13719 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13720 if (RT_FAILURE(vrc))
13721 hr = setErrorBoth(E_FAIL, vrc,
13722 tr("Could not load the external authentication library '%s' (%Rrc)"),
13723 filename.c_str(), vrc);
13724 }
13725
13726 /* The auth library might need the machine lock. */
13727 alock.release();
13728
13729 if (FAILED(hr))
13730 return hr;
13731
13732 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13733 {
13734 enum VRDEAuthParams
13735 {
13736 parmUuid = 1,
13737 parmGuestJudgement,
13738 parmUser,
13739 parmPassword,
13740 parmDomain,
13741 parmClientId
13742 };
13743
13744 AuthResult result = AuthResultAccessDenied;
13745
13746 Guid uuid(aAuthParams[parmUuid]);
13747 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13748 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13749
13750 result = AuthLibAuthenticate(&mAuthLibCtx,
13751 uuid.raw(), guestJudgement,
13752 aAuthParams[parmUser].c_str(),
13753 aAuthParams[parmPassword].c_str(),
13754 aAuthParams[parmDomain].c_str(),
13755 u32ClientId);
13756
13757 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13758 size_t cbPassword = aAuthParams[parmPassword].length();
13759 if (cbPassword)
13760 {
13761 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13762 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13763 }
13764
13765 if (result == AuthResultAccessGranted)
13766 aResult = "granted";
13767 else
13768 aResult = "denied";
13769
13770 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13771 aAuthParams[parmUser].c_str(), aResult.c_str()));
13772 }
13773 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13774 {
13775 enum VRDEAuthDisconnectParams
13776 {
13777 parmUuid = 1,
13778 parmClientId
13779 };
13780
13781 Guid uuid(aAuthParams[parmUuid]);
13782 uint32_t u32ClientId = 0;
13783 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13784 }
13785 else
13786 {
13787 hr = E_INVALIDARG;
13788 }
13789
13790 return hr;
13791}
13792
13793// public methods only for internal purposes
13794/////////////////////////////////////////////////////////////////////////////
13795
13796#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13797/**
13798 * Called from the client watcher thread to check for expected or unexpected
13799 * death of the client process that has a direct session to this machine.
13800 *
13801 * On Win32 and on OS/2, this method is called only when we've got the
13802 * mutex (i.e. the client has either died or terminated normally) so it always
13803 * returns @c true (the client is terminated, the session machine is
13804 * uninitialized).
13805 *
13806 * On other platforms, the method returns @c true if the client process has
13807 * terminated normally or abnormally and the session machine was uninitialized,
13808 * and @c false if the client process is still alive.
13809 *
13810 * @note Locks this object for writing.
13811 */
13812bool SessionMachine::i_checkForDeath()
13813{
13814 Uninit::Reason reason;
13815 bool terminated = false;
13816
13817 /* Enclose autoCaller with a block because calling uninit() from under it
13818 * will deadlock. */
13819 {
13820 AutoCaller autoCaller(this);
13821 if (!autoCaller.isOk())
13822 {
13823 /* return true if not ready, to cause the client watcher to exclude
13824 * the corresponding session from watching */
13825 LogFlowThisFunc(("Already uninitialized!\n"));
13826 return true;
13827 }
13828
13829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13830
13831 /* Determine the reason of death: if the session state is Closing here,
13832 * everything is fine. Otherwise it means that the client did not call
13833 * OnSessionEnd() before it released the IPC semaphore. This may happen
13834 * either because the client process has abnormally terminated, or
13835 * because it simply forgot to call ISession::Close() before exiting. We
13836 * threat the latter also as an abnormal termination (see
13837 * Session::uninit() for details). */
13838 reason = mData->mSession.mState == SessionState_Unlocking ?
13839 Uninit::Normal :
13840 Uninit::Abnormal;
13841
13842 if (mClientToken)
13843 terminated = mClientToken->release();
13844 } /* AutoCaller block */
13845
13846 if (terminated)
13847 uninit(reason);
13848
13849 return terminated;
13850}
13851
13852void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13853{
13854 LogFlowThisFunc(("\n"));
13855
13856 strTokenId.setNull();
13857
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturnVoid(autoCaller.rc());
13860
13861 Assert(mClientToken);
13862 if (mClientToken)
13863 mClientToken->getId(strTokenId);
13864}
13865#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13866IToken *SessionMachine::i_getToken()
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), NULL);
13872
13873 Assert(mClientToken);
13874 if (mClientToken)
13875 return mClientToken->getToken();
13876 else
13877 return NULL;
13878}
13879#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13880
13881Machine::ClientToken *SessionMachine::i_getClientToken()
13882{
13883 LogFlowThisFunc(("\n"));
13884
13885 AutoCaller autoCaller(this);
13886 AssertComRCReturn(autoCaller.rc(), NULL);
13887
13888 return mClientToken;
13889}
13890
13891
13892/**
13893 * @note Locks this object for reading.
13894 */
13895HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13896{
13897 LogFlowThisFunc(("\n"));
13898
13899 AutoCaller autoCaller(this);
13900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13901
13902 ComPtr<IInternalSessionControl> directControl;
13903 {
13904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13905 if (mData->mSession.mLockType == LockType_VM)
13906 directControl = mData->mSession.mDirectControl;
13907 }
13908
13909 /* ignore notifications sent after #OnSessionEnd() is called */
13910 if (!directControl)
13911 return S_OK;
13912
13913 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13914}
13915
13916/**
13917 * @note Locks this object for reading.
13918 */
13919HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13920 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13921 IN_BSTR aGuestIp, LONG aGuestPort)
13922{
13923 LogFlowThisFunc(("\n"));
13924
13925 AutoCaller autoCaller(this);
13926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13927
13928 ComPtr<IInternalSessionControl> directControl;
13929 {
13930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13931 if (mData->mSession.mLockType == LockType_VM)
13932 directControl = mData->mSession.mDirectControl;
13933 }
13934
13935 /* ignore notifications sent after #OnSessionEnd() is called */
13936 if (!directControl)
13937 return S_OK;
13938 /*
13939 * instead acting like callback we ask IVirtualBox deliver corresponding event
13940 */
13941
13942 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13943 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13944 return S_OK;
13945}
13946
13947/**
13948 * @note Locks this object for reading.
13949 */
13950HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13956
13957 ComPtr<IInternalSessionControl> directControl;
13958 {
13959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13960 if (mData->mSession.mLockType == LockType_VM)
13961 directControl = mData->mSession.mDirectControl;
13962 }
13963
13964 /* ignore notifications sent after #OnSessionEnd() is called */
13965 if (!directControl)
13966 return S_OK;
13967
13968 return directControl->OnAudioAdapterChange(audioAdapter);
13969}
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnSerialPortChange(serialPort);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnParallelPortChange(parallelPort);
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onStorageControllerChange()
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnStorageControllerChange();
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 mParent->i_onMediumChanged(aAttachment);
14061
14062 /* ignore notifications sent after #OnSessionEnd() is called */
14063 if (!directControl)
14064 return S_OK;
14065
14066 return directControl->OnMediumChange(aAttachment, aForce);
14067}
14068
14069HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14070{
14071 LogFlowThisFunc(("\n"));
14072
14073 AutoCaller autoCaller(this);
14074 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14075
14076 ComPtr<IInternalSessionControl> directControl;
14077 {
14078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14079 if (mData->mSession.mLockType == LockType_VM)
14080 directControl = mData->mSession.mDirectControl;
14081 }
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnVMProcessPriorityChange(aPriority);
14088}
14089
14090/**
14091 * @note Locks this object for reading.
14092 */
14093HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 AutoCaller autoCaller(this);
14098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14099
14100 ComPtr<IInternalSessionControl> directControl;
14101 {
14102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14103 if (mData->mSession.mLockType == LockType_VM)
14104 directControl = mData->mSession.mDirectControl;
14105 }
14106
14107 /* ignore notifications sent after #OnSessionEnd() is called */
14108 if (!directControl)
14109 return S_OK;
14110
14111 return directControl->OnCPUChange(aCPU, aRemove);
14112}
14113
14114HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnVRDEServerChange(aRestart);
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnRecordingChange(aEnable);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onUSBControllerChange()
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnUSBControllerChange();
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::i_onSharedFolderChange()
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturnRC(autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturnRC(autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnClipboardModeChange(aClipboardMode);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturnRC(autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnDnDModeChange(aDnDMode);
14277}
14278
14279/**
14280 * @note Locks this object for reading.
14281 */
14282HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 if (mData->mSession.mLockType == LockType_VM)
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14301}
14302
14303/**
14304 * @note Locks this object for reading.
14305 */
14306HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14307{
14308 LogFlowThisFunc(("\n"));
14309
14310 AutoCaller autoCaller(this);
14311 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14312
14313 ComPtr<IInternalSessionControl> directControl;
14314 {
14315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14316 if (mData->mSession.mLockType == LockType_VM)
14317 directControl = mData->mSession.mDirectControl;
14318 }
14319
14320 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14321
14322 /* ignore notifications sent after #OnSessionEnd() is called */
14323 if (!directControl)
14324 return S_OK;
14325
14326 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14327}
14328
14329/**
14330 * Returns @c true if this machine's USB controller reports it has a matching
14331 * filter for the given USB device and @c false otherwise.
14332 *
14333 * @note locks this object for reading.
14334 */
14335bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14336{
14337 AutoCaller autoCaller(this);
14338 /* silently return if not ready -- this method may be called after the
14339 * direct machine session has been called */
14340 if (!autoCaller.isOk())
14341 return false;
14342
14343#ifdef VBOX_WITH_USB
14344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14345
14346 switch (mData->mMachineState)
14347 {
14348 case MachineState_Starting:
14349 case MachineState_Restoring:
14350 case MachineState_TeleportingIn:
14351 case MachineState_Paused:
14352 case MachineState_Running:
14353 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14354 * elsewhere... */
14355 alock.release();
14356 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14357 default: break;
14358 }
14359#else
14360 NOREF(aDevice);
14361 NOREF(aMaskedIfs);
14362#endif
14363 return false;
14364}
14365
14366/**
14367 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14368 */
14369HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14370 IVirtualBoxErrorInfo *aError,
14371 ULONG aMaskedIfs,
14372 const com::Utf8Str &aCaptureFilename)
14373{
14374 LogFlowThisFunc(("\n"));
14375
14376 AutoCaller autoCaller(this);
14377
14378 /* This notification may happen after the machine object has been
14379 * uninitialized (the session was closed), so don't assert. */
14380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14381
14382 ComPtr<IInternalSessionControl> directControl;
14383 {
14384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14385 if (mData->mSession.mLockType == LockType_VM)
14386 directControl = mData->mSession.mDirectControl;
14387 }
14388
14389 /* fail on notifications sent after #OnSessionEnd() is called, it is
14390 * expected by the caller */
14391 if (!directControl)
14392 return E_FAIL;
14393
14394 /* No locks should be held at this point. */
14395 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14396 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14397
14398 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14399}
14400
14401/**
14402 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14403 */
14404HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14405 IVirtualBoxErrorInfo *aError)
14406{
14407 LogFlowThisFunc(("\n"));
14408
14409 AutoCaller autoCaller(this);
14410
14411 /* This notification may happen after the machine object has been
14412 * uninitialized (the session was closed), so don't assert. */
14413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14414
14415 ComPtr<IInternalSessionControl> directControl;
14416 {
14417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14418 if (mData->mSession.mLockType == LockType_VM)
14419 directControl = mData->mSession.mDirectControl;
14420 }
14421
14422 /* fail on notifications sent after #OnSessionEnd() is called, it is
14423 * expected by the caller */
14424 if (!directControl)
14425 return E_FAIL;
14426
14427 /* No locks should be held at this point. */
14428 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14429 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14430
14431 return directControl->OnUSBDeviceDetach(aId, aError);
14432}
14433
14434// protected methods
14435/////////////////////////////////////////////////////////////////////////////
14436
14437/**
14438 * Deletes the given file if it is no longer in use by either the current machine state
14439 * (if the machine is "saved") or any of the machine's snapshots.
14440 *
14441 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14442 * but is different for each SnapshotMachine. When calling this, the order of calling this
14443 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14444 * is therefore critical. I know, it's all rather messy.
14445 *
14446 * @param strStateFile
14447 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14448 * the test for whether the saved state file is in use.
14449 */
14450void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14451 Snapshot *pSnapshotToIgnore)
14452{
14453 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14454 if ( (strStateFile.isNotEmpty())
14455 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14456 )
14457 // ... and it must also not be shared with other snapshots
14458 if ( !mData->mFirstSnapshot
14459 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14460 // this checks the SnapshotMachine's state file paths
14461 )
14462 RTFileDelete(strStateFile.c_str());
14463}
14464
14465/**
14466 * Locks the attached media.
14467 *
14468 * All attached hard disks are locked for writing and DVD/floppy are locked for
14469 * reading. Parents of attached hard disks (if any) are locked for reading.
14470 *
14471 * This method also performs accessibility check of all media it locks: if some
14472 * media is inaccessible, the method will return a failure and a bunch of
14473 * extended error info objects per each inaccessible medium.
14474 *
14475 * Note that this method is atomic: if it returns a success, all media are
14476 * locked as described above; on failure no media is locked at all (all
14477 * succeeded individual locks will be undone).
14478 *
14479 * The caller is responsible for doing the necessary state sanity checks.
14480 *
14481 * The locks made by this method must be undone by calling #unlockMedia() when
14482 * no more needed.
14483 */
14484HRESULT SessionMachine::i_lockMedia()
14485{
14486 AutoCaller autoCaller(this);
14487 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14488
14489 AutoMultiWriteLock2 alock(this->lockHandle(),
14490 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14491
14492 /* bail out if trying to lock things with already set up locking */
14493 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14494
14495 MultiResult mrc(S_OK);
14496
14497 /* Collect locking information for all medium objects attached to the VM. */
14498 for (MediumAttachmentList::const_iterator
14499 it = mMediumAttachments->begin();
14500 it != mMediumAttachments->end();
14501 ++it)
14502 {
14503 MediumAttachment *pAtt = *it;
14504 DeviceType_T devType = pAtt->i_getType();
14505 Medium *pMedium = pAtt->i_getMedium();
14506
14507 MediumLockList *pMediumLockList(new MediumLockList());
14508 // There can be attachments without a medium (floppy/dvd), and thus
14509 // it's impossible to create a medium lock list. It still makes sense
14510 // to have the empty medium lock list in the map in case a medium is
14511 // attached later.
14512 if (pMedium != NULL)
14513 {
14514 MediumType_T mediumType = pMedium->i_getType();
14515 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14516 || mediumType == MediumType_Shareable;
14517 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14518
14519 alock.release();
14520 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14521 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14522 false /* fMediumLockWriteAll */,
14523 NULL,
14524 *pMediumLockList);
14525 alock.acquire();
14526 if (FAILED(mrc))
14527 {
14528 delete pMediumLockList;
14529 mData->mSession.mLockedMedia.Clear();
14530 break;
14531 }
14532 }
14533
14534 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14535 if (FAILED(rc))
14536 {
14537 mData->mSession.mLockedMedia.Clear();
14538 mrc = setError(rc,
14539 tr("Collecting locking information for all attached media failed"));
14540 break;
14541 }
14542 }
14543
14544 if (SUCCEEDED(mrc))
14545 {
14546 /* Now lock all media. If this fails, nothing is locked. */
14547 alock.release();
14548 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14549 alock.acquire();
14550 if (FAILED(rc))
14551 {
14552 mrc = setError(rc,
14553 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14554 }
14555 }
14556
14557 return mrc;
14558}
14559
14560/**
14561 * Undoes the locks made by by #lockMedia().
14562 */
14563HRESULT SessionMachine::i_unlockMedia()
14564{
14565 AutoCaller autoCaller(this);
14566 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14567
14568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14569
14570 /* we may be holding important error info on the current thread;
14571 * preserve it */
14572 ErrorInfoKeeper eik;
14573
14574 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14575 AssertComRC(rc);
14576 return rc;
14577}
14578
14579/**
14580 * Helper to change the machine state (reimplementation).
14581 *
14582 * @note Locks this object for writing.
14583 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14584 * it can cause crashes in random places due to unexpectedly committing
14585 * the current settings. The caller is responsible for that. The call
14586 * to saveStateSettings is fine, because this method does not commit.
14587 */
14588HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14589{
14590 LogFlowThisFuncEnter();
14591 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14592
14593 AutoCaller autoCaller(this);
14594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14595
14596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14597
14598 MachineState_T oldMachineState = mData->mMachineState;
14599
14600 AssertMsgReturn(oldMachineState != aMachineState,
14601 ("oldMachineState=%s, aMachineState=%s\n",
14602 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14603 E_FAIL);
14604
14605 HRESULT rc = S_OK;
14606
14607 int stsFlags = 0;
14608 bool deleteSavedState = false;
14609
14610 /* detect some state transitions */
14611
14612 if ( ( oldMachineState == MachineState_Saved
14613 && aMachineState == MachineState_Restoring)
14614 || ( ( oldMachineState == MachineState_PoweredOff
14615 || oldMachineState == MachineState_Teleported
14616 || oldMachineState == MachineState_Aborted
14617 )
14618 && ( aMachineState == MachineState_TeleportingIn
14619 || aMachineState == MachineState_Starting
14620 )
14621 )
14622 )
14623 {
14624 /* The EMT thread is about to start */
14625
14626 /* Nothing to do here for now... */
14627
14628 /// @todo NEWMEDIA don't let mDVDDrive and other children
14629 /// change anything when in the Starting/Restoring state
14630 }
14631 else if ( ( oldMachineState == MachineState_Running
14632 || oldMachineState == MachineState_Paused
14633 || oldMachineState == MachineState_Teleporting
14634 || oldMachineState == MachineState_OnlineSnapshotting
14635 || oldMachineState == MachineState_LiveSnapshotting
14636 || oldMachineState == MachineState_Stuck
14637 || oldMachineState == MachineState_Starting
14638 || oldMachineState == MachineState_Stopping
14639 || oldMachineState == MachineState_Saving
14640 || oldMachineState == MachineState_Restoring
14641 || oldMachineState == MachineState_TeleportingPausedVM
14642 || oldMachineState == MachineState_TeleportingIn
14643 )
14644 && ( aMachineState == MachineState_PoweredOff
14645 || aMachineState == MachineState_Saved
14646 || aMachineState == MachineState_Teleported
14647 || aMachineState == MachineState_Aborted
14648 )
14649 )
14650 {
14651 /* The EMT thread has just stopped, unlock attached media. Note that as
14652 * opposed to locking that is done from Console, we do unlocking here
14653 * because the VM process may have aborted before having a chance to
14654 * properly unlock all media it locked. */
14655
14656 unlockMedia();
14657 }
14658
14659 if (oldMachineState == MachineState_Restoring)
14660 {
14661 if (aMachineState != MachineState_Saved)
14662 {
14663 /*
14664 * delete the saved state file once the machine has finished
14665 * restoring from it (note that Console sets the state from
14666 * Restoring to Saved if the VM couldn't restore successfully,
14667 * to give the user an ability to fix an error and retry --
14668 * we keep the saved state file in this case)
14669 */
14670 deleteSavedState = true;
14671 }
14672 }
14673 else if ( oldMachineState == MachineState_Saved
14674 && ( aMachineState == MachineState_PoweredOff
14675 || aMachineState == MachineState_Aborted
14676 || aMachineState == MachineState_Teleported
14677 )
14678 )
14679 {
14680 /*
14681 * delete the saved state after SessionMachine::ForgetSavedState() is called
14682 * or if the VM process (owning a direct VM session) crashed while the
14683 * VM was Saved
14684 */
14685
14686 /// @todo (dmik)
14687 // Not sure that deleting the saved state file just because of the
14688 // client death before it attempted to restore the VM is a good
14689 // thing. But when it crashes we need to go to the Aborted state
14690 // which cannot have the saved state file associated... The only
14691 // way to fix this is to make the Aborted condition not a VM state
14692 // but a bool flag: i.e., when a crash occurs, set it to true and
14693 // change the state to PoweredOff or Saved depending on the
14694 // saved state presence.
14695
14696 deleteSavedState = true;
14697 mData->mCurrentStateModified = TRUE;
14698 stsFlags |= SaveSTS_CurStateModified;
14699 }
14700
14701 if ( aMachineState == MachineState_Starting
14702 || aMachineState == MachineState_Restoring
14703 || aMachineState == MachineState_TeleportingIn
14704 )
14705 {
14706 /* set the current state modified flag to indicate that the current
14707 * state is no more identical to the state in the
14708 * current snapshot */
14709 if (!mData->mCurrentSnapshot.isNull())
14710 {
14711 mData->mCurrentStateModified = TRUE;
14712 stsFlags |= SaveSTS_CurStateModified;
14713 }
14714 }
14715
14716 if (deleteSavedState)
14717 {
14718 if (mRemoveSavedState)
14719 {
14720 Assert(!mSSData->strStateFilePath.isEmpty());
14721
14722 // it is safe to delete the saved state file if ...
14723 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14724 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14725 // ... none of the snapshots share the saved state file
14726 )
14727 RTFileDelete(mSSData->strStateFilePath.c_str());
14728 }
14729
14730 mSSData->strStateFilePath.setNull();
14731 stsFlags |= SaveSTS_StateFilePath;
14732 }
14733
14734 /* redirect to the underlying peer machine */
14735 mPeer->i_setMachineState(aMachineState);
14736
14737 if ( oldMachineState != MachineState_RestoringSnapshot
14738 && ( aMachineState == MachineState_PoweredOff
14739 || aMachineState == MachineState_Teleported
14740 || aMachineState == MachineState_Aborted
14741 || aMachineState == MachineState_Saved))
14742 {
14743 /* the machine has stopped execution
14744 * (or the saved state file was adopted) */
14745 stsFlags |= SaveSTS_StateTimeStamp;
14746 }
14747
14748 if ( ( oldMachineState == MachineState_PoweredOff
14749 || oldMachineState == MachineState_Aborted
14750 || oldMachineState == MachineState_Teleported
14751 )
14752 && aMachineState == MachineState_Saved)
14753 {
14754 /* the saved state file was adopted */
14755 Assert(!mSSData->strStateFilePath.isEmpty());
14756 stsFlags |= SaveSTS_StateFilePath;
14757 }
14758
14759#ifdef VBOX_WITH_GUEST_PROPS
14760 if ( aMachineState == MachineState_PoweredOff
14761 || aMachineState == MachineState_Aborted
14762 || aMachineState == MachineState_Teleported)
14763 {
14764 /* Make sure any transient guest properties get removed from the
14765 * property store on shutdown. */
14766 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14767
14768 /* remove it from the settings representation */
14769 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14770 for (settings::GuestPropertiesList::iterator
14771 it = llGuestProperties.begin();
14772 it != llGuestProperties.end();
14773 /*nothing*/)
14774 {
14775 const settings::GuestProperty &prop = *it;
14776 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14777 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14778 {
14779 it = llGuestProperties.erase(it);
14780 fNeedsSaving = true;
14781 }
14782 else
14783 {
14784 ++it;
14785 }
14786 }
14787
14788 /* Additionally remove it from the HWData representation. Required to
14789 * keep everything in sync, as this is what the API keeps using. */
14790 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14791 for (HWData::GuestPropertyMap::iterator
14792 it = llHWGuestProperties.begin();
14793 it != llHWGuestProperties.end();
14794 /*nothing*/)
14795 {
14796 uint32_t fFlags = it->second.mFlags;
14797 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14798 {
14799 /* iterator where we need to continue after the erase call
14800 * (C++03 is a fact still, and it doesn't return the iterator
14801 * which would allow continuing) */
14802 HWData::GuestPropertyMap::iterator it2 = it;
14803 ++it2;
14804 llHWGuestProperties.erase(it);
14805 it = it2;
14806 fNeedsSaving = true;
14807 }
14808 else
14809 {
14810 ++it;
14811 }
14812 }
14813
14814 if (fNeedsSaving)
14815 {
14816 mData->mCurrentStateModified = TRUE;
14817 stsFlags |= SaveSTS_CurStateModified;
14818 }
14819 }
14820#endif /* VBOX_WITH_GUEST_PROPS */
14821
14822 rc = i_saveStateSettings(stsFlags);
14823
14824 if ( ( oldMachineState != MachineState_PoweredOff
14825 && oldMachineState != MachineState_Aborted
14826 && oldMachineState != MachineState_Teleported
14827 )
14828 && ( aMachineState == MachineState_PoweredOff
14829 || aMachineState == MachineState_Aborted
14830 || aMachineState == MachineState_Teleported
14831 )
14832 )
14833 {
14834 /* we've been shut down for any reason */
14835 /* no special action so far */
14836 }
14837
14838 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14839 LogFlowThisFuncLeave();
14840 return rc;
14841}
14842
14843/**
14844 * Sends the current machine state value to the VM process.
14845 *
14846 * @note Locks this object for reading, then calls a client process.
14847 */
14848HRESULT SessionMachine::i_updateMachineStateOnClient()
14849{
14850 AutoCaller autoCaller(this);
14851 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14852
14853 ComPtr<IInternalSessionControl> directControl;
14854 {
14855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14856 AssertReturn(!!mData, E_FAIL);
14857 if (mData->mSession.mLockType == LockType_VM)
14858 directControl = mData->mSession.mDirectControl;
14859
14860 /* directControl may be already set to NULL here in #OnSessionEnd()
14861 * called too early by the direct session process while there is still
14862 * some operation (like deleting the snapshot) in progress. The client
14863 * process in this case is waiting inside Session::close() for the
14864 * "end session" process object to complete, while #uninit() called by
14865 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14866 * operation to complete. For now, we accept this inconsistent behavior
14867 * and simply do nothing here. */
14868
14869 if (mData->mSession.mState == SessionState_Unlocking)
14870 return S_OK;
14871 }
14872
14873 /* ignore notifications sent after #OnSessionEnd() is called */
14874 if (!directControl)
14875 return S_OK;
14876
14877 return directControl->UpdateMachineState(mData->mMachineState);
14878}
14879
14880
14881/*static*/
14882HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14883{
14884 va_list args;
14885 va_start(args, pcszMsg);
14886 HRESULT rc = setErrorInternal(aResultCode,
14887 getStaticClassIID(),
14888 getStaticComponentName(),
14889 Utf8Str(pcszMsg, args),
14890 false /* aWarning */,
14891 true /* aLogIt */);
14892 va_end(args);
14893 return rc;
14894}
14895
14896
14897HRESULT Machine::updateState(MachineState_T aState)
14898{
14899 NOREF(aState);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14904{
14905 NOREF(aProgress);
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::endPowerUp(LONG aResult)
14910{
14911 NOREF(aResult);
14912 ReturnComNotImplemented();
14913}
14914
14915HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14916{
14917 NOREF(aProgress);
14918 ReturnComNotImplemented();
14919}
14920
14921HRESULT Machine::endPoweringDown(LONG aResult,
14922 const com::Utf8Str &aErrMsg)
14923{
14924 NOREF(aResult);
14925 NOREF(aErrMsg);
14926 ReturnComNotImplemented();
14927}
14928
14929HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14930 BOOL *aMatched,
14931 ULONG *aMaskedInterfaces)
14932{
14933 NOREF(aDevice);
14934 NOREF(aMatched);
14935 NOREF(aMaskedInterfaces);
14936 ReturnComNotImplemented();
14937
14938}
14939
14940HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14941{
14942 NOREF(aId); NOREF(aCaptureFilename);
14943 ReturnComNotImplemented();
14944}
14945
14946HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14947 BOOL aDone)
14948{
14949 NOREF(aId);
14950 NOREF(aDone);
14951 ReturnComNotImplemented();
14952}
14953
14954HRESULT Machine::autoCaptureUSBDevices()
14955{
14956 ReturnComNotImplemented();
14957}
14958
14959HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14960{
14961 NOREF(aDone);
14962 ReturnComNotImplemented();
14963}
14964
14965HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14966 ComPtr<IProgress> &aProgress)
14967{
14968 NOREF(aSession);
14969 NOREF(aProgress);
14970 ReturnComNotImplemented();
14971}
14972
14973HRESULT Machine::finishOnlineMergeMedium()
14974{
14975 ReturnComNotImplemented();
14976}
14977
14978HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14979 std::vector<com::Utf8Str> &aValues,
14980 std::vector<LONG64> &aTimestamps,
14981 std::vector<com::Utf8Str> &aFlags)
14982{
14983 NOREF(aNames);
14984 NOREF(aValues);
14985 NOREF(aTimestamps);
14986 NOREF(aFlags);
14987 ReturnComNotImplemented();
14988}
14989
14990HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14991 const com::Utf8Str &aValue,
14992 LONG64 aTimestamp,
14993 const com::Utf8Str &aFlags)
14994{
14995 NOREF(aName);
14996 NOREF(aValue);
14997 NOREF(aTimestamp);
14998 NOREF(aFlags);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::lockMedia()
15003{
15004 ReturnComNotImplemented();
15005}
15006
15007HRESULT Machine::unlockMedia()
15008{
15009 ReturnComNotImplemented();
15010}
15011
15012HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15013 ComPtr<IMediumAttachment> &aNewAttachment)
15014{
15015 NOREF(aAttachment);
15016 NOREF(aNewAttachment);
15017 ReturnComNotImplemented();
15018}
15019
15020HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15021 ULONG aCpuUser,
15022 ULONG aCpuKernel,
15023 ULONG aCpuIdle,
15024 ULONG aMemTotal,
15025 ULONG aMemFree,
15026 ULONG aMemBalloon,
15027 ULONG aMemShared,
15028 ULONG aMemCache,
15029 ULONG aPagedTotal,
15030 ULONG aMemAllocTotal,
15031 ULONG aMemFreeTotal,
15032 ULONG aMemBalloonTotal,
15033 ULONG aMemSharedTotal,
15034 ULONG aVmNetRx,
15035 ULONG aVmNetTx)
15036{
15037 NOREF(aValidStats);
15038 NOREF(aCpuUser);
15039 NOREF(aCpuKernel);
15040 NOREF(aCpuIdle);
15041 NOREF(aMemTotal);
15042 NOREF(aMemFree);
15043 NOREF(aMemBalloon);
15044 NOREF(aMemShared);
15045 NOREF(aMemCache);
15046 NOREF(aPagedTotal);
15047 NOREF(aMemAllocTotal);
15048 NOREF(aMemFreeTotal);
15049 NOREF(aMemBalloonTotal);
15050 NOREF(aMemSharedTotal);
15051 NOREF(aVmNetRx);
15052 NOREF(aVmNetTx);
15053 ReturnComNotImplemented();
15054}
15055
15056HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15057 com::Utf8Str &aResult)
15058{
15059 NOREF(aAuthParams);
15060 NOREF(aResult);
15061 ReturnComNotImplemented();
15062}
15063
15064HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15065{
15066 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15067
15068 AutoCaller autoCaller(this);
15069 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15070
15071 HRESULT rc = S_OK;
15072
15073 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15074 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15075 rc = getUSBDeviceFilters(usbDeviceFilters);
15076 if (FAILED(rc)) return rc;
15077
15078 NOREF(aFlags);
15079 com::Utf8Str osTypeId;
15080 ComObjPtr<GuestOSType> osType = NULL;
15081
15082 /* Get the guest os type as a string from the VB. */
15083 rc = getOSTypeId(osTypeId);
15084 if (FAILED(rc)) return rc;
15085
15086 /* Get the os type obj that coresponds, can be used to get
15087 * the defaults for this guest OS. */
15088 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15089 if (FAILED(rc)) return rc;
15090
15091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15092
15093 /* Let the OS type select 64-bit ness. */
15094 mHWData->mLongMode = osType->i_is64Bit()
15095 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15096
15097 /* Apply network adapters defaults */
15098 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15099 mNetworkAdapters[slot]->i_applyDefaults(osType);
15100
15101 /* Apply serial port defaults */
15102 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15103 mSerialPorts[slot]->i_applyDefaults(osType);
15104
15105 /* Apply parallel port defaults - not OS dependent*/
15106 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15107 mParallelPorts[slot]->i_applyDefaults();
15108
15109
15110 /* Let the OS type enable the X2APIC */
15111 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15112
15113 /* This one covers IOAPICEnabled. */
15114 mBIOSSettings->i_applyDefaults(osType);
15115
15116 /* Initialize default record settings. */
15117 mRecordingSettings->i_applyDefaults();
15118
15119 /* Initialize default BIOS settings here */
15120 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15121 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15122
15123 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15124 if (FAILED(rc)) return rc;
15125
15126 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15127 if (FAILED(rc)) return rc;
15128
15129 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15130 if (FAILED(rc)) return rc;
15131
15132 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15133 if (FAILED(rc)) return rc;
15134
15135 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15136 if (FAILED(rc)) return rc;
15137
15138 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15139 if (FAILED(rc)) return rc;
15140
15141 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15142 if (FAILED(rc)) return rc;
15143
15144 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15145 if (FAILED(rc)) return rc;
15146
15147 BOOL mRTCUseUTC;
15148 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15149 if (FAILED(rc)) return rc;
15150
15151 setRTCUseUTC(mRTCUseUTC);
15152 if (FAILED(rc)) return rc;
15153
15154 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15155 if (FAILED(rc)) return rc;
15156
15157 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15158 if (FAILED(rc)) return rc;
15159
15160 /* Audio stuff. */
15161 AudioCodecType_T audioCodec;
15162 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15163 if (FAILED(rc)) return rc;
15164
15165 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15166 if (FAILED(rc)) return rc;
15167
15168 AudioControllerType_T audioController;
15169 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15170 if (FAILED(rc)) return rc;
15171
15172 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15173 if (FAILED(rc)) return rc;
15174
15175 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15176 if (FAILED(rc)) return rc;
15177
15178 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15179 if (FAILED(rc)) return rc;
15180
15181 /* Storage Controllers */
15182 StorageControllerType_T hdStorageControllerType;
15183 StorageBus_T hdStorageBusType;
15184 StorageControllerType_T dvdStorageControllerType;
15185 StorageBus_T dvdStorageBusType;
15186 BOOL recommendedFloppy;
15187 ComPtr<IStorageController> floppyController;
15188 ComPtr<IStorageController> hdController;
15189 ComPtr<IStorageController> dvdController;
15190 Utf8Str strFloppyName, strDVDName, strHDName;
15191
15192 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15193 strFloppyName = Bstr("Floppy 1").raw();
15194 strDVDName = Bstr("DVD 1").raw();
15195 strHDName = Bstr("HDD 1").raw();
15196
15197 /* Floppy recommended? add one. */
15198 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15199 if (FAILED(rc)) return rc;
15200 if (recommendedFloppy)
15201 {
15202 rc = addStorageController(strFloppyName,
15203 StorageBus_Floppy,
15204 floppyController);
15205 if (FAILED(rc)) return rc;
15206 }
15207
15208 /* Setup one DVD storage controller. */
15209 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15210 if (FAILED(rc)) return rc;
15211
15212 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15213 if (FAILED(rc)) return rc;
15214
15215 rc = addStorageController(strDVDName,
15216 dvdStorageBusType,
15217 dvdController);
15218 if (FAILED(rc)) return rc;
15219
15220 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15221 if (FAILED(rc)) return rc;
15222
15223 /* Setup one HDD storage controller. */
15224 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15225 if (FAILED(rc)) return rc;
15226
15227 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15228 if (FAILED(rc)) return rc;
15229
15230 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15231 {
15232 rc = addStorageController(strHDName,
15233 hdStorageBusType,
15234 hdController);
15235 if (FAILED(rc)) return rc;
15236
15237 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15238 if (FAILED(rc)) return rc;
15239 }
15240 else
15241 {
15242 /* The HD controller is the same as DVD: */
15243 hdController = dvdController;
15244 strHDName = Bstr("DVD 1").raw();
15245 }
15246
15247 /* Limit the AHCI port count if it's used because windows has trouble with
15248 * too many ports and other guest (OS X in particular) may take extra long
15249 * boot: */
15250
15251 // pParent = static_cast<Medium*>(aP)
15252 IStorageController *temp = hdController;
15253 ComObjPtr<StorageController> storageController;
15254 storageController = static_cast<StorageController *>(temp);
15255
15256 // tempHDController = aHDController;
15257 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15258 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15259 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15260 storageController->COMSETTER(PortCount)(1);
15261
15262 /* USB stuff */
15263
15264 bool ohciEnabled = false;
15265
15266 ComPtr<IUSBController> usbController;
15267 BOOL recommendedUSB3;
15268 BOOL recommendedUSB;
15269 BOOL usbProxyAvailable;
15270
15271 getUSBProxyAvailable(&usbProxyAvailable);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15275 if (FAILED(rc)) return rc;
15276 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15277 if (FAILED(rc)) return rc;
15278
15279 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15280 {
15281#ifdef VBOX_WITH_EXTPACK
15282 /* USB 3.0 is only available if the proper ExtPack is installed. */
15283 ExtPackManager *aManager = mParent->i_getExtPackManager();
15284 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15285 {
15286 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15287 if (FAILED(rc)) return rc;
15288
15289 /* xHci includes OHCI */
15290 ohciEnabled = true;
15291 }
15292#endif
15293 }
15294 if ( !ohciEnabled
15295 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15296 {
15297 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15298 if (FAILED(rc)) return rc;
15299 ohciEnabled = true;
15300
15301#ifdef VBOX_WITH_EXTPACK
15302 /* USB 2.0 is only available if the proper ExtPack is installed.
15303 * Note. Configuring EHCI here and providing messages about
15304 * the missing extpack isn't exactly clean, but it is a
15305 * necessary evil to patch over legacy compatability issues
15306 * introduced by the new distribution model. */
15307 ExtPackManager *manager = mParent->i_getExtPackManager();
15308 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15309 {
15310 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15311 if (FAILED(rc)) return rc;
15312 }
15313#endif
15314 }
15315
15316 /* Set recommended human interface device types: */
15317 BOOL recommendedUSBHID;
15318 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15319 if (FAILED(rc)) return rc;
15320
15321 if (recommendedUSBHID)
15322 {
15323 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15324 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15325 if (!ohciEnabled && !usbDeviceFilters.isNull())
15326 {
15327 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15328 if (FAILED(rc)) return rc;
15329 }
15330 }
15331
15332 BOOL recommendedUSBTablet;
15333 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15334 if (FAILED(rc)) return rc;
15335
15336 if (recommendedUSBTablet)
15337 {
15338 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15339 if (!ohciEnabled && !usbDeviceFilters.isNull())
15340 {
15341 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15342 if (FAILED(rc)) return rc;
15343 }
15344 }
15345 return S_OK;
15346}
15347
15348/* This isn't handled entirely by the wrapper generator yet. */
15349#ifdef VBOX_WITH_XPCOM
15350NS_DECL_CLASSINFO(SessionMachine)
15351NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15352
15353NS_DECL_CLASSINFO(SnapshotMachine)
15354NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15355#endif
Note: See TracBrowser for help on using the repository browser.

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