VirtualBox

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

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

Main: bugref:6913: Fixed NULL id in the IMediumRegisteredEvent and fixed generation other medium and storage device events

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

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