VirtualBox

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

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

Main: bugref:8612: Fixed error happened during VM creation with default settings. Also changed storage controller name generation according to bus type

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 533.8 KB
Line 
1/* $Id: MachineImpl.cpp 78296 2019-04-25 15:52:38Z 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 (SUCCEEDED(rc))
4181 {
4182 if (fIndirect && medium != aM)
4183 mParent->i_onMediumConfigChanged(medium);
4184 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4185 }
4186
4187 return rc;
4188}
4189
4190HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4191 LONG aDevice)
4192{
4193 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4194 aName.c_str(), aControllerPort, aDevice));
4195
4196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4197
4198 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4199 if (FAILED(rc)) return rc;
4200
4201 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4202
4203 /* Check for an existing controller. */
4204 ComObjPtr<StorageController> ctl;
4205 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4206 if (FAILED(rc)) return rc;
4207
4208 StorageControllerType_T ctrlType;
4209 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4210 if (FAILED(rc))
4211 return setError(E_FAIL,
4212 tr("Could not get type of controller '%s'"),
4213 aName.c_str());
4214
4215 bool fSilent = false;
4216 Utf8Str strReconfig;
4217
4218 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4219 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4220 if ( mData->mMachineState == MachineState_Paused
4221 && strReconfig == "1")
4222 fSilent = true;
4223
4224 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4225 bool fHotplug = false;
4226 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4227 fHotplug = true;
4228
4229 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4230 return setError(VBOX_E_INVALID_VM_STATE,
4231 tr("Controller '%s' does not support hotplugging"),
4232 aName.c_str());
4233
4234 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4235 aName,
4236 aControllerPort,
4237 aDevice);
4238 if (!pAttach)
4239 return setError(VBOX_E_OBJECT_NOT_FOUND,
4240 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4241 aDevice, aControllerPort, aName.c_str());
4242
4243 if (fHotplug && !pAttach->i_getHotPluggable())
4244 return setError(VBOX_E_NOT_SUPPORTED,
4245 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4246 aDevice, aControllerPort, aName.c_str());
4247
4248 /*
4249 * The VM has to detach the device before we delete any implicit diffs.
4250 * If this fails we can roll back without loosing data.
4251 */
4252 if (fHotplug || fSilent)
4253 {
4254 alock.release();
4255 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4256 alock.acquire();
4257 }
4258 if (FAILED(rc)) return rc;
4259
4260 /* If we are here everything went well and we can delete the implicit now. */
4261 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4262
4263 alock.release();
4264
4265 /* Save modified registries, but skip this machine as it's the caller's
4266 * job to save its settings like all other settings changes. */
4267 mParent->i_unmarkRegistryModified(i_getId());
4268 mParent->i_saveModifiedRegistries();
4269
4270 if (SUCCEEDED(rc))
4271 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4272
4273 return rc;
4274}
4275
4276HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4277 LONG aDevice, BOOL aPassthrough)
4278{
4279 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4280 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4281
4282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4283
4284 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4285 if (FAILED(rc)) return rc;
4286
4287 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4288
4289 /* Check for an existing controller. */
4290 ComObjPtr<StorageController> ctl;
4291 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4292 if (FAILED(rc)) return rc;
4293
4294 StorageControllerType_T ctrlType;
4295 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4296 if (FAILED(rc))
4297 return setError(E_FAIL,
4298 tr("Could not get type of controller '%s'"),
4299 aName.c_str());
4300
4301 bool fSilent = false;
4302 Utf8Str strReconfig;
4303
4304 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4305 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4306 if ( mData->mMachineState == MachineState_Paused
4307 && strReconfig == "1")
4308 fSilent = true;
4309
4310 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4311 bool fHotplug = false;
4312 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4313 fHotplug = true;
4314
4315 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4316 return setError(VBOX_E_INVALID_VM_STATE,
4317 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4318 aName.c_str());
4319
4320 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4321 aName,
4322 aControllerPort,
4323 aDevice);
4324 if (!pAttach)
4325 return setError(VBOX_E_OBJECT_NOT_FOUND,
4326 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4327 aDevice, aControllerPort, aName.c_str());
4328
4329
4330 i_setModified(IsModified_Storage);
4331 mMediumAttachments.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->i_getType() != DeviceType_DVD)
4336 return setError(E_INVALIDARG,
4337 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4338 aDevice, aControllerPort, aName.c_str());
4339 pAttach->i_updatePassthrough(!!aPassthrough);
4340
4341 attLock.release();
4342 alock.release();
4343 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4344 if (SUCCEEDED(rc))
4345 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4346
4347 return rc;
4348}
4349
4350HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4351 LONG aDevice, BOOL aTemporaryEject)
4352{
4353
4354 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4355 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4356
4357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4358
4359 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4360 if (FAILED(rc)) return rc;
4361
4362 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4363 aName,
4364 aControllerPort,
4365 aDevice);
4366 if (!pAttach)
4367 return setError(VBOX_E_OBJECT_NOT_FOUND,
4368 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4369 aDevice, aControllerPort, aName.c_str());
4370
4371
4372 i_setModified(IsModified_Storage);
4373 mMediumAttachments.backup();
4374
4375 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4376
4377 if (pAttach->i_getType() != DeviceType_DVD)
4378 return setError(E_INVALIDARG,
4379 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4380 aDevice, aControllerPort, aName.c_str());
4381 pAttach->i_updateTempEject(!!aTemporaryEject);
4382
4383 return S_OK;
4384}
4385
4386HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4387 LONG aDevice, BOOL aNonRotational)
4388{
4389
4390 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4391 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4392
4393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4394
4395 HRESULT rc = i_checkStateDependency(MutableStateDep);
4396 if (FAILED(rc)) return rc;
4397
4398 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4399
4400 if (Global::IsOnlineOrTransient(mData->mMachineState))
4401 return setError(VBOX_E_INVALID_VM_STATE,
4402 tr("Invalid machine state: %s"),
4403 Global::stringifyMachineState(mData->mMachineState));
4404
4405 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4406 aName,
4407 aControllerPort,
4408 aDevice);
4409 if (!pAttach)
4410 return setError(VBOX_E_OBJECT_NOT_FOUND,
4411 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4412 aDevice, aControllerPort, aName.c_str());
4413
4414
4415 i_setModified(IsModified_Storage);
4416 mMediumAttachments.backup();
4417
4418 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4419
4420 if (pAttach->i_getType() != DeviceType_HardDisk)
4421 return setError(E_INVALIDARG,
4422 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"),
4423 aDevice, aControllerPort, aName.c_str());
4424 pAttach->i_updateNonRotational(!!aNonRotational);
4425
4426 return S_OK;
4427}
4428
4429HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4430 LONG aDevice, BOOL aDiscard)
4431{
4432
4433 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4434 aName.c_str(), aControllerPort, aDevice, aDiscard));
4435
4436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4437
4438 HRESULT rc = i_checkStateDependency(MutableStateDep);
4439 if (FAILED(rc)) return rc;
4440
4441 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4442
4443 if (Global::IsOnlineOrTransient(mData->mMachineState))
4444 return setError(VBOX_E_INVALID_VM_STATE,
4445 tr("Invalid machine state: %s"),
4446 Global::stringifyMachineState(mData->mMachineState));
4447
4448 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4449 aName,
4450 aControllerPort,
4451 aDevice);
4452 if (!pAttach)
4453 return setError(VBOX_E_OBJECT_NOT_FOUND,
4454 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457
4458 i_setModified(IsModified_Storage);
4459 mMediumAttachments.backup();
4460
4461 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4462
4463 if (pAttach->i_getType() != DeviceType_HardDisk)
4464 return setError(E_INVALIDARG,
4465 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"),
4466 aDevice, aControllerPort, aName.c_str());
4467 pAttach->i_updateDiscard(!!aDiscard);
4468
4469 return S_OK;
4470}
4471
4472HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4473 LONG aDevice, BOOL aHotPluggable)
4474{
4475 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4476 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4477
4478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4479
4480 HRESULT rc = i_checkStateDependency(MutableStateDep);
4481 if (FAILED(rc)) return rc;
4482
4483 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4484
4485 if (Global::IsOnlineOrTransient(mData->mMachineState))
4486 return setError(VBOX_E_INVALID_VM_STATE,
4487 tr("Invalid machine state: %s"),
4488 Global::stringifyMachineState(mData->mMachineState));
4489
4490 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4491 aName,
4492 aControllerPort,
4493 aDevice);
4494 if (!pAttach)
4495 return setError(VBOX_E_OBJECT_NOT_FOUND,
4496 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4497 aDevice, aControllerPort, aName.c_str());
4498
4499 /* Check for an existing controller. */
4500 ComObjPtr<StorageController> ctl;
4501 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4502 if (FAILED(rc)) return rc;
4503
4504 StorageControllerType_T ctrlType;
4505 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4506 if (FAILED(rc))
4507 return setError(E_FAIL,
4508 tr("Could not get type of controller '%s'"),
4509 aName.c_str());
4510
4511 if (!i_isControllerHotplugCapable(ctrlType))
4512 return setError(VBOX_E_NOT_SUPPORTED,
4513 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4514 aName.c_str());
4515
4516 i_setModified(IsModified_Storage);
4517 mMediumAttachments.backup();
4518
4519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4520
4521 if (pAttach->i_getType() == DeviceType_Floppy)
4522 return setError(E_INVALIDARG,
4523 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"),
4524 aDevice, aControllerPort, aName.c_str());
4525 pAttach->i_updateHotPluggable(!!aHotPluggable);
4526
4527 return S_OK;
4528}
4529
4530HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4531 LONG aDevice)
4532{
4533 int rc = S_OK;
4534 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4535 aName.c_str(), aControllerPort, aDevice));
4536
4537 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4538
4539 return rc;
4540}
4541
4542HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4543 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4544{
4545 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4546 aName.c_str(), aControllerPort, aDevice));
4547
4548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4549
4550 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4551 if (FAILED(rc)) return rc;
4552
4553 if (Global::IsOnlineOrTransient(mData->mMachineState))
4554 return setError(VBOX_E_INVALID_VM_STATE,
4555 tr("Invalid machine state: %s"),
4556 Global::stringifyMachineState(mData->mMachineState));
4557
4558 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4559 aName,
4560 aControllerPort,
4561 aDevice);
4562 if (!pAttach)
4563 return setError(VBOX_E_OBJECT_NOT_FOUND,
4564 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4565 aDevice, aControllerPort, aName.c_str());
4566
4567
4568 i_setModified(IsModified_Storage);
4569 mMediumAttachments.backup();
4570
4571 IBandwidthGroup *iB = aBandwidthGroup;
4572 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4573 if (aBandwidthGroup && group.isNull())
4574 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4575
4576 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4577
4578 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4579 if (strBandwidthGroupOld.isNotEmpty())
4580 {
4581 /* Get the bandwidth group object and release it - this must not fail. */
4582 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4583 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4584 Assert(SUCCEEDED(rc));
4585
4586 pBandwidthGroupOld->i_release();
4587 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4588 }
4589
4590 if (!group.isNull())
4591 {
4592 group->i_reference();
4593 pAttach->i_updateBandwidthGroup(group->i_getName());
4594 }
4595
4596 return S_OK;
4597}
4598
4599HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4600 LONG aControllerPort,
4601 LONG aDevice,
4602 DeviceType_T aType)
4603{
4604 HRESULT rc = S_OK;
4605
4606 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4607 aName.c_str(), aControllerPort, aDevice, aType));
4608
4609 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4610
4611 return rc;
4612}
4613
4614
4615HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4616 LONG aControllerPort,
4617 LONG aDevice,
4618 BOOL aForce)
4619{
4620 int rc = S_OK;
4621 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4622 aName.c_str(), aControllerPort, aForce));
4623
4624 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4625
4626 return rc;
4627}
4628
4629HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4630 LONG aControllerPort,
4631 LONG aDevice,
4632 const ComPtr<IMedium> &aMedium,
4633 BOOL aForce)
4634{
4635 int rc = S_OK;
4636 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4637 aName.c_str(), aControllerPort, aDevice, aForce));
4638
4639 // request the host lock first, since might be calling Host methods for getting host drives;
4640 // next, protect the media tree all the while we're in here, as well as our member variables
4641 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4642 this->lockHandle(),
4643 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4644
4645 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4646 aName,
4647 aControllerPort,
4648 aDevice);
4649 if (pAttach.isNull())
4650 return setError(VBOX_E_OBJECT_NOT_FOUND,
4651 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4652 aDevice, aControllerPort, aName.c_str());
4653
4654 /* Remember previously mounted medium. The medium before taking the
4655 * backup is not necessarily the same thing. */
4656 ComObjPtr<Medium> oldmedium;
4657 oldmedium = pAttach->i_getMedium();
4658
4659 IMedium *iM = aMedium;
4660 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4661 if (aMedium && pMedium.isNull())
4662 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4663
4664 AutoCaller mediumCaller(pMedium);
4665 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4666
4667 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4668 if (pMedium)
4669 {
4670 DeviceType_T mediumType = pAttach->i_getType();
4671 switch (mediumType)
4672 {
4673 case DeviceType_DVD:
4674 case DeviceType_Floppy:
4675 break;
4676
4677 default:
4678 return setError(VBOX_E_INVALID_OBJECT_STATE,
4679 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4680 aControllerPort,
4681 aDevice,
4682 aName.c_str());
4683 }
4684 }
4685
4686 i_setModified(IsModified_Storage);
4687 mMediumAttachments.backup();
4688
4689 {
4690 // The backup operation makes the pAttach reference point to the
4691 // old settings. Re-get the correct reference.
4692 pAttach = i_findAttachment(*mMediumAttachments.data(),
4693 aName,
4694 aControllerPort,
4695 aDevice);
4696 if (!oldmedium.isNull())
4697 oldmedium->i_removeBackReference(mData->mUuid);
4698 if (!pMedium.isNull())
4699 {
4700 pMedium->i_addBackReference(mData->mUuid);
4701
4702 mediumLock.release();
4703 multiLock.release();
4704 i_addMediumToRegistry(pMedium);
4705 multiLock.acquire();
4706 mediumLock.acquire();
4707 }
4708
4709 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4710 pAttach->i_updateMedium(pMedium);
4711 }
4712
4713 i_setModified(IsModified_Storage);
4714
4715 mediumLock.release();
4716 multiLock.release();
4717 rc = i_onMediumChange(pAttach, aForce);
4718 multiLock.acquire();
4719 mediumLock.acquire();
4720
4721 /* On error roll back this change only. */
4722 if (FAILED(rc))
4723 {
4724 if (!pMedium.isNull())
4725 pMedium->i_removeBackReference(mData->mUuid);
4726 pAttach = i_findAttachment(*mMediumAttachments.data(),
4727 aName,
4728 aControllerPort,
4729 aDevice);
4730 /* If the attachment is gone in the meantime, bail out. */
4731 if (pAttach.isNull())
4732 return rc;
4733 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4734 if (!oldmedium.isNull())
4735 oldmedium->i_addBackReference(mData->mUuid);
4736 pAttach->i_updateMedium(oldmedium);
4737 }
4738
4739 mediumLock.release();
4740 multiLock.release();
4741
4742 /* Save modified registries, but skip this machine as it's the caller's
4743 * job to save its settings like all other settings changes. */
4744 mParent->i_unmarkRegistryModified(i_getId());
4745 mParent->i_saveModifiedRegistries();
4746
4747 return rc;
4748}
4749HRESULT Machine::getMedium(const com::Utf8Str &aName,
4750 LONG aControllerPort,
4751 LONG aDevice,
4752 ComPtr<IMedium> &aMedium)
4753{
4754 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4755 aName.c_str(), aControllerPort, aDevice));
4756
4757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4758
4759 aMedium = NULL;
4760
4761 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4762 aName,
4763 aControllerPort,
4764 aDevice);
4765 if (pAttach.isNull())
4766 return setError(VBOX_E_OBJECT_NOT_FOUND,
4767 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4768 aDevice, aControllerPort, aName.c_str());
4769
4770 aMedium = pAttach->i_getMedium();
4771
4772 return S_OK;
4773}
4774
4775HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4776{
4777
4778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4779
4780 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4781
4782 return S_OK;
4783}
4784
4785HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4786{
4787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4788
4789 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4790
4791 return S_OK;
4792}
4793
4794HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4795{
4796 /* Do not assert if slot is out of range, just return the advertised
4797 status. testdriver/vbox.py triggers this in logVmInfo. */
4798 if (aSlot >= mNetworkAdapters.size())
4799 return setError(E_INVALIDARG,
4800 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4801 aSlot, mNetworkAdapters.size());
4802
4803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4806
4807 return S_OK;
4808}
4809
4810HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4811{
4812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4813
4814 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4815 size_t i = 0;
4816 for (settings::StringsMap::const_iterator
4817 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4818 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4819 ++it, ++i)
4820 aKeys[i] = it->first;
4821
4822 return S_OK;
4823}
4824
4825 /**
4826 * @note Locks this object for reading.
4827 */
4828HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4829 com::Utf8Str &aValue)
4830{
4831 /* start with nothing found */
4832 aValue = "";
4833
4834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4835
4836 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4837 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4838 // found:
4839 aValue = it->second; // source is a Utf8Str
4840
4841 /* return the result to caller (may be empty) */
4842 return S_OK;
4843}
4844
4845 /**
4846 * @note Locks mParent for writing + this object for writing.
4847 */
4848HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4849{
4850 /* Because control characters in aKey have caused problems in the settings
4851 * they are rejected unless the key should be deleted. */
4852 if (!aValue.isEmpty())
4853 {
4854 for (size_t i = 0; i < aKey.length(); ++i)
4855 {
4856 char ch = aKey[i];
4857 if (RTLocCIsCntrl(ch))
4858 return E_INVALIDARG;
4859 }
4860 }
4861
4862 Utf8Str strOldValue; // empty
4863
4864 // locking note: we only hold the read lock briefly to look up the old value,
4865 // then release it and call the onExtraCanChange callbacks. There is a small
4866 // chance of a race insofar as the callback might be called twice if two callers
4867 // change the same key at the same time, but that's a much better solution
4868 // than the deadlock we had here before. The actual changing of the extradata
4869 // is then performed under the write lock and race-free.
4870
4871 // look up the old value first; if nothing has changed then we need not do anything
4872 {
4873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4874
4875 // For snapshots don't even think about allowing changes, extradata
4876 // is global for a machine, so there is nothing snapshot specific.
4877 if (i_isSnapshotMachine())
4878 return setError(VBOX_E_INVALID_VM_STATE,
4879 tr("Cannot set extradata for a snapshot"));
4880
4881 // check if the right IMachine instance is used
4882 if (mData->mRegistered && !i_isSessionMachine())
4883 return setError(VBOX_E_INVALID_VM_STATE,
4884 tr("Cannot set extradata for an immutable machine"));
4885
4886 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4887 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4888 strOldValue = it->second;
4889 }
4890
4891 bool fChanged;
4892 if ((fChanged = (strOldValue != aValue)))
4893 {
4894 // ask for permission from all listeners outside the locks;
4895 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4896 // lock to copy the list of callbacks to invoke
4897 Bstr error;
4898 Bstr bstrValue(aValue);
4899
4900 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4901 {
4902 const char *sep = error.isEmpty() ? "" : ": ";
4903 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4904 return setError(E_ACCESSDENIED,
4905 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4906 aKey.c_str(),
4907 aValue.c_str(),
4908 sep,
4909 error.raw());
4910 }
4911
4912 // data is changing and change not vetoed: then write it out under the lock
4913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4914
4915 if (aValue.isEmpty())
4916 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4917 else
4918 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4919 // creates a new key if needed
4920
4921 bool fNeedsGlobalSaveSettings = false;
4922 // This saving of settings is tricky: there is no "old state" for the
4923 // extradata items at all (unlike all other settings), so the old/new
4924 // settings comparison would give a wrong result!
4925 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4926
4927 if (fNeedsGlobalSaveSettings)
4928 {
4929 // save the global settings; for that we should hold only the VirtualBox lock
4930 alock.release();
4931 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4932 mParent->i_saveSettings();
4933 }
4934 }
4935
4936 // fire notification outside the lock
4937 if (fChanged)
4938 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4939
4940 return S_OK;
4941}
4942
4943HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4944{
4945 aProgress = NULL;
4946 NOREF(aSettingsFilePath);
4947 ReturnComNotImplemented();
4948}
4949
4950HRESULT Machine::saveSettings()
4951{
4952 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4955 if (FAILED(rc)) return rc;
4956
4957 /* the settings file path may never be null */
4958 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4959
4960 /* save all VM data excluding snapshots */
4961 bool fNeedsGlobalSaveSettings = false;
4962 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4963 mlock.release();
4964
4965 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4966 {
4967 // save the global settings; for that we should hold only the VirtualBox lock
4968 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4969 rc = mParent->i_saveSettings();
4970 }
4971
4972 return rc;
4973}
4974
4975
4976HRESULT Machine::discardSettings()
4977{
4978 /*
4979 * We need to take the machine list lock here as well as the machine one
4980 * or we'll get into trouble should any media stuff require rolling back.
4981 *
4982 * Details:
4983 *
4984 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4985 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4986 * 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]
4987 * 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
4988 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4989 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4990 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4991 * 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
4992 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4993 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4994 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4995 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4996 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4997 * 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]
4998 * 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] (*)
4999 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5000 * 0:005> k
5001 * # Child-SP RetAddr Call Site
5002 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5003 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5004 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5005 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5006 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5007 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5008 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5009 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5010 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5011 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5012 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5013 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5014 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5015 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5016 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5017 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5018 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5019 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5020 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5021 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5022 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5023 *
5024 */
5025 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5027
5028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5029 if (FAILED(rc)) return rc;
5030
5031 /*
5032 * during this rollback, the session will be notified if data has
5033 * been actually changed
5034 */
5035 i_rollback(true /* aNotify */);
5036
5037 return S_OK;
5038}
5039
5040/** @note Locks objects! */
5041HRESULT Machine::unregister(AutoCaller &autoCaller,
5042 CleanupMode_T aCleanupMode,
5043 std::vector<ComPtr<IMedium> > &aMedia)
5044{
5045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5046
5047 Guid id(i_getId());
5048
5049 if (mData->mSession.mState != SessionState_Unlocked)
5050 return setError(VBOX_E_INVALID_OBJECT_STATE,
5051 tr("Cannot unregister the machine '%s' while it is locked"),
5052 mUserData->s.strName.c_str());
5053
5054 // wait for state dependents to drop to zero
5055 i_ensureNoStateDependencies();
5056
5057 if (!mData->mAccessible)
5058 {
5059 // inaccessible maschines can only be unregistered; uninitialize ourselves
5060 // here because currently there may be no unregistered that are inaccessible
5061 // (this state combination is not supported). Note releasing the caller and
5062 // leaving the lock before calling uninit()
5063 alock.release();
5064 autoCaller.release();
5065
5066 uninit();
5067
5068 mParent->i_unregisterMachine(this, id);
5069 // calls VirtualBox::i_saveSettings()
5070
5071 return S_OK;
5072 }
5073
5074 HRESULT rc = S_OK;
5075
5076 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5077 // discard saved state
5078 if (mData->mMachineState == MachineState_Saved)
5079 {
5080 // add the saved state file to the list of files the caller should delete
5081 Assert(!mSSData->strStateFilePath.isEmpty());
5082 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5083
5084 mSSData->strStateFilePath.setNull();
5085
5086 // unconditionally set the machine state to powered off, we now
5087 // know no session has locked the machine
5088 mData->mMachineState = MachineState_PoweredOff;
5089 }
5090
5091 size_t cSnapshots = 0;
5092 if (mData->mFirstSnapshot)
5093 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5094 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5095 // fail now before we start detaching media
5096 return setError(VBOX_E_INVALID_OBJECT_STATE,
5097 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5098 mUserData->s.strName.c_str(), cSnapshots);
5099
5100 // This list collects the medium objects from all medium attachments
5101 // which we will detach from the machine and its snapshots, in a specific
5102 // order which allows for closing all media without getting "media in use"
5103 // errors, simply by going through the list from the front to the back:
5104 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5105 // and must be closed before the parent media from the snapshots, or closing the parents
5106 // will fail because they still have children);
5107 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5108 // the root ("first") snapshot of the machine.
5109 MediaList llMedia;
5110
5111 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5112 && mMediumAttachments->size()
5113 )
5114 {
5115 // we have media attachments: detach them all and add the Medium objects to our list
5116 if (aCleanupMode != CleanupMode_UnregisterOnly)
5117 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5118 else
5119 return setError(VBOX_E_INVALID_OBJECT_STATE,
5120 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5121 mUserData->s.strName.c_str(), mMediumAttachments->size());
5122 }
5123
5124 if (cSnapshots)
5125 {
5126 // add the media from the medium attachments of the snapshots to llMedia
5127 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5128 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5129 // into the children first
5130
5131 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5132 MachineState_T oldState = mData->mMachineState;
5133 mData->mMachineState = MachineState_DeletingSnapshot;
5134
5135 // make a copy of the first snapshot so the refcount does not drop to 0
5136 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5137 // because of the AutoCaller voodoo)
5138 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5139
5140 // GO!
5141 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5142
5143 mData->mMachineState = oldState;
5144 }
5145
5146 if (FAILED(rc))
5147 {
5148 i_rollbackMedia();
5149 return rc;
5150 }
5151
5152 // commit all the media changes made above
5153 i_commitMedia();
5154
5155 mData->mRegistered = false;
5156
5157 // machine lock no longer needed
5158 alock.release();
5159
5160 // return media to caller
5161 aMedia.resize(llMedia.size());
5162 size_t i = 0;
5163 for (MediaList::const_iterator
5164 it = llMedia.begin();
5165 it != llMedia.end();
5166 ++it, ++i)
5167 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5168
5169 mParent->i_unregisterMachine(this, id);
5170 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5171
5172 return S_OK;
5173}
5174
5175/**
5176 * Task record for deleting a machine config.
5177 */
5178class Machine::DeleteConfigTask
5179 : public Machine::Task
5180{
5181public:
5182 DeleteConfigTask(Machine *m,
5183 Progress *p,
5184 const Utf8Str &t,
5185 const RTCList<ComPtr<IMedium> > &llMediums,
5186 const StringsList &llFilesToDelete)
5187 : Task(m, p, t),
5188 m_llMediums(llMediums),
5189 m_llFilesToDelete(llFilesToDelete)
5190 {}
5191
5192private:
5193 void handler()
5194 {
5195 try
5196 {
5197 m_pMachine->i_deleteConfigHandler(*this);
5198 }
5199 catch (...)
5200 {
5201 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5202 }
5203 }
5204
5205 RTCList<ComPtr<IMedium> > m_llMediums;
5206 StringsList m_llFilesToDelete;
5207
5208 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5209};
5210
5211/**
5212 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5213 * SessionMachine::taskHandler().
5214 *
5215 * @note Locks this object for writing.
5216 *
5217 * @param task
5218 * @return
5219 */
5220void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5221{
5222 LogFlowThisFuncEnter();
5223
5224 AutoCaller autoCaller(this);
5225 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5226 if (FAILED(autoCaller.rc()))
5227 {
5228 /* we might have been uninitialized because the session was accidentally
5229 * closed by the client, so don't assert */
5230 HRESULT rc = setError(E_FAIL,
5231 tr("The session has been accidentally closed"));
5232 task.m_pProgress->i_notifyComplete(rc);
5233 LogFlowThisFuncLeave();
5234 return;
5235 }
5236
5237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5238
5239 HRESULT rc = S_OK;
5240
5241 try
5242 {
5243 ULONG uLogHistoryCount = 3;
5244 ComPtr<ISystemProperties> systemProperties;
5245 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5246 if (FAILED(rc)) throw rc;
5247
5248 if (!systemProperties.isNull())
5249 {
5250 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5251 if (FAILED(rc)) throw rc;
5252 }
5253
5254 MachineState_T oldState = mData->mMachineState;
5255 i_setMachineState(MachineState_SettingUp);
5256 alock.release();
5257 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5258 {
5259 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5260 {
5261 AutoCaller mac(pMedium);
5262 if (FAILED(mac.rc())) throw mac.rc();
5263 Utf8Str strLocation = pMedium->i_getLocationFull();
5264 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5265 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5266 if (FAILED(rc)) throw rc;
5267 }
5268 if (pMedium->i_isMediumFormatFile())
5269 {
5270 ComPtr<IProgress> pProgress2;
5271 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5272 if (FAILED(rc)) throw rc;
5273 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5274 if (FAILED(rc)) throw rc;
5275 }
5276
5277 /* Close the medium, deliberately without checking the return
5278 * code, and without leaving any trace in the error info, as
5279 * a failure here is a very minor issue, which shouldn't happen
5280 * as above we even managed to delete the medium. */
5281 {
5282 ErrorInfoKeeper eik;
5283 pMedium->Close();
5284 }
5285 }
5286 i_setMachineState(oldState);
5287 alock.acquire();
5288
5289 // delete the files pushed on the task list by Machine::Delete()
5290 // (this includes saved states of the machine and snapshots and
5291 // medium storage files from the IMedium list passed in, and the
5292 // machine XML file)
5293 for (StringsList::const_iterator
5294 it = task.m_llFilesToDelete.begin();
5295 it != task.m_llFilesToDelete.end();
5296 ++it)
5297 {
5298 const Utf8Str &strFile = *it;
5299 LogFunc(("Deleting file %s\n", strFile.c_str()));
5300 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5301 if (FAILED(rc)) throw rc;
5302
5303 int vrc = RTFileDelete(strFile.c_str());
5304 if (RT_FAILURE(vrc))
5305 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5306 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5307 }
5308
5309 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5310 if (FAILED(rc)) throw rc;
5311
5312 /* delete the settings only when the file actually exists */
5313 if (mData->pMachineConfigFile->fileExists())
5314 {
5315 /* Delete any backup or uncommitted XML files. Ignore failures.
5316 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5317 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5318 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5319 RTFileDelete(otherXml.c_str());
5320 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5321 RTFileDelete(otherXml.c_str());
5322
5323 /* delete the Logs folder, nothing important should be left
5324 * there (we don't check for errors because the user might have
5325 * some private files there that we don't want to delete) */
5326 Utf8Str logFolder;
5327 getLogFolder(logFolder);
5328 Assert(logFolder.length());
5329 if (RTDirExists(logFolder.c_str()))
5330 {
5331 /* Delete all VBox.log[.N] files from the Logs folder
5332 * (this must be in sync with the rotation logic in
5333 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5334 * files that may have been created by the GUI. */
5335 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5336 logFolder.c_str(), RTPATH_DELIMITER);
5337 RTFileDelete(log.c_str());
5338 log = Utf8StrFmt("%s%cVBox.png",
5339 logFolder.c_str(), RTPATH_DELIMITER);
5340 RTFileDelete(log.c_str());
5341 for (int i = uLogHistoryCount; i > 0; i--)
5342 {
5343 log = Utf8StrFmt("%s%cVBox.log.%d",
5344 logFolder.c_str(), RTPATH_DELIMITER, i);
5345 RTFileDelete(log.c_str());
5346 log = Utf8StrFmt("%s%cVBox.png.%d",
5347 logFolder.c_str(), RTPATH_DELIMITER, i);
5348 RTFileDelete(log.c_str());
5349 }
5350#if defined(RT_OS_WINDOWS)
5351 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5352 RTFileDelete(log.c_str());
5353 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5354 RTFileDelete(log.c_str());
5355#endif
5356
5357 RTDirRemove(logFolder.c_str());
5358 }
5359
5360 /* delete the Snapshots folder, nothing important should be left
5361 * there (we don't check for errors because the user might have
5362 * some private files there that we don't want to delete) */
5363 Utf8Str strFullSnapshotFolder;
5364 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5365 Assert(!strFullSnapshotFolder.isEmpty());
5366 if (RTDirExists(strFullSnapshotFolder.c_str()))
5367 RTDirRemove(strFullSnapshotFolder.c_str());
5368
5369 // delete the directory that contains the settings file, but only
5370 // if it matches the VM name
5371 Utf8Str settingsDir;
5372 if (i_isInOwnDir(&settingsDir))
5373 RTDirRemove(settingsDir.c_str());
5374 }
5375
5376 alock.release();
5377
5378 mParent->i_saveModifiedRegistries();
5379 }
5380 catch (HRESULT aRC) { rc = aRC; }
5381
5382 task.m_pProgress->i_notifyComplete(rc);
5383
5384 LogFlowThisFuncLeave();
5385}
5386
5387HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5388{
5389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5390
5391 HRESULT rc = i_checkStateDependency(MutableStateDep);
5392 if (FAILED(rc)) return rc;
5393
5394 if (mData->mRegistered)
5395 return setError(VBOX_E_INVALID_VM_STATE,
5396 tr("Cannot delete settings of a registered machine"));
5397
5398 // collect files to delete
5399 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5400 if (mData->pMachineConfigFile->fileExists())
5401 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5402
5403 RTCList<ComPtr<IMedium> > llMediums;
5404 for (size_t i = 0; i < aMedia.size(); ++i)
5405 {
5406 IMedium *pIMedium(aMedia[i]);
5407 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5408 if (pMedium.isNull())
5409 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5410 SafeArray<BSTR> ids;
5411 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5412 if (FAILED(rc)) return rc;
5413 /* At this point the medium should not have any back references
5414 * anymore. If it has it is attached to another VM and *must* not
5415 * deleted. */
5416 if (ids.size() < 1)
5417 llMediums.append(pMedium);
5418 }
5419
5420 ComObjPtr<Progress> pProgress;
5421 pProgress.createObject();
5422 rc = pProgress->init(i_getVirtualBox(),
5423 static_cast<IMachine*>(this) /* aInitiator */,
5424 tr("Deleting files"),
5425 true /* fCancellable */,
5426 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5427 tr("Collecting file inventory"));
5428 if (FAILED(rc))
5429 return rc;
5430
5431 /* create and start the task on a separate thread (note that it will not
5432 * start working until we release alock) */
5433 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5434 rc = pTask->createThread();
5435 if (FAILED(rc))
5436 return rc;
5437
5438 pProgress.queryInterfaceTo(aProgress.asOutParam());
5439
5440 LogFlowFuncLeave();
5441
5442 return S_OK;
5443}
5444
5445HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5446{
5447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5448
5449 ComObjPtr<Snapshot> pSnapshot;
5450 HRESULT rc;
5451
5452 if (aNameOrId.isEmpty())
5453 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5454 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5455 else
5456 {
5457 Guid uuid(aNameOrId);
5458 if (uuid.isValid())
5459 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5460 else
5461 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5462 }
5463 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5464
5465 return rc;
5466}
5467
5468HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5469 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5470{
5471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5472
5473 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5474 if (FAILED(rc)) return rc;
5475
5476 ComObjPtr<SharedFolder> sharedFolder;
5477 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5478 if (SUCCEEDED(rc))
5479 return setError(VBOX_E_OBJECT_IN_USE,
5480 tr("Shared folder named '%s' already exists"),
5481 aName.c_str());
5482
5483 sharedFolder.createObject();
5484 rc = sharedFolder->init(i_getMachine(),
5485 aName,
5486 aHostPath,
5487 !!aWritable,
5488 !!aAutomount,
5489 aAutoMountPoint,
5490 true /* fFailOnError */);
5491 if (FAILED(rc)) return rc;
5492
5493 i_setModified(IsModified_SharedFolders);
5494 mHWData.backup();
5495 mHWData->mSharedFolders.push_back(sharedFolder);
5496
5497 /* inform the direct session if any */
5498 alock.release();
5499 i_onSharedFolderChange();
5500
5501 return S_OK;
5502}
5503
5504HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5505{
5506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5507
5508 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5509 if (FAILED(rc)) return rc;
5510
5511 ComObjPtr<SharedFolder> sharedFolder;
5512 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5513 if (FAILED(rc)) return rc;
5514
5515 i_setModified(IsModified_SharedFolders);
5516 mHWData.backup();
5517 mHWData->mSharedFolders.remove(sharedFolder);
5518
5519 /* inform the direct session if any */
5520 alock.release();
5521 i_onSharedFolderChange();
5522
5523 return S_OK;
5524}
5525
5526HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5527{
5528 /* start with No */
5529 *aCanShow = FALSE;
5530
5531 ComPtr<IInternalSessionControl> directControl;
5532 {
5533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5534
5535 if (mData->mSession.mState != SessionState_Locked)
5536 return setError(VBOX_E_INVALID_VM_STATE,
5537 tr("Machine is not locked for session (session state: %s)"),
5538 Global::stringifySessionState(mData->mSession.mState));
5539
5540 if (mData->mSession.mLockType == LockType_VM)
5541 directControl = mData->mSession.mDirectControl;
5542 }
5543
5544 /* ignore calls made after #OnSessionEnd() is called */
5545 if (!directControl)
5546 return S_OK;
5547
5548 LONG64 dummy;
5549 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5550}
5551
5552HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5553{
5554 ComPtr<IInternalSessionControl> directControl;
5555 {
5556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5557
5558 if (mData->mSession.mState != SessionState_Locked)
5559 return setError(E_FAIL,
5560 tr("Machine is not locked for session (session state: %s)"),
5561 Global::stringifySessionState(mData->mSession.mState));
5562
5563 if (mData->mSession.mLockType == LockType_VM)
5564 directControl = mData->mSession.mDirectControl;
5565 }
5566
5567 /* ignore calls made after #OnSessionEnd() is called */
5568 if (!directControl)
5569 return S_OK;
5570
5571 BOOL dummy;
5572 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5573}
5574
5575#ifdef VBOX_WITH_GUEST_PROPS
5576/**
5577 * Look up a guest property in VBoxSVC's internal structures.
5578 */
5579HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5580 com::Utf8Str &aValue,
5581 LONG64 *aTimestamp,
5582 com::Utf8Str &aFlags) const
5583{
5584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5585
5586 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5587 if (it != mHWData->mGuestProperties.end())
5588 {
5589 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5590 aValue = it->second.strValue;
5591 *aTimestamp = it->second.mTimestamp;
5592 GuestPropWriteFlags(it->second.mFlags, szFlags);
5593 aFlags = Utf8Str(szFlags);
5594 }
5595
5596 return S_OK;
5597}
5598
5599/**
5600 * Query the VM that a guest property belongs to for the property.
5601 * @returns E_ACCESSDENIED if the VM process is not available or not
5602 * currently handling queries and the lookup should then be done in
5603 * VBoxSVC.
5604 */
5605HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5606 com::Utf8Str &aValue,
5607 LONG64 *aTimestamp,
5608 com::Utf8Str &aFlags) const
5609{
5610 HRESULT rc = S_OK;
5611 Bstr bstrValue;
5612 Bstr bstrFlags;
5613
5614 ComPtr<IInternalSessionControl> directControl;
5615 {
5616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5617 if (mData->mSession.mLockType == LockType_VM)
5618 directControl = mData->mSession.mDirectControl;
5619 }
5620
5621 /* ignore calls made after #OnSessionEnd() is called */
5622 if (!directControl)
5623 rc = E_ACCESSDENIED;
5624 else
5625 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5626 0 /* accessMode */,
5627 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5628
5629 aValue = bstrValue;
5630 aFlags = bstrFlags;
5631
5632 return rc;
5633}
5634#endif // VBOX_WITH_GUEST_PROPS
5635
5636HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5637 com::Utf8Str &aValue,
5638 LONG64 *aTimestamp,
5639 com::Utf8Str &aFlags)
5640{
5641#ifndef VBOX_WITH_GUEST_PROPS
5642 ReturnComNotImplemented();
5643#else // VBOX_WITH_GUEST_PROPS
5644
5645 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5646
5647 if (rc == E_ACCESSDENIED)
5648 /* The VM is not running or the service is not (yet) accessible */
5649 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5650 return rc;
5651#endif // VBOX_WITH_GUEST_PROPS
5652}
5653
5654HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5655{
5656 LONG64 dummyTimestamp;
5657 com::Utf8Str dummyFlags;
5658 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5659 return rc;
5660
5661}
5662HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5663{
5664 com::Utf8Str dummyFlags;
5665 com::Utf8Str dummyValue;
5666 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5667 return rc;
5668}
5669
5670#ifdef VBOX_WITH_GUEST_PROPS
5671/**
5672 * Set a guest property in VBoxSVC's internal structures.
5673 */
5674HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5675 const com::Utf8Str &aFlags, bool fDelete)
5676{
5677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5678 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5679 if (FAILED(rc)) return rc;
5680
5681 try
5682 {
5683 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5684 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5685 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5686
5687 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5688 if (it == mHWData->mGuestProperties.end())
5689 {
5690 if (!fDelete)
5691 {
5692 i_setModified(IsModified_MachineData);
5693 mHWData.backupEx();
5694
5695 RTTIMESPEC time;
5696 HWData::GuestProperty prop;
5697 prop.strValue = Bstr(aValue).raw();
5698 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5699 prop.mFlags = fFlags;
5700 mHWData->mGuestProperties[aName] = prop;
5701 }
5702 }
5703 else
5704 {
5705 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5706 {
5707 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5708 }
5709 else
5710 {
5711 i_setModified(IsModified_MachineData);
5712 mHWData.backupEx();
5713
5714 /* The backupEx() operation invalidates our iterator,
5715 * so get a new one. */
5716 it = mHWData->mGuestProperties.find(aName);
5717 Assert(it != mHWData->mGuestProperties.end());
5718
5719 if (!fDelete)
5720 {
5721 RTTIMESPEC time;
5722 it->second.strValue = aValue;
5723 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5724 it->second.mFlags = fFlags;
5725 }
5726 else
5727 mHWData->mGuestProperties.erase(it);
5728 }
5729 }
5730
5731 if (SUCCEEDED(rc))
5732 {
5733 alock.release();
5734
5735 mParent->i_onGuestPropertyChange(mData->mUuid,
5736 Bstr(aName).raw(),
5737 Bstr(aValue).raw(),
5738 Bstr(aFlags).raw());
5739 }
5740 }
5741 catch (std::bad_alloc &)
5742 {
5743 rc = E_OUTOFMEMORY;
5744 }
5745
5746 return rc;
5747}
5748
5749/**
5750 * Set a property on the VM that that property belongs to.
5751 * @returns E_ACCESSDENIED if the VM process is not available or not
5752 * currently handling queries and the setting should then be done in
5753 * VBoxSVC.
5754 */
5755HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5756 const com::Utf8Str &aFlags, bool fDelete)
5757{
5758 HRESULT rc;
5759
5760 try
5761 {
5762 ComPtr<IInternalSessionControl> directControl;
5763 {
5764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5765 if (mData->mSession.mLockType == LockType_VM)
5766 directControl = mData->mSession.mDirectControl;
5767 }
5768
5769 Bstr dummy1; /* will not be changed (setter) */
5770 Bstr dummy2; /* will not be changed (setter) */
5771 LONG64 dummy64;
5772 if (!directControl)
5773 rc = E_ACCESSDENIED;
5774 else
5775 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5776 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5777 fDelete ? 2 : 1 /* accessMode */,
5778 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5779 }
5780 catch (std::bad_alloc &)
5781 {
5782 rc = E_OUTOFMEMORY;
5783 }
5784
5785 return rc;
5786}
5787#endif // VBOX_WITH_GUEST_PROPS
5788
5789HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5790 const com::Utf8Str &aFlags)
5791{
5792#ifndef VBOX_WITH_GUEST_PROPS
5793 ReturnComNotImplemented();
5794#else // VBOX_WITH_GUEST_PROPS
5795 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5796 if (rc == E_ACCESSDENIED)
5797 /* The VM is not running or the service is not (yet) accessible */
5798 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5799 return rc;
5800#endif // VBOX_WITH_GUEST_PROPS
5801}
5802
5803HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5804{
5805 return setGuestProperty(aProperty, aValue, "");
5806}
5807
5808HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5809{
5810#ifndef VBOX_WITH_GUEST_PROPS
5811 ReturnComNotImplemented();
5812#else // VBOX_WITH_GUEST_PROPS
5813 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5814 if (rc == E_ACCESSDENIED)
5815 /* The VM is not running or the service is not (yet) accessible */
5816 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5817 return rc;
5818#endif // VBOX_WITH_GUEST_PROPS
5819}
5820
5821#ifdef VBOX_WITH_GUEST_PROPS
5822/**
5823 * Enumerate the guest properties in VBoxSVC's internal structures.
5824 */
5825HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5826 std::vector<com::Utf8Str> &aNames,
5827 std::vector<com::Utf8Str> &aValues,
5828 std::vector<LONG64> &aTimestamps,
5829 std::vector<com::Utf8Str> &aFlags)
5830{
5831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5832 Utf8Str strPatterns(aPatterns);
5833
5834 /*
5835 * Look for matching patterns and build up a list.
5836 */
5837 HWData::GuestPropertyMap propMap;
5838 for (HWData::GuestPropertyMap::const_iterator
5839 it = mHWData->mGuestProperties.begin();
5840 it != mHWData->mGuestProperties.end();
5841 ++it)
5842 {
5843 if ( strPatterns.isEmpty()
5844 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5845 RTSTR_MAX,
5846 it->first.c_str(),
5847 RTSTR_MAX,
5848 NULL)
5849 )
5850 propMap.insert(*it);
5851 }
5852
5853 alock.release();
5854
5855 /*
5856 * And build up the arrays for returning the property information.
5857 */
5858 size_t cEntries = propMap.size();
5859
5860 aNames.resize(cEntries);
5861 aValues.resize(cEntries);
5862 aTimestamps.resize(cEntries);
5863 aFlags.resize(cEntries);
5864
5865 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5866 size_t i = 0;
5867 for (HWData::GuestPropertyMap::const_iterator
5868 it = propMap.begin();
5869 it != propMap.end();
5870 ++it, ++i)
5871 {
5872 aNames[i] = it->first;
5873 aValues[i] = it->second.strValue;
5874 aTimestamps[i] = it->second.mTimestamp;
5875 GuestPropWriteFlags(it->second.mFlags, szFlags);
5876 aFlags[i] = Utf8Str(szFlags);
5877 }
5878
5879 return S_OK;
5880}
5881
5882/**
5883 * Enumerate the properties managed by a VM.
5884 * @returns E_ACCESSDENIED if the VM process is not available or not
5885 * currently handling queries and the setting should then be done in
5886 * VBoxSVC.
5887 */
5888HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5889 std::vector<com::Utf8Str> &aNames,
5890 std::vector<com::Utf8Str> &aValues,
5891 std::vector<LONG64> &aTimestamps,
5892 std::vector<com::Utf8Str> &aFlags)
5893{
5894 HRESULT rc;
5895 ComPtr<IInternalSessionControl> directControl;
5896 {
5897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5898 if (mData->mSession.mLockType == LockType_VM)
5899 directControl = mData->mSession.mDirectControl;
5900 }
5901
5902 com::SafeArray<BSTR> bNames;
5903 com::SafeArray<BSTR> bValues;
5904 com::SafeArray<LONG64> bTimestamps;
5905 com::SafeArray<BSTR> bFlags;
5906
5907 if (!directControl)
5908 rc = E_ACCESSDENIED;
5909 else
5910 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5911 ComSafeArrayAsOutParam(bNames),
5912 ComSafeArrayAsOutParam(bValues),
5913 ComSafeArrayAsOutParam(bTimestamps),
5914 ComSafeArrayAsOutParam(bFlags));
5915 size_t i;
5916 aNames.resize(bNames.size());
5917 for (i = 0; i < bNames.size(); ++i)
5918 aNames[i] = Utf8Str(bNames[i]);
5919 aValues.resize(bValues.size());
5920 for (i = 0; i < bValues.size(); ++i)
5921 aValues[i] = Utf8Str(bValues[i]);
5922 aTimestamps.resize(bTimestamps.size());
5923 for (i = 0; i < bTimestamps.size(); ++i)
5924 aTimestamps[i] = bTimestamps[i];
5925 aFlags.resize(bFlags.size());
5926 for (i = 0; i < bFlags.size(); ++i)
5927 aFlags[i] = Utf8Str(bFlags[i]);
5928
5929 return rc;
5930}
5931#endif // VBOX_WITH_GUEST_PROPS
5932HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5933 std::vector<com::Utf8Str> &aNames,
5934 std::vector<com::Utf8Str> &aValues,
5935 std::vector<LONG64> &aTimestamps,
5936 std::vector<com::Utf8Str> &aFlags)
5937{
5938#ifndef VBOX_WITH_GUEST_PROPS
5939 ReturnComNotImplemented();
5940#else // VBOX_WITH_GUEST_PROPS
5941
5942 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5943
5944 if (rc == E_ACCESSDENIED)
5945 /* The VM is not running or the service is not (yet) accessible */
5946 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5947 return rc;
5948#endif // VBOX_WITH_GUEST_PROPS
5949}
5950
5951HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5952 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5953{
5954 MediumAttachmentList atts;
5955
5956 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5957 if (FAILED(rc)) return rc;
5958
5959 aMediumAttachments.resize(atts.size());
5960 size_t i = 0;
5961 for (MediumAttachmentList::const_iterator
5962 it = atts.begin();
5963 it != atts.end();
5964 ++it, ++i)
5965 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5966
5967 return S_OK;
5968}
5969
5970HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5971 LONG aControllerPort,
5972 LONG aDevice,
5973 ComPtr<IMediumAttachment> &aAttachment)
5974{
5975 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5976 aName.c_str(), aControllerPort, aDevice));
5977
5978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5979
5980 aAttachment = NULL;
5981
5982 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5983 aName,
5984 aControllerPort,
5985 aDevice);
5986 if (pAttach.isNull())
5987 return setError(VBOX_E_OBJECT_NOT_FOUND,
5988 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5989 aDevice, aControllerPort, aName.c_str());
5990
5991 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5992
5993 return S_OK;
5994}
5995
5996
5997HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5998 StorageBus_T aConnectionType,
5999 ComPtr<IStorageController> &aController)
6000{
6001 if ( (aConnectionType <= StorageBus_Null)
6002 || (aConnectionType > StorageBus_PCIe))
6003 return setError(E_INVALIDARG,
6004 tr("Invalid connection type: %d"),
6005 aConnectionType);
6006
6007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6008
6009 HRESULT rc = i_checkStateDependency(MutableStateDep);
6010 if (FAILED(rc)) return rc;
6011
6012 /* try to find one with the name first. */
6013 ComObjPtr<StorageController> ctrl;
6014
6015 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6016 if (SUCCEEDED(rc))
6017 return setError(VBOX_E_OBJECT_IN_USE,
6018 tr("Storage controller named '%s' already exists"),
6019 aName.c_str());
6020
6021 ctrl.createObject();
6022
6023 /* get a new instance number for the storage controller */
6024 ULONG ulInstance = 0;
6025 bool fBootable = true;
6026 for (StorageControllerList::const_iterator
6027 it = mStorageControllers->begin();
6028 it != mStorageControllers->end();
6029 ++it)
6030 {
6031 if ((*it)->i_getStorageBus() == aConnectionType)
6032 {
6033 ULONG ulCurInst = (*it)->i_getInstance();
6034
6035 if (ulCurInst >= ulInstance)
6036 ulInstance = ulCurInst + 1;
6037
6038 /* Only one controller of each type can be marked as bootable. */
6039 if ((*it)->i_getBootable())
6040 fBootable = false;
6041 }
6042 }
6043
6044 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6045 if (FAILED(rc)) return rc;
6046
6047 i_setModified(IsModified_Storage);
6048 mStorageControllers.backup();
6049 mStorageControllers->push_back(ctrl);
6050
6051 ctrl.queryInterfaceTo(aController.asOutParam());
6052
6053 /* inform the direct session if any */
6054 alock.release();
6055 i_onStorageControllerChange(i_getId(), aName);
6056
6057 return S_OK;
6058}
6059
6060HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6061 ComPtr<IStorageController> &aStorageController)
6062{
6063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6064
6065 ComObjPtr<StorageController> ctrl;
6066
6067 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6068 if (SUCCEEDED(rc))
6069 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6070
6071 return rc;
6072}
6073
6074HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6075 ULONG aInstance,
6076 ComPtr<IStorageController> &aStorageController)
6077{
6078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6079
6080 for (StorageControllerList::const_iterator
6081 it = mStorageControllers->begin();
6082 it != mStorageControllers->end();
6083 ++it)
6084 {
6085 if ( (*it)->i_getStorageBus() == aConnectionType
6086 && (*it)->i_getInstance() == aInstance)
6087 {
6088 (*it).queryInterfaceTo(aStorageController.asOutParam());
6089 return S_OK;
6090 }
6091 }
6092
6093 return setError(VBOX_E_OBJECT_NOT_FOUND,
6094 tr("Could not find a storage controller with instance number '%lu'"),
6095 aInstance);
6096}
6097
6098HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6099{
6100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6101
6102 HRESULT rc = i_checkStateDependency(MutableStateDep);
6103 if (FAILED(rc)) return rc;
6104
6105 ComObjPtr<StorageController> ctrl;
6106
6107 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6108 if (SUCCEEDED(rc))
6109 {
6110 /* Ensure that only one controller of each type is marked as bootable. */
6111 if (aBootable == TRUE)
6112 {
6113 for (StorageControllerList::const_iterator
6114 it = mStorageControllers->begin();
6115 it != mStorageControllers->end();
6116 ++it)
6117 {
6118 ComObjPtr<StorageController> aCtrl = (*it);
6119
6120 if ( (aCtrl->i_getName() != aName)
6121 && aCtrl->i_getBootable() == TRUE
6122 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6123 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6124 {
6125 aCtrl->i_setBootable(FALSE);
6126 break;
6127 }
6128 }
6129 }
6130
6131 if (SUCCEEDED(rc))
6132 {
6133 ctrl->i_setBootable(aBootable);
6134 i_setModified(IsModified_Storage);
6135 }
6136 }
6137
6138 if (SUCCEEDED(rc))
6139 {
6140 /* inform the direct session if any */
6141 alock.release();
6142 i_onStorageControllerChange(i_getId(), aName);
6143 }
6144
6145 return rc;
6146}
6147
6148HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6149{
6150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6151
6152 HRESULT rc = i_checkStateDependency(MutableStateDep);
6153 if (FAILED(rc)) return rc;
6154
6155 ComObjPtr<StorageController> ctrl;
6156 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6157 if (FAILED(rc)) return rc;
6158
6159 {
6160 /* find all attached devices to the appropriate storage controller and detach them all */
6161 // make a temporary list because detachDevice invalidates iterators into
6162 // mMediumAttachments
6163 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6164
6165 for (MediumAttachmentList::const_iterator
6166 it = llAttachments2.begin();
6167 it != llAttachments2.end();
6168 ++it)
6169 {
6170 MediumAttachment *pAttachTemp = *it;
6171
6172 AutoCaller localAutoCaller(pAttachTemp);
6173 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6174
6175 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6176
6177 if (pAttachTemp->i_getControllerName() == aName)
6178 {
6179 rc = i_detachDevice(pAttachTemp, alock, NULL);
6180 if (FAILED(rc)) return rc;
6181 }
6182 }
6183 }
6184
6185 /* We can remove it now. */
6186 i_setModified(IsModified_Storage);
6187 mStorageControllers.backup();
6188
6189 ctrl->i_unshare();
6190
6191 mStorageControllers->remove(ctrl);
6192
6193 /* inform the direct session if any */
6194 alock.release();
6195 i_onStorageControllerChange(i_getId(), aName);
6196
6197 return S_OK;
6198}
6199
6200HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6201 ComPtr<IUSBController> &aController)
6202{
6203 if ( (aType <= USBControllerType_Null)
6204 || (aType >= USBControllerType_Last))
6205 return setError(E_INVALIDARG,
6206 tr("Invalid USB controller type: %d"),
6207 aType);
6208
6209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6210
6211 HRESULT rc = i_checkStateDependency(MutableStateDep);
6212 if (FAILED(rc)) return rc;
6213
6214 /* try to find one with the same type first. */
6215 ComObjPtr<USBController> ctrl;
6216
6217 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6218 if (SUCCEEDED(rc))
6219 return setError(VBOX_E_OBJECT_IN_USE,
6220 tr("USB controller named '%s' already exists"),
6221 aName.c_str());
6222
6223 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6224 ULONG maxInstances;
6225 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6226 if (FAILED(rc))
6227 return rc;
6228
6229 ULONG cInstances = i_getUSBControllerCountByType(aType);
6230 if (cInstances >= maxInstances)
6231 return setError(E_INVALIDARG,
6232 tr("Too many USB controllers of this type"));
6233
6234 ctrl.createObject();
6235
6236 rc = ctrl->init(this, aName, aType);
6237 if (FAILED(rc)) return rc;
6238
6239 i_setModified(IsModified_USB);
6240 mUSBControllers.backup();
6241 mUSBControllers->push_back(ctrl);
6242
6243 ctrl.queryInterfaceTo(aController.asOutParam());
6244
6245 /* inform the direct session if any */
6246 alock.release();
6247 i_onUSBControllerChange();
6248
6249 return S_OK;
6250}
6251
6252HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6253{
6254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6255
6256 ComObjPtr<USBController> ctrl;
6257
6258 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6259 if (SUCCEEDED(rc))
6260 ctrl.queryInterfaceTo(aController.asOutParam());
6261
6262 return rc;
6263}
6264
6265HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6266 ULONG *aControllers)
6267{
6268 if ( (aType <= USBControllerType_Null)
6269 || (aType >= USBControllerType_Last))
6270 return setError(E_INVALIDARG,
6271 tr("Invalid USB controller type: %d"),
6272 aType);
6273
6274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6275
6276 ComObjPtr<USBController> ctrl;
6277
6278 *aControllers = i_getUSBControllerCountByType(aType);
6279
6280 return S_OK;
6281}
6282
6283HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6284{
6285
6286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6287
6288 HRESULT rc = i_checkStateDependency(MutableStateDep);
6289 if (FAILED(rc)) return rc;
6290
6291 ComObjPtr<USBController> ctrl;
6292 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6293 if (FAILED(rc)) return rc;
6294
6295 i_setModified(IsModified_USB);
6296 mUSBControllers.backup();
6297
6298 ctrl->i_unshare();
6299
6300 mUSBControllers->remove(ctrl);
6301
6302 /* inform the direct session if any */
6303 alock.release();
6304 i_onUSBControllerChange();
6305
6306 return S_OK;
6307}
6308
6309HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6310 ULONG *aOriginX,
6311 ULONG *aOriginY,
6312 ULONG *aWidth,
6313 ULONG *aHeight,
6314 BOOL *aEnabled)
6315{
6316 uint32_t u32OriginX= 0;
6317 uint32_t u32OriginY= 0;
6318 uint32_t u32Width = 0;
6319 uint32_t u32Height = 0;
6320 uint16_t u16Flags = 0;
6321
6322 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6323 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6324 if (RT_FAILURE(vrc))
6325 {
6326#ifdef RT_OS_WINDOWS
6327 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6328 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6329 * So just assign fEnable to TRUE again.
6330 * The right fix would be to change GUI API wrappers to make sure that parameters
6331 * are changed only if API succeeds.
6332 */
6333 *aEnabled = TRUE;
6334#endif
6335 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6336 tr("Saved guest size is not available (%Rrc)"),
6337 vrc);
6338 }
6339
6340 *aOriginX = u32OriginX;
6341 *aOriginY = u32OriginY;
6342 *aWidth = u32Width;
6343 *aHeight = u32Height;
6344 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6345
6346 return S_OK;
6347}
6348
6349HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6350 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6351{
6352 if (aScreenId != 0)
6353 return E_NOTIMPL;
6354
6355 if ( aBitmapFormat != BitmapFormat_BGR0
6356 && aBitmapFormat != BitmapFormat_BGRA
6357 && aBitmapFormat != BitmapFormat_RGBA
6358 && aBitmapFormat != BitmapFormat_PNG)
6359 return setError(E_NOTIMPL,
6360 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6361
6362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 uint8_t *pu8Data = NULL;
6365 uint32_t cbData = 0;
6366 uint32_t u32Width = 0;
6367 uint32_t u32Height = 0;
6368
6369 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6370
6371 if (RT_FAILURE(vrc))
6372 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6373 tr("Saved thumbnail data is not available (%Rrc)"),
6374 vrc);
6375
6376 HRESULT hr = S_OK;
6377
6378 *aWidth = u32Width;
6379 *aHeight = u32Height;
6380
6381 if (cbData > 0)
6382 {
6383 /* Convert pixels to the format expected by the API caller. */
6384 if (aBitmapFormat == BitmapFormat_BGR0)
6385 {
6386 /* [0] B, [1] G, [2] R, [3] 0. */
6387 aData.resize(cbData);
6388 memcpy(&aData.front(), pu8Data, cbData);
6389 }
6390 else if (aBitmapFormat == BitmapFormat_BGRA)
6391 {
6392 /* [0] B, [1] G, [2] R, [3] A. */
6393 aData.resize(cbData);
6394 for (uint32_t i = 0; i < cbData; i += 4)
6395 {
6396 aData[i] = pu8Data[i];
6397 aData[i + 1] = pu8Data[i + 1];
6398 aData[i + 2] = pu8Data[i + 2];
6399 aData[i + 3] = 0xff;
6400 }
6401 }
6402 else if (aBitmapFormat == BitmapFormat_RGBA)
6403 {
6404 /* [0] R, [1] G, [2] B, [3] A. */
6405 aData.resize(cbData);
6406 for (uint32_t i = 0; i < cbData; i += 4)
6407 {
6408 aData[i] = pu8Data[i + 2];
6409 aData[i + 1] = pu8Data[i + 1];
6410 aData[i + 2] = pu8Data[i];
6411 aData[i + 3] = 0xff;
6412 }
6413 }
6414 else if (aBitmapFormat == BitmapFormat_PNG)
6415 {
6416 uint8_t *pu8PNG = NULL;
6417 uint32_t cbPNG = 0;
6418 uint32_t cxPNG = 0;
6419 uint32_t cyPNG = 0;
6420
6421 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6422
6423 if (RT_SUCCESS(vrc))
6424 {
6425 aData.resize(cbPNG);
6426 if (cbPNG)
6427 memcpy(&aData.front(), pu8PNG, cbPNG);
6428 }
6429 else
6430 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6431 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6432 vrc);
6433
6434 RTMemFree(pu8PNG);
6435 }
6436 }
6437
6438 freeSavedDisplayScreenshot(pu8Data);
6439
6440 return hr;
6441}
6442
6443HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6444 ULONG *aWidth,
6445 ULONG *aHeight,
6446 std::vector<BitmapFormat_T> &aBitmapFormats)
6447{
6448 if (aScreenId != 0)
6449 return E_NOTIMPL;
6450
6451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6452
6453 uint8_t *pu8Data = NULL;
6454 uint32_t cbData = 0;
6455 uint32_t u32Width = 0;
6456 uint32_t u32Height = 0;
6457
6458 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6459
6460 if (RT_FAILURE(vrc))
6461 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6462 tr("Saved screenshot data is not available (%Rrc)"),
6463 vrc);
6464
6465 *aWidth = u32Width;
6466 *aHeight = u32Height;
6467 aBitmapFormats.resize(1);
6468 aBitmapFormats[0] = BitmapFormat_PNG;
6469
6470 freeSavedDisplayScreenshot(pu8Data);
6471
6472 return S_OK;
6473}
6474
6475HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6476 BitmapFormat_T aBitmapFormat,
6477 ULONG *aWidth,
6478 ULONG *aHeight,
6479 std::vector<BYTE> &aData)
6480{
6481 if (aScreenId != 0)
6482 return E_NOTIMPL;
6483
6484 if (aBitmapFormat != BitmapFormat_PNG)
6485 return E_NOTIMPL;
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 uint8_t *pu8Data = NULL;
6490 uint32_t cbData = 0;
6491 uint32_t u32Width = 0;
6492 uint32_t u32Height = 0;
6493
6494 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6495
6496 if (RT_FAILURE(vrc))
6497 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6498 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6499 vrc);
6500
6501 *aWidth = u32Width;
6502 *aHeight = u32Height;
6503
6504 aData.resize(cbData);
6505 if (cbData)
6506 memcpy(&aData.front(), pu8Data, cbData);
6507
6508 freeSavedDisplayScreenshot(pu8Data);
6509
6510 return S_OK;
6511}
6512
6513HRESULT Machine::hotPlugCPU(ULONG aCpu)
6514{
6515 HRESULT rc = S_OK;
6516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6517
6518 if (!mHWData->mCPUHotPlugEnabled)
6519 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6520
6521 if (aCpu >= mHWData->mCPUCount)
6522 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6523
6524 if (mHWData->mCPUAttached[aCpu])
6525 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6526
6527 alock.release();
6528 rc = i_onCPUChange(aCpu, false);
6529 alock.acquire();
6530 if (FAILED(rc)) return rc;
6531
6532 i_setModified(IsModified_MachineData);
6533 mHWData.backup();
6534 mHWData->mCPUAttached[aCpu] = true;
6535
6536 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6537 if (Global::IsOnline(mData->mMachineState))
6538 i_saveSettings(NULL);
6539
6540 return S_OK;
6541}
6542
6543HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6544{
6545 HRESULT rc = S_OK;
6546
6547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6548
6549 if (!mHWData->mCPUHotPlugEnabled)
6550 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6551
6552 if (aCpu >= SchemaDefs::MaxCPUCount)
6553 return setError(E_INVALIDARG,
6554 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6555 SchemaDefs::MaxCPUCount);
6556
6557 if (!mHWData->mCPUAttached[aCpu])
6558 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6559
6560 /* CPU 0 can't be detached */
6561 if (aCpu == 0)
6562 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6563
6564 alock.release();
6565 rc = i_onCPUChange(aCpu, true);
6566 alock.acquire();
6567 if (FAILED(rc)) return rc;
6568
6569 i_setModified(IsModified_MachineData);
6570 mHWData.backup();
6571 mHWData->mCPUAttached[aCpu] = false;
6572
6573 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6574 if (Global::IsOnline(mData->mMachineState))
6575 i_saveSettings(NULL);
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6581{
6582 *aAttached = false;
6583
6584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 /* If hotplug is enabled the CPU is always enabled. */
6587 if (!mHWData->mCPUHotPlugEnabled)
6588 {
6589 if (aCpu < mHWData->mCPUCount)
6590 *aAttached = true;
6591 }
6592 else
6593 {
6594 if (aCpu < SchemaDefs::MaxCPUCount)
6595 *aAttached = mHWData->mCPUAttached[aCpu];
6596 }
6597
6598 return S_OK;
6599}
6600
6601HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6602{
6603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6604
6605 Utf8Str log = i_getLogFilename(aIdx);
6606 if (!RTFileExists(log.c_str()))
6607 log.setNull();
6608 aFilename = log;
6609
6610 return S_OK;
6611}
6612
6613HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6614{
6615 if (aSize < 0)
6616 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6617
6618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 HRESULT rc = S_OK;
6621 Utf8Str log = i_getLogFilename(aIdx);
6622
6623 /* do not unnecessarily hold the lock while doing something which does
6624 * not need the lock and potentially takes a long time. */
6625 alock.release();
6626
6627 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6628 * keeps the SOAP reply size under 1M for the webservice (we're using
6629 * base64 encoded strings for binary data for years now, avoiding the
6630 * expansion of each byte array element to approx. 25 bytes of XML. */
6631 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6632 aData.resize(cbData);
6633
6634 RTFILE LogFile;
6635 int vrc = RTFileOpen(&LogFile, log.c_str(),
6636 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6637 if (RT_SUCCESS(vrc))
6638 {
6639 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6640 if (RT_SUCCESS(vrc))
6641 aData.resize(cbData);
6642 else
6643 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6644 tr("Could not read log file '%s' (%Rrc)"),
6645 log.c_str(), vrc);
6646 RTFileClose(LogFile);
6647 }
6648 else
6649 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6650 tr("Could not open log file '%s' (%Rrc)"),
6651 log.c_str(), vrc);
6652
6653 if (FAILED(rc))
6654 aData.resize(0);
6655
6656 return rc;
6657}
6658
6659
6660/**
6661 * Currently this method doesn't attach device to the running VM,
6662 * just makes sure it's plugged on next VM start.
6663 */
6664HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6665{
6666 // lock scope
6667 {
6668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6669
6670 HRESULT rc = i_checkStateDependency(MutableStateDep);
6671 if (FAILED(rc)) return rc;
6672
6673 ChipsetType_T aChipset = ChipsetType_PIIX3;
6674 COMGETTER(ChipsetType)(&aChipset);
6675
6676 if (aChipset != ChipsetType_ICH9)
6677 {
6678 return setError(E_INVALIDARG,
6679 tr("Host PCI attachment only supported with ICH9 chipset"));
6680 }
6681
6682 // check if device with this host PCI address already attached
6683 for (HWData::PCIDeviceAssignmentList::const_iterator
6684 it = mHWData->mPCIDeviceAssignments.begin();
6685 it != mHWData->mPCIDeviceAssignments.end();
6686 ++it)
6687 {
6688 LONG iHostAddress = -1;
6689 ComPtr<PCIDeviceAttachment> pAttach;
6690 pAttach = *it;
6691 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6692 if (iHostAddress == aHostAddress)
6693 return setError(E_INVALIDARG,
6694 tr("Device with host PCI address already attached to this VM"));
6695 }
6696
6697 ComObjPtr<PCIDeviceAttachment> pda;
6698 char name[32];
6699
6700 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6701 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6702 pda.createObject();
6703 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6704 i_setModified(IsModified_MachineData);
6705 mHWData.backup();
6706 mHWData->mPCIDeviceAssignments.push_back(pda);
6707 }
6708
6709 return S_OK;
6710}
6711
6712/**
6713 * Currently this method doesn't detach device from the running VM,
6714 * just makes sure it's not plugged on next VM start.
6715 */
6716HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6717{
6718 ComObjPtr<PCIDeviceAttachment> pAttach;
6719 bool fRemoved = false;
6720 HRESULT rc;
6721
6722 // lock scope
6723 {
6724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 rc = i_checkStateDependency(MutableStateDep);
6727 if (FAILED(rc)) return rc;
6728
6729 for (HWData::PCIDeviceAssignmentList::const_iterator
6730 it = mHWData->mPCIDeviceAssignments.begin();
6731 it != mHWData->mPCIDeviceAssignments.end();
6732 ++it)
6733 {
6734 LONG iHostAddress = -1;
6735 pAttach = *it;
6736 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6737 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6738 {
6739 i_setModified(IsModified_MachineData);
6740 mHWData.backup();
6741 mHWData->mPCIDeviceAssignments.remove(pAttach);
6742 fRemoved = true;
6743 break;
6744 }
6745 }
6746 }
6747
6748
6749 /* Fire event outside of the lock */
6750 if (fRemoved)
6751 {
6752 Assert(!pAttach.isNull());
6753 ComPtr<IEventSource> es;
6754 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6755 Assert(SUCCEEDED(rc));
6756 Bstr mid;
6757 rc = this->COMGETTER(Id)(mid.asOutParam());
6758 Assert(SUCCEEDED(rc));
6759 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6760 }
6761
6762 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6763 tr("No host PCI device %08x attached"),
6764 aHostAddress
6765 );
6766}
6767
6768HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6769{
6770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6771
6772 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6773 size_t i = 0;
6774 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6775 it = mHWData->mPCIDeviceAssignments.begin();
6776 it != mHWData->mPCIDeviceAssignments.end();
6777 ++it, ++i)
6778 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6779
6780 return S_OK;
6781}
6782
6783HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6784{
6785 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6786
6787 return S_OK;
6788}
6789
6790HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6791{
6792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6793
6794 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6795
6796 return S_OK;
6797}
6798
6799HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6800{
6801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6802 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6803 if (SUCCEEDED(hrc))
6804 {
6805 hrc = mHWData.backupEx();
6806 if (SUCCEEDED(hrc))
6807 {
6808 i_setModified(IsModified_MachineData);
6809 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6810 }
6811 }
6812 return hrc;
6813}
6814
6815HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6816{
6817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6818 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6819 return S_OK;
6820}
6821
6822HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6823{
6824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6825 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6826 if (SUCCEEDED(hrc))
6827 {
6828 hrc = mHWData.backupEx();
6829 if (SUCCEEDED(hrc))
6830 {
6831 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6832 if (SUCCEEDED(hrc))
6833 i_setModified(IsModified_MachineData);
6834 }
6835 }
6836 return hrc;
6837}
6838
6839HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6840{
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6844
6845 return S_OK;
6846}
6847
6848HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6849{
6850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6851 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6852 if (SUCCEEDED(hrc))
6853 {
6854 hrc = mHWData.backupEx();
6855 if (SUCCEEDED(hrc))
6856 {
6857 i_setModified(IsModified_MachineData);
6858 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6859 }
6860 }
6861 return hrc;
6862}
6863
6864HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6865{
6866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6867
6868 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6869
6870 return S_OK;
6871}
6872
6873HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6874{
6875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6876
6877 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6878 if ( SUCCEEDED(hrc)
6879 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6880 {
6881 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6882 int vrc;
6883
6884 if (aAutostartEnabled)
6885 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6886 else
6887 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6888
6889 if (RT_SUCCESS(vrc))
6890 {
6891 hrc = mHWData.backupEx();
6892 if (SUCCEEDED(hrc))
6893 {
6894 i_setModified(IsModified_MachineData);
6895 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6896 }
6897 }
6898 else if (vrc == VERR_NOT_SUPPORTED)
6899 hrc = setError(VBOX_E_NOT_SUPPORTED,
6900 tr("The VM autostart feature is not supported on this platform"));
6901 else if (vrc == VERR_PATH_NOT_FOUND)
6902 hrc = setError(E_FAIL,
6903 tr("The path to the autostart database is not set"));
6904 else
6905 hrc = setError(E_UNEXPECTED,
6906 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6907 aAutostartEnabled ? "Adding" : "Removing",
6908 mUserData->s.strName.c_str(), vrc);
6909 }
6910 return hrc;
6911}
6912
6913HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6914{
6915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6916
6917 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6918
6919 return S_OK;
6920}
6921
6922HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6923{
6924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6925 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6926 if (SUCCEEDED(hrc))
6927 {
6928 hrc = mHWData.backupEx();
6929 if (SUCCEEDED(hrc))
6930 {
6931 i_setModified(IsModified_MachineData);
6932 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6933 }
6934 }
6935 return hrc;
6936}
6937
6938HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6939{
6940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6941
6942 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6943
6944 return S_OK;
6945}
6946
6947HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6948{
6949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6950 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6951 if ( SUCCEEDED(hrc)
6952 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6953 {
6954 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6955 int vrc;
6956
6957 if (aAutostopType != AutostopType_Disabled)
6958 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6959 else
6960 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6961
6962 if (RT_SUCCESS(vrc))
6963 {
6964 hrc = mHWData.backupEx();
6965 if (SUCCEEDED(hrc))
6966 {
6967 i_setModified(IsModified_MachineData);
6968 mHWData->mAutostart.enmAutostopType = aAutostopType;
6969 }
6970 }
6971 else if (vrc == VERR_NOT_SUPPORTED)
6972 hrc = setError(VBOX_E_NOT_SUPPORTED,
6973 tr("The VM autostop feature is not supported on this platform"));
6974 else if (vrc == VERR_PATH_NOT_FOUND)
6975 hrc = setError(E_FAIL,
6976 tr("The path to the autostart database is not set"));
6977 else
6978 hrc = setError(E_UNEXPECTED,
6979 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6980 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6981 mUserData->s.strName.c_str(), vrc);
6982 }
6983 return hrc;
6984}
6985
6986HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6987{
6988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6989
6990 aDefaultFrontend = mHWData->mDefaultFrontend;
6991
6992 return S_OK;
6993}
6994
6995HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6996{
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6999 if (SUCCEEDED(hrc))
7000 {
7001 hrc = mHWData.backupEx();
7002 if (SUCCEEDED(hrc))
7003 {
7004 i_setModified(IsModified_MachineData);
7005 mHWData->mDefaultFrontend = aDefaultFrontend;
7006 }
7007 }
7008 return hrc;
7009}
7010
7011HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7012{
7013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7014 size_t cbIcon = mUserData->s.ovIcon.size();
7015 aIcon.resize(cbIcon);
7016 if (cbIcon)
7017 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7018 return S_OK;
7019}
7020
7021HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7022{
7023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7024 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7025 if (SUCCEEDED(hrc))
7026 {
7027 i_setModified(IsModified_MachineData);
7028 mUserData.backup();
7029 size_t cbIcon = aIcon.size();
7030 mUserData->s.ovIcon.resize(cbIcon);
7031 if (cbIcon)
7032 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7033 }
7034 return hrc;
7035}
7036
7037HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7038{
7039#ifdef VBOX_WITH_USB
7040 *aUSBProxyAvailable = true;
7041#else
7042 *aUSBProxyAvailable = false;
7043#endif
7044 return S_OK;
7045}
7046
7047HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7048{
7049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7050
7051 *aVMProcessPriority = mUserData->s.enmVMPriority;
7052
7053 return S_OK;
7054}
7055
7056HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7057{
7058 RT_NOREF(aVMProcessPriority);
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 hrc = mUserData.backupEx();
7064 if (SUCCEEDED(hrc))
7065 {
7066 i_setModified(IsModified_MachineData);
7067 mUserData->s.enmVMPriority = aVMProcessPriority;
7068 }
7069 }
7070 alock.release();
7071 if (SUCCEEDED(hrc))
7072 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7073 return hrc;
7074}
7075
7076HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7077 ComPtr<IProgress> &aProgress)
7078{
7079 ComObjPtr<Progress> pP;
7080 Progress *ppP = pP;
7081 IProgress *iP = static_cast<IProgress *>(ppP);
7082 IProgress **pProgress = &iP;
7083
7084 IMachine *pTarget = aTarget;
7085
7086 /* Convert the options. */
7087 RTCList<CloneOptions_T> optList;
7088 if (aOptions.size())
7089 for (size_t i = 0; i < aOptions.size(); ++i)
7090 optList.append(aOptions[i]);
7091
7092 if (optList.contains(CloneOptions_Link))
7093 {
7094 if (!i_isSnapshotMachine())
7095 return setError(E_INVALIDARG,
7096 tr("Linked clone can only be created from a snapshot"));
7097 if (aMode != CloneMode_MachineState)
7098 return setError(E_INVALIDARG,
7099 tr("Linked clone can only be created for a single machine state"));
7100 }
7101 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7102
7103 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7104
7105 HRESULT rc = pWorker->start(pProgress);
7106
7107 pP = static_cast<Progress *>(*pProgress);
7108 pP.queryInterfaceTo(aProgress.asOutParam());
7109
7110 return rc;
7111
7112}
7113
7114HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7115 const com::Utf8Str &aType,
7116 ComPtr<IProgress> &aProgress)
7117{
7118 LogFlowThisFuncEnter();
7119
7120 ComObjPtr<Progress> progress;
7121
7122 progress.createObject();
7123
7124 HRESULT rc = S_OK;
7125 Utf8Str targetPath = aTargetPath;
7126 Utf8Str type = aType;
7127
7128 /* Initialize our worker task */
7129 MachineMoveVM* task = NULL;
7130 try
7131 {
7132 task = new MachineMoveVM(this, targetPath, type, progress);
7133 }
7134 catch(...)
7135 {
7136 delete task;
7137 return rc;
7138 }
7139
7140 /*
7141 * task pointer will be owned by the ThreadTask class.
7142 * There is no need to call operator "delete" in the end.
7143 */
7144 rc = task->init();
7145 if (SUCCEEDED(rc))
7146 {
7147 rc = task->createThread();
7148 if (FAILED(rc))
7149 {
7150 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7151 }
7152
7153 /* Return progress to the caller */
7154 progress.queryInterfaceTo(aProgress.asOutParam());
7155 }
7156
7157 LogFlowThisFuncLeave();
7158 return rc;
7159
7160}
7161
7162HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7163{
7164 NOREF(aProgress);
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166
7167 // This check should always fail.
7168 HRESULT rc = i_checkStateDependency(MutableStateDep);
7169 if (FAILED(rc)) return rc;
7170
7171 AssertFailedReturn(E_NOTIMPL);
7172}
7173
7174HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7175{
7176 NOREF(aSavedStateFile);
7177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7178
7179 // This check should always fail.
7180 HRESULT rc = i_checkStateDependency(MutableStateDep);
7181 if (FAILED(rc)) return rc;
7182
7183 AssertFailedReturn(E_NOTIMPL);
7184}
7185
7186HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7187{
7188 NOREF(aFRemoveFile);
7189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 // This check should always fail.
7192 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7193 if (FAILED(rc)) return rc;
7194
7195 AssertFailedReturn(E_NOTIMPL);
7196}
7197
7198// public methods for internal purposes
7199/////////////////////////////////////////////////////////////////////////////
7200
7201/**
7202 * Adds the given IsModified_* flag to the dirty flags of the machine.
7203 * This must be called either during i_loadSettings or under the machine write lock.
7204 * @param fl Flag
7205 * @param fAllowStateModification If state modifications are allowed.
7206 */
7207void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7208{
7209 mData->flModifications |= fl;
7210 if (fAllowStateModification && i_isStateModificationAllowed())
7211 mData->mCurrentStateModified = true;
7212}
7213
7214/**
7215 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7216 * care of the write locking.
7217 *
7218 * @param fModification The flag to add.
7219 * @param fAllowStateModification If state modifications are allowed.
7220 */
7221void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7222{
7223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7224 i_setModified(fModification, fAllowStateModification);
7225}
7226
7227/**
7228 * Saves the registry entry of this machine to the given configuration node.
7229 *
7230 * @param data Machine registry data.
7231 *
7232 * @note locks this object for reading.
7233 */
7234HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7235{
7236 AutoLimitedCaller autoCaller(this);
7237 AssertComRCReturnRC(autoCaller.rc());
7238
7239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7240
7241 data.uuid = mData->mUuid;
7242 data.strSettingsFile = mData->m_strConfigFile;
7243
7244 return S_OK;
7245}
7246
7247/**
7248 * Calculates the absolute path of the given path taking the directory of the
7249 * machine settings file as the current directory.
7250 *
7251 * @param strPath Path to calculate the absolute path for.
7252 * @param aResult Where to put the result (used only on success, can be the
7253 * same Utf8Str instance as passed in @a aPath).
7254 * @return IPRT result.
7255 *
7256 * @note Locks this object for reading.
7257 */
7258int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7259{
7260 AutoCaller autoCaller(this);
7261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7262
7263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7264
7265 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7266
7267 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7268
7269 strSettingsDir.stripFilename();
7270 char szFolder[RTPATH_MAX];
7271 size_t cbFolder = sizeof(szFolder);
7272 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7273 if (RT_SUCCESS(vrc))
7274 aResult = szFolder;
7275
7276 return vrc;
7277}
7278
7279/**
7280 * Copies strSource to strTarget, making it relative to the machine folder
7281 * if it is a subdirectory thereof, or simply copying it otherwise.
7282 *
7283 * @param strSource Path to evaluate and copy.
7284 * @param strTarget Buffer to receive target path.
7285 *
7286 * @note Locks this object for reading.
7287 */
7288void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7289 Utf8Str &strTarget)
7290{
7291 AutoCaller autoCaller(this);
7292 AssertComRCReturn(autoCaller.rc(), (void)0);
7293
7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7295
7296 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7297 // use strTarget as a temporary buffer to hold the machine settings dir
7298 strTarget = mData->m_strConfigFileFull;
7299 strTarget.stripFilename();
7300 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7301 {
7302 // is relative: then append what's left
7303 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7304 // for empty paths (only possible for subdirs) use "." to avoid
7305 // triggering default settings for not present config attributes.
7306 if (strTarget.isEmpty())
7307 strTarget = ".";
7308 }
7309 else
7310 // is not relative: then overwrite
7311 strTarget = strSource;
7312}
7313
7314/**
7315 * Returns the full path to the machine's log folder in the
7316 * \a aLogFolder argument.
7317 */
7318void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7319{
7320 AutoCaller autoCaller(this);
7321 AssertComRCReturnVoid(autoCaller.rc());
7322
7323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7324
7325 char szTmp[RTPATH_MAX];
7326 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7327 if (RT_SUCCESS(vrc))
7328 {
7329 if (szTmp[0] && !mUserData.isNull())
7330 {
7331 char szTmp2[RTPATH_MAX];
7332 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7333 if (RT_SUCCESS(vrc))
7334 aLogFolder = Utf8StrFmt("%s%c%s",
7335 szTmp2,
7336 RTPATH_DELIMITER,
7337 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7338 }
7339 else
7340 vrc = VERR_PATH_IS_RELATIVE;
7341 }
7342
7343 if (RT_FAILURE(vrc))
7344 {
7345 // fallback if VBOX_USER_LOGHOME is not set or invalid
7346 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7347 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7348 aLogFolder.append(RTPATH_DELIMITER);
7349 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7350 }
7351}
7352
7353/**
7354 * Returns the full path to the machine's log file for an given index.
7355 */
7356Utf8Str Machine::i_getLogFilename(ULONG idx)
7357{
7358 Utf8Str logFolder;
7359 getLogFolder(logFolder);
7360 Assert(logFolder.length());
7361
7362 Utf8Str log;
7363 if (idx == 0)
7364 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7365#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7366 else if (idx == 1)
7367 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7368 else
7369 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7370#else
7371 else
7372 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7373#endif
7374 return log;
7375}
7376
7377/**
7378 * Returns the full path to the machine's hardened log file.
7379 */
7380Utf8Str Machine::i_getHardeningLogFilename(void)
7381{
7382 Utf8Str strFilename;
7383 getLogFolder(strFilename);
7384 Assert(strFilename.length());
7385 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7386 return strFilename;
7387}
7388
7389
7390/**
7391 * Composes a unique saved state filename based on the current system time. The filename is
7392 * granular to the second so this will work so long as no more than one snapshot is taken on
7393 * a machine per second.
7394 *
7395 * Before version 4.1, we used this formula for saved state files:
7396 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7397 * which no longer works because saved state files can now be shared between the saved state of the
7398 * "saved" machine and an online snapshot, and the following would cause problems:
7399 * 1) save machine
7400 * 2) create online snapshot from that machine state --> reusing saved state file
7401 * 3) save machine again --> filename would be reused, breaking the online snapshot
7402 *
7403 * So instead we now use a timestamp.
7404 *
7405 * @param strStateFilePath
7406 */
7407
7408void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7409{
7410 AutoCaller autoCaller(this);
7411 AssertComRCReturnVoid(autoCaller.rc());
7412
7413 {
7414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7415 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7416 }
7417
7418 RTTIMESPEC ts;
7419 RTTimeNow(&ts);
7420 RTTIME time;
7421 RTTimeExplode(&time, &ts);
7422
7423 strStateFilePath += RTPATH_DELIMITER;
7424 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7425 time.i32Year, time.u8Month, time.u8MonthDay,
7426 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7427}
7428
7429/**
7430 * Returns whether at least one USB controller is present for the VM.
7431 */
7432bool Machine::i_isUSBControllerPresent()
7433{
7434 AutoCaller autoCaller(this);
7435 AssertComRCReturn(autoCaller.rc(), false);
7436
7437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7438
7439 return (mUSBControllers->size() > 0);
7440}
7441
7442/**
7443 * @note Locks this object for writing, calls the client process
7444 * (inside the lock).
7445 */
7446HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7447 const Utf8Str &strFrontend,
7448 const Utf8Str &strEnvironment,
7449 ProgressProxy *aProgress)
7450{
7451 LogFlowThisFuncEnter();
7452
7453 AssertReturn(aControl, E_FAIL);
7454 AssertReturn(aProgress, E_FAIL);
7455 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7456
7457 AutoCaller autoCaller(this);
7458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7459
7460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7461
7462 if (!mData->mRegistered)
7463 return setError(E_UNEXPECTED,
7464 tr("The machine '%s' is not registered"),
7465 mUserData->s.strName.c_str());
7466
7467 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7468
7469 /* The process started when launching a VM with separate UI/VM processes is always
7470 * the UI process, i.e. needs special handling as it won't claim the session. */
7471 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7472
7473 if (fSeparate)
7474 {
7475 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7476 return setError(VBOX_E_INVALID_OBJECT_STATE,
7477 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7478 mUserData->s.strName.c_str());
7479 }
7480 else
7481 {
7482 if ( mData->mSession.mState == SessionState_Locked
7483 || mData->mSession.mState == SessionState_Spawning
7484 || mData->mSession.mState == SessionState_Unlocking)
7485 return setError(VBOX_E_INVALID_OBJECT_STATE,
7486 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7487 mUserData->s.strName.c_str());
7488
7489 /* may not be busy */
7490 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7491 }
7492
7493 /* get the path to the executable */
7494 char szPath[RTPATH_MAX];
7495 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7496 size_t cchBufLeft = strlen(szPath);
7497 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7498 szPath[cchBufLeft] = 0;
7499 char *pszNamePart = szPath + cchBufLeft;
7500 cchBufLeft = sizeof(szPath) - cchBufLeft;
7501
7502 int vrc = VINF_SUCCESS;
7503 RTPROCESS pid = NIL_RTPROCESS;
7504
7505 RTENV env = RTENV_DEFAULT;
7506
7507 if (!strEnvironment.isEmpty())
7508 {
7509 char *newEnvStr = NULL;
7510
7511 do
7512 {
7513 /* clone the current environment */
7514 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7515 AssertRCBreakStmt(vrc2, vrc = vrc2);
7516
7517 newEnvStr = RTStrDup(strEnvironment.c_str());
7518 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7519
7520 /* put new variables to the environment
7521 * (ignore empty variable names here since RTEnv API
7522 * intentionally doesn't do that) */
7523 char *var = newEnvStr;
7524 for (char *p = newEnvStr; *p; ++p)
7525 {
7526 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7527 {
7528 *p = '\0';
7529 if (*var)
7530 {
7531 char *val = strchr(var, '=');
7532 if (val)
7533 {
7534 *val++ = '\0';
7535 vrc2 = RTEnvSetEx(env, var, val);
7536 }
7537 else
7538 vrc2 = RTEnvUnsetEx(env, var);
7539 if (RT_FAILURE(vrc2))
7540 break;
7541 }
7542 var = p + 1;
7543 }
7544 }
7545 if (RT_SUCCESS(vrc2) && *var)
7546 vrc2 = RTEnvPutEx(env, var);
7547
7548 AssertRCBreakStmt(vrc2, vrc = vrc2);
7549 }
7550 while (0);
7551
7552 if (newEnvStr != NULL)
7553 RTStrFree(newEnvStr);
7554 }
7555
7556 /* Hardening logging */
7557#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7558 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7559 {
7560 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7561 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7562 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7563 {
7564 Utf8Str strStartupLogDir = strHardeningLogFile;
7565 strStartupLogDir.stripFilename();
7566 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7567 file without stripping the file. */
7568 }
7569 strSupHardeningLogArg.append(strHardeningLogFile);
7570
7571 /* Remove legacy log filename to avoid confusion. */
7572 Utf8Str strOldStartupLogFile;
7573 getLogFolder(strOldStartupLogFile);
7574 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7575 RTFileDelete(strOldStartupLogFile.c_str());
7576 }
7577 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7578#else
7579 const char *pszSupHardeningLogArg = NULL;
7580#endif
7581
7582 Utf8Str strCanonicalName;
7583
7584#ifdef VBOX_WITH_QTGUI
7585 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7586 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7587 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7588 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7589 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7590 {
7591 strCanonicalName = "GUI/Qt";
7592# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7593 /* Modify the base path so that we don't need to use ".." below. */
7594 RTPathStripTrailingSlash(szPath);
7595 RTPathStripFilename(szPath);
7596 cchBufLeft = strlen(szPath);
7597 pszNamePart = szPath + cchBufLeft;
7598 cchBufLeft = sizeof(szPath) - cchBufLeft;
7599
7600# define OSX_APP_NAME "VirtualBoxVM"
7601# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7602
7603 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7604 if ( strAppOverride.contains(".")
7605 || strAppOverride.contains("/")
7606 || strAppOverride.contains("\\")
7607 || strAppOverride.contains(":"))
7608 strAppOverride.setNull();
7609 Utf8Str strAppPath;
7610 if (!strAppOverride.isEmpty())
7611 {
7612 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7613 Utf8Str strFullPath(szPath);
7614 strFullPath.append(strAppPath);
7615 /* there is a race, but people using this deserve the failure */
7616 if (!RTFileExists(strFullPath.c_str()))
7617 strAppOverride.setNull();
7618 }
7619 if (strAppOverride.isEmpty())
7620 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7621 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7622 strcpy(pszNamePart, strAppPath.c_str());
7623# else
7624 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7625 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7626 strcpy(pszNamePart, s_szVirtualBox_exe);
7627# endif
7628
7629 Utf8Str idStr = mData->mUuid.toString();
7630 const char *apszArgs[] =
7631 {
7632 szPath,
7633 "--comment", mUserData->s.strName.c_str(),
7634 "--startvm", idStr.c_str(),
7635 "--no-startvm-errormsgbox",
7636 NULL, /* For "--separate". */
7637 NULL, /* For "--sup-startup-log". */
7638 NULL
7639 };
7640 unsigned iArg = 6;
7641 if (fSeparate)
7642 apszArgs[iArg++] = "--separate";
7643 apszArgs[iArg++] = pszSupHardeningLogArg;
7644
7645 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7646 }
7647#else /* !VBOX_WITH_QTGUI */
7648 if (0)
7649 ;
7650#endif /* VBOX_WITH_QTGUI */
7651
7652 else
7653
7654#ifdef VBOX_WITH_VBOXSDL
7655 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7656 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7657 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7658 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7659 {
7660 strCanonicalName = "GUI/SDL";
7661 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7662 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7663 strcpy(pszNamePart, s_szVBoxSDL_exe);
7664
7665 Utf8Str idStr = mData->mUuid.toString();
7666 const char *apszArgs[] =
7667 {
7668 szPath,
7669 "--comment", mUserData->s.strName.c_str(),
7670 "--startvm", idStr.c_str(),
7671 NULL, /* For "--separate". */
7672 NULL, /* For "--sup-startup-log". */
7673 NULL
7674 };
7675 unsigned iArg = 5;
7676 if (fSeparate)
7677 apszArgs[iArg++] = "--separate";
7678 apszArgs[iArg++] = pszSupHardeningLogArg;
7679
7680 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7681 }
7682#else /* !VBOX_WITH_VBOXSDL */
7683 if (0)
7684 ;
7685#endif /* !VBOX_WITH_VBOXSDL */
7686
7687 else
7688
7689#ifdef VBOX_WITH_HEADLESS
7690 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7691 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7692 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7693 )
7694 {
7695 strCanonicalName = "headless";
7696 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7697 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7698 * and a VM works even if the server has not been installed.
7699 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7700 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7701 * differently in 4.0 and 3.x.
7702 */
7703 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7704 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7705 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7706
7707 Utf8Str idStr = mData->mUuid.toString();
7708 const char *apszArgs[] =
7709 {
7710 szPath,
7711 "--comment", mUserData->s.strName.c_str(),
7712 "--startvm", idStr.c_str(),
7713 "--vrde", "config",
7714 NULL, /* For "--capture". */
7715 NULL, /* For "--sup-startup-log". */
7716 NULL
7717 };
7718 unsigned iArg = 7;
7719 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7720 apszArgs[iArg++] = "--capture";
7721 apszArgs[iArg++] = pszSupHardeningLogArg;
7722
7723# ifdef RT_OS_WINDOWS
7724 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7725# else
7726 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7727# endif
7728 }
7729#else /* !VBOX_WITH_HEADLESS */
7730 if (0)
7731 ;
7732#endif /* !VBOX_WITH_HEADLESS */
7733 else
7734 {
7735 RTEnvDestroy(env);
7736 return setError(E_INVALIDARG,
7737 tr("Invalid frontend name: '%s'"),
7738 strFrontend.c_str());
7739 }
7740
7741 RTEnvDestroy(env);
7742
7743 if (RT_FAILURE(vrc))
7744 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7745 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7746 mUserData->s.strName.c_str(), vrc);
7747
7748 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7749
7750 if (!fSeparate)
7751 {
7752 /*
7753 * Note that we don't release the lock here before calling the client,
7754 * because it doesn't need to call us back if called with a NULL argument.
7755 * Releasing the lock here is dangerous because we didn't prepare the
7756 * launch data yet, but the client we've just started may happen to be
7757 * too fast and call LockMachine() that will fail (because of PID, etc.),
7758 * so that the Machine will never get out of the Spawning session state.
7759 */
7760
7761 /* inform the session that it will be a remote one */
7762 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7763#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7764 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7765#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7766 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7767#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7768 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7769
7770 if (FAILED(rc))
7771 {
7772 /* restore the session state */
7773 mData->mSession.mState = SessionState_Unlocked;
7774 alock.release();
7775 mParent->i_addProcessToReap(pid);
7776 /* The failure may occur w/o any error info (from RPC), so provide one */
7777 return setError(VBOX_E_VM_ERROR,
7778 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7779 }
7780
7781 /* attach launch data to the machine */
7782 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7783 mData->mSession.mRemoteControls.push_back(aControl);
7784 mData->mSession.mProgress = aProgress;
7785 mData->mSession.mPID = pid;
7786 mData->mSession.mState = SessionState_Spawning;
7787 Assert(strCanonicalName.isNotEmpty());
7788 mData->mSession.mName = strCanonicalName;
7789 }
7790 else
7791 {
7792 /* For separate UI process we declare the launch as completed instantly, as the
7793 * actual headless VM start may or may not come. No point in remembering anything
7794 * yet, as what matters for us is when the headless VM gets started. */
7795 aProgress->i_notifyComplete(S_OK);
7796 }
7797
7798 alock.release();
7799 mParent->i_addProcessToReap(pid);
7800
7801 LogFlowThisFuncLeave();
7802 return S_OK;
7803}
7804
7805/**
7806 * Returns @c true if the given session machine instance has an open direct
7807 * session (and optionally also for direct sessions which are closing) and
7808 * returns the session control machine instance if so.
7809 *
7810 * Note that when the method returns @c false, the arguments remain unchanged.
7811 *
7812 * @param aMachine Session machine object.
7813 * @param aControl Direct session control object (optional).
7814 * @param aRequireVM If true then only allow VM sessions.
7815 * @param aAllowClosing If true then additionally a session which is currently
7816 * being closed will also be allowed.
7817 *
7818 * @note locks this object for reading.
7819 */
7820bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7821 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7822 bool aRequireVM /*= false*/,
7823 bool aAllowClosing /*= false*/)
7824{
7825 AutoLimitedCaller autoCaller(this);
7826 AssertComRCReturn(autoCaller.rc(), false);
7827
7828 /* just return false for inaccessible machines */
7829 if (getObjectState().getState() != ObjectState::Ready)
7830 return false;
7831
7832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7833
7834 if ( ( mData->mSession.mState == SessionState_Locked
7835 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7836 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7837 )
7838 {
7839 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7840
7841 aMachine = mData->mSession.mMachine;
7842
7843 if (aControl != NULL)
7844 *aControl = mData->mSession.mDirectControl;
7845
7846 return true;
7847 }
7848
7849 return false;
7850}
7851
7852/**
7853 * Returns @c true if the given machine has an spawning direct session.
7854 *
7855 * @note locks this object for reading.
7856 */
7857bool Machine::i_isSessionSpawning()
7858{
7859 AutoLimitedCaller autoCaller(this);
7860 AssertComRCReturn(autoCaller.rc(), false);
7861
7862 /* just return false for inaccessible machines */
7863 if (getObjectState().getState() != ObjectState::Ready)
7864 return false;
7865
7866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7867
7868 if (mData->mSession.mState == SessionState_Spawning)
7869 return true;
7870
7871 return false;
7872}
7873
7874/**
7875 * Called from the client watcher thread to check for unexpected client process
7876 * death during Session_Spawning state (e.g. before it successfully opened a
7877 * direct session).
7878 *
7879 * On Win32 and on OS/2, this method is called only when we've got the
7880 * direct client's process termination notification, so it always returns @c
7881 * true.
7882 *
7883 * On other platforms, this method returns @c true if the client process is
7884 * terminated and @c false if it's still alive.
7885 *
7886 * @note Locks this object for writing.
7887 */
7888bool Machine::i_checkForSpawnFailure()
7889{
7890 AutoCaller autoCaller(this);
7891 if (!autoCaller.isOk())
7892 {
7893 /* nothing to do */
7894 LogFlowThisFunc(("Already uninitialized!\n"));
7895 return true;
7896 }
7897
7898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7899
7900 if (mData->mSession.mState != SessionState_Spawning)
7901 {
7902 /* nothing to do */
7903 LogFlowThisFunc(("Not spawning any more!\n"));
7904 return true;
7905 }
7906
7907 HRESULT rc = S_OK;
7908
7909 /* PID not yet initialized, skip check. */
7910 if (mData->mSession.mPID == NIL_RTPROCESS)
7911 return false;
7912
7913 RTPROCSTATUS status;
7914 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7915
7916 if (vrc != VERR_PROCESS_RUNNING)
7917 {
7918 Utf8Str strExtraInfo;
7919
7920#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7921 /* If the startup logfile exists and is of non-zero length, tell the
7922 user to look there for more details to encourage them to attach it
7923 when reporting startup issues. */
7924 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7925 uint64_t cbStartupLogFile = 0;
7926 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7927 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7928 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7929#endif
7930
7931 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7932 rc = setError(E_FAIL,
7933 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7934 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7935 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7936 rc = setError(E_FAIL,
7937 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7938 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7939 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7940 rc = setError(E_FAIL,
7941 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7942 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7943 else
7944 rc = setErrorBoth(E_FAIL, vrc,
7945 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7946 i_getName().c_str(), vrc, strExtraInfo.c_str());
7947 }
7948
7949 if (FAILED(rc))
7950 {
7951 /* Close the remote session, remove the remote control from the list
7952 * and reset session state to Closed (@note keep the code in sync with
7953 * the relevant part in LockMachine()). */
7954
7955 Assert(mData->mSession.mRemoteControls.size() == 1);
7956 if (mData->mSession.mRemoteControls.size() == 1)
7957 {
7958 ErrorInfoKeeper eik;
7959 mData->mSession.mRemoteControls.front()->Uninitialize();
7960 }
7961
7962 mData->mSession.mRemoteControls.clear();
7963 mData->mSession.mState = SessionState_Unlocked;
7964
7965 /* finalize the progress after setting the state */
7966 if (!mData->mSession.mProgress.isNull())
7967 {
7968 mData->mSession.mProgress->notifyComplete(rc);
7969 mData->mSession.mProgress.setNull();
7970 }
7971
7972 mData->mSession.mPID = NIL_RTPROCESS;
7973
7974 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7975 return true;
7976 }
7977
7978 return false;
7979}
7980
7981/**
7982 * Checks whether the machine can be registered. If so, commits and saves
7983 * all settings.
7984 *
7985 * @note Must be called from mParent's write lock. Locks this object and
7986 * children for writing.
7987 */
7988HRESULT Machine::i_prepareRegister()
7989{
7990 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7991
7992 AutoLimitedCaller autoCaller(this);
7993 AssertComRCReturnRC(autoCaller.rc());
7994
7995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7996
7997 /* wait for state dependents to drop to zero */
7998 i_ensureNoStateDependencies();
7999
8000 if (!mData->mAccessible)
8001 return setError(VBOX_E_INVALID_OBJECT_STATE,
8002 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8003 mUserData->s.strName.c_str(),
8004 mData->mUuid.toString().c_str());
8005
8006 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8007
8008 if (mData->mRegistered)
8009 return setError(VBOX_E_INVALID_OBJECT_STATE,
8010 tr("The machine '%s' with UUID {%s} is already registered"),
8011 mUserData->s.strName.c_str(),
8012 mData->mUuid.toString().c_str());
8013
8014 HRESULT rc = S_OK;
8015
8016 // Ensure the settings are saved. If we are going to be registered and
8017 // no config file exists yet, create it by calling i_saveSettings() too.
8018 if ( (mData->flModifications)
8019 || (!mData->pMachineConfigFile->fileExists())
8020 )
8021 {
8022 rc = i_saveSettings(NULL);
8023 // no need to check whether VirtualBox.xml needs saving too since
8024 // we can't have a machine XML file rename pending
8025 if (FAILED(rc)) return rc;
8026 }
8027
8028 /* more config checking goes here */
8029
8030 if (SUCCEEDED(rc))
8031 {
8032 /* we may have had implicit modifications we want to fix on success */
8033 i_commit();
8034
8035 mData->mRegistered = true;
8036 }
8037 else
8038 {
8039 /* we may have had implicit modifications we want to cancel on failure*/
8040 i_rollback(false /* aNotify */);
8041 }
8042
8043 return rc;
8044}
8045
8046/**
8047 * Increases the number of objects dependent on the machine state or on the
8048 * registered state. Guarantees that these two states will not change at least
8049 * until #i_releaseStateDependency() is called.
8050 *
8051 * Depending on the @a aDepType value, additional state checks may be made.
8052 * These checks will set extended error info on failure. See
8053 * #i_checkStateDependency() for more info.
8054 *
8055 * If this method returns a failure, the dependency is not added and the caller
8056 * is not allowed to rely on any particular machine state or registration state
8057 * value and may return the failed result code to the upper level.
8058 *
8059 * @param aDepType Dependency type to add.
8060 * @param aState Current machine state (NULL if not interested).
8061 * @param aRegistered Current registered state (NULL if not interested).
8062 *
8063 * @note Locks this object for writing.
8064 */
8065HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8066 MachineState_T *aState /* = NULL */,
8067 BOOL *aRegistered /* = NULL */)
8068{
8069 AutoCaller autoCaller(this);
8070 AssertComRCReturnRC(autoCaller.rc());
8071
8072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8073
8074 HRESULT rc = i_checkStateDependency(aDepType);
8075 if (FAILED(rc)) return rc;
8076
8077 {
8078 if (mData->mMachineStateChangePending != 0)
8079 {
8080 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8081 * drop to zero so don't add more. It may make sense to wait a bit
8082 * and retry before reporting an error (since the pending state
8083 * transition should be really quick) but let's just assert for
8084 * now to see if it ever happens on practice. */
8085
8086 AssertFailed();
8087
8088 return setError(E_ACCESSDENIED,
8089 tr("Machine state change is in progress. Please retry the operation later."));
8090 }
8091
8092 ++mData->mMachineStateDeps;
8093 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8094 }
8095
8096 if (aState)
8097 *aState = mData->mMachineState;
8098 if (aRegistered)
8099 *aRegistered = mData->mRegistered;
8100
8101 return S_OK;
8102}
8103
8104/**
8105 * Decreases the number of objects dependent on the machine state.
8106 * Must always complete the #i_addStateDependency() call after the state
8107 * dependency is no more necessary.
8108 */
8109void Machine::i_releaseStateDependency()
8110{
8111 AutoCaller autoCaller(this);
8112 AssertComRCReturnVoid(autoCaller.rc());
8113
8114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8115
8116 /* releaseStateDependency() w/o addStateDependency()? */
8117 AssertReturnVoid(mData->mMachineStateDeps != 0);
8118 -- mData->mMachineStateDeps;
8119
8120 if (mData->mMachineStateDeps == 0)
8121 {
8122 /* inform i_ensureNoStateDependencies() that there are no more deps */
8123 if (mData->mMachineStateChangePending != 0)
8124 {
8125 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8126 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8127 }
8128 }
8129}
8130
8131Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8132{
8133 /* start with nothing found */
8134 Utf8Str strResult("");
8135
8136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8137
8138 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8139 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8140 // found:
8141 strResult = it->second; // source is a Utf8Str
8142
8143 return strResult;
8144}
8145
8146// protected methods
8147/////////////////////////////////////////////////////////////////////////////
8148
8149/**
8150 * Performs machine state checks based on the @a aDepType value. If a check
8151 * fails, this method will set extended error info, otherwise it will return
8152 * S_OK. It is supposed, that on failure, the caller will immediately return
8153 * the return value of this method to the upper level.
8154 *
8155 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8156 *
8157 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8158 * current state of this machine object allows to change settings of the
8159 * machine (i.e. the machine is not registered, or registered but not running
8160 * and not saved). It is useful to call this method from Machine setters
8161 * before performing any change.
8162 *
8163 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8164 * as for MutableStateDep except that if the machine is saved, S_OK is also
8165 * returned. This is useful in setters which allow changing machine
8166 * properties when it is in the saved state.
8167 *
8168 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8169 * if the current state of this machine object allows to change runtime
8170 * changeable settings of the machine (i.e. the machine is not registered, or
8171 * registered but either running or not running and not saved). It is useful
8172 * to call this method from Machine setters before performing any changes to
8173 * runtime changeable settings.
8174 *
8175 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8176 * the same as for MutableOrRunningStateDep except that if the machine is
8177 * saved, S_OK is also returned. This is useful in setters which allow
8178 * changing runtime and saved state changeable machine properties.
8179 *
8180 * @param aDepType Dependency type to check.
8181 *
8182 * @note Non Machine based classes should use #i_addStateDependency() and
8183 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8184 * template.
8185 *
8186 * @note This method must be called from under this object's read or write
8187 * lock.
8188 */
8189HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8190{
8191 switch (aDepType)
8192 {
8193 case AnyStateDep:
8194 {
8195 break;
8196 }
8197 case MutableStateDep:
8198 {
8199 if ( mData->mRegistered
8200 && ( !i_isSessionMachine()
8201 || ( mData->mMachineState != MachineState_Aborted
8202 && mData->mMachineState != MachineState_Teleported
8203 && mData->mMachineState != MachineState_PoweredOff
8204 )
8205 )
8206 )
8207 return setError(VBOX_E_INVALID_VM_STATE,
8208 tr("The machine is not mutable (state is %s)"),
8209 Global::stringifyMachineState(mData->mMachineState));
8210 break;
8211 }
8212 case MutableOrSavedStateDep:
8213 {
8214 if ( mData->mRegistered
8215 && ( !i_isSessionMachine()
8216 || ( mData->mMachineState != MachineState_Aborted
8217 && mData->mMachineState != MachineState_Teleported
8218 && mData->mMachineState != MachineState_Saved
8219 && mData->mMachineState != MachineState_PoweredOff
8220 )
8221 )
8222 )
8223 return setError(VBOX_E_INVALID_VM_STATE,
8224 tr("The machine is not mutable or saved (state is %s)"),
8225 Global::stringifyMachineState(mData->mMachineState));
8226 break;
8227 }
8228 case MutableOrRunningStateDep:
8229 {
8230 if ( mData->mRegistered
8231 && ( !i_isSessionMachine()
8232 || ( mData->mMachineState != MachineState_Aborted
8233 && mData->mMachineState != MachineState_Teleported
8234 && mData->mMachineState != MachineState_PoweredOff
8235 && !Global::IsOnline(mData->mMachineState)
8236 )
8237 )
8238 )
8239 return setError(VBOX_E_INVALID_VM_STATE,
8240 tr("The machine is not mutable or running (state is %s)"),
8241 Global::stringifyMachineState(mData->mMachineState));
8242 break;
8243 }
8244 case MutableOrSavedOrRunningStateDep:
8245 {
8246 if ( mData->mRegistered
8247 && ( !i_isSessionMachine()
8248 || ( mData->mMachineState != MachineState_Aborted
8249 && mData->mMachineState != MachineState_Teleported
8250 && mData->mMachineState != MachineState_Saved
8251 && mData->mMachineState != MachineState_PoweredOff
8252 && !Global::IsOnline(mData->mMachineState)
8253 )
8254 )
8255 )
8256 return setError(VBOX_E_INVALID_VM_STATE,
8257 tr("The machine is not mutable, saved or running (state is %s)"),
8258 Global::stringifyMachineState(mData->mMachineState));
8259 break;
8260 }
8261 }
8262
8263 return S_OK;
8264}
8265
8266/**
8267 * Helper to initialize all associated child objects and allocate data
8268 * structures.
8269 *
8270 * This method must be called as a part of the object's initialization procedure
8271 * (usually done in the #init() method).
8272 *
8273 * @note Must be called only from #init() or from #i_registeredInit().
8274 */
8275HRESULT Machine::initDataAndChildObjects()
8276{
8277 AutoCaller autoCaller(this);
8278 AssertComRCReturnRC(autoCaller.rc());
8279 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8280 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8281
8282 AssertReturn(!mData->mAccessible, E_FAIL);
8283
8284 /* allocate data structures */
8285 mSSData.allocate();
8286 mUserData.allocate();
8287 mHWData.allocate();
8288 mMediumAttachments.allocate();
8289 mStorageControllers.allocate();
8290 mUSBControllers.allocate();
8291
8292 /* initialize mOSTypeId */
8293 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8294
8295/** @todo r=bird: init() methods never fails, right? Why don't we make them
8296 * return void then! */
8297
8298 /* create associated BIOS settings object */
8299 unconst(mBIOSSettings).createObject();
8300 mBIOSSettings->init(this);
8301
8302 /* create associated record settings object */
8303 unconst(mRecordingSettings).createObject();
8304 mRecordingSettings->init(this);
8305
8306 /* create an associated VRDE object (default is disabled) */
8307 unconst(mVRDEServer).createObject();
8308 mVRDEServer->init(this);
8309
8310 /* create associated serial port objects */
8311 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8312 {
8313 unconst(mSerialPorts[slot]).createObject();
8314 mSerialPorts[slot]->init(this, slot);
8315 }
8316
8317 /* create associated parallel port objects */
8318 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8319 {
8320 unconst(mParallelPorts[slot]).createObject();
8321 mParallelPorts[slot]->init(this, slot);
8322 }
8323
8324 /* create the audio adapter object (always present, default is disabled) */
8325 unconst(mAudioAdapter).createObject();
8326 mAudioAdapter->init(this);
8327
8328 /* create the USB device filters object (always present) */
8329 unconst(mUSBDeviceFilters).createObject();
8330 mUSBDeviceFilters->init(this);
8331
8332 /* create associated network adapter objects */
8333 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8334 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8335 {
8336 unconst(mNetworkAdapters[slot]).createObject();
8337 mNetworkAdapters[slot]->init(this, slot);
8338 }
8339
8340 /* create the bandwidth control */
8341 unconst(mBandwidthControl).createObject();
8342 mBandwidthControl->init(this);
8343
8344 return S_OK;
8345}
8346
8347/**
8348 * Helper to uninitialize all associated child objects and to free all data
8349 * structures.
8350 *
8351 * This method must be called as a part of the object's uninitialization
8352 * procedure (usually done in the #uninit() method).
8353 *
8354 * @note Must be called only from #uninit() or from #i_registeredInit().
8355 */
8356void Machine::uninitDataAndChildObjects()
8357{
8358 AutoCaller autoCaller(this);
8359 AssertComRCReturnVoid(autoCaller.rc());
8360 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8361 || getObjectState().getState() == ObjectState::Limited);
8362
8363 /* tell all our other child objects we've been uninitialized */
8364 if (mBandwidthControl)
8365 {
8366 mBandwidthControl->uninit();
8367 unconst(mBandwidthControl).setNull();
8368 }
8369
8370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8371 {
8372 if (mNetworkAdapters[slot])
8373 {
8374 mNetworkAdapters[slot]->uninit();
8375 unconst(mNetworkAdapters[slot]).setNull();
8376 }
8377 }
8378
8379 if (mUSBDeviceFilters)
8380 {
8381 mUSBDeviceFilters->uninit();
8382 unconst(mUSBDeviceFilters).setNull();
8383 }
8384
8385 if (mAudioAdapter)
8386 {
8387 mAudioAdapter->uninit();
8388 unconst(mAudioAdapter).setNull();
8389 }
8390
8391 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8392 {
8393 if (mParallelPorts[slot])
8394 {
8395 mParallelPorts[slot]->uninit();
8396 unconst(mParallelPorts[slot]).setNull();
8397 }
8398 }
8399
8400 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8401 {
8402 if (mSerialPorts[slot])
8403 {
8404 mSerialPorts[slot]->uninit();
8405 unconst(mSerialPorts[slot]).setNull();
8406 }
8407 }
8408
8409 if (mVRDEServer)
8410 {
8411 mVRDEServer->uninit();
8412 unconst(mVRDEServer).setNull();
8413 }
8414
8415 if (mBIOSSettings)
8416 {
8417 mBIOSSettings->uninit();
8418 unconst(mBIOSSettings).setNull();
8419 }
8420
8421 if (mRecordingSettings)
8422 {
8423 mRecordingSettings->uninit();
8424 unconst(mRecordingSettings).setNull();
8425 }
8426
8427 /* Deassociate media (only when a real Machine or a SnapshotMachine
8428 * instance is uninitialized; SessionMachine instances refer to real
8429 * Machine media). This is necessary for a clean re-initialization of
8430 * the VM after successfully re-checking the accessibility state. Note
8431 * that in case of normal Machine or SnapshotMachine uninitialization (as
8432 * a result of unregistering or deleting the snapshot), outdated media
8433 * attachments will already be uninitialized and deleted, so this
8434 * code will not affect them. */
8435 if ( !mMediumAttachments.isNull()
8436 && !i_isSessionMachine()
8437 )
8438 {
8439 for (MediumAttachmentList::const_iterator
8440 it = mMediumAttachments->begin();
8441 it != mMediumAttachments->end();
8442 ++it)
8443 {
8444 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8445 if (pMedium.isNull())
8446 continue;
8447 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8448 AssertComRC(rc);
8449 }
8450 }
8451
8452 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8453 {
8454 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8455 if (mData->mFirstSnapshot)
8456 {
8457 // snapshots tree is protected by machine write lock; strictly
8458 // this isn't necessary here since we're deleting the entire
8459 // machine, but otherwise we assert in Snapshot::uninit()
8460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8461 mData->mFirstSnapshot->uninit();
8462 mData->mFirstSnapshot.setNull();
8463 }
8464
8465 mData->mCurrentSnapshot.setNull();
8466 }
8467
8468 /* free data structures (the essential mData structure is not freed here
8469 * since it may be still in use) */
8470 mMediumAttachments.free();
8471 mStorageControllers.free();
8472 mUSBControllers.free();
8473 mHWData.free();
8474 mUserData.free();
8475 mSSData.free();
8476}
8477
8478/**
8479 * Returns a pointer to the Machine object for this machine that acts like a
8480 * parent for complex machine data objects such as shared folders, etc.
8481 *
8482 * For primary Machine objects and for SnapshotMachine objects, returns this
8483 * object's pointer itself. For SessionMachine objects, returns the peer
8484 * (primary) machine pointer.
8485 */
8486Machine *Machine::i_getMachine()
8487{
8488 if (i_isSessionMachine())
8489 return (Machine*)mPeer;
8490 return this;
8491}
8492
8493/**
8494 * Makes sure that there are no machine state dependents. If necessary, waits
8495 * for the number of dependents to drop to zero.
8496 *
8497 * Make sure this method is called from under this object's write lock to
8498 * guarantee that no new dependents may be added when this method returns
8499 * control to the caller.
8500 *
8501 * @note Locks this object for writing. The lock will be released while waiting
8502 * (if necessary).
8503 *
8504 * @warning To be used only in methods that change the machine state!
8505 */
8506void Machine::i_ensureNoStateDependencies()
8507{
8508 AssertReturnVoid(isWriteLockOnCurrentThread());
8509
8510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8511
8512 /* Wait for all state dependents if necessary */
8513 if (mData->mMachineStateDeps != 0)
8514 {
8515 /* lazy semaphore creation */
8516 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8517 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8518
8519 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8520 mData->mMachineStateDeps));
8521
8522 ++mData->mMachineStateChangePending;
8523
8524 /* reset the semaphore before waiting, the last dependent will signal
8525 * it */
8526 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8527
8528 alock.release();
8529
8530 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8531
8532 alock.acquire();
8533
8534 -- mData->mMachineStateChangePending;
8535 }
8536}
8537
8538/**
8539 * Changes the machine state and informs callbacks.
8540 *
8541 * This method is not intended to fail so it either returns S_OK or asserts (and
8542 * returns a failure).
8543 *
8544 * @note Locks this object for writing.
8545 */
8546HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8547{
8548 LogFlowThisFuncEnter();
8549 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8550 Assert(aMachineState != MachineState_Null);
8551
8552 AutoCaller autoCaller(this);
8553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8554
8555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8556
8557 /* wait for state dependents to drop to zero */
8558 i_ensureNoStateDependencies();
8559
8560 MachineState_T const enmOldState = mData->mMachineState;
8561 if (enmOldState != aMachineState)
8562 {
8563 mData->mMachineState = aMachineState;
8564 RTTimeNow(&mData->mLastStateChange);
8565
8566#ifdef VBOX_WITH_DTRACE_R3_MAIN
8567 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8568#endif
8569 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8570 }
8571
8572 LogFlowThisFuncLeave();
8573 return S_OK;
8574}
8575
8576/**
8577 * Searches for a shared folder with the given logical name
8578 * in the collection of shared folders.
8579 *
8580 * @param aName logical name of the shared folder
8581 * @param aSharedFolder where to return the found object
8582 * @param aSetError whether to set the error info if the folder is
8583 * not found
8584 * @return
8585 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8586 *
8587 * @note
8588 * must be called from under the object's lock!
8589 */
8590HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8591 ComObjPtr<SharedFolder> &aSharedFolder,
8592 bool aSetError /* = false */)
8593{
8594 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8595 for (HWData::SharedFolderList::const_iterator
8596 it = mHWData->mSharedFolders.begin();
8597 it != mHWData->mSharedFolders.end();
8598 ++it)
8599 {
8600 SharedFolder *pSF = *it;
8601 AutoCaller autoCaller(pSF);
8602 if (pSF->i_getName() == aName)
8603 {
8604 aSharedFolder = pSF;
8605 rc = S_OK;
8606 break;
8607 }
8608 }
8609
8610 if (aSetError && FAILED(rc))
8611 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8612
8613 return rc;
8614}
8615
8616/**
8617 * Initializes all machine instance data from the given settings structures
8618 * from XML. The exception is the machine UUID which needs special handling
8619 * depending on the caller's use case, so the caller needs to set that herself.
8620 *
8621 * This gets called in several contexts during machine initialization:
8622 *
8623 * -- When machine XML exists on disk already and needs to be loaded into memory,
8624 * for example, from #i_registeredInit() to load all registered machines on
8625 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8626 * attached to the machine should be part of some media registry already.
8627 *
8628 * -- During OVF import, when a machine config has been constructed from an
8629 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8630 * ensure that the media listed as attachments in the config (which have
8631 * been imported from the OVF) receive the correct registry ID.
8632 *
8633 * -- During VM cloning.
8634 *
8635 * @param config Machine settings from XML.
8636 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8637 * for each attached medium in the config.
8638 * @return
8639 */
8640HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8641 const Guid *puuidRegistry)
8642{
8643 // copy name, description, OS type, teleporter, UTC etc.
8644 mUserData->s = config.machineUserData;
8645
8646 // look up the object by Id to check it is valid
8647 ComObjPtr<GuestOSType> pGuestOSType;
8648 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8649 if (!pGuestOSType.isNull())
8650 mUserData->s.strOsType = pGuestOSType->i_id();
8651
8652 // stateFile (optional)
8653 if (config.strStateFile.isEmpty())
8654 mSSData->strStateFilePath.setNull();
8655 else
8656 {
8657 Utf8Str stateFilePathFull(config.strStateFile);
8658 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8659 if (RT_FAILURE(vrc))
8660 return setErrorBoth(E_FAIL, vrc,
8661 tr("Invalid saved state file path '%s' (%Rrc)"),
8662 config.strStateFile.c_str(),
8663 vrc);
8664 mSSData->strStateFilePath = stateFilePathFull;
8665 }
8666
8667 // snapshot folder needs special processing so set it again
8668 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8669 if (FAILED(rc)) return rc;
8670
8671 /* Copy the extra data items (config may or may not be the same as
8672 * mData->pMachineConfigFile) if necessary. When loading the XML files
8673 * from disk they are the same, but not for OVF import. */
8674 if (mData->pMachineConfigFile != &config)
8675 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8676
8677 /* currentStateModified (optional, default is true) */
8678 mData->mCurrentStateModified = config.fCurrentStateModified;
8679
8680 mData->mLastStateChange = config.timeLastStateChange;
8681
8682 /*
8683 * note: all mUserData members must be assigned prior this point because
8684 * we need to commit changes in order to let mUserData be shared by all
8685 * snapshot machine instances.
8686 */
8687 mUserData.commitCopy();
8688
8689 // machine registry, if present (must be loaded before snapshots)
8690 if (config.canHaveOwnMediaRegistry())
8691 {
8692 // determine machine folder
8693 Utf8Str strMachineFolder = i_getSettingsFileFull();
8694 strMachineFolder.stripFilename();
8695 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8696 config.mediaRegistry,
8697 strMachineFolder);
8698 if (FAILED(rc)) return rc;
8699 }
8700
8701 /* Snapshot node (optional) */
8702 size_t cRootSnapshots;
8703 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8704 {
8705 // there must be only one root snapshot
8706 Assert(cRootSnapshots == 1);
8707
8708 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8709
8710 rc = i_loadSnapshot(snap,
8711 config.uuidCurrentSnapshot,
8712 NULL); // no parent == first snapshot
8713 if (FAILED(rc)) return rc;
8714 }
8715
8716 // hardware data
8717 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8718 if (FAILED(rc)) return rc;
8719
8720 /*
8721 * NOTE: the assignment below must be the last thing to do,
8722 * otherwise it will be not possible to change the settings
8723 * somewhere in the code above because all setters will be
8724 * blocked by i_checkStateDependency(MutableStateDep).
8725 */
8726
8727 /* set the machine state to Aborted or Saved when appropriate */
8728 if (config.fAborted)
8729 {
8730 mSSData->strStateFilePath.setNull();
8731
8732 /* no need to use i_setMachineState() during init() */
8733 mData->mMachineState = MachineState_Aborted;
8734 }
8735 else if (!mSSData->strStateFilePath.isEmpty())
8736 {
8737 /* no need to use i_setMachineState() during init() */
8738 mData->mMachineState = MachineState_Saved;
8739 }
8740
8741 // after loading settings, we are no longer different from the XML on disk
8742 mData->flModifications = 0;
8743
8744 return S_OK;
8745}
8746
8747/**
8748 * Recursively loads all snapshots starting from the given.
8749 *
8750 * @param data snapshot settings.
8751 * @param aCurSnapshotId Current snapshot ID from the settings file.
8752 * @param aParentSnapshot Parent snapshot.
8753 */
8754HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8755 const Guid &aCurSnapshotId,
8756 Snapshot *aParentSnapshot)
8757{
8758 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8759 AssertReturn(!i_isSessionMachine(), E_FAIL);
8760
8761 HRESULT rc = S_OK;
8762
8763 Utf8Str strStateFile;
8764 if (!data.strStateFile.isEmpty())
8765 {
8766 /* optional */
8767 strStateFile = data.strStateFile;
8768 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8769 if (RT_FAILURE(vrc))
8770 return setErrorBoth(E_FAIL, vrc,
8771 tr("Invalid saved state file path '%s' (%Rrc)"),
8772 strStateFile.c_str(),
8773 vrc);
8774 }
8775
8776 /* create a snapshot machine object */
8777 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8778 pSnapshotMachine.createObject();
8779 rc = pSnapshotMachine->initFromSettings(this,
8780 data.hardware,
8781 &data.debugging,
8782 &data.autostart,
8783 data.uuid.ref(),
8784 strStateFile);
8785 if (FAILED(rc)) return rc;
8786
8787 /* create a snapshot object */
8788 ComObjPtr<Snapshot> pSnapshot;
8789 pSnapshot.createObject();
8790 /* initialize the snapshot */
8791 rc = pSnapshot->init(mParent, // VirtualBox object
8792 data.uuid,
8793 data.strName,
8794 data.strDescription,
8795 data.timestamp,
8796 pSnapshotMachine,
8797 aParentSnapshot);
8798 if (FAILED(rc)) return rc;
8799
8800 /* memorize the first snapshot if necessary */
8801 if (!mData->mFirstSnapshot)
8802 mData->mFirstSnapshot = pSnapshot;
8803
8804 /* memorize the current snapshot when appropriate */
8805 if ( !mData->mCurrentSnapshot
8806 && pSnapshot->i_getId() == aCurSnapshotId
8807 )
8808 mData->mCurrentSnapshot = pSnapshot;
8809
8810 // now create the children
8811 for (settings::SnapshotsList::const_iterator
8812 it = data.llChildSnapshots.begin();
8813 it != data.llChildSnapshots.end();
8814 ++it)
8815 {
8816 const settings::Snapshot &childData = *it;
8817 // recurse
8818 rc = i_loadSnapshot(childData,
8819 aCurSnapshotId,
8820 pSnapshot); // parent = the one we created above
8821 if (FAILED(rc)) return rc;
8822 }
8823
8824 return rc;
8825}
8826
8827/**
8828 * Loads settings into mHWData.
8829 *
8830 * @param puuidRegistry Registry ID.
8831 * @param puuidSnapshot Snapshot ID
8832 * @param data Reference to the hardware settings.
8833 * @param pDbg Pointer to the debugging settings.
8834 * @param pAutostart Pointer to the autostart settings.
8835 */
8836HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8837 const Guid *puuidSnapshot,
8838 const settings::Hardware &data,
8839 const settings::Debugging *pDbg,
8840 const settings::Autostart *pAutostart)
8841{
8842 AssertReturn(!i_isSessionMachine(), E_FAIL);
8843
8844 HRESULT rc = S_OK;
8845
8846 try
8847 {
8848 ComObjPtr<GuestOSType> pGuestOSType;
8849 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8850
8851 /* The hardware version attribute (optional). */
8852 mHWData->mHWVersion = data.strVersion;
8853 mHWData->mHardwareUUID = data.uuid;
8854
8855 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8856 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8857 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8858 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8859 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8860 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8861 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8862 mHWData->mPAEEnabled = data.fPAE;
8863 mHWData->mLongMode = data.enmLongMode;
8864 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8865 mHWData->mAPIC = data.fAPIC;
8866 mHWData->mX2APIC = data.fX2APIC;
8867 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8868 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8869 mHWData->mSpecCtrl = data.fSpecCtrl;
8870 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8871 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8872 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8873 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8874 mHWData->mCPUCount = data.cCPUs;
8875 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8876 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8877 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8878 mHWData->mCpuProfile = data.strCpuProfile;
8879
8880 // cpu
8881 if (mHWData->mCPUHotPlugEnabled)
8882 {
8883 for (settings::CpuList::const_iterator
8884 it = data.llCpus.begin();
8885 it != data.llCpus.end();
8886 ++it)
8887 {
8888 const settings::Cpu &cpu = *it;
8889
8890 mHWData->mCPUAttached[cpu.ulId] = true;
8891 }
8892 }
8893
8894 // cpuid leafs
8895 for (settings::CpuIdLeafsList::const_iterator
8896 it = data.llCpuIdLeafs.begin();
8897 it != data.llCpuIdLeafs.end();
8898 ++it)
8899 {
8900 const settings::CpuIdLeaf &rLeaf= *it;
8901 if ( rLeaf.idx < UINT32_C(0x20)
8902 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8903 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8904 mHWData->mCpuIdLeafList.push_back(rLeaf);
8905 /* else: just ignore */
8906 }
8907
8908 mHWData->mMemorySize = data.ulMemorySizeMB;
8909 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8910
8911 // boot order
8912 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8913 {
8914 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8915 if (it == data.mapBootOrder.end())
8916 mHWData->mBootOrder[i] = DeviceType_Null;
8917 else
8918 mHWData->mBootOrder[i] = it->second;
8919 }
8920
8921 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8922 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8923 mHWData->mMonitorCount = data.cMonitors;
8924 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8925 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8926 mHWData->mFirmwareType = data.firmwareType;
8927 mHWData->mPointingHIDType = data.pointingHIDType;
8928 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8929 mHWData->mChipsetType = data.chipsetType;
8930 mHWData->mParavirtProvider = data.paravirtProvider;
8931 mHWData->mParavirtDebug = data.strParavirtDebug;
8932 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8933 mHWData->mHPETEnabled = data.fHPETEnabled;
8934
8935 /* VRDEServer */
8936 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8937 if (FAILED(rc)) return rc;
8938
8939 /* BIOS */
8940 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8941 if (FAILED(rc)) return rc;
8942
8943 /* Recording settings */
8944 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8945 if (FAILED(rc)) return rc;
8946
8947 // Bandwidth control (must come before network adapters)
8948 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8949 if (FAILED(rc)) return rc;
8950
8951 /* USB controllers */
8952 for (settings::USBControllerList::const_iterator
8953 it = data.usbSettings.llUSBControllers.begin();
8954 it != data.usbSettings.llUSBControllers.end();
8955 ++it)
8956 {
8957 const settings::USBController &settingsCtrl = *it;
8958 ComObjPtr<USBController> newCtrl;
8959
8960 newCtrl.createObject();
8961 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8962 mUSBControllers->push_back(newCtrl);
8963 }
8964
8965 /* USB device filters */
8966 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8967 if (FAILED(rc)) return rc;
8968
8969 // network adapters (establish array size first and apply defaults, to
8970 // ensure reading the same settings as we saved, since the list skips
8971 // adapters having defaults)
8972 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8973 size_t oldCount = mNetworkAdapters.size();
8974 if (newCount > oldCount)
8975 {
8976 mNetworkAdapters.resize(newCount);
8977 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8978 {
8979 unconst(mNetworkAdapters[slot]).createObject();
8980 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8981 }
8982 }
8983 else if (newCount < oldCount)
8984 mNetworkAdapters.resize(newCount);
8985 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8986 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8987 for (settings::NetworkAdaptersList::const_iterator
8988 it = data.llNetworkAdapters.begin();
8989 it != data.llNetworkAdapters.end();
8990 ++it)
8991 {
8992 const settings::NetworkAdapter &nic = *it;
8993
8994 /* slot uniqueness is guaranteed by XML Schema */
8995 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8996 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 // serial ports (establish defaults first, to ensure reading the same
9001 // settings as we saved, since the list skips ports having defaults)
9002 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9003 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9004 for (settings::SerialPortsList::const_iterator
9005 it = data.llSerialPorts.begin();
9006 it != data.llSerialPorts.end();
9007 ++it)
9008 {
9009 const settings::SerialPort &s = *it;
9010
9011 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9012 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9013 if (FAILED(rc)) return rc;
9014 }
9015
9016 // parallel ports (establish defaults first, to ensure reading the same
9017 // settings as we saved, since the list skips ports having defaults)
9018 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9019 mParallelPorts[i]->i_applyDefaults();
9020 for (settings::ParallelPortsList::const_iterator
9021 it = data.llParallelPorts.begin();
9022 it != data.llParallelPorts.end();
9023 ++it)
9024 {
9025 const settings::ParallelPort &p = *it;
9026
9027 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9028 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9029 if (FAILED(rc)) return rc;
9030 }
9031
9032 /* AudioAdapter */
9033 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9034 if (FAILED(rc)) return rc;
9035
9036 /* storage controllers */
9037 rc = i_loadStorageControllers(data.storage,
9038 puuidRegistry,
9039 puuidSnapshot);
9040 if (FAILED(rc)) return rc;
9041
9042 /* Shared folders */
9043 for (settings::SharedFoldersList::const_iterator
9044 it = data.llSharedFolders.begin();
9045 it != data.llSharedFolders.end();
9046 ++it)
9047 {
9048 const settings::SharedFolder &sf = *it;
9049
9050 ComObjPtr<SharedFolder> sharedFolder;
9051 /* Check for double entries. Not allowed! */
9052 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9053 if (SUCCEEDED(rc))
9054 return setError(VBOX_E_OBJECT_IN_USE,
9055 tr("Shared folder named '%s' already exists"),
9056 sf.strName.c_str());
9057
9058 /* Create the new shared folder. Don't break on error. This will be
9059 * reported when the machine starts. */
9060 sharedFolder.createObject();
9061 rc = sharedFolder->init(i_getMachine(),
9062 sf.strName,
9063 sf.strHostPath,
9064 RT_BOOL(sf.fWritable),
9065 RT_BOOL(sf.fAutoMount),
9066 sf.strAutoMountPoint,
9067 false /* fFailOnError */);
9068 if (FAILED(rc)) return rc;
9069 mHWData->mSharedFolders.push_back(sharedFolder);
9070 }
9071
9072 // Clipboard
9073 mHWData->mClipboardMode = data.clipboardMode;
9074
9075 // drag'n'drop
9076 mHWData->mDnDMode = data.dndMode;
9077
9078 // guest settings
9079 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9080
9081 // IO settings
9082 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9083 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9084
9085 // Host PCI devices
9086 for (settings::HostPCIDeviceAttachmentList::const_iterator
9087 it = data.pciAttachments.begin();
9088 it != data.pciAttachments.end();
9089 ++it)
9090 {
9091 const settings::HostPCIDeviceAttachment &hpda = *it;
9092 ComObjPtr<PCIDeviceAttachment> pda;
9093
9094 pda.createObject();
9095 pda->i_loadSettings(this, hpda);
9096 mHWData->mPCIDeviceAssignments.push_back(pda);
9097 }
9098
9099 /*
9100 * (The following isn't really real hardware, but it lives in HWData
9101 * for reasons of convenience.)
9102 */
9103
9104#ifdef VBOX_WITH_GUEST_PROPS
9105 /* Guest properties (optional) */
9106
9107 /* Only load transient guest properties for configs which have saved
9108 * state, because there shouldn't be any for powered off VMs. The same
9109 * logic applies for snapshots, as offline snapshots shouldn't have
9110 * any such properties. They confuse the code in various places.
9111 * Note: can't rely on the machine state, as it isn't set yet. */
9112 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9113 /* apologies for the hacky unconst() usage, but this needs hacking
9114 * actually inconsistent settings into consistency, otherwise there
9115 * will be some corner cases where the inconsistency survives
9116 * surprisingly long without getting fixed, especially for snapshots
9117 * as there are no config changes. */
9118 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9119 for (settings::GuestPropertiesList::iterator
9120 it = llGuestProperties.begin();
9121 it != llGuestProperties.end();
9122 /*nothing*/)
9123 {
9124 const settings::GuestProperty &prop = *it;
9125 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9126 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9127 if ( fSkipTransientGuestProperties
9128 && ( fFlags & GUEST_PROP_F_TRANSIENT
9129 || fFlags & GUEST_PROP_F_TRANSRESET))
9130 {
9131 it = llGuestProperties.erase(it);
9132 continue;
9133 }
9134 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9135 mHWData->mGuestProperties[prop.strName] = property;
9136 ++it;
9137 }
9138#endif /* VBOX_WITH_GUEST_PROPS defined */
9139
9140 rc = i_loadDebugging(pDbg);
9141 if (FAILED(rc))
9142 return rc;
9143
9144 mHWData->mAutostart = *pAutostart;
9145
9146 /* default frontend */
9147 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9148 }
9149 catch (std::bad_alloc &)
9150 {
9151 return E_OUTOFMEMORY;
9152 }
9153
9154 AssertComRC(rc);
9155 return rc;
9156}
9157
9158/**
9159 * Called from i_loadHardware() to load the debugging settings of the
9160 * machine.
9161 *
9162 * @param pDbg Pointer to the settings.
9163 */
9164HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9165{
9166 mHWData->mDebugging = *pDbg;
9167 /* no more processing currently required, this will probably change. */
9168 return S_OK;
9169}
9170
9171/**
9172 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9173 *
9174 * @param data storage settings.
9175 * @param puuidRegistry media registry ID to set media to or NULL;
9176 * see Machine::i_loadMachineDataFromSettings()
9177 * @param puuidSnapshot snapshot ID
9178 * @return
9179 */
9180HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9181 const Guid *puuidRegistry,
9182 const Guid *puuidSnapshot)
9183{
9184 AssertReturn(!i_isSessionMachine(), E_FAIL);
9185
9186 HRESULT rc = S_OK;
9187
9188 for (settings::StorageControllersList::const_iterator
9189 it = data.llStorageControllers.begin();
9190 it != data.llStorageControllers.end();
9191 ++it)
9192 {
9193 const settings::StorageController &ctlData = *it;
9194
9195 ComObjPtr<StorageController> pCtl;
9196 /* Try to find one with the name first. */
9197 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9198 if (SUCCEEDED(rc))
9199 return setError(VBOX_E_OBJECT_IN_USE,
9200 tr("Storage controller named '%s' already exists"),
9201 ctlData.strName.c_str());
9202
9203 pCtl.createObject();
9204 rc = pCtl->init(this,
9205 ctlData.strName,
9206 ctlData.storageBus,
9207 ctlData.ulInstance,
9208 ctlData.fBootable);
9209 if (FAILED(rc)) return rc;
9210
9211 mStorageControllers->push_back(pCtl);
9212
9213 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9214 if (FAILED(rc)) return rc;
9215
9216 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9217 if (FAILED(rc)) return rc;
9218
9219 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9220 if (FAILED(rc)) return rc;
9221
9222 /* Load the attached devices now. */
9223 rc = i_loadStorageDevices(pCtl,
9224 ctlData,
9225 puuidRegistry,
9226 puuidSnapshot);
9227 if (FAILED(rc)) return rc;
9228 }
9229
9230 return S_OK;
9231}
9232
9233/**
9234 * Called from i_loadStorageControllers for a controller's devices.
9235 *
9236 * @param aStorageController
9237 * @param data
9238 * @param puuidRegistry media registry ID to set media to or NULL; see
9239 * Machine::i_loadMachineDataFromSettings()
9240 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9241 * @return
9242 */
9243HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9244 const settings::StorageController &data,
9245 const Guid *puuidRegistry,
9246 const Guid *puuidSnapshot)
9247{
9248 HRESULT rc = S_OK;
9249
9250 /* paranoia: detect duplicate attachments */
9251 for (settings::AttachedDevicesList::const_iterator
9252 it = data.llAttachedDevices.begin();
9253 it != data.llAttachedDevices.end();
9254 ++it)
9255 {
9256 const settings::AttachedDevice &ad = *it;
9257
9258 for (settings::AttachedDevicesList::const_iterator it2 = it;
9259 it2 != data.llAttachedDevices.end();
9260 ++it2)
9261 {
9262 if (it == it2)
9263 continue;
9264
9265 const settings::AttachedDevice &ad2 = *it2;
9266
9267 if ( ad.lPort == ad2.lPort
9268 && ad.lDevice == ad2.lDevice)
9269 {
9270 return setError(E_FAIL,
9271 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9272 aStorageController->i_getName().c_str(),
9273 ad.lPort,
9274 ad.lDevice,
9275 mUserData->s.strName.c_str());
9276 }
9277 }
9278 }
9279
9280 for (settings::AttachedDevicesList::const_iterator
9281 it = data.llAttachedDevices.begin();
9282 it != data.llAttachedDevices.end();
9283 ++it)
9284 {
9285 const settings::AttachedDevice &dev = *it;
9286 ComObjPtr<Medium> medium;
9287
9288 switch (dev.deviceType)
9289 {
9290 case DeviceType_Floppy:
9291 case DeviceType_DVD:
9292 if (dev.strHostDriveSrc.isNotEmpty())
9293 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9294 false /* fRefresh */, medium);
9295 else
9296 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9297 dev.uuid,
9298 false /* fRefresh */,
9299 false /* aSetError */,
9300 medium);
9301 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9302 // This is not an error. The host drive or UUID might have vanished, so just go
9303 // ahead without this removeable medium attachment
9304 rc = S_OK;
9305 break;
9306
9307 case DeviceType_HardDisk:
9308 {
9309 /* find a hard disk by UUID */
9310 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9311 if (FAILED(rc))
9312 {
9313 if (i_isSnapshotMachine())
9314 {
9315 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9316 // so the user knows that the bad disk is in a snapshot somewhere
9317 com::ErrorInfo info;
9318 return setError(E_FAIL,
9319 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9320 puuidSnapshot->raw(),
9321 info.getText().raw());
9322 }
9323 else
9324 return rc;
9325 }
9326
9327 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9328
9329 if (medium->i_getType() == MediumType_Immutable)
9330 {
9331 if (i_isSnapshotMachine())
9332 return setError(E_FAIL,
9333 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9334 "of the virtual machine '%s' ('%s')"),
9335 medium->i_getLocationFull().c_str(),
9336 dev.uuid.raw(),
9337 puuidSnapshot->raw(),
9338 mUserData->s.strName.c_str(),
9339 mData->m_strConfigFileFull.c_str());
9340
9341 return setError(E_FAIL,
9342 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9343 medium->i_getLocationFull().c_str(),
9344 dev.uuid.raw(),
9345 mUserData->s.strName.c_str(),
9346 mData->m_strConfigFileFull.c_str());
9347 }
9348
9349 if (medium->i_getType() == MediumType_MultiAttach)
9350 {
9351 if (i_isSnapshotMachine())
9352 return setError(E_FAIL,
9353 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9354 "of the virtual machine '%s' ('%s')"),
9355 medium->i_getLocationFull().c_str(),
9356 dev.uuid.raw(),
9357 puuidSnapshot->raw(),
9358 mUserData->s.strName.c_str(),
9359 mData->m_strConfigFileFull.c_str());
9360
9361 return setError(E_FAIL,
9362 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9363 medium->i_getLocationFull().c_str(),
9364 dev.uuid.raw(),
9365 mUserData->s.strName.c_str(),
9366 mData->m_strConfigFileFull.c_str());
9367 }
9368
9369 if ( !i_isSnapshotMachine()
9370 && medium->i_getChildren().size() != 0
9371 )
9372 return setError(E_FAIL,
9373 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9374 "because it has %d differencing child hard disks"),
9375 medium->i_getLocationFull().c_str(),
9376 dev.uuid.raw(),
9377 mUserData->s.strName.c_str(),
9378 mData->m_strConfigFileFull.c_str(),
9379 medium->i_getChildren().size());
9380
9381 if (i_findAttachment(*mMediumAttachments.data(),
9382 medium))
9383 return setError(E_FAIL,
9384 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9385 medium->i_getLocationFull().c_str(),
9386 dev.uuid.raw(),
9387 mUserData->s.strName.c_str(),
9388 mData->m_strConfigFileFull.c_str());
9389
9390 break;
9391 }
9392
9393 default:
9394 return setError(E_FAIL,
9395 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9396 medium->i_getLocationFull().c_str(),
9397 mUserData->s.strName.c_str(),
9398 mData->m_strConfigFileFull.c_str());
9399 }
9400
9401 if (FAILED(rc))
9402 break;
9403
9404 /* Bandwidth groups are loaded at this point. */
9405 ComObjPtr<BandwidthGroup> pBwGroup;
9406
9407 if (!dev.strBwGroup.isEmpty())
9408 {
9409 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9410 if (FAILED(rc))
9411 return setError(E_FAIL,
9412 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9413 medium->i_getLocationFull().c_str(),
9414 dev.strBwGroup.c_str(),
9415 mUserData->s.strName.c_str(),
9416 mData->m_strConfigFileFull.c_str());
9417 pBwGroup->i_reference();
9418 }
9419
9420 const Utf8Str controllerName = aStorageController->i_getName();
9421 ComObjPtr<MediumAttachment> pAttachment;
9422 pAttachment.createObject();
9423 rc = pAttachment->init(this,
9424 medium,
9425 controllerName,
9426 dev.lPort,
9427 dev.lDevice,
9428 dev.deviceType,
9429 false,
9430 dev.fPassThrough,
9431 dev.fTempEject,
9432 dev.fNonRotational,
9433 dev.fDiscard,
9434 dev.fHotPluggable,
9435 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9436 if (FAILED(rc)) break;
9437
9438 /* associate the medium with this machine and snapshot */
9439 if (!medium.isNull())
9440 {
9441 AutoCaller medCaller(medium);
9442 if (FAILED(medCaller.rc())) return medCaller.rc();
9443 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9444
9445 if (i_isSnapshotMachine())
9446 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9447 else
9448 rc = medium->i_addBackReference(mData->mUuid);
9449 /* If the medium->addBackReference fails it sets an appropriate
9450 * error message, so no need to do any guesswork here. */
9451
9452 if (puuidRegistry)
9453 // caller wants registry ID to be set on all attached media (OVF import case)
9454 medium->i_addRegistry(*puuidRegistry);
9455 }
9456
9457 if (FAILED(rc))
9458 break;
9459
9460 /* back up mMediumAttachments to let registeredInit() properly rollback
9461 * on failure (= limited accessibility) */
9462 i_setModified(IsModified_Storage);
9463 mMediumAttachments.backup();
9464 mMediumAttachments->push_back(pAttachment);
9465 }
9466
9467 return rc;
9468}
9469
9470/**
9471 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9472 *
9473 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9474 * @param aSnapshot where to return the found snapshot
9475 * @param aSetError true to set extended error info on failure
9476 */
9477HRESULT Machine::i_findSnapshotById(const Guid &aId,
9478 ComObjPtr<Snapshot> &aSnapshot,
9479 bool aSetError /* = false */)
9480{
9481 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9482
9483 if (!mData->mFirstSnapshot)
9484 {
9485 if (aSetError)
9486 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9487 return E_FAIL;
9488 }
9489
9490 if (aId.isZero())
9491 aSnapshot = mData->mFirstSnapshot;
9492 else
9493 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9494
9495 if (!aSnapshot)
9496 {
9497 if (aSetError)
9498 return setError(E_FAIL,
9499 tr("Could not find a snapshot with UUID {%s}"),
9500 aId.toString().c_str());
9501 return E_FAIL;
9502 }
9503
9504 return S_OK;
9505}
9506
9507/**
9508 * Returns the snapshot with the given name or fails of no such snapshot.
9509 *
9510 * @param strName snapshot name to find
9511 * @param aSnapshot where to return the found snapshot
9512 * @param aSetError true to set extended error info on failure
9513 */
9514HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9515 ComObjPtr<Snapshot> &aSnapshot,
9516 bool aSetError /* = false */)
9517{
9518 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9519
9520 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9521
9522 if (!mData->mFirstSnapshot)
9523 {
9524 if (aSetError)
9525 return setError(VBOX_E_OBJECT_NOT_FOUND,
9526 tr("This machine does not have any snapshots"));
9527 return VBOX_E_OBJECT_NOT_FOUND;
9528 }
9529
9530 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9531
9532 if (!aSnapshot)
9533 {
9534 if (aSetError)
9535 return setError(VBOX_E_OBJECT_NOT_FOUND,
9536 tr("Could not find a snapshot named '%s'"), strName.c_str());
9537 return VBOX_E_OBJECT_NOT_FOUND;
9538 }
9539
9540 return S_OK;
9541}
9542
9543/**
9544 * Returns a storage controller object with the given name.
9545 *
9546 * @param aName storage controller name to find
9547 * @param aStorageController where to return the found storage controller
9548 * @param aSetError true to set extended error info on failure
9549 */
9550HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9551 ComObjPtr<StorageController> &aStorageController,
9552 bool aSetError /* = false */)
9553{
9554 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9555
9556 for (StorageControllerList::const_iterator
9557 it = mStorageControllers->begin();
9558 it != mStorageControllers->end();
9559 ++it)
9560 {
9561 if ((*it)->i_getName() == aName)
9562 {
9563 aStorageController = (*it);
9564 return S_OK;
9565 }
9566 }
9567
9568 if (aSetError)
9569 return setError(VBOX_E_OBJECT_NOT_FOUND,
9570 tr("Could not find a storage controller named '%s'"),
9571 aName.c_str());
9572 return VBOX_E_OBJECT_NOT_FOUND;
9573}
9574
9575/**
9576 * Returns a USB controller object with the given name.
9577 *
9578 * @param aName USB controller name to find
9579 * @param aUSBController where to return the found USB controller
9580 * @param aSetError true to set extended error info on failure
9581 */
9582HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9583 ComObjPtr<USBController> &aUSBController,
9584 bool aSetError /* = false */)
9585{
9586 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9587
9588 for (USBControllerList::const_iterator
9589 it = mUSBControllers->begin();
9590 it != mUSBControllers->end();
9591 ++it)
9592 {
9593 if ((*it)->i_getName() == aName)
9594 {
9595 aUSBController = (*it);
9596 return S_OK;
9597 }
9598 }
9599
9600 if (aSetError)
9601 return setError(VBOX_E_OBJECT_NOT_FOUND,
9602 tr("Could not find a storage controller named '%s'"),
9603 aName.c_str());
9604 return VBOX_E_OBJECT_NOT_FOUND;
9605}
9606
9607/**
9608 * Returns the number of USB controller instance of the given type.
9609 *
9610 * @param enmType USB controller type.
9611 */
9612ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9613{
9614 ULONG cCtrls = 0;
9615
9616 for (USBControllerList::const_iterator
9617 it = mUSBControllers->begin();
9618 it != mUSBControllers->end();
9619 ++it)
9620 {
9621 if ((*it)->i_getControllerType() == enmType)
9622 cCtrls++;
9623 }
9624
9625 return cCtrls;
9626}
9627
9628HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9629 MediumAttachmentList &atts)
9630{
9631 AutoCaller autoCaller(this);
9632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9633
9634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9635
9636 for (MediumAttachmentList::const_iterator
9637 it = mMediumAttachments->begin();
9638 it != mMediumAttachments->end();
9639 ++it)
9640 {
9641 const ComObjPtr<MediumAttachment> &pAtt = *it;
9642 // should never happen, but deal with NULL pointers in the list.
9643 AssertContinue(!pAtt.isNull());
9644
9645 // getControllerName() needs caller+read lock
9646 AutoCaller autoAttCaller(pAtt);
9647 if (FAILED(autoAttCaller.rc()))
9648 {
9649 atts.clear();
9650 return autoAttCaller.rc();
9651 }
9652 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9653
9654 if (pAtt->i_getControllerName() == aName)
9655 atts.push_back(pAtt);
9656 }
9657
9658 return S_OK;
9659}
9660
9661
9662/**
9663 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9664 * file if the machine name was changed and about creating a new settings file
9665 * if this is a new machine.
9666 *
9667 * @note Must be never called directly but only from #saveSettings().
9668 */
9669HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9670{
9671 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9672
9673 HRESULT rc = S_OK;
9674
9675 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9676
9677 /// @todo need to handle primary group change, too
9678
9679 /* attempt to rename the settings file if machine name is changed */
9680 if ( mUserData->s.fNameSync
9681 && mUserData.isBackedUp()
9682 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9683 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9684 )
9685 {
9686 bool dirRenamed = false;
9687 bool fileRenamed = false;
9688
9689 Utf8Str configFile, newConfigFile;
9690 Utf8Str configFilePrev, newConfigFilePrev;
9691 Utf8Str configDir, newConfigDir;
9692
9693 do
9694 {
9695 int vrc = VINF_SUCCESS;
9696
9697 Utf8Str name = mUserData.backedUpData()->s.strName;
9698 Utf8Str newName = mUserData->s.strName;
9699 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9700 if (group == "/")
9701 group.setNull();
9702 Utf8Str newGroup = mUserData->s.llGroups.front();
9703 if (newGroup == "/")
9704 newGroup.setNull();
9705
9706 configFile = mData->m_strConfigFileFull;
9707
9708 /* first, rename the directory if it matches the group and machine name */
9709 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9710 group.c_str(), RTPATH_DELIMITER, name.c_str());
9711 /** @todo hack, make somehow use of ComposeMachineFilename */
9712 if (mUserData->s.fDirectoryIncludesUUID)
9713 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9714 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9715 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9716 /** @todo hack, make somehow use of ComposeMachineFilename */
9717 if (mUserData->s.fDirectoryIncludesUUID)
9718 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9719 configDir = configFile;
9720 configDir.stripFilename();
9721 newConfigDir = configDir;
9722 if ( configDir.length() >= groupPlusName.length()
9723 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9724 groupPlusName.c_str()))
9725 {
9726 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9727 Utf8Str newConfigBaseDir(newConfigDir);
9728 newConfigDir.append(newGroupPlusName);
9729 /* consistency: use \ if appropriate on the platform */
9730 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9731 /* new dir and old dir cannot be equal here because of 'if'
9732 * above and because name != newName */
9733 Assert(configDir != newConfigDir);
9734 if (!fSettingsFileIsNew)
9735 {
9736 /* perform real rename only if the machine is not new */
9737 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9738 if ( vrc == VERR_FILE_NOT_FOUND
9739 || vrc == VERR_PATH_NOT_FOUND)
9740 {
9741 /* create the parent directory, then retry renaming */
9742 Utf8Str parent(newConfigDir);
9743 parent.stripFilename();
9744 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9745 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9746 }
9747 if (RT_FAILURE(vrc))
9748 {
9749 rc = setErrorBoth(E_FAIL, vrc,
9750 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9751 configDir.c_str(),
9752 newConfigDir.c_str(),
9753 vrc);
9754 break;
9755 }
9756 /* delete subdirectories which are no longer needed */
9757 Utf8Str dir(configDir);
9758 dir.stripFilename();
9759 while (dir != newConfigBaseDir && dir != ".")
9760 {
9761 vrc = RTDirRemove(dir.c_str());
9762 if (RT_FAILURE(vrc))
9763 break;
9764 dir.stripFilename();
9765 }
9766 dirRenamed = true;
9767 }
9768 }
9769
9770 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9771 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9772
9773 /* then try to rename the settings file itself */
9774 if (newConfigFile != configFile)
9775 {
9776 /* get the path to old settings file in renamed directory */
9777 configFile = Utf8StrFmt("%s%c%s",
9778 newConfigDir.c_str(),
9779 RTPATH_DELIMITER,
9780 RTPathFilename(configFile.c_str()));
9781 if (!fSettingsFileIsNew)
9782 {
9783 /* perform real rename only if the machine is not new */
9784 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9785 if (RT_FAILURE(vrc))
9786 {
9787 rc = setErrorBoth(E_FAIL, vrc,
9788 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9789 configFile.c_str(),
9790 newConfigFile.c_str(),
9791 vrc);
9792 break;
9793 }
9794 fileRenamed = true;
9795 configFilePrev = configFile;
9796 configFilePrev += "-prev";
9797 newConfigFilePrev = newConfigFile;
9798 newConfigFilePrev += "-prev";
9799 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9800 }
9801 }
9802
9803 // update m_strConfigFileFull amd mConfigFile
9804 mData->m_strConfigFileFull = newConfigFile;
9805 // compute the relative path too
9806 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9807
9808 // store the old and new so that VirtualBox::i_saveSettings() can update
9809 // the media registry
9810 if ( mData->mRegistered
9811 && (configDir != newConfigDir || configFile != newConfigFile))
9812 {
9813 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9814
9815 if (pfNeedsGlobalSaveSettings)
9816 *pfNeedsGlobalSaveSettings = true;
9817 }
9818
9819 // in the saved state file path, replace the old directory with the new directory
9820 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9821 {
9822 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9823 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9824 }
9825
9826 // and do the same thing for the saved state file paths of all the online snapshots
9827 if (mData->mFirstSnapshot)
9828 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9829 newConfigDir.c_str());
9830 }
9831 while (0);
9832
9833 if (FAILED(rc))
9834 {
9835 /* silently try to rename everything back */
9836 if (fileRenamed)
9837 {
9838 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9839 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9840 }
9841 if (dirRenamed)
9842 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9843 }
9844
9845 if (FAILED(rc)) return rc;
9846 }
9847
9848 if (fSettingsFileIsNew)
9849 {
9850 /* create a virgin config file */
9851 int vrc = VINF_SUCCESS;
9852
9853 /* ensure the settings directory exists */
9854 Utf8Str path(mData->m_strConfigFileFull);
9855 path.stripFilename();
9856 if (!RTDirExists(path.c_str()))
9857 {
9858 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9859 if (RT_FAILURE(vrc))
9860 {
9861 return setErrorBoth(E_FAIL, vrc,
9862 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9863 path.c_str(),
9864 vrc);
9865 }
9866 }
9867
9868 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9869 path = Utf8Str(mData->m_strConfigFileFull);
9870 RTFILE f = NIL_RTFILE;
9871 vrc = RTFileOpen(&f, path.c_str(),
9872 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9873 if (RT_FAILURE(vrc))
9874 return setErrorBoth(E_FAIL, vrc,
9875 tr("Could not create the settings file '%s' (%Rrc)"),
9876 path.c_str(),
9877 vrc);
9878 RTFileClose(f);
9879 }
9880
9881 return rc;
9882}
9883
9884/**
9885 * Saves and commits machine data, user data and hardware data.
9886 *
9887 * Note that on failure, the data remains uncommitted.
9888 *
9889 * @a aFlags may combine the following flags:
9890 *
9891 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9892 * Used when saving settings after an operation that makes them 100%
9893 * correspond to the settings from the current snapshot.
9894 * - SaveS_Force: settings will be saved without doing a deep compare of the
9895 * settings structures. This is used when this is called because snapshots
9896 * have changed to avoid the overhead of the deep compare.
9897 *
9898 * @note Must be called from under this object's write lock. Locks children for
9899 * writing.
9900 *
9901 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9902 * initialized to false and that will be set to true by this function if
9903 * the caller must invoke VirtualBox::i_saveSettings() because the global
9904 * settings have changed. This will happen if a machine rename has been
9905 * saved and the global machine and media registries will therefore need
9906 * updating.
9907 * @param aFlags Flags.
9908 */
9909HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9910 int aFlags /*= 0*/)
9911{
9912 LogFlowThisFuncEnter();
9913
9914 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9915
9916 /* make sure child objects are unable to modify the settings while we are
9917 * saving them */
9918 i_ensureNoStateDependencies();
9919
9920 AssertReturn(!i_isSnapshotMachine(),
9921 E_FAIL);
9922
9923 if (!mData->mAccessible)
9924 return setError(VBOX_E_INVALID_VM_STATE,
9925 tr("The machine is not accessible, so cannot save settings"));
9926
9927 HRESULT rc = S_OK;
9928 bool fNeedsWrite = false;
9929
9930 /* First, prepare to save settings. It will care about renaming the
9931 * settings directory and file if the machine name was changed and about
9932 * creating a new settings file if this is a new machine. */
9933 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9934 if (FAILED(rc)) return rc;
9935
9936 // keep a pointer to the current settings structures
9937 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9938 settings::MachineConfigFile *pNewConfig = NULL;
9939
9940 try
9941 {
9942 // make a fresh one to have everyone write stuff into
9943 pNewConfig = new settings::MachineConfigFile(NULL);
9944 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9945
9946 // now go and copy all the settings data from COM to the settings structures
9947 // (this calls i_saveSettings() on all the COM objects in the machine)
9948 i_copyMachineDataToSettings(*pNewConfig);
9949
9950 if (aFlags & SaveS_ResetCurStateModified)
9951 {
9952 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9953 mData->mCurrentStateModified = FALSE;
9954 fNeedsWrite = true; // always, no need to compare
9955 }
9956 else if (aFlags & SaveS_Force)
9957 {
9958 fNeedsWrite = true; // always, no need to compare
9959 }
9960 else
9961 {
9962 if (!mData->mCurrentStateModified)
9963 {
9964 // do a deep compare of the settings that we just saved with the settings
9965 // previously stored in the config file; this invokes MachineConfigFile::operator==
9966 // which does a deep compare of all the settings, which is expensive but less expensive
9967 // than writing out XML in vain
9968 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9969
9970 // could still be modified if any settings changed
9971 mData->mCurrentStateModified = fAnySettingsChanged;
9972
9973 fNeedsWrite = fAnySettingsChanged;
9974 }
9975 else
9976 fNeedsWrite = true;
9977 }
9978
9979 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9980
9981 if (fNeedsWrite)
9982 // now spit it all out!
9983 pNewConfig->write(mData->m_strConfigFileFull);
9984
9985 mData->pMachineConfigFile = pNewConfig;
9986 delete pOldConfig;
9987 i_commit();
9988
9989 // after saving settings, we are no longer different from the XML on disk
9990 mData->flModifications = 0;
9991 }
9992 catch (HRESULT err)
9993 {
9994 // we assume that error info is set by the thrower
9995 rc = err;
9996
9997 // restore old config
9998 delete pNewConfig;
9999 mData->pMachineConfigFile = pOldConfig;
10000 }
10001 catch (...)
10002 {
10003 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10004 }
10005
10006 if (fNeedsWrite)
10007 {
10008 /* Fire the data change event, even on failure (since we've already
10009 * committed all data). This is done only for SessionMachines because
10010 * mutable Machine instances are always not registered (i.e. private
10011 * to the client process that creates them) and thus don't need to
10012 * inform callbacks. */
10013 if (i_isSessionMachine())
10014 mParent->i_onMachineDataChange(mData->mUuid);
10015 }
10016
10017 LogFlowThisFunc(("rc=%08X\n", rc));
10018 LogFlowThisFuncLeave();
10019 return rc;
10020}
10021
10022/**
10023 * Implementation for saving the machine settings into the given
10024 * settings::MachineConfigFile instance. This copies machine extradata
10025 * from the previous machine config file in the instance data, if any.
10026 *
10027 * This gets called from two locations:
10028 *
10029 * -- Machine::i_saveSettings(), during the regular XML writing;
10030 *
10031 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10032 * exported to OVF and we write the VirtualBox proprietary XML
10033 * into a <vbox:Machine> tag.
10034 *
10035 * This routine fills all the fields in there, including snapshots, *except*
10036 * for the following:
10037 *
10038 * -- fCurrentStateModified. There is some special logic associated with that.
10039 *
10040 * The caller can then call MachineConfigFile::write() or do something else
10041 * with it.
10042 *
10043 * Caller must hold the machine lock!
10044 *
10045 * This throws XML errors and HRESULT, so the caller must have a catch block!
10046 */
10047void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10048{
10049 // deep copy extradata, being extra careful with self assignment (the STL
10050 // map assignment on Mac OS X clang based Xcode isn't checking)
10051 if (&config != mData->pMachineConfigFile)
10052 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10053
10054 config.uuid = mData->mUuid;
10055
10056 // copy name, description, OS type, teleport, UTC etc.
10057 config.machineUserData = mUserData->s;
10058
10059 if ( mData->mMachineState == MachineState_Saved
10060 || mData->mMachineState == MachineState_Restoring
10061 // when doing certain snapshot operations we may or may not have
10062 // a saved state in the current state, so keep everything as is
10063 || ( ( mData->mMachineState == MachineState_Snapshotting
10064 || mData->mMachineState == MachineState_DeletingSnapshot
10065 || mData->mMachineState == MachineState_RestoringSnapshot)
10066 && (!mSSData->strStateFilePath.isEmpty())
10067 )
10068 )
10069 {
10070 Assert(!mSSData->strStateFilePath.isEmpty());
10071 /* try to make the file name relative to the settings file dir */
10072 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10073 }
10074 else
10075 {
10076 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10077 config.strStateFile.setNull();
10078 }
10079
10080 if (mData->mCurrentSnapshot)
10081 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10082 else
10083 config.uuidCurrentSnapshot.clear();
10084
10085 config.timeLastStateChange = mData->mLastStateChange;
10086 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10087 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10088
10089 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10090 if (FAILED(rc)) throw rc;
10091
10092 // save machine's media registry if this is VirtualBox 4.0 or later
10093 if (config.canHaveOwnMediaRegistry())
10094 {
10095 // determine machine folder
10096 Utf8Str strMachineFolder = i_getSettingsFileFull();
10097 strMachineFolder.stripFilename();
10098 mParent->i_saveMediaRegistry(config.mediaRegistry,
10099 i_getId(), // only media with registry ID == machine UUID
10100 strMachineFolder);
10101 // this throws HRESULT
10102 }
10103
10104 // save snapshots
10105 rc = i_saveAllSnapshots(config);
10106 if (FAILED(rc)) throw rc;
10107}
10108
10109/**
10110 * Saves all snapshots of the machine into the given machine config file. Called
10111 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10112 * @param config
10113 * @return
10114 */
10115HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10116{
10117 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10118
10119 HRESULT rc = S_OK;
10120
10121 try
10122 {
10123 config.llFirstSnapshot.clear();
10124
10125 if (mData->mFirstSnapshot)
10126 {
10127 // the settings use a list for "the first snapshot"
10128 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10129
10130 // get reference to the snapshot on the list and work on that
10131 // element straight in the list to avoid excessive copying later
10132 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10133 if (FAILED(rc)) throw rc;
10134 }
10135
10136// if (mType == IsSessionMachine)
10137// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10138
10139 }
10140 catch (HRESULT err)
10141 {
10142 /* we assume that error info is set by the thrower */
10143 rc = err;
10144 }
10145 catch (...)
10146 {
10147 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10148 }
10149
10150 return rc;
10151}
10152
10153/**
10154 * Saves the VM hardware configuration. It is assumed that the
10155 * given node is empty.
10156 *
10157 * @param data Reference to the settings object for the hardware config.
10158 * @param pDbg Pointer to the settings object for the debugging config
10159 * which happens to live in mHWData.
10160 * @param pAutostart Pointer to the settings object for the autostart config
10161 * which happens to live in mHWData.
10162 */
10163HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10164 settings::Autostart *pAutostart)
10165{
10166 HRESULT rc = S_OK;
10167
10168 try
10169 {
10170 /* The hardware version attribute (optional).
10171 Automatically upgrade from 1 to current default hardware version
10172 when there is no saved state. (ugly!) */
10173 if ( mHWData->mHWVersion == "1"
10174 && mSSData->strStateFilePath.isEmpty()
10175 )
10176 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10177
10178 data.strVersion = mHWData->mHWVersion;
10179 data.uuid = mHWData->mHardwareUUID;
10180
10181 // CPU
10182 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10183 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10184 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10185 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10186 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10187 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10188 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10189 data.fPAE = !!mHWData->mPAEEnabled;
10190 data.enmLongMode = mHWData->mLongMode;
10191 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10192 data.fAPIC = !!mHWData->mAPIC;
10193 data.fX2APIC = !!mHWData->mX2APIC;
10194 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10195 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10196 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10197 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10198 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10199 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10200 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10201 data.cCPUs = mHWData->mCPUCount;
10202 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10203 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10204 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10205 data.strCpuProfile = mHWData->mCpuProfile;
10206
10207 data.llCpus.clear();
10208 if (data.fCpuHotPlug)
10209 {
10210 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10211 {
10212 if (mHWData->mCPUAttached[idx])
10213 {
10214 settings::Cpu cpu;
10215 cpu.ulId = idx;
10216 data.llCpus.push_back(cpu);
10217 }
10218 }
10219 }
10220
10221 /* Standard and Extended CPUID leafs. */
10222 data.llCpuIdLeafs.clear();
10223 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10224
10225 // memory
10226 data.ulMemorySizeMB = mHWData->mMemorySize;
10227 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10228
10229 // firmware
10230 data.firmwareType = mHWData->mFirmwareType;
10231
10232 // HID
10233 data.pointingHIDType = mHWData->mPointingHIDType;
10234 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10235
10236 // chipset
10237 data.chipsetType = mHWData->mChipsetType;
10238
10239 // paravirt
10240 data.paravirtProvider = mHWData->mParavirtProvider;
10241 data.strParavirtDebug = mHWData->mParavirtDebug;
10242
10243 // emulated USB card reader
10244 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10245
10246 // HPET
10247 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10248
10249 // boot order
10250 data.mapBootOrder.clear();
10251 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10252 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10253
10254 // display
10255 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10256 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10257 data.cMonitors = mHWData->mMonitorCount;
10258 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10259 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10260
10261 /* VRDEServer settings (optional) */
10262 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10263 if (FAILED(rc)) throw rc;
10264
10265 /* BIOS settings (required) */
10266 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10267 if (FAILED(rc)) throw rc;
10268
10269 /* Recording settings (required) */
10270 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10271 if (FAILED(rc)) throw rc;
10272
10273 /* USB Controller (required) */
10274 data.usbSettings.llUSBControllers.clear();
10275 for (USBControllerList::const_iterator
10276 it = mUSBControllers->begin();
10277 it != mUSBControllers->end();
10278 ++it)
10279 {
10280 ComObjPtr<USBController> ctrl = *it;
10281 settings::USBController settingsCtrl;
10282
10283 settingsCtrl.strName = ctrl->i_getName();
10284 settingsCtrl.enmType = ctrl->i_getControllerType();
10285
10286 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10287 }
10288
10289 /* USB device filters (required) */
10290 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10291 if (FAILED(rc)) throw rc;
10292
10293 /* Network adapters (required) */
10294 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10295 data.llNetworkAdapters.clear();
10296 /* Write out only the nominal number of network adapters for this
10297 * chipset type. Since Machine::commit() hasn't been called there
10298 * may be extra NIC settings in the vector. */
10299 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10300 {
10301 settings::NetworkAdapter nic;
10302 nic.ulSlot = (uint32_t)slot;
10303 /* paranoia check... must not be NULL, but must not crash either. */
10304 if (mNetworkAdapters[slot])
10305 {
10306 if (mNetworkAdapters[slot]->i_hasDefaults())
10307 continue;
10308
10309 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10310 if (FAILED(rc)) throw rc;
10311
10312 data.llNetworkAdapters.push_back(nic);
10313 }
10314 }
10315
10316 /* Serial ports */
10317 data.llSerialPorts.clear();
10318 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10319 {
10320 if (mSerialPorts[slot]->i_hasDefaults())
10321 continue;
10322
10323 settings::SerialPort s;
10324 s.ulSlot = slot;
10325 rc = mSerialPorts[slot]->i_saveSettings(s);
10326 if (FAILED(rc)) return rc;
10327
10328 data.llSerialPorts.push_back(s);
10329 }
10330
10331 /* Parallel ports */
10332 data.llParallelPorts.clear();
10333 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10334 {
10335 if (mParallelPorts[slot]->i_hasDefaults())
10336 continue;
10337
10338 settings::ParallelPort p;
10339 p.ulSlot = slot;
10340 rc = mParallelPorts[slot]->i_saveSettings(p);
10341 if (FAILED(rc)) return rc;
10342
10343 data.llParallelPorts.push_back(p);
10344 }
10345
10346 /* Audio adapter */
10347 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10348 if (FAILED(rc)) return rc;
10349
10350 rc = i_saveStorageControllers(data.storage);
10351 if (FAILED(rc)) return rc;
10352
10353 /* Shared folders */
10354 data.llSharedFolders.clear();
10355 for (HWData::SharedFolderList::const_iterator
10356 it = mHWData->mSharedFolders.begin();
10357 it != mHWData->mSharedFolders.end();
10358 ++it)
10359 {
10360 SharedFolder *pSF = *it;
10361 AutoCaller sfCaller(pSF);
10362 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10363 settings::SharedFolder sf;
10364 sf.strName = pSF->i_getName();
10365 sf.strHostPath = pSF->i_getHostPath();
10366 sf.fWritable = !!pSF->i_isWritable();
10367 sf.fAutoMount = !!pSF->i_isAutoMounted();
10368 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10369
10370 data.llSharedFolders.push_back(sf);
10371 }
10372
10373 // clipboard
10374 data.clipboardMode = mHWData->mClipboardMode;
10375
10376 // drag'n'drop
10377 data.dndMode = mHWData->mDnDMode;
10378
10379 /* Guest */
10380 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10381
10382 // IO settings
10383 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10384 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10385
10386 /* BandwidthControl (required) */
10387 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10388 if (FAILED(rc)) throw rc;
10389
10390 /* Host PCI devices */
10391 data.pciAttachments.clear();
10392 for (HWData::PCIDeviceAssignmentList::const_iterator
10393 it = mHWData->mPCIDeviceAssignments.begin();
10394 it != mHWData->mPCIDeviceAssignments.end();
10395 ++it)
10396 {
10397 ComObjPtr<PCIDeviceAttachment> pda = *it;
10398 settings::HostPCIDeviceAttachment hpda;
10399
10400 rc = pda->i_saveSettings(hpda);
10401 if (FAILED(rc)) throw rc;
10402
10403 data.pciAttachments.push_back(hpda);
10404 }
10405
10406 // guest properties
10407 data.llGuestProperties.clear();
10408#ifdef VBOX_WITH_GUEST_PROPS
10409 for (HWData::GuestPropertyMap::const_iterator
10410 it = mHWData->mGuestProperties.begin();
10411 it != mHWData->mGuestProperties.end();
10412 ++it)
10413 {
10414 HWData::GuestProperty property = it->second;
10415
10416 /* Remove transient guest properties at shutdown unless we
10417 * are saving state. Note that restoring snapshot intentionally
10418 * keeps them, they will be removed if appropriate once the final
10419 * machine state is set (as crashes etc. need to work). */
10420 if ( ( mData->mMachineState == MachineState_PoweredOff
10421 || mData->mMachineState == MachineState_Aborted
10422 || mData->mMachineState == MachineState_Teleported)
10423 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10424 continue;
10425 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10426 prop.strName = it->first;
10427 prop.strValue = property.strValue;
10428 prop.timestamp = property.mTimestamp;
10429 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10430 GuestPropWriteFlags(property.mFlags, szFlags);
10431 prop.strFlags = szFlags;
10432
10433 data.llGuestProperties.push_back(prop);
10434 }
10435
10436 /* I presume this doesn't require a backup(). */
10437 mData->mGuestPropertiesModified = FALSE;
10438#endif /* VBOX_WITH_GUEST_PROPS defined */
10439
10440 *pDbg = mHWData->mDebugging;
10441 *pAutostart = mHWData->mAutostart;
10442
10443 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10444 }
10445 catch (std::bad_alloc &)
10446 {
10447 return E_OUTOFMEMORY;
10448 }
10449
10450 AssertComRC(rc);
10451 return rc;
10452}
10453
10454/**
10455 * Saves the storage controller configuration.
10456 *
10457 * @param data storage settings.
10458 */
10459HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10460{
10461 data.llStorageControllers.clear();
10462
10463 for (StorageControllerList::const_iterator
10464 it = mStorageControllers->begin();
10465 it != mStorageControllers->end();
10466 ++it)
10467 {
10468 HRESULT rc;
10469 ComObjPtr<StorageController> pCtl = *it;
10470
10471 settings::StorageController ctl;
10472 ctl.strName = pCtl->i_getName();
10473 ctl.controllerType = pCtl->i_getControllerType();
10474 ctl.storageBus = pCtl->i_getStorageBus();
10475 ctl.ulInstance = pCtl->i_getInstance();
10476 ctl.fBootable = pCtl->i_getBootable();
10477
10478 /* Save the port count. */
10479 ULONG portCount;
10480 rc = pCtl->COMGETTER(PortCount)(&portCount);
10481 ComAssertComRCRet(rc, rc);
10482 ctl.ulPortCount = portCount;
10483
10484 /* Save fUseHostIOCache */
10485 BOOL fUseHostIOCache;
10486 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10487 ComAssertComRCRet(rc, rc);
10488 ctl.fUseHostIOCache = !!fUseHostIOCache;
10489
10490 /* save the devices now. */
10491 rc = i_saveStorageDevices(pCtl, ctl);
10492 ComAssertComRCRet(rc, rc);
10493
10494 data.llStorageControllers.push_back(ctl);
10495 }
10496
10497 return S_OK;
10498}
10499
10500/**
10501 * Saves the hard disk configuration.
10502 */
10503HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10504 settings::StorageController &data)
10505{
10506 MediumAttachmentList atts;
10507
10508 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10509 if (FAILED(rc)) return rc;
10510
10511 data.llAttachedDevices.clear();
10512 for (MediumAttachmentList::const_iterator
10513 it = atts.begin();
10514 it != atts.end();
10515 ++it)
10516 {
10517 settings::AttachedDevice dev;
10518 IMediumAttachment *iA = *it;
10519 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10520 Medium *pMedium = pAttach->i_getMedium();
10521
10522 dev.deviceType = pAttach->i_getType();
10523 dev.lPort = pAttach->i_getPort();
10524 dev.lDevice = pAttach->i_getDevice();
10525 dev.fPassThrough = pAttach->i_getPassthrough();
10526 dev.fHotPluggable = pAttach->i_getHotPluggable();
10527 if (pMedium)
10528 {
10529 if (pMedium->i_isHostDrive())
10530 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10531 else
10532 dev.uuid = pMedium->i_getId();
10533 dev.fTempEject = pAttach->i_getTempEject();
10534 dev.fNonRotational = pAttach->i_getNonRotational();
10535 dev.fDiscard = pAttach->i_getDiscard();
10536 }
10537
10538 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10539
10540 data.llAttachedDevices.push_back(dev);
10541 }
10542
10543 return S_OK;
10544}
10545
10546/**
10547 * Saves machine state settings as defined by aFlags
10548 * (SaveSTS_* values).
10549 *
10550 * @param aFlags Combination of SaveSTS_* flags.
10551 *
10552 * @note Locks objects for writing.
10553 */
10554HRESULT Machine::i_saveStateSettings(int aFlags)
10555{
10556 if (aFlags == 0)
10557 return S_OK;
10558
10559 AutoCaller autoCaller(this);
10560 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10561
10562 /* This object's write lock is also necessary to serialize file access
10563 * (prevent concurrent reads and writes) */
10564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10565
10566 HRESULT rc = S_OK;
10567
10568 Assert(mData->pMachineConfigFile);
10569
10570 try
10571 {
10572 if (aFlags & SaveSTS_CurStateModified)
10573 mData->pMachineConfigFile->fCurrentStateModified = true;
10574
10575 if (aFlags & SaveSTS_StateFilePath)
10576 {
10577 if (!mSSData->strStateFilePath.isEmpty())
10578 /* try to make the file name relative to the settings file dir */
10579 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10580 else
10581 mData->pMachineConfigFile->strStateFile.setNull();
10582 }
10583
10584 if (aFlags & SaveSTS_StateTimeStamp)
10585 {
10586 Assert( mData->mMachineState != MachineState_Aborted
10587 || mSSData->strStateFilePath.isEmpty());
10588
10589 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10590
10591 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10592/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10593 }
10594
10595 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10596 }
10597 catch (...)
10598 {
10599 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10600 }
10601
10602 return rc;
10603}
10604
10605/**
10606 * Ensures that the given medium is added to a media registry. If this machine
10607 * was created with 4.0 or later, then the machine registry is used. Otherwise
10608 * the global VirtualBox media registry is used.
10609 *
10610 * Caller must NOT hold machine lock, media tree or any medium locks!
10611 *
10612 * @param pMedium
10613 */
10614void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10615{
10616 /* Paranoia checks: do not hold machine or media tree locks. */
10617 AssertReturnVoid(!isWriteLockOnCurrentThread());
10618 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10619
10620 ComObjPtr<Medium> pBase;
10621 {
10622 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10623 pBase = pMedium->i_getBase();
10624 }
10625
10626 /* Paranoia checks: do not hold medium locks. */
10627 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10628 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10629
10630 // decide which medium registry to use now that the medium is attached:
10631 Guid uuid;
10632 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10633 if (fCanHaveOwnMediaRegistry)
10634 // machine XML is VirtualBox 4.0 or higher:
10635 uuid = i_getId(); // machine UUID
10636 else
10637 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10638
10639 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10640 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10641 if (pMedium->i_addRegistry(uuid))
10642 mParent->i_markRegistryModified(uuid);
10643
10644 /* For more complex hard disk structures it can happen that the base
10645 * medium isn't yet associated with any medium registry. Do that now. */
10646 if (pMedium != pBase)
10647 {
10648 /* Tree lock needed by Medium::addRegistry when recursing. */
10649 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10650 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10651 {
10652 treeLock.release();
10653 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10654 treeLock.acquire();
10655 }
10656 if (pBase->i_addRegistryRecursive(uuid))
10657 {
10658 treeLock.release();
10659 mParent->i_markRegistryModified(uuid);
10660 }
10661 }
10662}
10663
10664/**
10665 * Creates differencing hard disks for all normal hard disks attached to this
10666 * machine and a new set of attachments to refer to created disks.
10667 *
10668 * Used when taking a snapshot or when deleting the current state. Gets called
10669 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10670 *
10671 * This method assumes that mMediumAttachments contains the original hard disk
10672 * attachments it needs to create diffs for. On success, these attachments will
10673 * be replaced with the created diffs.
10674 *
10675 * Attachments with non-normal hard disks are left as is.
10676 *
10677 * If @a aOnline is @c false then the original hard disks that require implicit
10678 * diffs will be locked for reading. Otherwise it is assumed that they are
10679 * already locked for writing (when the VM was started). Note that in the latter
10680 * case it is responsibility of the caller to lock the newly created diffs for
10681 * writing if this method succeeds.
10682 *
10683 * @param aProgress Progress object to run (must contain at least as
10684 * many operations left as the number of hard disks
10685 * attached).
10686 * @param aWeight Weight of this operation.
10687 * @param aOnline Whether the VM was online prior to this operation.
10688 *
10689 * @note The progress object is not marked as completed, neither on success nor
10690 * on failure. This is a responsibility of the caller.
10691 *
10692 * @note Locks this object and the media tree for writing.
10693 */
10694HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10695 ULONG aWeight,
10696 bool aOnline)
10697{
10698 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10699
10700 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10701 AssertReturn(!!pProgressControl, E_INVALIDARG);
10702
10703 AutoCaller autoCaller(this);
10704 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10705
10706 AutoMultiWriteLock2 alock(this->lockHandle(),
10707 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10708
10709 /* must be in a protective state because we release the lock below */
10710 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10711 || mData->mMachineState == MachineState_OnlineSnapshotting
10712 || mData->mMachineState == MachineState_LiveSnapshotting
10713 || mData->mMachineState == MachineState_RestoringSnapshot
10714 || mData->mMachineState == MachineState_DeletingSnapshot
10715 , E_FAIL);
10716
10717 HRESULT rc = S_OK;
10718
10719 // use appropriate locked media map (online or offline)
10720 MediumLockListMap lockedMediaOffline;
10721 MediumLockListMap *lockedMediaMap;
10722 if (aOnline)
10723 lockedMediaMap = &mData->mSession.mLockedMedia;
10724 else
10725 lockedMediaMap = &lockedMediaOffline;
10726
10727 try
10728 {
10729 if (!aOnline)
10730 {
10731 /* lock all attached hard disks early to detect "in use"
10732 * situations before creating actual diffs */
10733 for (MediumAttachmentList::const_iterator
10734 it = mMediumAttachments->begin();
10735 it != mMediumAttachments->end();
10736 ++it)
10737 {
10738 MediumAttachment *pAtt = *it;
10739 if (pAtt->i_getType() == DeviceType_HardDisk)
10740 {
10741 Medium *pMedium = pAtt->i_getMedium();
10742 Assert(pMedium);
10743
10744 MediumLockList *pMediumLockList(new MediumLockList());
10745 alock.release();
10746 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10747 NULL /* pToLockWrite */,
10748 false /* fMediumLockWriteAll */,
10749 NULL,
10750 *pMediumLockList);
10751 alock.acquire();
10752 if (FAILED(rc))
10753 {
10754 delete pMediumLockList;
10755 throw rc;
10756 }
10757 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10758 if (FAILED(rc))
10759 {
10760 throw setError(rc,
10761 tr("Collecting locking information for all attached media failed"));
10762 }
10763 }
10764 }
10765
10766 /* Now lock all media. If this fails, nothing is locked. */
10767 alock.release();
10768 rc = lockedMediaMap->Lock();
10769 alock.acquire();
10770 if (FAILED(rc))
10771 {
10772 throw setError(rc,
10773 tr("Locking of attached media failed"));
10774 }
10775 }
10776
10777 /* remember the current list (note that we don't use backup() since
10778 * mMediumAttachments may be already backed up) */
10779 MediumAttachmentList atts = *mMediumAttachments.data();
10780
10781 /* start from scratch */
10782 mMediumAttachments->clear();
10783
10784 /* go through remembered attachments and create diffs for normal hard
10785 * disks and attach them */
10786 for (MediumAttachmentList::const_iterator
10787 it = atts.begin();
10788 it != atts.end();
10789 ++it)
10790 {
10791 MediumAttachment *pAtt = *it;
10792
10793 DeviceType_T devType = pAtt->i_getType();
10794 Medium *pMedium = pAtt->i_getMedium();
10795
10796 if ( devType != DeviceType_HardDisk
10797 || pMedium == NULL
10798 || pMedium->i_getType() != MediumType_Normal)
10799 {
10800 /* copy the attachment as is */
10801
10802 /** @todo the progress object created in SessionMachine::TakeSnaphot
10803 * only expects operations for hard disks. Later other
10804 * device types need to show up in the progress as well. */
10805 if (devType == DeviceType_HardDisk)
10806 {
10807 if (pMedium == NULL)
10808 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10809 aWeight); // weight
10810 else
10811 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10812 pMedium->i_getBase()->i_getName().c_str()).raw(),
10813 aWeight); // weight
10814 }
10815
10816 mMediumAttachments->push_back(pAtt);
10817 continue;
10818 }
10819
10820 /* need a diff */
10821 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10822 pMedium->i_getBase()->i_getName().c_str()).raw(),
10823 aWeight); // weight
10824
10825 Utf8Str strFullSnapshotFolder;
10826 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10827
10828 ComObjPtr<Medium> diff;
10829 diff.createObject();
10830 // store the diff in the same registry as the parent
10831 // (this cannot fail here because we can't create implicit diffs for
10832 // unregistered images)
10833 Guid uuidRegistryParent;
10834 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10835 Assert(fInRegistry); NOREF(fInRegistry);
10836 rc = diff->init(mParent,
10837 pMedium->i_getPreferredDiffFormat(),
10838 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10839 uuidRegistryParent,
10840 DeviceType_HardDisk);
10841 if (FAILED(rc)) throw rc;
10842
10843 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10844 * the push_back? Looks like we're going to release medium with the
10845 * wrong kind of lock (general issue with if we fail anywhere at all)
10846 * and an orphaned VDI in the snapshots folder. */
10847
10848 /* update the appropriate lock list */
10849 MediumLockList *pMediumLockList;
10850 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10851 AssertComRCThrowRC(rc);
10852 if (aOnline)
10853 {
10854 alock.release();
10855 /* The currently attached medium will be read-only, change
10856 * the lock type to read. */
10857 rc = pMediumLockList->Update(pMedium, false);
10858 alock.acquire();
10859 AssertComRCThrowRC(rc);
10860 }
10861
10862 /* release the locks before the potentially lengthy operation */
10863 alock.release();
10864 rc = pMedium->i_createDiffStorage(diff,
10865 pMedium->i_getPreferredDiffVariant(),
10866 pMediumLockList,
10867 NULL /* aProgress */,
10868 true /* aWait */,
10869 false /* aNotify */);
10870 alock.acquire();
10871 if (FAILED(rc)) throw rc;
10872
10873 /* actual lock list update is done in Machine::i_commitMedia */
10874
10875 rc = diff->i_addBackReference(mData->mUuid);
10876 AssertComRCThrowRC(rc);
10877
10878 /* add a new attachment */
10879 ComObjPtr<MediumAttachment> attachment;
10880 attachment.createObject();
10881 rc = attachment->init(this,
10882 diff,
10883 pAtt->i_getControllerName(),
10884 pAtt->i_getPort(),
10885 pAtt->i_getDevice(),
10886 DeviceType_HardDisk,
10887 true /* aImplicit */,
10888 false /* aPassthrough */,
10889 false /* aTempEject */,
10890 pAtt->i_getNonRotational(),
10891 pAtt->i_getDiscard(),
10892 pAtt->i_getHotPluggable(),
10893 pAtt->i_getBandwidthGroup());
10894 if (FAILED(rc)) throw rc;
10895
10896 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10897 AssertComRCThrowRC(rc);
10898 mMediumAttachments->push_back(attachment);
10899 }
10900 }
10901 catch (HRESULT aRC) { rc = aRC; }
10902
10903 /* unlock all hard disks we locked when there is no VM */
10904 if (!aOnline)
10905 {
10906 ErrorInfoKeeper eik;
10907
10908 HRESULT rc1 = lockedMediaMap->Clear();
10909 AssertComRC(rc1);
10910 }
10911
10912 return rc;
10913}
10914
10915/**
10916 * Deletes implicit differencing hard disks created either by
10917 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10918 * mMediumAttachments.
10919 *
10920 * Note that to delete hard disks created by #attachDevice() this method is
10921 * called from #i_rollbackMedia() when the changes are rolled back.
10922 *
10923 * @note Locks this object and the media tree for writing.
10924 */
10925HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10926{
10927 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10928
10929 AutoCaller autoCaller(this);
10930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10931
10932 AutoMultiWriteLock2 alock(this->lockHandle(),
10933 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10934
10935 /* We absolutely must have backed up state. */
10936 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10937
10938 /* Check if there are any implicitly created diff images. */
10939 bool fImplicitDiffs = false;
10940 for (MediumAttachmentList::const_iterator
10941 it = mMediumAttachments->begin();
10942 it != mMediumAttachments->end();
10943 ++it)
10944 {
10945 const ComObjPtr<MediumAttachment> &pAtt = *it;
10946 if (pAtt->i_isImplicit())
10947 {
10948 fImplicitDiffs = true;
10949 break;
10950 }
10951 }
10952 /* If there is nothing to do, leave early. This saves lots of image locking
10953 * effort. It also avoids a MachineStateChanged event without real reason.
10954 * This is important e.g. when loading a VM config, because there should be
10955 * no events. Otherwise API clients can become thoroughly confused for
10956 * inaccessible VMs (the code for loading VM configs uses this method for
10957 * cleanup if the config makes no sense), as they take such events as an
10958 * indication that the VM is alive, and they would force the VM config to
10959 * be reread, leading to an endless loop. */
10960 if (!fImplicitDiffs)
10961 return S_OK;
10962
10963 HRESULT rc = S_OK;
10964 MachineState_T oldState = mData->mMachineState;
10965
10966 /* will release the lock before the potentially lengthy operation,
10967 * so protect with the special state (unless already protected) */
10968 if ( oldState != MachineState_Snapshotting
10969 && oldState != MachineState_OnlineSnapshotting
10970 && oldState != MachineState_LiveSnapshotting
10971 && oldState != MachineState_RestoringSnapshot
10972 && oldState != MachineState_DeletingSnapshot
10973 && oldState != MachineState_DeletingSnapshotOnline
10974 && oldState != MachineState_DeletingSnapshotPaused
10975 )
10976 i_setMachineState(MachineState_SettingUp);
10977
10978 // use appropriate locked media map (online or offline)
10979 MediumLockListMap lockedMediaOffline;
10980 MediumLockListMap *lockedMediaMap;
10981 if (aOnline)
10982 lockedMediaMap = &mData->mSession.mLockedMedia;
10983 else
10984 lockedMediaMap = &lockedMediaOffline;
10985
10986 try
10987 {
10988 if (!aOnline)
10989 {
10990 /* lock all attached hard disks early to detect "in use"
10991 * situations before deleting actual diffs */
10992 for (MediumAttachmentList::const_iterator
10993 it = mMediumAttachments->begin();
10994 it != mMediumAttachments->end();
10995 ++it)
10996 {
10997 MediumAttachment *pAtt = *it;
10998 if (pAtt->i_getType() == DeviceType_HardDisk)
10999 {
11000 Medium *pMedium = pAtt->i_getMedium();
11001 Assert(pMedium);
11002
11003 MediumLockList *pMediumLockList(new MediumLockList());
11004 alock.release();
11005 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11006 NULL /* pToLockWrite */,
11007 false /* fMediumLockWriteAll */,
11008 NULL,
11009 *pMediumLockList);
11010 alock.acquire();
11011
11012 if (FAILED(rc))
11013 {
11014 delete pMediumLockList;
11015 throw rc;
11016 }
11017
11018 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11019 if (FAILED(rc))
11020 throw rc;
11021 }
11022 }
11023
11024 if (FAILED(rc))
11025 throw rc;
11026 } // end of offline
11027
11028 /* Lock lists are now up to date and include implicitly created media */
11029
11030 /* Go through remembered attachments and delete all implicitly created
11031 * diffs and fix up the attachment information */
11032 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11033 MediumAttachmentList implicitAtts;
11034 for (MediumAttachmentList::const_iterator
11035 it = mMediumAttachments->begin();
11036 it != mMediumAttachments->end();
11037 ++it)
11038 {
11039 ComObjPtr<MediumAttachment> pAtt = *it;
11040 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11041 if (pMedium.isNull())
11042 continue;
11043
11044 // Implicit attachments go on the list for deletion and back references are removed.
11045 if (pAtt->i_isImplicit())
11046 {
11047 /* Deassociate and mark for deletion */
11048 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11049 rc = pMedium->i_removeBackReference(mData->mUuid);
11050 if (FAILED(rc))
11051 throw rc;
11052 implicitAtts.push_back(pAtt);
11053 continue;
11054 }
11055
11056 /* Was this medium attached before? */
11057 if (!i_findAttachment(oldAtts, pMedium))
11058 {
11059 /* no: de-associate */
11060 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11061 rc = pMedium->i_removeBackReference(mData->mUuid);
11062 if (FAILED(rc))
11063 throw rc;
11064 continue;
11065 }
11066 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11067 }
11068
11069 /* If there are implicit attachments to delete, throw away the lock
11070 * map contents (which will unlock all media) since the medium
11071 * attachments will be rolled back. Below we need to completely
11072 * recreate the lock map anyway since it is infinitely complex to
11073 * do this incrementally (would need reconstructing each attachment
11074 * change, which would be extremely hairy). */
11075 if (implicitAtts.size() != 0)
11076 {
11077 ErrorInfoKeeper eik;
11078
11079 HRESULT rc1 = lockedMediaMap->Clear();
11080 AssertComRC(rc1);
11081 }
11082
11083 /* rollback hard disk changes */
11084 mMediumAttachments.rollback();
11085
11086 MultiResult mrc(S_OK);
11087
11088 // Delete unused implicit diffs.
11089 if (implicitAtts.size() != 0)
11090 {
11091 alock.release();
11092
11093 for (MediumAttachmentList::const_iterator
11094 it = implicitAtts.begin();
11095 it != implicitAtts.end();
11096 ++it)
11097 {
11098 // Remove medium associated with this attachment.
11099 ComObjPtr<MediumAttachment> pAtt = *it;
11100 Assert(pAtt);
11101 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11102 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11103 Assert(pMedium);
11104
11105 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11106 // continue on delete failure, just collect error messages
11107 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11108 pMedium->i_getLocationFull().c_str() ));
11109 mrc = rc;
11110 }
11111 // Clear the list of deleted implicit attachments now, while not
11112 // holding the lock, as it will ultimately trigger Medium::uninit()
11113 // calls which assume that the media tree lock isn't held.
11114 implicitAtts.clear();
11115
11116 alock.acquire();
11117
11118 /* if there is a VM recreate media lock map as mentioned above,
11119 * otherwise it is a waste of time and we leave things unlocked */
11120 if (aOnline)
11121 {
11122 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11123 /* must never be NULL, but better safe than sorry */
11124 if (!pMachine.isNull())
11125 {
11126 alock.release();
11127 rc = mData->mSession.mMachine->i_lockMedia();
11128 alock.acquire();
11129 if (FAILED(rc))
11130 throw rc;
11131 }
11132 }
11133 }
11134 }
11135 catch (HRESULT aRC) {rc = aRC;}
11136
11137 if (mData->mMachineState == MachineState_SettingUp)
11138 i_setMachineState(oldState);
11139
11140 /* unlock all hard disks we locked when there is no VM */
11141 if (!aOnline)
11142 {
11143 ErrorInfoKeeper eik;
11144
11145 HRESULT rc1 = lockedMediaMap->Clear();
11146 AssertComRC(rc1);
11147 }
11148
11149 return rc;
11150}
11151
11152
11153/**
11154 * Looks through the given list of media attachments for one with the given parameters
11155 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11156 * can be searched as well if needed.
11157 *
11158 * @param ll
11159 * @param aControllerName
11160 * @param aControllerPort
11161 * @param aDevice
11162 * @return
11163 */
11164MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11165 const Utf8Str &aControllerName,
11166 LONG aControllerPort,
11167 LONG aDevice)
11168{
11169 for (MediumAttachmentList::const_iterator
11170 it = ll.begin();
11171 it != ll.end();
11172 ++it)
11173 {
11174 MediumAttachment *pAttach = *it;
11175 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11176 return pAttach;
11177 }
11178
11179 return NULL;
11180}
11181
11182/**
11183 * Looks through the given list of media attachments for one with the given parameters
11184 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11185 * can be searched as well if needed.
11186 *
11187 * @param ll
11188 * @param pMedium
11189 * @return
11190 */
11191MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11192 ComObjPtr<Medium> pMedium)
11193{
11194 for (MediumAttachmentList::const_iterator
11195 it = ll.begin();
11196 it != ll.end();
11197 ++it)
11198 {
11199 MediumAttachment *pAttach = *it;
11200 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11201 if (pMediumThis == pMedium)
11202 return pAttach;
11203 }
11204
11205 return NULL;
11206}
11207
11208/**
11209 * Looks through the given list of media attachments for one with the given parameters
11210 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11211 * can be searched as well if needed.
11212 *
11213 * @param ll
11214 * @param id
11215 * @return
11216 */
11217MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11218 Guid &id)
11219{
11220 for (MediumAttachmentList::const_iterator
11221 it = ll.begin();
11222 it != ll.end();
11223 ++it)
11224 {
11225 MediumAttachment *pAttach = *it;
11226 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11227 if (pMediumThis->i_getId() == id)
11228 return pAttach;
11229 }
11230
11231 return NULL;
11232}
11233
11234/**
11235 * Main implementation for Machine::DetachDevice. This also gets called
11236 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11237 *
11238 * @param pAttach Medium attachment to detach.
11239 * @param writeLock Machine write lock which the caller must have locked once.
11240 * This may be released temporarily in here.
11241 * @param pSnapshot If NULL, then the detachment is for the current machine.
11242 * Otherwise this is for a SnapshotMachine, and this must be
11243 * its snapshot.
11244 * @return
11245 */
11246HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11247 AutoWriteLock &writeLock,
11248 Snapshot *pSnapshot)
11249{
11250 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11251 DeviceType_T mediumType = pAttach->i_getType();
11252
11253 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11254
11255 if (pAttach->i_isImplicit())
11256 {
11257 /* attempt to implicitly delete the implicitly created diff */
11258
11259 /// @todo move the implicit flag from MediumAttachment to Medium
11260 /// and forbid any hard disk operation when it is implicit. Or maybe
11261 /// a special media state for it to make it even more simple.
11262
11263 Assert(mMediumAttachments.isBackedUp());
11264
11265 /* will release the lock before the potentially lengthy operation, so
11266 * protect with the special state */
11267 MachineState_T oldState = mData->mMachineState;
11268 i_setMachineState(MachineState_SettingUp);
11269
11270 writeLock.release();
11271
11272 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11273 true /*aWait*/,
11274 false /*aNotify*/);
11275
11276 writeLock.acquire();
11277
11278 i_setMachineState(oldState);
11279
11280 if (FAILED(rc)) return rc;
11281 }
11282
11283 i_setModified(IsModified_Storage);
11284 mMediumAttachments.backup();
11285 mMediumAttachments->remove(pAttach);
11286
11287 if (!oldmedium.isNull())
11288 {
11289 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11290 if (pSnapshot)
11291 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11292 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11293 else if (mediumType != DeviceType_HardDisk)
11294 oldmedium->i_removeBackReference(mData->mUuid);
11295 }
11296
11297 return S_OK;
11298}
11299
11300/**
11301 * Goes thru all media of the given list and
11302 *
11303 * 1) calls i_detachDevice() on each of them for this machine and
11304 * 2) adds all Medium objects found in the process to the given list,
11305 * depending on cleanupMode.
11306 *
11307 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11308 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11309 * media to the list.
11310 *
11311 * This gets called from Machine::Unregister, both for the actual Machine and
11312 * the SnapshotMachine objects that might be found in the snapshots.
11313 *
11314 * Requires caller and locking. The machine lock must be passed in because it
11315 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11316 *
11317 * @param writeLock Machine lock from top-level caller; this gets passed to
11318 * i_detachDevice.
11319 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11320 * object if called for a SnapshotMachine.
11321 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11322 * added to llMedia; if Full, then all media get added;
11323 * otherwise no media get added.
11324 * @param llMedia Caller's list to receive Medium objects which got detached so
11325 * caller can close() them, depending on cleanupMode.
11326 * @return
11327 */
11328HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11329 Snapshot *pSnapshot,
11330 CleanupMode_T cleanupMode,
11331 MediaList &llMedia)
11332{
11333 Assert(isWriteLockOnCurrentThread());
11334
11335 HRESULT rc;
11336
11337 // make a temporary list because i_detachDevice invalidates iterators into
11338 // mMediumAttachments
11339 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11340
11341 for (MediumAttachmentList::iterator
11342 it = llAttachments2.begin();
11343 it != llAttachments2.end();
11344 ++it)
11345 {
11346 ComObjPtr<MediumAttachment> &pAttach = *it;
11347 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11348
11349 if (!pMedium.isNull())
11350 {
11351 AutoCaller mac(pMedium);
11352 if (FAILED(mac.rc())) return mac.rc();
11353 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11354 DeviceType_T devType = pMedium->i_getDeviceType();
11355 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11356 && devType == DeviceType_HardDisk)
11357 || (cleanupMode == CleanupMode_Full)
11358 )
11359 {
11360 llMedia.push_back(pMedium);
11361 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11362 /* Not allowed to keep this lock as below we need the parent
11363 * medium lock, and the lock order is parent to child. */
11364 lock.release();
11365 /*
11366 * Search for medias which are not attached to any machine, but
11367 * in the chain to an attached disk. Mediums are only consided
11368 * if they are:
11369 * - have only one child
11370 * - no references to any machines
11371 * - are of normal medium type
11372 */
11373 while (!pParent.isNull())
11374 {
11375 AutoCaller mac1(pParent);
11376 if (FAILED(mac1.rc())) return mac1.rc();
11377 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11378 if (pParent->i_getChildren().size() == 1)
11379 {
11380 if ( pParent->i_getMachineBackRefCount() == 0
11381 && pParent->i_getType() == MediumType_Normal
11382 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11383 llMedia.push_back(pParent);
11384 }
11385 else
11386 break;
11387 pParent = pParent->i_getParent();
11388 }
11389 }
11390 }
11391
11392 // real machine: then we need to use the proper method
11393 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11394
11395 if (FAILED(rc))
11396 return rc;
11397 }
11398
11399 return S_OK;
11400}
11401
11402/**
11403 * Perform deferred hard disk detachments.
11404 *
11405 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11406 * changed (not backed up).
11407 *
11408 * If @a aOnline is @c true then this method will also unlock the old hard
11409 * disks for which the new implicit diffs were created and will lock these new
11410 * diffs for writing.
11411 *
11412 * @param aOnline Whether the VM was online prior to this operation.
11413 *
11414 * @note Locks this object for writing!
11415 */
11416void Machine::i_commitMedia(bool aOnline /*= false*/)
11417{
11418 AutoCaller autoCaller(this);
11419 AssertComRCReturnVoid(autoCaller.rc());
11420
11421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11422
11423 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11424
11425 HRESULT rc = S_OK;
11426
11427 /* no attach/detach operations -- nothing to do */
11428 if (!mMediumAttachments.isBackedUp())
11429 return;
11430
11431 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11432 bool fMediaNeedsLocking = false;
11433
11434 /* enumerate new attachments */
11435 for (MediumAttachmentList::const_iterator
11436 it = mMediumAttachments->begin();
11437 it != mMediumAttachments->end();
11438 ++it)
11439 {
11440 MediumAttachment *pAttach = *it;
11441
11442 pAttach->i_commit();
11443
11444 Medium *pMedium = pAttach->i_getMedium();
11445 bool fImplicit = pAttach->i_isImplicit();
11446
11447 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11448 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11449 fImplicit));
11450
11451 /** @todo convert all this Machine-based voodoo to MediumAttachment
11452 * based commit logic. */
11453 if (fImplicit)
11454 {
11455 /* convert implicit attachment to normal */
11456 pAttach->i_setImplicit(false);
11457
11458 if ( aOnline
11459 && pMedium
11460 && pAttach->i_getType() == DeviceType_HardDisk
11461 )
11462 {
11463 /* update the appropriate lock list */
11464 MediumLockList *pMediumLockList;
11465 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11466 AssertComRC(rc);
11467 if (pMediumLockList)
11468 {
11469 /* unlock if there's a need to change the locking */
11470 if (!fMediaNeedsLocking)
11471 {
11472 rc = mData->mSession.mLockedMedia.Unlock();
11473 AssertComRC(rc);
11474 fMediaNeedsLocking = true;
11475 }
11476 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11477 AssertComRC(rc);
11478 rc = pMediumLockList->Append(pMedium, true);
11479 AssertComRC(rc);
11480 }
11481 }
11482
11483 continue;
11484 }
11485
11486 if (pMedium)
11487 {
11488 /* was this medium attached before? */
11489 for (MediumAttachmentList::iterator
11490 oldIt = oldAtts.begin();
11491 oldIt != oldAtts.end();
11492 ++oldIt)
11493 {
11494 MediumAttachment *pOldAttach = *oldIt;
11495 if (pOldAttach->i_getMedium() == pMedium)
11496 {
11497 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11498
11499 /* yes: remove from old to avoid de-association */
11500 oldAtts.erase(oldIt);
11501 break;
11502 }
11503 }
11504 }
11505 }
11506
11507 /* enumerate remaining old attachments and de-associate from the
11508 * current machine state */
11509 for (MediumAttachmentList::const_iterator
11510 it = oldAtts.begin();
11511 it != oldAtts.end();
11512 ++it)
11513 {
11514 MediumAttachment *pAttach = *it;
11515 Medium *pMedium = pAttach->i_getMedium();
11516
11517 /* Detach only hard disks, since DVD/floppy media is detached
11518 * instantly in MountMedium. */
11519 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11520 {
11521 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11522
11523 /* now de-associate from the current machine state */
11524 rc = pMedium->i_removeBackReference(mData->mUuid);
11525 AssertComRC(rc);
11526
11527 if (aOnline)
11528 {
11529 /* unlock since medium is not used anymore */
11530 MediumLockList *pMediumLockList;
11531 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11532 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11533 {
11534 /* this happens for online snapshots, there the attachment
11535 * is changing, but only to a diff image created under
11536 * the old one, so there is no separate lock list */
11537 Assert(!pMediumLockList);
11538 }
11539 else
11540 {
11541 AssertComRC(rc);
11542 if (pMediumLockList)
11543 {
11544 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11545 AssertComRC(rc);
11546 }
11547 }
11548 }
11549 }
11550 }
11551
11552 /* take media locks again so that the locking state is consistent */
11553 if (fMediaNeedsLocking)
11554 {
11555 Assert(aOnline);
11556 rc = mData->mSession.mLockedMedia.Lock();
11557 AssertComRC(rc);
11558 }
11559
11560 /* commit the hard disk changes */
11561 mMediumAttachments.commit();
11562
11563 if (i_isSessionMachine())
11564 {
11565 /*
11566 * Update the parent machine to point to the new owner.
11567 * This is necessary because the stored parent will point to the
11568 * session machine otherwise and cause crashes or errors later
11569 * when the session machine gets invalid.
11570 */
11571 /** @todo Change the MediumAttachment class to behave like any other
11572 * class in this regard by creating peer MediumAttachment
11573 * objects for session machines and share the data with the peer
11574 * machine.
11575 */
11576 for (MediumAttachmentList::const_iterator
11577 it = mMediumAttachments->begin();
11578 it != mMediumAttachments->end();
11579 ++it)
11580 (*it)->i_updateParentMachine(mPeer);
11581
11582 /* attach new data to the primary machine and reshare it */
11583 mPeer->mMediumAttachments.attach(mMediumAttachments);
11584 }
11585
11586 return;
11587}
11588
11589/**
11590 * Perform deferred deletion of implicitly created diffs.
11591 *
11592 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11593 * changed (not backed up).
11594 *
11595 * @note Locks this object for writing!
11596 */
11597void Machine::i_rollbackMedia()
11598{
11599 AutoCaller autoCaller(this);
11600 AssertComRCReturnVoid(autoCaller.rc());
11601
11602 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11603 LogFlowThisFunc(("Entering rollbackMedia\n"));
11604
11605 HRESULT rc = S_OK;
11606
11607 /* no attach/detach operations -- nothing to do */
11608 if (!mMediumAttachments.isBackedUp())
11609 return;
11610
11611 /* enumerate new attachments */
11612 for (MediumAttachmentList::const_iterator
11613 it = mMediumAttachments->begin();
11614 it != mMediumAttachments->end();
11615 ++it)
11616 {
11617 MediumAttachment *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_removeBackReference(mData->mUuid);
11625 AssertComRC(rc);
11626 }
11627 }
11628
11629 (*it)->i_rollback();
11630
11631 pAttach = *it;
11632 /* Fix up the backrefs for DVD/floppy media. */
11633 if (pAttach->i_getType() != DeviceType_HardDisk)
11634 {
11635 Medium *pMedium = pAttach->i_getMedium();
11636 if (pMedium)
11637 {
11638 rc = pMedium->i_addBackReference(mData->mUuid);
11639 AssertComRC(rc);
11640 }
11641 }
11642 }
11643
11644 /** @todo convert all this Machine-based voodoo to MediumAttachment
11645 * based rollback logic. */
11646 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11647
11648 return;
11649}
11650
11651/**
11652 * Returns true if the settings file is located in the directory named exactly
11653 * as the machine; this means, among other things, that the machine directory
11654 * should be auto-renamed.
11655 *
11656 * @param aSettingsDir if not NULL, the full machine settings file directory
11657 * name will be assigned there.
11658 *
11659 * @note Doesn't lock anything.
11660 * @note Not thread safe (must be called from this object's lock).
11661 */
11662bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11663{
11664 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11665 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11666 if (aSettingsDir)
11667 *aSettingsDir = strMachineDirName;
11668 strMachineDirName.stripPath(); // vmname
11669 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11670 strConfigFileOnly.stripPath() // vmname.vbox
11671 .stripSuffix(); // vmname
11672 /** @todo hack, make somehow use of ComposeMachineFilename */
11673 if (mUserData->s.fDirectoryIncludesUUID)
11674 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11675
11676 AssertReturn(!strMachineDirName.isEmpty(), false);
11677 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11678
11679 return strMachineDirName == strConfigFileOnly;
11680}
11681
11682/**
11683 * Discards all changes to machine settings.
11684 *
11685 * @param aNotify Whether to notify the direct session about changes or not.
11686 *
11687 * @note Locks objects for writing!
11688 */
11689void Machine::i_rollback(bool aNotify)
11690{
11691 AutoCaller autoCaller(this);
11692 AssertComRCReturn(autoCaller.rc(), (void)0);
11693
11694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11695
11696 if (!mStorageControllers.isNull())
11697 {
11698 if (mStorageControllers.isBackedUp())
11699 {
11700 /* unitialize all new devices (absent in the backed up list). */
11701 StorageControllerList *backedList = mStorageControllers.backedUpData();
11702 for (StorageControllerList::const_iterator
11703 it = mStorageControllers->begin();
11704 it != mStorageControllers->end();
11705 ++it)
11706 {
11707 if ( std::find(backedList->begin(), backedList->end(), *it)
11708 == backedList->end()
11709 )
11710 {
11711 (*it)->uninit();
11712 }
11713 }
11714
11715 /* restore the list */
11716 mStorageControllers.rollback();
11717 }
11718
11719 /* rollback any changes to devices after restoring the list */
11720 if (mData->flModifications & IsModified_Storage)
11721 {
11722 for (StorageControllerList::const_iterator
11723 it = mStorageControllers->begin();
11724 it != mStorageControllers->end();
11725 ++it)
11726 {
11727 (*it)->i_rollback();
11728 }
11729 }
11730 }
11731
11732 if (!mUSBControllers.isNull())
11733 {
11734 if (mUSBControllers.isBackedUp())
11735 {
11736 /* unitialize all new devices (absent in the backed up list). */
11737 USBControllerList *backedList = mUSBControllers.backedUpData();
11738 for (USBControllerList::const_iterator
11739 it = mUSBControllers->begin();
11740 it != mUSBControllers->end();
11741 ++it)
11742 {
11743 if ( std::find(backedList->begin(), backedList->end(), *it)
11744 == backedList->end()
11745 )
11746 {
11747 (*it)->uninit();
11748 }
11749 }
11750
11751 /* restore the list */
11752 mUSBControllers.rollback();
11753 }
11754
11755 /* rollback any changes to devices after restoring the list */
11756 if (mData->flModifications & IsModified_USB)
11757 {
11758 for (USBControllerList::const_iterator
11759 it = mUSBControllers->begin();
11760 it != mUSBControllers->end();
11761 ++it)
11762 {
11763 (*it)->i_rollback();
11764 }
11765 }
11766 }
11767
11768 mUserData.rollback();
11769
11770 mHWData.rollback();
11771
11772 if (mData->flModifications & IsModified_Storage)
11773 i_rollbackMedia();
11774
11775 if (mBIOSSettings)
11776 mBIOSSettings->i_rollback();
11777
11778 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11779 mRecordingSettings->i_rollback();
11780
11781 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11782 mVRDEServer->i_rollback();
11783
11784 if (mAudioAdapter)
11785 mAudioAdapter->i_rollback();
11786
11787 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11788 mUSBDeviceFilters->i_rollback();
11789
11790 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11791 mBandwidthControl->i_rollback();
11792
11793 if (!mHWData.isNull())
11794 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11795 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11796 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11797 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11798
11799 if (mData->flModifications & IsModified_NetworkAdapters)
11800 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11801 if ( mNetworkAdapters[slot]
11802 && mNetworkAdapters[slot]->i_isModified())
11803 {
11804 mNetworkAdapters[slot]->i_rollback();
11805 networkAdapters[slot] = mNetworkAdapters[slot];
11806 }
11807
11808 if (mData->flModifications & IsModified_SerialPorts)
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11810 if ( mSerialPorts[slot]
11811 && mSerialPorts[slot]->i_isModified())
11812 {
11813 mSerialPorts[slot]->i_rollback();
11814 serialPorts[slot] = mSerialPorts[slot];
11815 }
11816
11817 if (mData->flModifications & IsModified_ParallelPorts)
11818 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11819 if ( mParallelPorts[slot]
11820 && mParallelPorts[slot]->i_isModified())
11821 {
11822 mParallelPorts[slot]->i_rollback();
11823 parallelPorts[slot] = mParallelPorts[slot];
11824 }
11825
11826 if (aNotify)
11827 {
11828 /* inform the direct session about changes */
11829
11830 ComObjPtr<Machine> that = this;
11831 uint32_t flModifications = mData->flModifications;
11832 alock.release();
11833
11834 if (flModifications & IsModified_SharedFolders)
11835 that->i_onSharedFolderChange();
11836
11837 if (flModifications & IsModified_VRDEServer)
11838 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11839 if (flModifications & IsModified_USB)
11840 that->i_onUSBControllerChange();
11841
11842 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11843 if (networkAdapters[slot])
11844 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11845 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11846 if (serialPorts[slot])
11847 that->i_onSerialPortChange(serialPorts[slot]);
11848 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11849 if (parallelPorts[slot])
11850 that->i_onParallelPortChange(parallelPorts[slot]);
11851
11852 if (flModifications & IsModified_Storage)
11853 {
11854 for (StorageControllerList::const_iterator
11855 it = mStorageControllers->begin();
11856 it != mStorageControllers->end();
11857 ++it)
11858 {
11859 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11860 }
11861 }
11862
11863
11864#if 0
11865 if (flModifications & IsModified_BandwidthControl)
11866 that->onBandwidthControlChange();
11867#endif
11868 }
11869}
11870
11871/**
11872 * Commits all the changes to machine settings.
11873 *
11874 * Note that this operation is supposed to never fail.
11875 *
11876 * @note Locks this object and children for writing.
11877 */
11878void Machine::i_commit()
11879{
11880 AutoCaller autoCaller(this);
11881 AssertComRCReturnVoid(autoCaller.rc());
11882
11883 AutoCaller peerCaller(mPeer);
11884 AssertComRCReturnVoid(peerCaller.rc());
11885
11886 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11887
11888 /*
11889 * use safe commit to ensure Snapshot machines (that share mUserData)
11890 * will still refer to a valid memory location
11891 */
11892 mUserData.commitCopy();
11893
11894 mHWData.commit();
11895
11896 if (mMediumAttachments.isBackedUp())
11897 i_commitMedia(Global::IsOnline(mData->mMachineState));
11898
11899 mBIOSSettings->i_commit();
11900 mRecordingSettings->i_commit();
11901 mVRDEServer->i_commit();
11902 mAudioAdapter->i_commit();
11903 mUSBDeviceFilters->i_commit();
11904 mBandwidthControl->i_commit();
11905
11906 /* Since mNetworkAdapters is a list which might have been changed (resized)
11907 * without using the Backupable<> template we need to handle the copying
11908 * of the list entries manually, including the creation of peers for the
11909 * new objects. */
11910 bool commitNetworkAdapters = false;
11911 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11912 if (mPeer)
11913 {
11914 /* commit everything, even the ones which will go away */
11915 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11916 mNetworkAdapters[slot]->i_commit();
11917 /* copy over the new entries, creating a peer and uninit the original */
11918 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11919 for (size_t slot = 0; slot < newSize; slot++)
11920 {
11921 /* look if this adapter has a peer device */
11922 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11923 if (!peer)
11924 {
11925 /* no peer means the adapter is a newly created one;
11926 * create a peer owning data this data share it with */
11927 peer.createObject();
11928 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11929 }
11930 mPeer->mNetworkAdapters[slot] = peer;
11931 }
11932 /* uninit any no longer needed network adapters */
11933 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11934 mNetworkAdapters[slot]->uninit();
11935 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11936 {
11937 if (mPeer->mNetworkAdapters[slot])
11938 mPeer->mNetworkAdapters[slot]->uninit();
11939 }
11940 /* Keep the original network adapter count until this point, so that
11941 * discarding a chipset type change will not lose settings. */
11942 mNetworkAdapters.resize(newSize);
11943 mPeer->mNetworkAdapters.resize(newSize);
11944 }
11945 else
11946 {
11947 /* we have no peer (our parent is the newly created machine);
11948 * just commit changes to the network adapters */
11949 commitNetworkAdapters = true;
11950 }
11951 if (commitNetworkAdapters)
11952 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11953 mNetworkAdapters[slot]->i_commit();
11954
11955 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11956 mSerialPorts[slot]->i_commit();
11957 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11958 mParallelPorts[slot]->i_commit();
11959
11960 bool commitStorageControllers = false;
11961
11962 if (mStorageControllers.isBackedUp())
11963 {
11964 mStorageControllers.commit();
11965
11966 if (mPeer)
11967 {
11968 /* Commit all changes to new controllers (this will reshare data with
11969 * peers for those who have peers) */
11970 StorageControllerList *newList = new StorageControllerList();
11971 for (StorageControllerList::const_iterator
11972 it = mStorageControllers->begin();
11973 it != mStorageControllers->end();
11974 ++it)
11975 {
11976 (*it)->i_commit();
11977
11978 /* look if this controller has a peer device */
11979 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11980 if (!peer)
11981 {
11982 /* no peer means the device is a newly created one;
11983 * create a peer owning data this device share it with */
11984 peer.createObject();
11985 peer->init(mPeer, *it, true /* aReshare */);
11986 }
11987 else
11988 {
11989 /* remove peer from the old list */
11990 mPeer->mStorageControllers->remove(peer);
11991 }
11992 /* and add it to the new list */
11993 newList->push_back(peer);
11994 }
11995
11996 /* uninit old peer's controllers that are left */
11997 for (StorageControllerList::const_iterator
11998 it = mPeer->mStorageControllers->begin();
11999 it != mPeer->mStorageControllers->end();
12000 ++it)
12001 {
12002 (*it)->uninit();
12003 }
12004
12005 /* attach new list of controllers to our peer */
12006 mPeer->mStorageControllers.attach(newList);
12007 }
12008 else
12009 {
12010 /* we have no peer (our parent is the newly created machine);
12011 * just commit changes to devices */
12012 commitStorageControllers = true;
12013 }
12014 }
12015 else
12016 {
12017 /* the list of controllers itself is not changed,
12018 * just commit changes to controllers themselves */
12019 commitStorageControllers = true;
12020 }
12021
12022 if (commitStorageControllers)
12023 {
12024 for (StorageControllerList::const_iterator
12025 it = mStorageControllers->begin();
12026 it != mStorageControllers->end();
12027 ++it)
12028 {
12029 (*it)->i_commit();
12030 }
12031 }
12032
12033 bool commitUSBControllers = false;
12034
12035 if (mUSBControllers.isBackedUp())
12036 {
12037 mUSBControllers.commit();
12038
12039 if (mPeer)
12040 {
12041 /* Commit all changes to new controllers (this will reshare data with
12042 * peers for those who have peers) */
12043 USBControllerList *newList = new USBControllerList();
12044 for (USBControllerList::const_iterator
12045 it = mUSBControllers->begin();
12046 it != mUSBControllers->end();
12047 ++it)
12048 {
12049 (*it)->i_commit();
12050
12051 /* look if this controller has a peer device */
12052 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12053 if (!peer)
12054 {
12055 /* no peer means the device is a newly created one;
12056 * create a peer owning data this device share it with */
12057 peer.createObject();
12058 peer->init(mPeer, *it, true /* aReshare */);
12059 }
12060 else
12061 {
12062 /* remove peer from the old list */
12063 mPeer->mUSBControllers->remove(peer);
12064 }
12065 /* and add it to the new list */
12066 newList->push_back(peer);
12067 }
12068
12069 /* uninit old peer's controllers that are left */
12070 for (USBControllerList::const_iterator
12071 it = mPeer->mUSBControllers->begin();
12072 it != mPeer->mUSBControllers->end();
12073 ++it)
12074 {
12075 (*it)->uninit();
12076 }
12077
12078 /* attach new list of controllers to our peer */
12079 mPeer->mUSBControllers.attach(newList);
12080 }
12081 else
12082 {
12083 /* we have no peer (our parent is the newly created machine);
12084 * just commit changes to devices */
12085 commitUSBControllers = true;
12086 }
12087 }
12088 else
12089 {
12090 /* the list of controllers itself is not changed,
12091 * just commit changes to controllers themselves */
12092 commitUSBControllers = true;
12093 }
12094
12095 if (commitUSBControllers)
12096 {
12097 for (USBControllerList::const_iterator
12098 it = mUSBControllers->begin();
12099 it != mUSBControllers->end();
12100 ++it)
12101 {
12102 (*it)->i_commit();
12103 }
12104 }
12105
12106 if (i_isSessionMachine())
12107 {
12108 /* attach new data to the primary machine and reshare it */
12109 mPeer->mUserData.attach(mUserData);
12110 mPeer->mHWData.attach(mHWData);
12111 /* mmMediumAttachments is reshared by fixupMedia */
12112 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12113 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12114 }
12115}
12116
12117/**
12118 * Copies all the hardware data from the given machine.
12119 *
12120 * Currently, only called when the VM is being restored from a snapshot. In
12121 * particular, this implies that the VM is not running during this method's
12122 * call.
12123 *
12124 * @note This method must be called from under this object's lock.
12125 *
12126 * @note This method doesn't call #i_commit(), so all data remains backed up and
12127 * unsaved.
12128 */
12129void Machine::i_copyFrom(Machine *aThat)
12130{
12131 AssertReturnVoid(!i_isSnapshotMachine());
12132 AssertReturnVoid(aThat->i_isSnapshotMachine());
12133
12134 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12135
12136 mHWData.assignCopy(aThat->mHWData);
12137
12138 // create copies of all shared folders (mHWData after attaching a copy
12139 // contains just references to original objects)
12140 for (HWData::SharedFolderList::iterator
12141 it = mHWData->mSharedFolders.begin();
12142 it != mHWData->mSharedFolders.end();
12143 ++it)
12144 {
12145 ComObjPtr<SharedFolder> folder;
12146 folder.createObject();
12147 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12148 AssertComRC(rc);
12149 *it = folder;
12150 }
12151
12152 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12153 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12154 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12155 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12156 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12157 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12158
12159 /* create private copies of all controllers */
12160 mStorageControllers.backup();
12161 mStorageControllers->clear();
12162 for (StorageControllerList::const_iterator
12163 it = aThat->mStorageControllers->begin();
12164 it != aThat->mStorageControllers->end();
12165 ++it)
12166 {
12167 ComObjPtr<StorageController> ctrl;
12168 ctrl.createObject();
12169 ctrl->initCopy(this, *it);
12170 mStorageControllers->push_back(ctrl);
12171 }
12172
12173 /* create private copies of all USB controllers */
12174 mUSBControllers.backup();
12175 mUSBControllers->clear();
12176 for (USBControllerList::const_iterator
12177 it = aThat->mUSBControllers->begin();
12178 it != aThat->mUSBControllers->end();
12179 ++it)
12180 {
12181 ComObjPtr<USBController> ctrl;
12182 ctrl.createObject();
12183 ctrl->initCopy(this, *it);
12184 mUSBControllers->push_back(ctrl);
12185 }
12186
12187 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12188 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12189 {
12190 if (mNetworkAdapters[slot].isNotNull())
12191 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12192 else
12193 {
12194 unconst(mNetworkAdapters[slot]).createObject();
12195 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12196 }
12197 }
12198 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12199 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12200 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12201 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12202}
12203
12204/**
12205 * Returns whether the given storage controller is hotplug capable.
12206 *
12207 * @returns true if the controller supports hotplugging
12208 * false otherwise.
12209 * @param enmCtrlType The controller type to check for.
12210 */
12211bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12212{
12213 ComPtr<ISystemProperties> systemProperties;
12214 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12215 if (FAILED(rc))
12216 return false;
12217
12218 BOOL aHotplugCapable = FALSE;
12219 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12220
12221 return RT_BOOL(aHotplugCapable);
12222}
12223
12224#ifdef VBOX_WITH_RESOURCE_USAGE_API
12225
12226void Machine::i_getDiskList(MediaList &list)
12227{
12228 for (MediumAttachmentList::const_iterator
12229 it = mMediumAttachments->begin();
12230 it != mMediumAttachments->end();
12231 ++it)
12232 {
12233 MediumAttachment *pAttach = *it;
12234 /* just in case */
12235 AssertContinue(pAttach);
12236
12237 AutoCaller localAutoCallerA(pAttach);
12238 if (FAILED(localAutoCallerA.rc())) continue;
12239
12240 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12241
12242 if (pAttach->i_getType() == DeviceType_HardDisk)
12243 list.push_back(pAttach->i_getMedium());
12244 }
12245}
12246
12247void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12248{
12249 AssertReturnVoid(isWriteLockOnCurrentThread());
12250 AssertPtrReturnVoid(aCollector);
12251
12252 pm::CollectorHAL *hal = aCollector->getHAL();
12253 /* Create sub metrics */
12254 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12255 "Percentage of processor time spent in user mode by the VM process.");
12256 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12257 "Percentage of processor time spent in kernel mode by the VM process.");
12258 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12259 "Size of resident portion of VM process in memory.");
12260 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12261 "Actual size of all VM disks combined.");
12262 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12263 "Network receive rate.");
12264 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12265 "Network transmit rate.");
12266 /* Create and register base metrics */
12267 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12268 cpuLoadUser, cpuLoadKernel);
12269 aCollector->registerBaseMetric(cpuLoad);
12270 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12271 ramUsageUsed);
12272 aCollector->registerBaseMetric(ramUsage);
12273 MediaList disks;
12274 i_getDiskList(disks);
12275 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12276 diskUsageUsed);
12277 aCollector->registerBaseMetric(diskUsage);
12278
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12281 new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12283 new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12285 new pm::AggregateMax()));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12287 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12288 new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12290 new pm::AggregateMin()));
12291 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12292 new pm::AggregateMax()));
12293
12294 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12295 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12296 new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12298 new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12300 new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12303 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12304 new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12306 new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12308 new pm::AggregateMax()));
12309
12310
12311 /* Guest metrics collector */
12312 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12313 aCollector->registerGuest(mCollectorGuest);
12314 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12315
12316 /* Create sub metrics */
12317 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12318 "Percentage of processor time spent in user mode as seen by the guest.");
12319 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12320 "Percentage of processor time spent in kernel mode as seen by the guest.");
12321 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12322 "Percentage of processor time spent idling as seen by the guest.");
12323
12324 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12325 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12326 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12327 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12328 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12329 pm::SubMetric *guestMemCache = new pm::SubMetric(
12330 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12331
12332 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12333 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12334
12335 /* Create and register base metrics */
12336 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12337 machineNetRx, machineNetTx);
12338 aCollector->registerBaseMetric(machineNetRate);
12339
12340 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12341 guestLoadUser, guestLoadKernel, guestLoadIdle);
12342 aCollector->registerBaseMetric(guestCpuLoad);
12343
12344 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12345 guestMemTotal, guestMemFree,
12346 guestMemBalloon, guestMemShared,
12347 guestMemCache, guestPagedTotal);
12348 aCollector->registerBaseMetric(guestCpuMem);
12349
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12351 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12356 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12384
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12389
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12394
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12399
12400 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12404}
12405
12406void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12407{
12408 AssertReturnVoid(isWriteLockOnCurrentThread());
12409
12410 if (aCollector)
12411 {
12412 aCollector->unregisterMetricsFor(aMachine);
12413 aCollector->unregisterBaseMetricsFor(aMachine);
12414 }
12415}
12416
12417#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12418
12419
12420////////////////////////////////////////////////////////////////////////////////
12421
12422DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12423
12424HRESULT SessionMachine::FinalConstruct()
12425{
12426 LogFlowThisFunc(("\n"));
12427
12428 mClientToken = NULL;
12429
12430 return BaseFinalConstruct();
12431}
12432
12433void SessionMachine::FinalRelease()
12434{
12435 LogFlowThisFunc(("\n"));
12436
12437 Assert(!mClientToken);
12438 /* paranoia, should not hang around any more */
12439 if (mClientToken)
12440 {
12441 delete mClientToken;
12442 mClientToken = NULL;
12443 }
12444
12445 uninit(Uninit::Unexpected);
12446
12447 BaseFinalRelease();
12448}
12449
12450/**
12451 * @note Must be called only by Machine::LockMachine() from its own write lock.
12452 */
12453HRESULT SessionMachine::init(Machine *aMachine)
12454{
12455 LogFlowThisFuncEnter();
12456 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12457
12458 AssertReturn(aMachine, E_INVALIDARG);
12459
12460 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12461
12462 /* Enclose the state transition NotReady->InInit->Ready */
12463 AutoInitSpan autoInitSpan(this);
12464 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12465
12466 HRESULT rc = S_OK;
12467
12468 RT_ZERO(mAuthLibCtx);
12469
12470 /* create the machine client token */
12471 try
12472 {
12473 mClientToken = new ClientToken(aMachine, this);
12474 if (!mClientToken->isReady())
12475 {
12476 delete mClientToken;
12477 mClientToken = NULL;
12478 rc = E_FAIL;
12479 }
12480 }
12481 catch (std::bad_alloc &)
12482 {
12483 rc = E_OUTOFMEMORY;
12484 }
12485 if (FAILED(rc))
12486 return rc;
12487
12488 /* memorize the peer Machine */
12489 unconst(mPeer) = aMachine;
12490 /* share the parent pointer */
12491 unconst(mParent) = aMachine->mParent;
12492
12493 /* take the pointers to data to share */
12494 mData.share(aMachine->mData);
12495 mSSData.share(aMachine->mSSData);
12496
12497 mUserData.share(aMachine->mUserData);
12498 mHWData.share(aMachine->mHWData);
12499 mMediumAttachments.share(aMachine->mMediumAttachments);
12500
12501 mStorageControllers.allocate();
12502 for (StorageControllerList::const_iterator
12503 it = aMachine->mStorageControllers->begin();
12504 it != aMachine->mStorageControllers->end();
12505 ++it)
12506 {
12507 ComObjPtr<StorageController> ctl;
12508 ctl.createObject();
12509 ctl->init(this, *it);
12510 mStorageControllers->push_back(ctl);
12511 }
12512
12513 mUSBControllers.allocate();
12514 for (USBControllerList::const_iterator
12515 it = aMachine->mUSBControllers->begin();
12516 it != aMachine->mUSBControllers->end();
12517 ++it)
12518 {
12519 ComObjPtr<USBController> ctl;
12520 ctl.createObject();
12521 ctl->init(this, *it);
12522 mUSBControllers->push_back(ctl);
12523 }
12524
12525 unconst(mBIOSSettings).createObject();
12526 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12527 unconst(mRecordingSettings).createObject();
12528 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12529 /* create another VRDEServer object that will be mutable */
12530 unconst(mVRDEServer).createObject();
12531 mVRDEServer->init(this, aMachine->mVRDEServer);
12532 /* create another audio adapter object that will be mutable */
12533 unconst(mAudioAdapter).createObject();
12534 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12535 /* create a list of serial ports that will be mutable */
12536 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12537 {
12538 unconst(mSerialPorts[slot]).createObject();
12539 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12540 }
12541 /* create a list of parallel ports that will be mutable */
12542 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12543 {
12544 unconst(mParallelPorts[slot]).createObject();
12545 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12546 }
12547
12548 /* create another USB device filters object that will be mutable */
12549 unconst(mUSBDeviceFilters).createObject();
12550 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12551
12552 /* create a list of network adapters that will be mutable */
12553 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12554 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12555 {
12556 unconst(mNetworkAdapters[slot]).createObject();
12557 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12558 }
12559
12560 /* create another bandwidth control object that will be mutable */
12561 unconst(mBandwidthControl).createObject();
12562 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12563
12564 /* default is to delete saved state on Saved -> PoweredOff transition */
12565 mRemoveSavedState = true;
12566
12567 /* Confirm a successful initialization when it's the case */
12568 autoInitSpan.setSucceeded();
12569
12570 miNATNetworksStarted = 0;
12571
12572 LogFlowThisFuncLeave();
12573 return rc;
12574}
12575
12576/**
12577 * Uninitializes this session object. If the reason is other than
12578 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12579 * or the client watcher code.
12580 *
12581 * @param aReason uninitialization reason
12582 *
12583 * @note Locks mParent + this object for writing.
12584 */
12585void SessionMachine::uninit(Uninit::Reason aReason)
12586{
12587 LogFlowThisFuncEnter();
12588 LogFlowThisFunc(("reason=%d\n", aReason));
12589
12590 /*
12591 * Strongly reference ourselves to prevent this object deletion after
12592 * mData->mSession.mMachine.setNull() below (which can release the last
12593 * reference and call the destructor). Important: this must be done before
12594 * accessing any members (and before AutoUninitSpan that does it as well).
12595 * This self reference will be released as the very last step on return.
12596 */
12597 ComObjPtr<SessionMachine> selfRef;
12598 if (aReason != Uninit::Unexpected)
12599 selfRef = this;
12600
12601 /* Enclose the state transition Ready->InUninit->NotReady */
12602 AutoUninitSpan autoUninitSpan(this);
12603 if (autoUninitSpan.uninitDone())
12604 {
12605 LogFlowThisFunc(("Already uninitialized\n"));
12606 LogFlowThisFuncLeave();
12607 return;
12608 }
12609
12610 if (autoUninitSpan.initFailed())
12611 {
12612 /* We've been called by init() because it's failed. It's not really
12613 * necessary (nor it's safe) to perform the regular uninit sequence
12614 * below, the following is enough.
12615 */
12616 LogFlowThisFunc(("Initialization failed.\n"));
12617 /* destroy the machine client token */
12618 if (mClientToken)
12619 {
12620 delete mClientToken;
12621 mClientToken = NULL;
12622 }
12623 uninitDataAndChildObjects();
12624 mData.free();
12625 unconst(mParent) = NULL;
12626 unconst(mPeer) = NULL;
12627 LogFlowThisFuncLeave();
12628 return;
12629 }
12630
12631 MachineState_T lastState;
12632 {
12633 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12634 lastState = mData->mMachineState;
12635 }
12636 NOREF(lastState);
12637
12638#ifdef VBOX_WITH_USB
12639 // release all captured USB devices, but do this before requesting the locks below
12640 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12641 {
12642 /* Console::captureUSBDevices() is called in the VM process only after
12643 * setting the machine state to Starting or Restoring.
12644 * Console::detachAllUSBDevices() will be called upon successful
12645 * termination. So, we need to release USB devices only if there was
12646 * an abnormal termination of a running VM.
12647 *
12648 * This is identical to SessionMachine::DetachAllUSBDevices except
12649 * for the aAbnormal argument. */
12650 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12651 AssertComRC(rc);
12652 NOREF(rc);
12653
12654 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12655 if (service)
12656 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12657 }
12658#endif /* VBOX_WITH_USB */
12659
12660 // we need to lock this object in uninit() because the lock is shared
12661 // with mPeer (as well as data we modify below). mParent lock is needed
12662 // by several calls to it.
12663 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12664
12665#ifdef VBOX_WITH_RESOURCE_USAGE_API
12666 /*
12667 * It is safe to call Machine::i_unregisterMetrics() here because
12668 * PerformanceCollector::samplerCallback no longer accesses guest methods
12669 * holding the lock.
12670 */
12671 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12672 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12673 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12674 if (mCollectorGuest)
12675 {
12676 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12677 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12678 mCollectorGuest = NULL;
12679 }
12680#endif
12681
12682 if (aReason == Uninit::Abnormal)
12683 {
12684 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12685
12686 /* reset the state to Aborted */
12687 if (mData->mMachineState != MachineState_Aborted)
12688 i_setMachineState(MachineState_Aborted);
12689 }
12690
12691 // any machine settings modified?
12692 if (mData->flModifications)
12693 {
12694 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12695 i_rollback(false /* aNotify */);
12696 }
12697
12698 mData->mSession.mPID = NIL_RTPROCESS;
12699
12700 if (aReason == Uninit::Unexpected)
12701 {
12702 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12703 * client watcher thread to update the set of machines that have open
12704 * sessions. */
12705 mParent->i_updateClientWatcher();
12706 }
12707
12708 /* uninitialize all remote controls */
12709 if (mData->mSession.mRemoteControls.size())
12710 {
12711 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12712 mData->mSession.mRemoteControls.size()));
12713
12714 /* Always restart a the beginning, since the iterator is invalidated
12715 * by using erase(). */
12716 for (Data::Session::RemoteControlList::iterator
12717 it = mData->mSession.mRemoteControls.begin();
12718 it != mData->mSession.mRemoteControls.end();
12719 it = mData->mSession.mRemoteControls.begin())
12720 {
12721 ComPtr<IInternalSessionControl> pControl = *it;
12722 mData->mSession.mRemoteControls.erase(it);
12723 multilock.release();
12724 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12725 HRESULT rc = pControl->Uninitialize();
12726 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12727 if (FAILED(rc))
12728 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12729 multilock.acquire();
12730 }
12731 mData->mSession.mRemoteControls.clear();
12732 }
12733
12734 /* Remove all references to the NAT network service. The service will stop
12735 * if all references (also from other VMs) are removed. */
12736 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12737 {
12738 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12739 {
12740 BOOL enabled;
12741 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12742 if ( FAILED(hrc)
12743 || !enabled)
12744 continue;
12745
12746 NetworkAttachmentType_T type;
12747 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12748 if ( SUCCEEDED(hrc)
12749 && type == NetworkAttachmentType_NATNetwork)
12750 {
12751 Bstr name;
12752 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12753 if (SUCCEEDED(hrc))
12754 {
12755 multilock.release();
12756 Utf8Str strName(name);
12757 LogRel(("VM '%s' stops using NAT network '%s'\n",
12758 mUserData->s.strName.c_str(), strName.c_str()));
12759 mParent->i_natNetworkRefDec(strName);
12760 multilock.acquire();
12761 }
12762 }
12763 }
12764 }
12765
12766 /*
12767 * An expected uninitialization can come only from #i_checkForDeath().
12768 * Otherwise it means that something's gone really wrong (for example,
12769 * the Session implementation has released the VirtualBox reference
12770 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12771 * etc). However, it's also possible, that the client releases the IPC
12772 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12773 * but the VirtualBox release event comes first to the server process.
12774 * This case is practically possible, so we should not assert on an
12775 * unexpected uninit, just log a warning.
12776 */
12777
12778 if (aReason == Uninit::Unexpected)
12779 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12780
12781 if (aReason != Uninit::Normal)
12782 {
12783 mData->mSession.mDirectControl.setNull();
12784 }
12785 else
12786 {
12787 /* this must be null here (see #OnSessionEnd()) */
12788 Assert(mData->mSession.mDirectControl.isNull());
12789 Assert(mData->mSession.mState == SessionState_Unlocking);
12790 Assert(!mData->mSession.mProgress.isNull());
12791 }
12792 if (mData->mSession.mProgress)
12793 {
12794 if (aReason == Uninit::Normal)
12795 mData->mSession.mProgress->i_notifyComplete(S_OK);
12796 else
12797 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12798 COM_IIDOF(ISession),
12799 getComponentName(),
12800 tr("The VM session was aborted"));
12801 mData->mSession.mProgress.setNull();
12802 }
12803
12804 if (mConsoleTaskData.mProgress)
12805 {
12806 Assert(aReason == Uninit::Abnormal);
12807 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12808 COM_IIDOF(ISession),
12809 getComponentName(),
12810 tr("The VM session was aborted"));
12811 mConsoleTaskData.mProgress.setNull();
12812 }
12813
12814 /* remove the association between the peer machine and this session machine */
12815 Assert( (SessionMachine*)mData->mSession.mMachine == this
12816 || aReason == Uninit::Unexpected);
12817
12818 /* reset the rest of session data */
12819 mData->mSession.mLockType = LockType_Null;
12820 mData->mSession.mMachine.setNull();
12821 mData->mSession.mState = SessionState_Unlocked;
12822 mData->mSession.mName.setNull();
12823
12824 /* destroy the machine client token before leaving the exclusive lock */
12825 if (mClientToken)
12826 {
12827 delete mClientToken;
12828 mClientToken = NULL;
12829 }
12830
12831 /* fire an event */
12832 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12833
12834 uninitDataAndChildObjects();
12835
12836 /* free the essential data structure last */
12837 mData.free();
12838
12839 /* release the exclusive lock before setting the below two to NULL */
12840 multilock.release();
12841
12842 unconst(mParent) = NULL;
12843 unconst(mPeer) = NULL;
12844
12845 AuthLibUnload(&mAuthLibCtx);
12846
12847 LogFlowThisFuncLeave();
12848}
12849
12850// util::Lockable interface
12851////////////////////////////////////////////////////////////////////////////////
12852
12853/**
12854 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12855 * with the primary Machine instance (mPeer).
12856 */
12857RWLockHandle *SessionMachine::lockHandle() const
12858{
12859 AssertReturn(mPeer != NULL, NULL);
12860 return mPeer->lockHandle();
12861}
12862
12863// IInternalMachineControl methods
12864////////////////////////////////////////////////////////////////////////////////
12865
12866/**
12867 * Passes collected guest statistics to performance collector object
12868 */
12869HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12870 ULONG aCpuKernel, ULONG aCpuIdle,
12871 ULONG aMemTotal, ULONG aMemFree,
12872 ULONG aMemBalloon, ULONG aMemShared,
12873 ULONG aMemCache, ULONG aPageTotal,
12874 ULONG aAllocVMM, ULONG aFreeVMM,
12875 ULONG aBalloonedVMM, ULONG aSharedVMM,
12876 ULONG aVmNetRx, ULONG aVmNetTx)
12877{
12878#ifdef VBOX_WITH_RESOURCE_USAGE_API
12879 if (mCollectorGuest)
12880 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12881 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12882 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12883 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12884
12885 return S_OK;
12886#else
12887 NOREF(aValidStats);
12888 NOREF(aCpuUser);
12889 NOREF(aCpuKernel);
12890 NOREF(aCpuIdle);
12891 NOREF(aMemTotal);
12892 NOREF(aMemFree);
12893 NOREF(aMemBalloon);
12894 NOREF(aMemShared);
12895 NOREF(aMemCache);
12896 NOREF(aPageTotal);
12897 NOREF(aAllocVMM);
12898 NOREF(aFreeVMM);
12899 NOREF(aBalloonedVMM);
12900 NOREF(aSharedVMM);
12901 NOREF(aVmNetRx);
12902 NOREF(aVmNetTx);
12903 return E_NOTIMPL;
12904#endif
12905}
12906
12907////////////////////////////////////////////////////////////////////////////////
12908//
12909// SessionMachine task records
12910//
12911////////////////////////////////////////////////////////////////////////////////
12912
12913/**
12914 * Task record for saving the machine state.
12915 */
12916class SessionMachine::SaveStateTask
12917 : public Machine::Task
12918{
12919public:
12920 SaveStateTask(SessionMachine *m,
12921 Progress *p,
12922 const Utf8Str &t,
12923 Reason_T enmReason,
12924 const Utf8Str &strStateFilePath)
12925 : Task(m, p, t),
12926 m_enmReason(enmReason),
12927 m_strStateFilePath(strStateFilePath)
12928 {}
12929
12930private:
12931 void handler()
12932 {
12933 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12934 }
12935
12936 Reason_T m_enmReason;
12937 Utf8Str m_strStateFilePath;
12938
12939 friend class SessionMachine;
12940};
12941
12942/**
12943 * Task thread implementation for SessionMachine::SaveState(), called from
12944 * SessionMachine::taskHandler().
12945 *
12946 * @note Locks this object for writing.
12947 *
12948 * @param task
12949 * @return
12950 */
12951void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12952{
12953 LogFlowThisFuncEnter();
12954
12955 AutoCaller autoCaller(this);
12956 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12957 if (FAILED(autoCaller.rc()))
12958 {
12959 /* we might have been uninitialized because the session was accidentally
12960 * closed by the client, so don't assert */
12961 HRESULT rc = setError(E_FAIL,
12962 tr("The session has been accidentally closed"));
12963 task.m_pProgress->i_notifyComplete(rc);
12964 LogFlowThisFuncLeave();
12965 return;
12966 }
12967
12968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12969
12970 HRESULT rc = S_OK;
12971
12972 try
12973 {
12974 ComPtr<IInternalSessionControl> directControl;
12975 if (mData->mSession.mLockType == LockType_VM)
12976 directControl = mData->mSession.mDirectControl;
12977 if (directControl.isNull())
12978 throw setError(VBOX_E_INVALID_VM_STATE,
12979 tr("Trying to save state without a running VM"));
12980 alock.release();
12981 BOOL fSuspendedBySave;
12982 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12983 Assert(!fSuspendedBySave);
12984 alock.acquire();
12985
12986 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12987 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12988 throw E_FAIL);
12989
12990 if (SUCCEEDED(rc))
12991 {
12992 mSSData->strStateFilePath = task.m_strStateFilePath;
12993
12994 /* save all VM settings */
12995 rc = i_saveSettings(NULL);
12996 // no need to check whether VirtualBox.xml needs saving also since
12997 // we can't have a name change pending at this point
12998 }
12999 else
13000 {
13001 // On failure, set the state to the state we had at the beginning.
13002 i_setMachineState(task.m_machineStateBackup);
13003 i_updateMachineStateOnClient();
13004
13005 // Delete the saved state file (might have been already created).
13006 // No need to check whether this is shared with a snapshot here
13007 // because we certainly created a fresh saved state file here.
13008 RTFileDelete(task.m_strStateFilePath.c_str());
13009 }
13010 }
13011 catch (HRESULT aRC) { rc = aRC; }
13012
13013 task.m_pProgress->i_notifyComplete(rc);
13014
13015 LogFlowThisFuncLeave();
13016}
13017
13018/**
13019 * @note Locks this object for writing.
13020 */
13021HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13022{
13023 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13024}
13025
13026HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13027{
13028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13029
13030 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13031 if (FAILED(rc)) return rc;
13032
13033 if ( mData->mMachineState != MachineState_Running
13034 && mData->mMachineState != MachineState_Paused
13035 )
13036 return setError(VBOX_E_INVALID_VM_STATE,
13037 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13038 Global::stringifyMachineState(mData->mMachineState));
13039
13040 ComObjPtr<Progress> pProgress;
13041 pProgress.createObject();
13042 rc = pProgress->init(i_getVirtualBox(),
13043 static_cast<IMachine *>(this) /* aInitiator */,
13044 tr("Saving the execution state of the virtual machine"),
13045 FALSE /* aCancelable */);
13046 if (FAILED(rc))
13047 return rc;
13048
13049 Utf8Str strStateFilePath;
13050 i_composeSavedStateFilename(strStateFilePath);
13051
13052 /* create and start the task on a separate thread (note that it will not
13053 * start working until we release alock) */
13054 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13055 rc = pTask->createThread();
13056 if (FAILED(rc))
13057 return rc;
13058
13059 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13060 i_setMachineState(MachineState_Saving);
13061 i_updateMachineStateOnClient();
13062
13063 pProgress.queryInterfaceTo(aProgress.asOutParam());
13064
13065 return S_OK;
13066}
13067
13068/**
13069 * @note Locks this object for writing.
13070 */
13071HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13072{
13073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13074
13075 HRESULT rc = i_checkStateDependency(MutableStateDep);
13076 if (FAILED(rc)) return rc;
13077
13078 if ( mData->mMachineState != MachineState_PoweredOff
13079 && mData->mMachineState != MachineState_Teleported
13080 && mData->mMachineState != MachineState_Aborted
13081 )
13082 return setError(VBOX_E_INVALID_VM_STATE,
13083 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13084 Global::stringifyMachineState(mData->mMachineState));
13085
13086 com::Utf8Str stateFilePathFull;
13087 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13088 if (RT_FAILURE(vrc))
13089 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13090 tr("Invalid saved state file path '%s' (%Rrc)"),
13091 aSavedStateFile.c_str(),
13092 vrc);
13093
13094 mSSData->strStateFilePath = stateFilePathFull;
13095
13096 /* The below i_setMachineState() will detect the state transition and will
13097 * update the settings file */
13098
13099 return i_setMachineState(MachineState_Saved);
13100}
13101
13102/**
13103 * @note Locks this object for writing.
13104 */
13105HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13106{
13107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13108
13109 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13110 if (FAILED(rc)) return rc;
13111
13112 if (mData->mMachineState != MachineState_Saved)
13113 return setError(VBOX_E_INVALID_VM_STATE,
13114 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13115 Global::stringifyMachineState(mData->mMachineState));
13116
13117 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13118
13119 /*
13120 * Saved -> PoweredOff transition will be detected in the SessionMachine
13121 * and properly handled.
13122 */
13123 rc = i_setMachineState(MachineState_PoweredOff);
13124 return rc;
13125}
13126
13127
13128/**
13129 * @note Locks the same as #i_setMachineState() does.
13130 */
13131HRESULT SessionMachine::updateState(MachineState_T aState)
13132{
13133 return i_setMachineState(aState);
13134}
13135
13136/**
13137 * @note Locks this object for writing.
13138 */
13139HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13140{
13141 IProgress *pProgress(aProgress);
13142
13143 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13144
13145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13146
13147 if (mData->mSession.mState != SessionState_Locked)
13148 return VBOX_E_INVALID_OBJECT_STATE;
13149
13150 if (!mData->mSession.mProgress.isNull())
13151 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13152
13153 /* If we didn't reference the NAT network service yet, add a reference to
13154 * force a start */
13155 if (miNATNetworksStarted < 1)
13156 {
13157 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13158 {
13159 BOOL enabled;
13160 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13161 if ( FAILED(hrc)
13162 || !enabled)
13163 continue;
13164
13165 NetworkAttachmentType_T type;
13166 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13167 if ( SUCCEEDED(hrc)
13168 && type == NetworkAttachmentType_NATNetwork)
13169 {
13170 Bstr name;
13171 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13172 if (SUCCEEDED(hrc))
13173 {
13174 Utf8Str strName(name);
13175 LogRel(("VM '%s' starts using NAT network '%s'\n",
13176 mUserData->s.strName.c_str(), strName.c_str()));
13177 mPeer->lockHandle()->unlockWrite();
13178 mParent->i_natNetworkRefInc(strName);
13179#ifdef RT_LOCK_STRICT
13180 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13181#else
13182 mPeer->lockHandle()->lockWrite();
13183#endif
13184 }
13185 }
13186 }
13187 miNATNetworksStarted++;
13188 }
13189
13190 LogFlowThisFunc(("returns S_OK.\n"));
13191 return S_OK;
13192}
13193
13194/**
13195 * @note Locks this object for writing.
13196 */
13197HRESULT SessionMachine::endPowerUp(LONG aResult)
13198{
13199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13200
13201 if (mData->mSession.mState != SessionState_Locked)
13202 return VBOX_E_INVALID_OBJECT_STATE;
13203
13204 /* Finalize the LaunchVMProcess progress object. */
13205 if (mData->mSession.mProgress)
13206 {
13207 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13208 mData->mSession.mProgress.setNull();
13209 }
13210
13211 if (SUCCEEDED((HRESULT)aResult))
13212 {
13213#ifdef VBOX_WITH_RESOURCE_USAGE_API
13214 /* The VM has been powered up successfully, so it makes sense
13215 * now to offer the performance metrics for a running machine
13216 * object. Doing it earlier wouldn't be safe. */
13217 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13218 mData->mSession.mPID);
13219#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13220 }
13221
13222 return S_OK;
13223}
13224
13225/**
13226 * @note Locks this object for writing.
13227 */
13228HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13229{
13230 LogFlowThisFuncEnter();
13231
13232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13233
13234 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13235 E_FAIL);
13236
13237 /* create a progress object to track operation completion */
13238 ComObjPtr<Progress> pProgress;
13239 pProgress.createObject();
13240 pProgress->init(i_getVirtualBox(),
13241 static_cast<IMachine *>(this) /* aInitiator */,
13242 tr("Stopping the virtual machine"),
13243 FALSE /* aCancelable */);
13244
13245 /* fill in the console task data */
13246 mConsoleTaskData.mLastState = mData->mMachineState;
13247 mConsoleTaskData.mProgress = pProgress;
13248
13249 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13250 i_setMachineState(MachineState_Stopping);
13251
13252 pProgress.queryInterfaceTo(aProgress.asOutParam());
13253
13254 return S_OK;
13255}
13256
13257/**
13258 * @note Locks this object for writing.
13259 */
13260HRESULT SessionMachine::endPoweringDown(LONG aResult,
13261 const com::Utf8Str &aErrMsg)
13262{
13263 LogFlowThisFuncEnter();
13264
13265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13266
13267 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13268 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13269 && mConsoleTaskData.mLastState != MachineState_Null,
13270 E_FAIL);
13271
13272 /*
13273 * On failure, set the state to the state we had when BeginPoweringDown()
13274 * was called (this is expected by Console::PowerDown() and the associated
13275 * task). On success the VM process already changed the state to
13276 * MachineState_PoweredOff, so no need to do anything.
13277 */
13278 if (FAILED(aResult))
13279 i_setMachineState(mConsoleTaskData.mLastState);
13280
13281 /* notify the progress object about operation completion */
13282 Assert(mConsoleTaskData.mProgress);
13283 if (SUCCEEDED(aResult))
13284 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13285 else
13286 {
13287 if (aErrMsg.length())
13288 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13289 COM_IIDOF(ISession),
13290 getComponentName(),
13291 aErrMsg.c_str());
13292 else
13293 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13294 }
13295
13296 /* clear out the temporary saved state data */
13297 mConsoleTaskData.mLastState = MachineState_Null;
13298 mConsoleTaskData.mProgress.setNull();
13299
13300 LogFlowThisFuncLeave();
13301 return S_OK;
13302}
13303
13304
13305/**
13306 * Goes through the USB filters of the given machine to see if the given
13307 * device matches any filter or not.
13308 *
13309 * @note Locks the same as USBController::hasMatchingFilter() does.
13310 */
13311HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13312 BOOL *aMatched,
13313 ULONG *aMaskedInterfaces)
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317#ifdef VBOX_WITH_USB
13318 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13319#else
13320 NOREF(aDevice);
13321 NOREF(aMaskedInterfaces);
13322 *aMatched = FALSE;
13323#endif
13324
13325 return S_OK;
13326}
13327
13328/**
13329 * @note Locks the same as Host::captureUSBDevice() does.
13330 */
13331HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_USB
13336 /* if captureDeviceForVM() fails, it must have set extended error info */
13337 clearError();
13338 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13339 if (FAILED(rc)) return rc;
13340
13341 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13342 AssertReturn(service, E_FAIL);
13343 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13344#else
13345 NOREF(aId);
13346 return E_NOTIMPL;
13347#endif
13348}
13349
13350/**
13351 * @note Locks the same as Host::detachUSBDevice() does.
13352 */
13353HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13354 BOOL aDone)
13355{
13356 LogFlowThisFunc(("\n"));
13357
13358#ifdef VBOX_WITH_USB
13359 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13360 AssertReturn(service, E_FAIL);
13361 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13362#else
13363 NOREF(aId);
13364 NOREF(aDone);
13365 return E_NOTIMPL;
13366#endif
13367}
13368
13369/**
13370 * Inserts all machine filters to the USB proxy service and then calls
13371 * Host::autoCaptureUSBDevices().
13372 *
13373 * Called by Console from the VM process upon VM startup.
13374 *
13375 * @note Locks what called methods lock.
13376 */
13377HRESULT SessionMachine::autoCaptureUSBDevices()
13378{
13379 LogFlowThisFunc(("\n"));
13380
13381#ifdef VBOX_WITH_USB
13382 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13383 AssertComRC(rc);
13384 NOREF(rc);
13385
13386 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13387 AssertReturn(service, E_FAIL);
13388 return service->autoCaptureDevicesForVM(this);
13389#else
13390 return S_OK;
13391#endif
13392}
13393
13394/**
13395 * Removes all machine filters from the USB proxy service and then calls
13396 * Host::detachAllUSBDevices().
13397 *
13398 * Called by Console from the VM process upon normal VM termination or by
13399 * SessionMachine::uninit() upon abnormal VM termination (from under the
13400 * Machine/SessionMachine lock).
13401 *
13402 * @note Locks what called methods lock.
13403 */
13404HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13405{
13406 LogFlowThisFunc(("\n"));
13407
13408#ifdef VBOX_WITH_USB
13409 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13410 AssertComRC(rc);
13411 NOREF(rc);
13412
13413 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13414 AssertReturn(service, E_FAIL);
13415 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13416#else
13417 NOREF(aDone);
13418 return S_OK;
13419#endif
13420}
13421
13422/**
13423 * @note Locks this object for writing.
13424 */
13425HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13426 ComPtr<IProgress> &aProgress)
13427{
13428 LogFlowThisFuncEnter();
13429
13430 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13431 /*
13432 * We don't assert below because it might happen that a non-direct session
13433 * informs us it is closed right after we've been uninitialized -- it's ok.
13434 */
13435
13436 /* get IInternalSessionControl interface */
13437 ComPtr<IInternalSessionControl> control(aSession);
13438
13439 ComAssertRet(!control.isNull(), E_INVALIDARG);
13440
13441 /* Creating a Progress object requires the VirtualBox lock, and
13442 * thus locking it here is required by the lock order rules. */
13443 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13444
13445 if (control == mData->mSession.mDirectControl)
13446 {
13447 /* The direct session is being normally closed by the client process
13448 * ----------------------------------------------------------------- */
13449
13450 /* go to the closing state (essential for all open*Session() calls and
13451 * for #i_checkForDeath()) */
13452 Assert(mData->mSession.mState == SessionState_Locked);
13453 mData->mSession.mState = SessionState_Unlocking;
13454
13455 /* set direct control to NULL to release the remote instance */
13456 mData->mSession.mDirectControl.setNull();
13457 LogFlowThisFunc(("Direct control is set to NULL\n"));
13458
13459 if (mData->mSession.mProgress)
13460 {
13461 /* finalize the progress, someone might wait if a frontend
13462 * closes the session before powering on the VM. */
13463 mData->mSession.mProgress->notifyComplete(E_FAIL,
13464 COM_IIDOF(ISession),
13465 getComponentName(),
13466 tr("The VM session was closed before any attempt to power it on"));
13467 mData->mSession.mProgress.setNull();
13468 }
13469
13470 /* Create the progress object the client will use to wait until
13471 * #i_checkForDeath() is called to uninitialize this session object after
13472 * it releases the IPC semaphore.
13473 * Note! Because we're "reusing" mProgress here, this must be a proxy
13474 * object just like for LaunchVMProcess. */
13475 Assert(mData->mSession.mProgress.isNull());
13476 ComObjPtr<ProgressProxy> progress;
13477 progress.createObject();
13478 ComPtr<IUnknown> pPeer(mPeer);
13479 progress->init(mParent, pPeer,
13480 Bstr(tr("Closing session")).raw(),
13481 FALSE /* aCancelable */);
13482 progress.queryInterfaceTo(aProgress.asOutParam());
13483 mData->mSession.mProgress = progress;
13484 }
13485 else
13486 {
13487 /* the remote session is being normally closed */
13488 bool found = false;
13489 for (Data::Session::RemoteControlList::iterator
13490 it = mData->mSession.mRemoteControls.begin();
13491 it != mData->mSession.mRemoteControls.end();
13492 ++it)
13493 {
13494 if (control == *it)
13495 {
13496 found = true;
13497 // This MUST be erase(it), not remove(*it) as the latter
13498 // triggers a very nasty use after free due to the place where
13499 // the value "lives".
13500 mData->mSession.mRemoteControls.erase(it);
13501 break;
13502 }
13503 }
13504 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13505 E_INVALIDARG);
13506 }
13507
13508 /* signal the client watcher thread, because the client is going away */
13509 mParent->i_updateClientWatcher();
13510
13511 LogFlowThisFuncLeave();
13512 return S_OK;
13513}
13514
13515HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13516 std::vector<com::Utf8Str> &aValues,
13517 std::vector<LONG64> &aTimestamps,
13518 std::vector<com::Utf8Str> &aFlags)
13519{
13520 LogFlowThisFunc(("\n"));
13521
13522#ifdef VBOX_WITH_GUEST_PROPS
13523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13524
13525 size_t cEntries = mHWData->mGuestProperties.size();
13526 aNames.resize(cEntries);
13527 aValues.resize(cEntries);
13528 aTimestamps.resize(cEntries);
13529 aFlags.resize(cEntries);
13530
13531 size_t i = 0;
13532 for (HWData::GuestPropertyMap::const_iterator
13533 it = mHWData->mGuestProperties.begin();
13534 it != mHWData->mGuestProperties.end();
13535 ++it, ++i)
13536 {
13537 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13538 aNames[i] = it->first;
13539 aValues[i] = it->second.strValue;
13540 aTimestamps[i] = it->second.mTimestamp;
13541
13542 /* If it is NULL, keep it NULL. */
13543 if (it->second.mFlags)
13544 {
13545 GuestPropWriteFlags(it->second.mFlags, szFlags);
13546 aFlags[i] = szFlags;
13547 }
13548 else
13549 aFlags[i] = "";
13550 }
13551 return S_OK;
13552#else
13553 ReturnComNotImplemented();
13554#endif
13555}
13556
13557HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13558 const com::Utf8Str &aValue,
13559 LONG64 aTimestamp,
13560 const com::Utf8Str &aFlags)
13561{
13562 LogFlowThisFunc(("\n"));
13563
13564#ifdef VBOX_WITH_GUEST_PROPS
13565 try
13566 {
13567 /*
13568 * Convert input up front.
13569 */
13570 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13571 if (aFlags.length())
13572 {
13573 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13574 AssertRCReturn(vrc, E_INVALIDARG);
13575 }
13576
13577 /*
13578 * Now grab the object lock, validate the state and do the update.
13579 */
13580
13581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13582
13583 if (!Global::IsOnline(mData->mMachineState))
13584 {
13585 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13586 VBOX_E_INVALID_VM_STATE);
13587 }
13588
13589 i_setModified(IsModified_MachineData);
13590 mHWData.backup();
13591
13592 bool fDelete = !aValue.length();
13593 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13594 if (it != mHWData->mGuestProperties.end())
13595 {
13596 if (!fDelete)
13597 {
13598 it->second.strValue = aValue;
13599 it->second.mTimestamp = aTimestamp;
13600 it->second.mFlags = fFlags;
13601 }
13602 else
13603 mHWData->mGuestProperties.erase(it);
13604
13605 mData->mGuestPropertiesModified = TRUE;
13606 }
13607 else if (!fDelete)
13608 {
13609 HWData::GuestProperty prop;
13610 prop.strValue = aValue;
13611 prop.mTimestamp = aTimestamp;
13612 prop.mFlags = fFlags;
13613
13614 mHWData->mGuestProperties[aName] = prop;
13615 mData->mGuestPropertiesModified = TRUE;
13616 }
13617
13618 alock.release();
13619
13620 mParent->i_onGuestPropertyChange(mData->mUuid,
13621 Bstr(aName).raw(),
13622 Bstr(aValue).raw(),
13623 Bstr(aFlags).raw());
13624 }
13625 catch (...)
13626 {
13627 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13628 }
13629 return S_OK;
13630#else
13631 ReturnComNotImplemented();
13632#endif
13633}
13634
13635
13636HRESULT SessionMachine::lockMedia()
13637{
13638 AutoMultiWriteLock2 alock(this->lockHandle(),
13639 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13640
13641 AssertReturn( mData->mMachineState == MachineState_Starting
13642 || mData->mMachineState == MachineState_Restoring
13643 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13644
13645 clearError();
13646 alock.release();
13647 return i_lockMedia();
13648}
13649
13650HRESULT SessionMachine::unlockMedia()
13651{
13652 HRESULT hrc = i_unlockMedia();
13653 return hrc;
13654}
13655
13656HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13657 ComPtr<IMediumAttachment> &aNewAttachment)
13658{
13659 // request the host lock first, since might be calling Host methods for getting host drives;
13660 // next, protect the media tree all the while we're in here, as well as our member variables
13661 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13662 this->lockHandle(),
13663 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13664
13665 IMediumAttachment *iAttach = aAttachment;
13666 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13667
13668 Utf8Str ctrlName;
13669 LONG lPort;
13670 LONG lDevice;
13671 bool fTempEject;
13672 {
13673 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13674
13675 /* Need to query the details first, as the IMediumAttachment reference
13676 * might be to the original settings, which we are going to change. */
13677 ctrlName = pAttach->i_getControllerName();
13678 lPort = pAttach->i_getPort();
13679 lDevice = pAttach->i_getDevice();
13680 fTempEject = pAttach->i_getTempEject();
13681 }
13682
13683 if (!fTempEject)
13684 {
13685 /* Remember previously mounted medium. The medium before taking the
13686 * backup is not necessarily the same thing. */
13687 ComObjPtr<Medium> oldmedium;
13688 oldmedium = pAttach->i_getMedium();
13689
13690 i_setModified(IsModified_Storage);
13691 mMediumAttachments.backup();
13692
13693 // The backup operation makes the pAttach reference point to the
13694 // old settings. Re-get the correct reference.
13695 pAttach = i_findAttachment(*mMediumAttachments.data(),
13696 ctrlName,
13697 lPort,
13698 lDevice);
13699
13700 {
13701 AutoCaller autoAttachCaller(this);
13702 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13703
13704 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13705 if (!oldmedium.isNull())
13706 oldmedium->i_removeBackReference(mData->mUuid);
13707
13708 pAttach->i_updateMedium(NULL);
13709 pAttach->i_updateEjected();
13710 }
13711
13712 i_setModified(IsModified_Storage);
13713 }
13714 else
13715 {
13716 {
13717 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13718 pAttach->i_updateEjected();
13719 }
13720 }
13721
13722 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13723
13724 return S_OK;
13725}
13726
13727HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13728 com::Utf8Str &aResult)
13729{
13730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13731
13732 HRESULT hr = S_OK;
13733
13734 if (!mAuthLibCtx.hAuthLibrary)
13735 {
13736 /* Load the external authentication library. */
13737 Bstr authLibrary;
13738 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13739
13740 Utf8Str filename = authLibrary;
13741
13742 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13743 if (RT_FAILURE(vrc))
13744 hr = setErrorBoth(E_FAIL, vrc,
13745 tr("Could not load the external authentication library '%s' (%Rrc)"),
13746 filename.c_str(), vrc);
13747 }
13748
13749 /* The auth library might need the machine lock. */
13750 alock.release();
13751
13752 if (FAILED(hr))
13753 return hr;
13754
13755 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13756 {
13757 enum VRDEAuthParams
13758 {
13759 parmUuid = 1,
13760 parmGuestJudgement,
13761 parmUser,
13762 parmPassword,
13763 parmDomain,
13764 parmClientId
13765 };
13766
13767 AuthResult result = AuthResultAccessDenied;
13768
13769 Guid uuid(aAuthParams[parmUuid]);
13770 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13771 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13772
13773 result = AuthLibAuthenticate(&mAuthLibCtx,
13774 uuid.raw(), guestJudgement,
13775 aAuthParams[parmUser].c_str(),
13776 aAuthParams[parmPassword].c_str(),
13777 aAuthParams[parmDomain].c_str(),
13778 u32ClientId);
13779
13780 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13781 size_t cbPassword = aAuthParams[parmPassword].length();
13782 if (cbPassword)
13783 {
13784 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13785 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13786 }
13787
13788 if (result == AuthResultAccessGranted)
13789 aResult = "granted";
13790 else
13791 aResult = "denied";
13792
13793 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13794 aAuthParams[parmUser].c_str(), aResult.c_str()));
13795 }
13796 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13797 {
13798 enum VRDEAuthDisconnectParams
13799 {
13800 parmUuid = 1,
13801 parmClientId
13802 };
13803
13804 Guid uuid(aAuthParams[parmUuid]);
13805 uint32_t u32ClientId = 0;
13806 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13807 }
13808 else
13809 {
13810 hr = E_INVALIDARG;
13811 }
13812
13813 return hr;
13814}
13815
13816// public methods only for internal purposes
13817/////////////////////////////////////////////////////////////////////////////
13818
13819#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13820/**
13821 * Called from the client watcher thread to check for expected or unexpected
13822 * death of the client process that has a direct session to this machine.
13823 *
13824 * On Win32 and on OS/2, this method is called only when we've got the
13825 * mutex (i.e. the client has either died or terminated normally) so it always
13826 * returns @c true (the client is terminated, the session machine is
13827 * uninitialized).
13828 *
13829 * On other platforms, the method returns @c true if the client process has
13830 * terminated normally or abnormally and the session machine was uninitialized,
13831 * and @c false if the client process is still alive.
13832 *
13833 * @note Locks this object for writing.
13834 */
13835bool SessionMachine::i_checkForDeath()
13836{
13837 Uninit::Reason reason;
13838 bool terminated = false;
13839
13840 /* Enclose autoCaller with a block because calling uninit() from under it
13841 * will deadlock. */
13842 {
13843 AutoCaller autoCaller(this);
13844 if (!autoCaller.isOk())
13845 {
13846 /* return true if not ready, to cause the client watcher to exclude
13847 * the corresponding session from watching */
13848 LogFlowThisFunc(("Already uninitialized!\n"));
13849 return true;
13850 }
13851
13852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13853
13854 /* Determine the reason of death: if the session state is Closing here,
13855 * everything is fine. Otherwise it means that the client did not call
13856 * OnSessionEnd() before it released the IPC semaphore. This may happen
13857 * either because the client process has abnormally terminated, or
13858 * because it simply forgot to call ISession::Close() before exiting. We
13859 * threat the latter also as an abnormal termination (see
13860 * Session::uninit() for details). */
13861 reason = mData->mSession.mState == SessionState_Unlocking ?
13862 Uninit::Normal :
13863 Uninit::Abnormal;
13864
13865 if (mClientToken)
13866 terminated = mClientToken->release();
13867 } /* AutoCaller block */
13868
13869 if (terminated)
13870 uninit(reason);
13871
13872 return terminated;
13873}
13874
13875void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13876{
13877 LogFlowThisFunc(("\n"));
13878
13879 strTokenId.setNull();
13880
13881 AutoCaller autoCaller(this);
13882 AssertComRCReturnVoid(autoCaller.rc());
13883
13884 Assert(mClientToken);
13885 if (mClientToken)
13886 mClientToken->getId(strTokenId);
13887}
13888#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13889IToken *SessionMachine::i_getToken()
13890{
13891 LogFlowThisFunc(("\n"));
13892
13893 AutoCaller autoCaller(this);
13894 AssertComRCReturn(autoCaller.rc(), NULL);
13895
13896 Assert(mClientToken);
13897 if (mClientToken)
13898 return mClientToken->getToken();
13899 else
13900 return NULL;
13901}
13902#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13903
13904Machine::ClientToken *SessionMachine::i_getClientToken()
13905{
13906 LogFlowThisFunc(("\n"));
13907
13908 AutoCaller autoCaller(this);
13909 AssertComRCReturn(autoCaller.rc(), NULL);
13910
13911 return mClientToken;
13912}
13913
13914
13915/**
13916 * @note Locks this object for reading.
13917 */
13918HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13924
13925 ComPtr<IInternalSessionControl> directControl;
13926 {
13927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13928 if (mData->mSession.mLockType == LockType_VM)
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13943 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13944 IN_BSTR aGuestIp, LONG aGuestPort)
13945{
13946 LogFlowThisFunc(("\n"));
13947
13948 AutoCaller autoCaller(this);
13949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13950
13951 ComPtr<IInternalSessionControl> directControl;
13952 {
13953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13954 if (mData->mSession.mLockType == LockType_VM)
13955 directControl = mData->mSession.mDirectControl;
13956 }
13957
13958 /* ignore notifications sent after #OnSessionEnd() is called */
13959 if (!directControl)
13960 return S_OK;
13961 /*
13962 * instead acting like callback we ask IVirtualBox deliver corresponding event
13963 */
13964
13965 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13966 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13967 return S_OK;
13968}
13969
13970/**
13971 * @note Locks this object for reading.
13972 */
13973HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13974{
13975 LogFlowThisFunc(("\n"));
13976
13977 AutoCaller autoCaller(this);
13978 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13979
13980 ComPtr<IInternalSessionControl> directControl;
13981 {
13982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13983 if (mData->mSession.mLockType == LockType_VM)
13984 directControl = mData->mSession.mDirectControl;
13985 }
13986
13987 /* ignore notifications sent after #OnSessionEnd() is called */
13988 if (!directControl)
13989 return S_OK;
13990
13991 return directControl->OnAudioAdapterChange(audioAdapter);
13992}
13993
13994/**
13995 * @note Locks this object for reading.
13996 */
13997HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13998{
13999 LogFlowThisFunc(("\n"));
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14003
14004 ComPtr<IInternalSessionControl> directControl;
14005 {
14006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14007 if (mData->mSession.mLockType == LockType_VM)
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 /* ignore notifications sent after #OnSessionEnd() is called */
14012 if (!directControl)
14013 return S_OK;
14014
14015 return directControl->OnSerialPortChange(serialPort);
14016}
14017
14018/**
14019 * @note Locks this object for reading.
14020 */
14021HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 if (mData->mSession.mLockType == LockType_VM)
14032 directControl = mData->mSession.mDirectControl;
14033 }
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnParallelPortChange(parallelPort);
14040}
14041
14042/**
14043 * @note Locks this object for reading.
14044 */
14045HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14060
14061 /* ignore notifications sent after #OnSessionEnd() is called */
14062 if (!directControl)
14063 return S_OK;
14064
14065 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14066}
14067
14068/**
14069 * @note Locks this object for reading.
14070 */
14071HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14072{
14073 LogFlowThisFunc(("\n"));
14074
14075 AutoCaller autoCaller(this);
14076 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14077
14078 ComPtr<IInternalSessionControl> directControl;
14079 {
14080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14081 if (mData->mSession.mLockType == LockType_VM)
14082 directControl = mData->mSession.mDirectControl;
14083 }
14084
14085 mParent->i_onMediumChanged(aAttachment);
14086
14087 /* ignore notifications sent after #OnSessionEnd() is called */
14088 if (!directControl)
14089 return S_OK;
14090
14091 return directControl->OnMediumChange(aAttachment, aForce);
14092}
14093
14094HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14100
14101 ComPtr<IInternalSessionControl> directControl;
14102 {
14103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14104 if (mData->mSession.mLockType == LockType_VM)
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnVMProcessPriorityChange(aPriority);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 if (mData->mSession.mLockType == LockType_VM)
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnCPUChange(aCPU, aRemove);
14137}
14138
14139HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 if (mData->mSession.mLockType == LockType_VM)
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnVRDEServerChange(aRestart);
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 if (mData->mSession.mLockType == LockType_VM)
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 /* ignore notifications sent after #OnSessionEnd() is called */
14202 if (!directControl)
14203 return S_OK;
14204
14205 return directControl->OnRecordingChange(aEnable);
14206}
14207
14208/**
14209 * @note Locks this object for reading.
14210 */
14211HRESULT SessionMachine::i_onUSBControllerChange()
14212{
14213 LogFlowThisFunc(("\n"));
14214
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14217
14218 ComPtr<IInternalSessionControl> directControl;
14219 {
14220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14221 if (mData->mSession.mLockType == LockType_VM)
14222 directControl = mData->mSession.mDirectControl;
14223 }
14224
14225 /* ignore notifications sent after #OnSessionEnd() is called */
14226 if (!directControl)
14227 return S_OK;
14228
14229 return directControl->OnUSBControllerChange();
14230}
14231
14232/**
14233 * @note Locks this object for reading.
14234 */
14235HRESULT SessionMachine::i_onSharedFolderChange()
14236{
14237 LogFlowThisFunc(("\n"));
14238
14239 AutoCaller autoCaller(this);
14240 AssertComRCReturnRC(autoCaller.rc());
14241
14242 ComPtr<IInternalSessionControl> directControl;
14243 {
14244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14245 if (mData->mSession.mLockType == LockType_VM)
14246 directControl = mData->mSession.mDirectControl;
14247 }
14248
14249 /* ignore notifications sent after #OnSessionEnd() is called */
14250 if (!directControl)
14251 return S_OK;
14252
14253 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14254}
14255
14256/**
14257 * @note Locks this object for reading.
14258 */
14259HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14260{
14261 LogFlowThisFunc(("\n"));
14262
14263 AutoCaller autoCaller(this);
14264 AssertComRCReturnRC(autoCaller.rc());
14265
14266 ComPtr<IInternalSessionControl> directControl;
14267 {
14268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14269 if (mData->mSession.mLockType == LockType_VM)
14270 directControl = mData->mSession.mDirectControl;
14271 }
14272
14273 /* ignore notifications sent after #OnSessionEnd() is called */
14274 if (!directControl)
14275 return S_OK;
14276
14277 return directControl->OnClipboardModeChange(aClipboardMode);
14278}
14279
14280/**
14281 * @note Locks this object for reading.
14282 */
14283HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14284{
14285 LogFlowThisFunc(("\n"));
14286
14287 AutoCaller autoCaller(this);
14288 AssertComRCReturnRC(autoCaller.rc());
14289
14290 ComPtr<IInternalSessionControl> directControl;
14291 {
14292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14293 if (mData->mSession.mLockType == LockType_VM)
14294 directControl = mData->mSession.mDirectControl;
14295 }
14296
14297 /* ignore notifications sent after #OnSessionEnd() is called */
14298 if (!directControl)
14299 return S_OK;
14300
14301 return directControl->OnDnDModeChange(aDnDMode);
14302}
14303
14304/**
14305 * @note Locks this object for reading.
14306 */
14307HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14308{
14309 LogFlowThisFunc(("\n"));
14310
14311 AutoCaller autoCaller(this);
14312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14313
14314 ComPtr<IInternalSessionControl> directControl;
14315 {
14316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14317 if (mData->mSession.mLockType == LockType_VM)
14318 directControl = mData->mSession.mDirectControl;
14319 }
14320
14321 /* ignore notifications sent after #OnSessionEnd() is called */
14322 if (!directControl)
14323 return S_OK;
14324
14325 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14326}
14327
14328/**
14329 * @note Locks this object for reading.
14330 */
14331HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14332{
14333 LogFlowThisFunc(("\n"));
14334
14335 AutoCaller autoCaller(this);
14336 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14337
14338 ComPtr<IInternalSessionControl> directControl;
14339 {
14340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14341 if (mData->mSession.mLockType == LockType_VM)
14342 directControl = mData->mSession.mDirectControl;
14343 }
14344
14345 /* ignore notifications sent after #OnSessionEnd() is called */
14346 if (!directControl)
14347 return S_OK;
14348
14349 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14350}
14351
14352/**
14353 * Returns @c true if this machine's USB controller reports it has a matching
14354 * filter for the given USB device and @c false otherwise.
14355 *
14356 * @note locks this object for reading.
14357 */
14358bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14359{
14360 AutoCaller autoCaller(this);
14361 /* silently return if not ready -- this method may be called after the
14362 * direct machine session has been called */
14363 if (!autoCaller.isOk())
14364 return false;
14365
14366#ifdef VBOX_WITH_USB
14367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14368
14369 switch (mData->mMachineState)
14370 {
14371 case MachineState_Starting:
14372 case MachineState_Restoring:
14373 case MachineState_TeleportingIn:
14374 case MachineState_Paused:
14375 case MachineState_Running:
14376 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14377 * elsewhere... */
14378 alock.release();
14379 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14380 default: break;
14381 }
14382#else
14383 NOREF(aDevice);
14384 NOREF(aMaskedIfs);
14385#endif
14386 return false;
14387}
14388
14389/**
14390 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14391 */
14392HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14393 IVirtualBoxErrorInfo *aError,
14394 ULONG aMaskedIfs,
14395 const com::Utf8Str &aCaptureFilename)
14396{
14397 LogFlowThisFunc(("\n"));
14398
14399 AutoCaller autoCaller(this);
14400
14401 /* This notification may happen after the machine object has been
14402 * uninitialized (the session was closed), so don't assert. */
14403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14404
14405 ComPtr<IInternalSessionControl> directControl;
14406 {
14407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14408 if (mData->mSession.mLockType == LockType_VM)
14409 directControl = mData->mSession.mDirectControl;
14410 }
14411
14412 /* fail on notifications sent after #OnSessionEnd() is called, it is
14413 * expected by the caller */
14414 if (!directControl)
14415 return E_FAIL;
14416
14417 /* No locks should be held at this point. */
14418 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14419 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14420
14421 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14422}
14423
14424/**
14425 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14426 */
14427HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14428 IVirtualBoxErrorInfo *aError)
14429{
14430 LogFlowThisFunc(("\n"));
14431
14432 AutoCaller autoCaller(this);
14433
14434 /* This notification may happen after the machine object has been
14435 * uninitialized (the session was closed), so don't assert. */
14436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14437
14438 ComPtr<IInternalSessionControl> directControl;
14439 {
14440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14441 if (mData->mSession.mLockType == LockType_VM)
14442 directControl = mData->mSession.mDirectControl;
14443 }
14444
14445 /* fail on notifications sent after #OnSessionEnd() is called, it is
14446 * expected by the caller */
14447 if (!directControl)
14448 return E_FAIL;
14449
14450 /* No locks should be held at this point. */
14451 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14452 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14453
14454 return directControl->OnUSBDeviceDetach(aId, aError);
14455}
14456
14457// protected methods
14458/////////////////////////////////////////////////////////////////////////////
14459
14460/**
14461 * Deletes the given file if it is no longer in use by either the current machine state
14462 * (if the machine is "saved") or any of the machine's snapshots.
14463 *
14464 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14465 * but is different for each SnapshotMachine. When calling this, the order of calling this
14466 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14467 * is therefore critical. I know, it's all rather messy.
14468 *
14469 * @param strStateFile
14470 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14471 * the test for whether the saved state file is in use.
14472 */
14473void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14474 Snapshot *pSnapshotToIgnore)
14475{
14476 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14477 if ( (strStateFile.isNotEmpty())
14478 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14479 )
14480 // ... and it must also not be shared with other snapshots
14481 if ( !mData->mFirstSnapshot
14482 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14483 // this checks the SnapshotMachine's state file paths
14484 )
14485 RTFileDelete(strStateFile.c_str());
14486}
14487
14488/**
14489 * Locks the attached media.
14490 *
14491 * All attached hard disks are locked for writing and DVD/floppy are locked for
14492 * reading. Parents of attached hard disks (if any) are locked for reading.
14493 *
14494 * This method also performs accessibility check of all media it locks: if some
14495 * media is inaccessible, the method will return a failure and a bunch of
14496 * extended error info objects per each inaccessible medium.
14497 *
14498 * Note that this method is atomic: if it returns a success, all media are
14499 * locked as described above; on failure no media is locked at all (all
14500 * succeeded individual locks will be undone).
14501 *
14502 * The caller is responsible for doing the necessary state sanity checks.
14503 *
14504 * The locks made by this method must be undone by calling #unlockMedia() when
14505 * no more needed.
14506 */
14507HRESULT SessionMachine::i_lockMedia()
14508{
14509 AutoCaller autoCaller(this);
14510 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14511
14512 AutoMultiWriteLock2 alock(this->lockHandle(),
14513 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14514
14515 /* bail out if trying to lock things with already set up locking */
14516 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14517
14518 MultiResult mrc(S_OK);
14519
14520 /* Collect locking information for all medium objects attached to the VM. */
14521 for (MediumAttachmentList::const_iterator
14522 it = mMediumAttachments->begin();
14523 it != mMediumAttachments->end();
14524 ++it)
14525 {
14526 MediumAttachment *pAtt = *it;
14527 DeviceType_T devType = pAtt->i_getType();
14528 Medium *pMedium = pAtt->i_getMedium();
14529
14530 MediumLockList *pMediumLockList(new MediumLockList());
14531 // There can be attachments without a medium (floppy/dvd), and thus
14532 // it's impossible to create a medium lock list. It still makes sense
14533 // to have the empty medium lock list in the map in case a medium is
14534 // attached later.
14535 if (pMedium != NULL)
14536 {
14537 MediumType_T mediumType = pMedium->i_getType();
14538 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14539 || mediumType == MediumType_Shareable;
14540 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14541
14542 alock.release();
14543 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14544 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14545 false /* fMediumLockWriteAll */,
14546 NULL,
14547 *pMediumLockList);
14548 alock.acquire();
14549 if (FAILED(mrc))
14550 {
14551 delete pMediumLockList;
14552 mData->mSession.mLockedMedia.Clear();
14553 break;
14554 }
14555 }
14556
14557 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14558 if (FAILED(rc))
14559 {
14560 mData->mSession.mLockedMedia.Clear();
14561 mrc = setError(rc,
14562 tr("Collecting locking information for all attached media failed"));
14563 break;
14564 }
14565 }
14566
14567 if (SUCCEEDED(mrc))
14568 {
14569 /* Now lock all media. If this fails, nothing is locked. */
14570 alock.release();
14571 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14572 alock.acquire();
14573 if (FAILED(rc))
14574 {
14575 mrc = setError(rc,
14576 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14577 }
14578 }
14579
14580 return mrc;
14581}
14582
14583/**
14584 * Undoes the locks made by by #lockMedia().
14585 */
14586HRESULT SessionMachine::i_unlockMedia()
14587{
14588 AutoCaller autoCaller(this);
14589 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14590
14591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14592
14593 /* we may be holding important error info on the current thread;
14594 * preserve it */
14595 ErrorInfoKeeper eik;
14596
14597 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14598 AssertComRC(rc);
14599 return rc;
14600}
14601
14602/**
14603 * Helper to change the machine state (reimplementation).
14604 *
14605 * @note Locks this object for writing.
14606 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14607 * it can cause crashes in random places due to unexpectedly committing
14608 * the current settings. The caller is responsible for that. The call
14609 * to saveStateSettings is fine, because this method does not commit.
14610 */
14611HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14612{
14613 LogFlowThisFuncEnter();
14614 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14615
14616 AutoCaller autoCaller(this);
14617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14618
14619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14620
14621 MachineState_T oldMachineState = mData->mMachineState;
14622
14623 AssertMsgReturn(oldMachineState != aMachineState,
14624 ("oldMachineState=%s, aMachineState=%s\n",
14625 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14626 E_FAIL);
14627
14628 HRESULT rc = S_OK;
14629
14630 int stsFlags = 0;
14631 bool deleteSavedState = false;
14632
14633 /* detect some state transitions */
14634
14635 if ( ( oldMachineState == MachineState_Saved
14636 && aMachineState == MachineState_Restoring)
14637 || ( ( oldMachineState == MachineState_PoweredOff
14638 || oldMachineState == MachineState_Teleported
14639 || oldMachineState == MachineState_Aborted
14640 )
14641 && ( aMachineState == MachineState_TeleportingIn
14642 || aMachineState == MachineState_Starting
14643 )
14644 )
14645 )
14646 {
14647 /* The EMT thread is about to start */
14648
14649 /* Nothing to do here for now... */
14650
14651 /// @todo NEWMEDIA don't let mDVDDrive and other children
14652 /// change anything when in the Starting/Restoring state
14653 }
14654 else if ( ( oldMachineState == MachineState_Running
14655 || oldMachineState == MachineState_Paused
14656 || oldMachineState == MachineState_Teleporting
14657 || oldMachineState == MachineState_OnlineSnapshotting
14658 || oldMachineState == MachineState_LiveSnapshotting
14659 || oldMachineState == MachineState_Stuck
14660 || oldMachineState == MachineState_Starting
14661 || oldMachineState == MachineState_Stopping
14662 || oldMachineState == MachineState_Saving
14663 || oldMachineState == MachineState_Restoring
14664 || oldMachineState == MachineState_TeleportingPausedVM
14665 || oldMachineState == MachineState_TeleportingIn
14666 )
14667 && ( aMachineState == MachineState_PoweredOff
14668 || aMachineState == MachineState_Saved
14669 || aMachineState == MachineState_Teleported
14670 || aMachineState == MachineState_Aborted
14671 )
14672 )
14673 {
14674 /* The EMT thread has just stopped, unlock attached media. Note that as
14675 * opposed to locking that is done from Console, we do unlocking here
14676 * because the VM process may have aborted before having a chance to
14677 * properly unlock all media it locked. */
14678
14679 unlockMedia();
14680 }
14681
14682 if (oldMachineState == MachineState_Restoring)
14683 {
14684 if (aMachineState != MachineState_Saved)
14685 {
14686 /*
14687 * delete the saved state file once the machine has finished
14688 * restoring from it (note that Console sets the state from
14689 * Restoring to Saved if the VM couldn't restore successfully,
14690 * to give the user an ability to fix an error and retry --
14691 * we keep the saved state file in this case)
14692 */
14693 deleteSavedState = true;
14694 }
14695 }
14696 else if ( oldMachineState == MachineState_Saved
14697 && ( aMachineState == MachineState_PoweredOff
14698 || aMachineState == MachineState_Aborted
14699 || aMachineState == MachineState_Teleported
14700 )
14701 )
14702 {
14703 /*
14704 * delete the saved state after SessionMachine::ForgetSavedState() is called
14705 * or if the VM process (owning a direct VM session) crashed while the
14706 * VM was Saved
14707 */
14708
14709 /// @todo (dmik)
14710 // Not sure that deleting the saved state file just because of the
14711 // client death before it attempted to restore the VM is a good
14712 // thing. But when it crashes we need to go to the Aborted state
14713 // which cannot have the saved state file associated... The only
14714 // way to fix this is to make the Aborted condition not a VM state
14715 // but a bool flag: i.e., when a crash occurs, set it to true and
14716 // change the state to PoweredOff or Saved depending on the
14717 // saved state presence.
14718
14719 deleteSavedState = true;
14720 mData->mCurrentStateModified = TRUE;
14721 stsFlags |= SaveSTS_CurStateModified;
14722 }
14723
14724 if ( aMachineState == MachineState_Starting
14725 || aMachineState == MachineState_Restoring
14726 || aMachineState == MachineState_TeleportingIn
14727 )
14728 {
14729 /* set the current state modified flag to indicate that the current
14730 * state is no more identical to the state in the
14731 * current snapshot */
14732 if (!mData->mCurrentSnapshot.isNull())
14733 {
14734 mData->mCurrentStateModified = TRUE;
14735 stsFlags |= SaveSTS_CurStateModified;
14736 }
14737 }
14738
14739 if (deleteSavedState)
14740 {
14741 if (mRemoveSavedState)
14742 {
14743 Assert(!mSSData->strStateFilePath.isEmpty());
14744
14745 // it is safe to delete the saved state file if ...
14746 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14747 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14748 // ... none of the snapshots share the saved state file
14749 )
14750 RTFileDelete(mSSData->strStateFilePath.c_str());
14751 }
14752
14753 mSSData->strStateFilePath.setNull();
14754 stsFlags |= SaveSTS_StateFilePath;
14755 }
14756
14757 /* redirect to the underlying peer machine */
14758 mPeer->i_setMachineState(aMachineState);
14759
14760 if ( oldMachineState != MachineState_RestoringSnapshot
14761 && ( aMachineState == MachineState_PoweredOff
14762 || aMachineState == MachineState_Teleported
14763 || aMachineState == MachineState_Aborted
14764 || aMachineState == MachineState_Saved))
14765 {
14766 /* the machine has stopped execution
14767 * (or the saved state file was adopted) */
14768 stsFlags |= SaveSTS_StateTimeStamp;
14769 }
14770
14771 if ( ( oldMachineState == MachineState_PoweredOff
14772 || oldMachineState == MachineState_Aborted
14773 || oldMachineState == MachineState_Teleported
14774 )
14775 && aMachineState == MachineState_Saved)
14776 {
14777 /* the saved state file was adopted */
14778 Assert(!mSSData->strStateFilePath.isEmpty());
14779 stsFlags |= SaveSTS_StateFilePath;
14780 }
14781
14782#ifdef VBOX_WITH_GUEST_PROPS
14783 if ( aMachineState == MachineState_PoweredOff
14784 || aMachineState == MachineState_Aborted
14785 || aMachineState == MachineState_Teleported)
14786 {
14787 /* Make sure any transient guest properties get removed from the
14788 * property store on shutdown. */
14789 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14790
14791 /* remove it from the settings representation */
14792 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14793 for (settings::GuestPropertiesList::iterator
14794 it = llGuestProperties.begin();
14795 it != llGuestProperties.end();
14796 /*nothing*/)
14797 {
14798 const settings::GuestProperty &prop = *it;
14799 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14800 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14801 {
14802 it = llGuestProperties.erase(it);
14803 fNeedsSaving = true;
14804 }
14805 else
14806 {
14807 ++it;
14808 }
14809 }
14810
14811 /* Additionally remove it from the HWData representation. Required to
14812 * keep everything in sync, as this is what the API keeps using. */
14813 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14814 for (HWData::GuestPropertyMap::iterator
14815 it = llHWGuestProperties.begin();
14816 it != llHWGuestProperties.end();
14817 /*nothing*/)
14818 {
14819 uint32_t fFlags = it->second.mFlags;
14820 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14821 {
14822 /* iterator where we need to continue after the erase call
14823 * (C++03 is a fact still, and it doesn't return the iterator
14824 * which would allow continuing) */
14825 HWData::GuestPropertyMap::iterator it2 = it;
14826 ++it2;
14827 llHWGuestProperties.erase(it);
14828 it = it2;
14829 fNeedsSaving = true;
14830 }
14831 else
14832 {
14833 ++it;
14834 }
14835 }
14836
14837 if (fNeedsSaving)
14838 {
14839 mData->mCurrentStateModified = TRUE;
14840 stsFlags |= SaveSTS_CurStateModified;
14841 }
14842 }
14843#endif /* VBOX_WITH_GUEST_PROPS */
14844
14845 rc = i_saveStateSettings(stsFlags);
14846
14847 if ( ( oldMachineState != MachineState_PoweredOff
14848 && oldMachineState != MachineState_Aborted
14849 && oldMachineState != MachineState_Teleported
14850 )
14851 && ( aMachineState == MachineState_PoweredOff
14852 || aMachineState == MachineState_Aborted
14853 || aMachineState == MachineState_Teleported
14854 )
14855 )
14856 {
14857 /* we've been shut down for any reason */
14858 /* no special action so far */
14859 }
14860
14861 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14862 LogFlowThisFuncLeave();
14863 return rc;
14864}
14865
14866/**
14867 * Sends the current machine state value to the VM process.
14868 *
14869 * @note Locks this object for reading, then calls a client process.
14870 */
14871HRESULT SessionMachine::i_updateMachineStateOnClient()
14872{
14873 AutoCaller autoCaller(this);
14874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14875
14876 ComPtr<IInternalSessionControl> directControl;
14877 {
14878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14879 AssertReturn(!!mData, E_FAIL);
14880 if (mData->mSession.mLockType == LockType_VM)
14881 directControl = mData->mSession.mDirectControl;
14882
14883 /* directControl may be already set to NULL here in #OnSessionEnd()
14884 * called too early by the direct session process while there is still
14885 * some operation (like deleting the snapshot) in progress. The client
14886 * process in this case is waiting inside Session::close() for the
14887 * "end session" process object to complete, while #uninit() called by
14888 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14889 * operation to complete. For now, we accept this inconsistent behavior
14890 * and simply do nothing here. */
14891
14892 if (mData->mSession.mState == SessionState_Unlocking)
14893 return S_OK;
14894 }
14895
14896 /* ignore notifications sent after #OnSessionEnd() is called */
14897 if (!directControl)
14898 return S_OK;
14899
14900 return directControl->UpdateMachineState(mData->mMachineState);
14901}
14902
14903
14904/*static*/
14905HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14906{
14907 va_list args;
14908 va_start(args, pcszMsg);
14909 HRESULT rc = setErrorInternal(aResultCode,
14910 getStaticClassIID(),
14911 getStaticComponentName(),
14912 Utf8Str(pcszMsg, args),
14913 false /* aWarning */,
14914 true /* aLogIt */);
14915 va_end(args);
14916 return rc;
14917}
14918
14919
14920HRESULT Machine::updateState(MachineState_T aState)
14921{
14922 NOREF(aState);
14923 ReturnComNotImplemented();
14924}
14925
14926HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14927{
14928 NOREF(aProgress);
14929 ReturnComNotImplemented();
14930}
14931
14932HRESULT Machine::endPowerUp(LONG aResult)
14933{
14934 NOREF(aResult);
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14939{
14940 NOREF(aProgress);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::endPoweringDown(LONG aResult,
14945 const com::Utf8Str &aErrMsg)
14946{
14947 NOREF(aResult);
14948 NOREF(aErrMsg);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14953 BOOL *aMatched,
14954 ULONG *aMaskedInterfaces)
14955{
14956 NOREF(aDevice);
14957 NOREF(aMatched);
14958 NOREF(aMaskedInterfaces);
14959 ReturnComNotImplemented();
14960
14961}
14962
14963HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14964{
14965 NOREF(aId); NOREF(aCaptureFilename);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14970 BOOL aDone)
14971{
14972 NOREF(aId);
14973 NOREF(aDone);
14974 ReturnComNotImplemented();
14975}
14976
14977HRESULT Machine::autoCaptureUSBDevices()
14978{
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14983{
14984 NOREF(aDone);
14985 ReturnComNotImplemented();
14986}
14987
14988HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14989 ComPtr<IProgress> &aProgress)
14990{
14991 NOREF(aSession);
14992 NOREF(aProgress);
14993 ReturnComNotImplemented();
14994}
14995
14996HRESULT Machine::finishOnlineMergeMedium()
14997{
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15002 std::vector<com::Utf8Str> &aValues,
15003 std::vector<LONG64> &aTimestamps,
15004 std::vector<com::Utf8Str> &aFlags)
15005{
15006 NOREF(aNames);
15007 NOREF(aValues);
15008 NOREF(aTimestamps);
15009 NOREF(aFlags);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15014 const com::Utf8Str &aValue,
15015 LONG64 aTimestamp,
15016 const com::Utf8Str &aFlags)
15017{
15018 NOREF(aName);
15019 NOREF(aValue);
15020 NOREF(aTimestamp);
15021 NOREF(aFlags);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::lockMedia()
15026{
15027 ReturnComNotImplemented();
15028}
15029
15030HRESULT Machine::unlockMedia()
15031{
15032 ReturnComNotImplemented();
15033}
15034
15035HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15036 ComPtr<IMediumAttachment> &aNewAttachment)
15037{
15038 NOREF(aAttachment);
15039 NOREF(aNewAttachment);
15040 ReturnComNotImplemented();
15041}
15042
15043HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15044 ULONG aCpuUser,
15045 ULONG aCpuKernel,
15046 ULONG aCpuIdle,
15047 ULONG aMemTotal,
15048 ULONG aMemFree,
15049 ULONG aMemBalloon,
15050 ULONG aMemShared,
15051 ULONG aMemCache,
15052 ULONG aPagedTotal,
15053 ULONG aMemAllocTotal,
15054 ULONG aMemFreeTotal,
15055 ULONG aMemBalloonTotal,
15056 ULONG aMemSharedTotal,
15057 ULONG aVmNetRx,
15058 ULONG aVmNetTx)
15059{
15060 NOREF(aValidStats);
15061 NOREF(aCpuUser);
15062 NOREF(aCpuKernel);
15063 NOREF(aCpuIdle);
15064 NOREF(aMemTotal);
15065 NOREF(aMemFree);
15066 NOREF(aMemBalloon);
15067 NOREF(aMemShared);
15068 NOREF(aMemCache);
15069 NOREF(aPagedTotal);
15070 NOREF(aMemAllocTotal);
15071 NOREF(aMemFreeTotal);
15072 NOREF(aMemBalloonTotal);
15073 NOREF(aMemSharedTotal);
15074 NOREF(aVmNetRx);
15075 NOREF(aVmNetTx);
15076 ReturnComNotImplemented();
15077}
15078
15079HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15080 com::Utf8Str &aResult)
15081{
15082 NOREF(aAuthParams);
15083 NOREF(aResult);
15084 ReturnComNotImplemented();
15085}
15086
15087com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15088{
15089 com::Utf8Str strControllerName = "Unknown";
15090 switch (aBusType)
15091 {
15092 case StorageBus_IDE:
15093 {
15094 strControllerName = "IDE";
15095 break;
15096 }
15097 case StorageBus_SATA:
15098 {
15099 strControllerName = "SATA";
15100 break;
15101 }
15102 case StorageBus_SCSI:
15103 {
15104 strControllerName = "SCSI";
15105 break;
15106 }
15107 case StorageBus_Floppy:
15108 {
15109 strControllerName = "Floppy";
15110 break;
15111 }
15112 case StorageBus_SAS:
15113 {
15114 strControllerName = "SAS";
15115 break;
15116 }
15117 case StorageBus_USB:
15118 {
15119 strControllerName = "USB";
15120 break;
15121 }
15122 default:
15123 break;
15124 }
15125 return strControllerName;
15126}
15127
15128HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15129{
15130 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15131
15132 AutoCaller autoCaller(this);
15133 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15134
15135 HRESULT rc = S_OK;
15136
15137 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15138 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15139 rc = getUSBDeviceFilters(usbDeviceFilters);
15140 if (FAILED(rc)) return rc;
15141
15142 NOREF(aFlags);
15143 com::Utf8Str osTypeId;
15144 ComObjPtr<GuestOSType> osType = NULL;
15145
15146 /* Get the guest os type as a string from the VB. */
15147 rc = getOSTypeId(osTypeId);
15148 if (FAILED(rc)) return rc;
15149
15150 /* Get the os type obj that coresponds, can be used to get
15151 * the defaults for this guest OS. */
15152 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15153 if (FAILED(rc)) return rc;
15154
15155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15156
15157 /* Let the OS type select 64-bit ness. */
15158 mHWData->mLongMode = osType->i_is64Bit()
15159 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15160
15161 /* Apply network adapters defaults */
15162 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15163 mNetworkAdapters[slot]->i_applyDefaults(osType);
15164
15165 /* Apply serial port defaults */
15166 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15167 mSerialPorts[slot]->i_applyDefaults(osType);
15168
15169 /* Apply parallel port defaults - not OS dependent*/
15170 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15171 mParallelPorts[slot]->i_applyDefaults();
15172
15173
15174 /* Let the OS type enable the X2APIC */
15175 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15176
15177 /* This one covers IOAPICEnabled. */
15178 mBIOSSettings->i_applyDefaults(osType);
15179
15180 /* Initialize default record settings. */
15181 mRecordingSettings->i_applyDefaults();
15182
15183 /* Initialize default BIOS settings here */
15184 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15185 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15186
15187 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15194 if (FAILED(rc)) return rc;
15195
15196 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15197 if (FAILED(rc)) return rc;
15198
15199 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15200 if (FAILED(rc)) return rc;
15201
15202 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15203 if (FAILED(rc)) return rc;
15204
15205 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15209 if (FAILED(rc)) return rc;
15210
15211 BOOL mRTCUseUTC;
15212 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15213 if (FAILED(rc)) return rc;
15214
15215 setRTCUseUTC(mRTCUseUTC);
15216 if (FAILED(rc)) return rc;
15217
15218 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15219 if (FAILED(rc)) return rc;
15220
15221 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15222 if (FAILED(rc)) return rc;
15223
15224 /* Audio stuff. */
15225 AudioControllerType_T audioController;
15226 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15227 if (FAILED(rc)) return rc;
15228
15229 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15230 if (FAILED(rc)) return rc;
15231
15232 AudioCodecType_T audioCodec;
15233 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15237 if (FAILED(rc)) return rc;
15238
15239 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15240 if (FAILED(rc)) return rc;
15241
15242 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15243 if (FAILED(rc)) return rc;
15244
15245 /* Storage Controllers */
15246 StorageControllerType_T hdStorageControllerType;
15247 StorageBus_T hdStorageBusType;
15248 StorageControllerType_T dvdStorageControllerType;
15249 StorageBus_T dvdStorageBusType;
15250 BOOL recommendedFloppy;
15251 ComPtr<IStorageController> floppyController;
15252 ComPtr<IStorageController> hdController;
15253 ComPtr<IStorageController> dvdController;
15254 Utf8Str strFloppyName, strDVDName, strHDName;
15255
15256 /* GUI auto generates controller names using bus type. Do the same*/
15257 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15258
15259 /* Floppy recommended? add one. */
15260 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15261 if (FAILED(rc)) return rc;
15262 if (recommendedFloppy)
15263 {
15264 rc = addStorageController(strFloppyName,
15265 StorageBus_Floppy,
15266 floppyController);
15267 if (FAILED(rc)) return rc;
15268 }
15269
15270 /* Setup one DVD storage controller. */
15271 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15275 if (FAILED(rc)) return rc;
15276
15277 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15278
15279 rc = addStorageController(strDVDName,
15280 dvdStorageBusType,
15281 dvdController);
15282 if (FAILED(rc)) return rc;
15283
15284 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15285 if (FAILED(rc)) return rc;
15286
15287 /* Setup one HDD storage controller. */
15288 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15289 if (FAILED(rc)) return rc;
15290
15291 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15292 if (FAILED(rc)) return rc;
15293
15294 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15295
15296 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15297 {
15298 rc = addStorageController(strHDName,
15299 hdStorageBusType,
15300 hdController);
15301 if (FAILED(rc)) return rc;
15302
15303 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15304 if (FAILED(rc)) return rc;
15305 }
15306 else
15307 {
15308 /* The HD controller is the same as DVD: */
15309 hdController = dvdController;
15310 }
15311
15312 /* Limit the AHCI port count if it's used because windows has trouble with
15313 * too many ports and other guest (OS X in particular) may take extra long
15314 * boot: */
15315
15316 // pParent = static_cast<Medium*>(aP)
15317 IStorageController *temp = hdController;
15318 ComObjPtr<StorageController> storageController;
15319 storageController = static_cast<StorageController *>(temp);
15320
15321 // tempHDController = aHDController;
15322 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15323 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15324 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15325 storageController->COMSETTER(PortCount)(1);
15326
15327 /* USB stuff */
15328
15329 bool ohciEnabled = false;
15330
15331 ComPtr<IUSBController> usbController;
15332 BOOL recommendedUSB3;
15333 BOOL recommendedUSB;
15334 BOOL usbProxyAvailable;
15335
15336 getUSBProxyAvailable(&usbProxyAvailable);
15337 if (FAILED(rc)) return rc;
15338
15339 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15340 if (FAILED(rc)) return rc;
15341 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15342 if (FAILED(rc)) return rc;
15343
15344 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15345 {
15346#ifdef VBOX_WITH_EXTPACK
15347 /* USB 3.0 is only available if the proper ExtPack is installed. */
15348 ExtPackManager *aManager = mParent->i_getExtPackManager();
15349 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15350 {
15351 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15352 if (FAILED(rc)) return rc;
15353
15354 /* xHci includes OHCI */
15355 ohciEnabled = true;
15356 }
15357#endif
15358 }
15359 if ( !ohciEnabled
15360 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15361 {
15362 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15363 if (FAILED(rc)) return rc;
15364 ohciEnabled = true;
15365
15366#ifdef VBOX_WITH_EXTPACK
15367 /* USB 2.0 is only available if the proper ExtPack is installed.
15368 * Note. Configuring EHCI here and providing messages about
15369 * the missing extpack isn't exactly clean, but it is a
15370 * necessary evil to patch over legacy compatability issues
15371 * introduced by the new distribution model. */
15372 ExtPackManager *manager = mParent->i_getExtPackManager();
15373 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15374 {
15375 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15376 if (FAILED(rc)) return rc;
15377 }
15378#endif
15379 }
15380
15381 /* Set recommended human interface device types: */
15382 BOOL recommendedUSBHID;
15383 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15384 if (FAILED(rc)) return rc;
15385
15386 if (recommendedUSBHID)
15387 {
15388 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15389 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15390 if (!ohciEnabled && !usbDeviceFilters.isNull())
15391 {
15392 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15393 if (FAILED(rc)) return rc;
15394 }
15395 }
15396
15397 BOOL recommendedUSBTablet;
15398 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15399 if (FAILED(rc)) return rc;
15400
15401 if (recommendedUSBTablet)
15402 {
15403 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15404 if (!ohciEnabled && !usbDeviceFilters.isNull())
15405 {
15406 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15407 if (FAILED(rc)) return rc;
15408 }
15409 }
15410 return S_OK;
15411}
15412
15413/* This isn't handled entirely by the wrapper generator yet. */
15414#ifdef VBOX_WITH_XPCOM
15415NS_DECL_CLASSINFO(SessionMachine)
15416NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15417
15418NS_DECL_CLASSINFO(SnapshotMachine)
15419NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15420#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