VirtualBox

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

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

Forward ported 130474,130475,130477,130479. bugref:9453

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 535.4 KB
Line 
1/* $Id: MachineImpl.cpp 78632 2019-05-21 13:56:11Z 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 mMDSClearOnSched = true;
201 mMDSClearOnVMEntry = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mDnDMode = DnDMode_Disabled;
217
218 mFirmwareType = FirmwareType_BIOS;
219 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
220 mPointingHIDType = PointingHIDType_PS2Mouse;
221 mChipsetType = ChipsetType_PIIX3;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param strOsType OS Type string (stored as is if aOsType is NULL).
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 const Utf8Str &strOsType,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 if (llGroups.size())
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = i_isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->i_id();
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352 }
353 else if (!strOsType.isEmpty())
354 {
355 /* Store OS type */
356 mUserData->s.strOsType = strOsType;
357
358 /* No guest OS type object. Pick some plausible defaults which the
359 * host can handle. There's no way to know or validate anything. */
360 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361 mHWData->mX2APIC = false;
362 }
363
364 /* Apply BIOS defaults. */
365 mBIOSSettings->i_applyDefaults(aOsType);
366
367 /* Apply record defaults. */
368 mRecordingSettings->i_applyDefaults();
369
370 /* Apply network adapters defaults */
371 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
372 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
373
374 /* Apply serial port defaults */
375 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
376 mSerialPorts[slot]->i_applyDefaults(aOsType);
377
378 /* Apply parallel port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
380 mParallelPorts[slot]->i_applyDefaults();
381
382 /* At this point the changing of the current state modification
383 * flag is allowed. */
384 i_allowStateModification();
385
386 /* commit all changes made during the initialization */
387 i_commit();
388 }
389
390 /* Confirm a successful initialization when it's the case */
391 if (SUCCEEDED(rc))
392 {
393 if (mData->mAccessible)
394 autoInitSpan.setSucceeded();
395 else
396 autoInitSpan.setLimited();
397 }
398
399 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
400 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
401 mData->mRegistered,
402 mData->mAccessible,
403 rc));
404
405 LogFlowThisFuncLeave();
406
407 return rc;
408}
409
410/**
411 * Initializes a new instance with data from machine XML (formerly Init_Registered).
412 * Gets called in two modes:
413 *
414 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
415 * UUID is specified and we mark the machine as "registered";
416 *
417 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
418 * and the machine remains unregistered until RegisterMachine() is called.
419 *
420 * @param aParent Associated parent object
421 * @param strConfigFile Local file system path to the VM settings file (can
422 * be relative to the VirtualBox config directory).
423 * @param aId UUID of the machine or NULL (see above).
424 *
425 * @return Success indicator. if not S_OK, the machine object is invalid
426 */
427HRESULT Machine::initFromSettings(VirtualBox *aParent,
428 const Utf8Str &strConfigFile,
429 const Guid *aId)
430{
431 LogFlowThisFuncEnter();
432 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
433
434 /* Enclose the state transition NotReady->InInit->Ready */
435 AutoInitSpan autoInitSpan(this);
436 AssertReturn(autoInitSpan.isOk(), E_FAIL);
437
438 HRESULT rc = initImpl(aParent, strConfigFile);
439 if (FAILED(rc)) return rc;
440
441 if (aId)
442 {
443 // loading a registered VM:
444 unconst(mData->mUuid) = *aId;
445 mData->mRegistered = TRUE;
446 // now load the settings from XML:
447 rc = i_registeredInit();
448 // this calls initDataAndChildObjects() and loadSettings()
449 }
450 else
451 {
452 // opening an unregistered VM (VirtualBox::OpenMachine()):
453 rc = initDataAndChildObjects();
454
455 if (SUCCEEDED(rc))
456 {
457 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
458 mData->mAccessible = TRUE;
459
460 try
461 {
462 // load and parse machine XML; this will throw on XML or logic errors
463 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
464
465 // reject VM UUID duplicates, they can happen if someone
466 // tries to register an already known VM config again
467 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
468 true /* fPermitInaccessible */,
469 false /* aDoSetError */,
470 NULL) != VBOX_E_OBJECT_NOT_FOUND)
471 {
472 throw setError(E_FAIL,
473 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
474 mData->m_strConfigFile.c_str());
475 }
476
477 // use UUID from machine config
478 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
479
480 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
481 NULL /* puuidRegistry */);
482 if (FAILED(rc)) throw rc;
483
484 /* At this point the changing of the current state modification
485 * flag is allowed. */
486 i_allowStateModification();
487
488 i_commit();
489 }
490 catch (HRESULT err)
491 {
492 /* we assume that error info is set by the thrower */
493 rc = err;
494 }
495 catch (...)
496 {
497 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
498 }
499 }
500 }
501
502 /* Confirm a successful initialization when it's the case */
503 if (SUCCEEDED(rc))
504 {
505 if (mData->mAccessible)
506 autoInitSpan.setSucceeded();
507 else
508 {
509 autoInitSpan.setLimited();
510
511 // uninit media from this machine's media registry, or else
512 // reloading the settings will fail
513 mParent->i_unregisterMachineMedia(i_getId());
514 }
515 }
516
517 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
518 "rc=%08X\n",
519 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
520 mData->mRegistered, mData->mAccessible, rc));
521
522 LogFlowThisFuncLeave();
523
524 return rc;
525}
526
527/**
528 * Initializes a new instance from a machine config that is already in memory
529 * (import OVF case). Since we are importing, the UUID in the machine
530 * config is ignored and we always generate a fresh one.
531 *
532 * @param aParent Associated parent object.
533 * @param strName Name for the new machine; this overrides what is specified in config.
534 * @param strSettingsFilename File name of .vbox file.
535 * @param config Machine configuration loaded and parsed from XML.
536 *
537 * @return Success indicator. if not S_OK, the machine object is invalid
538 */
539HRESULT Machine::init(VirtualBox *aParent,
540 const Utf8Str &strName,
541 const Utf8Str &strSettingsFilename,
542 const settings::MachineConfigFile &config)
543{
544 LogFlowThisFuncEnter();
545
546 /* Enclose the state transition NotReady->InInit->Ready */
547 AutoInitSpan autoInitSpan(this);
548 AssertReturn(autoInitSpan.isOk(), E_FAIL);
549
550 HRESULT rc = initImpl(aParent, strSettingsFilename);
551 if (FAILED(rc)) return rc;
552
553 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
554 if (FAILED(rc)) return rc;
555
556 rc = initDataAndChildObjects();
557
558 if (SUCCEEDED(rc))
559 {
560 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
561 mData->mAccessible = TRUE;
562
563 // create empty machine config for instance data
564 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
565
566 // generate fresh UUID, ignore machine config
567 unconst(mData->mUuid).create();
568
569 rc = i_loadMachineDataFromSettings(config,
570 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
571
572 // override VM name as well, it may be different
573 mUserData->s.strName = strName;
574
575 if (SUCCEEDED(rc))
576 {
577 /* At this point the changing of the current state modification
578 * flag is allowed. */
579 i_allowStateModification();
580
581 /* commit all changes made during the initialization */
582 i_commit();
583 }
584 }
585
586 /* Confirm a successful initialization when it's the case */
587 if (SUCCEEDED(rc))
588 {
589 if (mData->mAccessible)
590 autoInitSpan.setSucceeded();
591 else
592 {
593 /* Ignore all errors from unregistering, they would destroy
594- * the more interesting error information we already have,
595- * pinpointing the issue with the VM config. */
596 ErrorInfoKeeper eik;
597
598 autoInitSpan.setLimited();
599
600 // uninit media from this machine's media registry, or else
601 // reloading the settings will fail
602 mParent->i_unregisterMachineMedia(i_getId());
603 }
604 }
605
606 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
607 "rc=%08X\n",
608 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
609 mData->mRegistered, mData->mAccessible, rc));
610
611 LogFlowThisFuncLeave();
612
613 return rc;
614}
615
616/**
617 * Shared code between the various init() implementations.
618 * @param aParent The VirtualBox object.
619 * @param strConfigFile Settings file.
620 * @return
621 */
622HRESULT Machine::initImpl(VirtualBox *aParent,
623 const Utf8Str &strConfigFile)
624{
625 LogFlowThisFuncEnter();
626
627 AssertReturn(aParent, E_INVALIDARG);
628 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
629
630 HRESULT rc = S_OK;
631
632 /* share the parent weakly */
633 unconst(mParent) = aParent;
634
635 /* allocate the essential machine data structure (the rest will be
636 * allocated later by initDataAndChildObjects() */
637 mData.allocate();
638
639 /* memorize the config file name (as provided) */
640 mData->m_strConfigFile = strConfigFile;
641
642 /* get the full file name */
643 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
644 if (RT_FAILURE(vrc1))
645 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
646 tr("Invalid machine settings file name '%s' (%Rrc)"),
647 strConfigFile.c_str(),
648 vrc1);
649
650 LogFlowThisFuncLeave();
651
652 return rc;
653}
654
655/**
656 * Tries to create a machine settings file in the path stored in the machine
657 * instance data. Used when a new machine is created to fail gracefully if
658 * the settings file could not be written (e.g. because machine dir is read-only).
659 * @return
660 */
661HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
662{
663 HRESULT rc = S_OK;
664
665 // when we create a new machine, we must be able to create the settings file
666 RTFILE f = NIL_RTFILE;
667 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
668 if ( RT_SUCCESS(vrc)
669 || vrc == VERR_SHARING_VIOLATION
670 )
671 {
672 if (RT_SUCCESS(vrc))
673 RTFileClose(f);
674 if (!fForceOverwrite)
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Machine settings file '%s' already exists"),
677 mData->m_strConfigFileFull.c_str());
678 else
679 {
680 /* try to delete the config file, as otherwise the creation
681 * of a new settings file will fail. */
682 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
683 if (RT_FAILURE(vrc2))
684 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
685 tr("Could not delete the existing settings file '%s' (%Rrc)"),
686 mData->m_strConfigFileFull.c_str(), vrc2);
687 }
688 }
689 else if ( vrc != VERR_FILE_NOT_FOUND
690 && vrc != VERR_PATH_NOT_FOUND
691 )
692 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
693 tr("Invalid machine settings file name '%s' (%Rrc)"),
694 mData->m_strConfigFileFull.c_str(),
695 vrc);
696 return rc;
697}
698
699/**
700 * Initializes the registered machine by loading the settings file.
701 * This method is separated from #init() in order to make it possible to
702 * retry the operation after VirtualBox startup instead of refusing to
703 * startup the whole VirtualBox server in case if the settings file of some
704 * registered VM is invalid or inaccessible.
705 *
706 * @note Must be always called from this object's write lock
707 * (unless called from #init() that doesn't need any locking).
708 * @note Locks the mUSBController method for writing.
709 * @note Subclasses must not call this method.
710 */
711HRESULT Machine::i_registeredInit()
712{
713 AssertReturn(!i_isSessionMachine(), E_FAIL);
714 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
715 AssertReturn(mData->mUuid.isValid(), E_FAIL);
716 AssertReturn(!mData->mAccessible, E_FAIL);
717
718 HRESULT rc = initDataAndChildObjects();
719
720 if (SUCCEEDED(rc))
721 {
722 /* Temporarily reset the registered flag in order to let setters
723 * potentially called from loadSettings() succeed (isMutable() used in
724 * all setters will return FALSE for a Machine instance if mRegistered
725 * is TRUE). */
726 mData->mRegistered = FALSE;
727
728 try
729 {
730 // load and parse machine XML; this will throw on XML or logic errors
731 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
732
733 if (mData->mUuid != mData->pMachineConfigFile->uuid)
734 throw setError(E_FAIL,
735 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
736 mData->pMachineConfigFile->uuid.raw(),
737 mData->m_strConfigFileFull.c_str(),
738 mData->mUuid.toString().c_str(),
739 mParent->i_settingsFilePath().c_str());
740
741 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
742 NULL /* const Guid *puuidRegistry */);
743 if (FAILED(rc)) throw rc;
744 }
745 catch (HRESULT err)
746 {
747 /* we assume that error info is set by the thrower */
748 rc = err;
749 }
750 catch (...)
751 {
752 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
753 }
754
755 /* Restore the registered flag (even on failure) */
756 mData->mRegistered = TRUE;
757 }
758
759 if (SUCCEEDED(rc))
760 {
761 /* Set mAccessible to TRUE only if we successfully locked and loaded
762 * the settings file */
763 mData->mAccessible = TRUE;
764
765 /* commit all changes made during loading the settings file */
766 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
767 /// @todo r=klaus for some reason the settings loading logic backs up
768 // the settings, and therefore a commit is needed. Should probably be changed.
769 }
770 else
771 {
772 /* If the machine is registered, then, instead of returning a
773 * failure, we mark it as inaccessible and set the result to
774 * success to give it a try later */
775
776 /* fetch the current error info */
777 mData->mAccessError = com::ErrorInfo();
778 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
779
780 /* rollback all changes */
781 i_rollback(false /* aNotify */);
782
783 // uninit media from this machine's media registry, or else
784 // reloading the settings will fail
785 mParent->i_unregisterMachineMedia(i_getId());
786
787 /* uninitialize the common part to make sure all data is reset to
788 * default (null) values */
789 uninitDataAndChildObjects();
790
791 rc = S_OK;
792 }
793
794 return rc;
795}
796
797/**
798 * Uninitializes the instance.
799 * Called either from FinalRelease() or by the parent when it gets destroyed.
800 *
801 * @note The caller of this method must make sure that this object
802 * a) doesn't have active callers on the current thread and b) is not locked
803 * by the current thread; otherwise uninit() will hang either a) due to
804 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
805 * a dead-lock caused by this thread waiting for all callers on the other
806 * threads are done but preventing them from doing so by holding a lock.
807 */
808void Machine::uninit()
809{
810 LogFlowThisFuncEnter();
811
812 Assert(!isWriteLockOnCurrentThread());
813
814 Assert(!uRegistryNeedsSaving);
815 if (uRegistryNeedsSaving)
816 {
817 AutoCaller autoCaller(this);
818 if (SUCCEEDED(autoCaller.rc()))
819 {
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821 i_saveSettings(NULL, Machine::SaveS_Force);
822 }
823 }
824
825 /* Enclose the state transition Ready->InUninit->NotReady */
826 AutoUninitSpan autoUninitSpan(this);
827 if (autoUninitSpan.uninitDone())
828 return;
829
830 Assert(!i_isSnapshotMachine());
831 Assert(!i_isSessionMachine());
832 Assert(!!mData);
833
834 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
835 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
836
837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
838
839 if (!mData->mSession.mMachine.isNull())
840 {
841 /* Theoretically, this can only happen if the VirtualBox server has been
842 * terminated while there were clients running that owned open direct
843 * sessions. Since in this case we are definitely called by
844 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
845 * won't happen on the client watcher thread (because it has a
846 * VirtualBox caller for the duration of the
847 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
848 * cannot happen until the VirtualBox caller is released). This is
849 * important, because SessionMachine::uninit() cannot correctly operate
850 * after we return from this method (it expects the Machine instance is
851 * still valid). We'll call it ourselves below.
852 */
853 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
854 (SessionMachine*)mData->mSession.mMachine));
855
856 if (Global::IsOnlineOrTransient(mData->mMachineState))
857 {
858 Log1WarningThisFunc(("Setting state to Aborted!\n"));
859 /* set machine state using SessionMachine reimplementation */
860 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
861 }
862
863 /*
864 * Uninitialize SessionMachine using public uninit() to indicate
865 * an unexpected uninitialization.
866 */
867 mData->mSession.mMachine->uninit();
868 /* SessionMachine::uninit() must set mSession.mMachine to null */
869 Assert(mData->mSession.mMachine.isNull());
870 }
871
872 // uninit media from this machine's media registry, if they're still there
873 Guid uuidMachine(i_getId());
874
875 /* the lock is no more necessary (SessionMachine is uninitialized) */
876 alock.release();
877
878 /* XXX This will fail with
879 * "cannot be closed because it is still attached to 1 virtual machines"
880 * because at this point we did not call uninitDataAndChildObjects() yet
881 * and therefore also removeBackReference() for all these mediums was not called! */
882
883 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
884 mParent->i_unregisterMachineMedia(uuidMachine);
885
886 // has machine been modified?
887 if (mData->flModifications)
888 {
889 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
890 i_rollback(false /* aNotify */);
891 }
892
893 if (mData->mAccessible)
894 uninitDataAndChildObjects();
895
896 /* free the essential data structure last */
897 mData.free();
898
899 LogFlowThisFuncLeave();
900}
901
902// Wrapped IMachine properties
903/////////////////////////////////////////////////////////////////////////////
904HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
905{
906 /* mParent is constant during life time, no need to lock */
907 ComObjPtr<VirtualBox> pVirtualBox(mParent);
908 aParent = pVirtualBox;
909
910 return S_OK;
911}
912
913
914HRESULT Machine::getAccessible(BOOL *aAccessible)
915{
916 /* In some cases (medium registry related), it is necessary to be able to
917 * go through the list of all machines. Happens when an inaccessible VM
918 * has a sensible medium registry. */
919 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
921
922 HRESULT rc = S_OK;
923
924 if (!mData->mAccessible)
925 {
926 /* try to initialize the VM once more if not accessible */
927
928 AutoReinitSpan autoReinitSpan(this);
929 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
930
931#ifdef DEBUG
932 LogFlowThisFunc(("Dumping media backreferences\n"));
933 mParent->i_dumpAllBackRefs();
934#endif
935
936 if (mData->pMachineConfigFile)
937 {
938 // reset the XML file to force loadSettings() (called from i_registeredInit())
939 // to parse it again; the file might have changed
940 delete mData->pMachineConfigFile;
941 mData->pMachineConfigFile = NULL;
942 }
943
944 rc = i_registeredInit();
945
946 if (SUCCEEDED(rc) && mData->mAccessible)
947 {
948 autoReinitSpan.setSucceeded();
949
950 /* make sure interesting parties will notice the accessibility
951 * state change */
952 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
953 mParent->i_onMachineDataChange(mData->mUuid);
954 }
955 }
956
957 if (SUCCEEDED(rc))
958 *aAccessible = mData->mAccessible;
959
960 LogFlowThisFuncLeave();
961
962 return rc;
963}
964
965HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
966{
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 aAccessError = errorInfo;
987 }
988
989 return rc;
990}
991
992HRESULT Machine::getName(com::Utf8Str &aName)
993{
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 aName = mUserData->s.strName;
997
998 return S_OK;
999}
1000
1001HRESULT Machine::setName(const com::Utf8Str &aName)
1002{
1003 // prohibit setting a UUID only as the machine name, or else it can
1004 // never be found by findMachine()
1005 Guid test(aName);
1006
1007 if (test.isValid())
1008 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1009
1010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 HRESULT rc = i_checkStateDependency(MutableStateDep);
1013 if (FAILED(rc)) return rc;
1014
1015 i_setModified(IsModified_MachineData);
1016 mUserData.backup();
1017 mUserData->s.strName = aName;
1018
1019 return S_OK;
1020}
1021
1022HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1023{
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 aDescription = mUserData->s.strDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1032{
1033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 // this can be done in principle in any state as it doesn't affect the VM
1036 // significantly, but play safe by not messing around while complex
1037 // activities are going on
1038 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1039 if (FAILED(rc)) return rc;
1040
1041 i_setModified(IsModified_MachineData);
1042 mUserData.backup();
1043 mUserData->s.strDescription = aDescription;
1044
1045 return S_OK;
1046}
1047
1048HRESULT Machine::getId(com::Guid &aId)
1049{
1050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 aId = mData->mUuid;
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1058{
1059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1060 aGroups.resize(mUserData->s.llGroups.size());
1061 size_t i = 0;
1062 for (StringsList::const_iterator
1063 it = mUserData->s.llGroups.begin();
1064 it != mUserData->s.llGroups.end();
1065 ++it, ++i)
1066 aGroups[i] = (*it);
1067
1068 return S_OK;
1069}
1070
1071HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1072{
1073 StringsList llGroups;
1074 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1075 if (FAILED(rc))
1076 return rc;
1077
1078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 rc = i_checkStateDependency(MutableOrSavedStateDep);
1081 if (FAILED(rc)) return rc;
1082
1083 i_setModified(IsModified_MachineData);
1084 mUserData.backup();
1085 mUserData->s.llGroups = llGroups;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1091{
1092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1093
1094 aOSTypeId = mUserData->s.strOsType;
1095
1096 return S_OK;
1097}
1098
1099HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1100{
1101 /* look up the object by Id to check it is valid */
1102 ComObjPtr<GuestOSType> pGuestOSType;
1103 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1104
1105 /* when setting, always use the "etalon" value for consistency -- lookup
1106 * by ID is case-insensitive and the input value may have different case */
1107 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1108
1109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 HRESULT rc = i_checkStateDependency(MutableStateDep);
1112 if (FAILED(rc)) return rc;
1113
1114 i_setModified(IsModified_MachineData);
1115 mUserData.backup();
1116 mUserData->s.strOsType = osTypeId;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1122{
1123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 *aFirmwareType = mHWData->mFirmwareType;
1126
1127 return S_OK;
1128}
1129
1130HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1131{
1132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 HRESULT rc = i_checkStateDependency(MutableStateDep);
1135 if (FAILED(rc)) return rc;
1136
1137 i_setModified(IsModified_MachineData);
1138 mHWData.backup();
1139 mHWData->mFirmwareType = aFirmwareType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1145{
1146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1149
1150 return S_OK;
1151}
1152
1153HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1154{
1155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 HRESULT rc = i_checkStateDependency(MutableStateDep);
1158 if (FAILED(rc)) return rc;
1159
1160 i_setModified(IsModified_MachineData);
1161 mHWData.backup();
1162 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1168{
1169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 *aPointingHIDType = mHWData->mPointingHIDType;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1177{
1178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 HRESULT rc = i_checkStateDependency(MutableStateDep);
1181 if (FAILED(rc)) return rc;
1182
1183 i_setModified(IsModified_MachineData);
1184 mHWData.backup();
1185 mHWData->mPointingHIDType = aPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1191{
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aChipsetType = mHWData->mChipsetType;
1195
1196 return S_OK;
1197}
1198
1199HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1200{
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 HRESULT rc = i_checkStateDependency(MutableStateDep);
1204 if (FAILED(rc)) return rc;
1205
1206 if (aChipsetType != mHWData->mChipsetType)
1207 {
1208 i_setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mChipsetType = aChipsetType;
1211
1212 // Resize network adapter array, to be finalized on commit/rollback.
1213 // We must not throw away entries yet, otherwise settings are lost
1214 // without a way to roll back.
1215 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1216 size_t oldCount = mNetworkAdapters.size();
1217 if (newCount > oldCount)
1218 {
1219 mNetworkAdapters.resize(newCount);
1220 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1221 {
1222 unconst(mNetworkAdapters[slot]).createObject();
1223 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1224 }
1225 }
1226 }
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1232{
1233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 aParavirtDebug = mHWData->mParavirtDebug;
1236 return S_OK;
1237}
1238
1239HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1240{
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = i_checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 /** @todo Parse/validate options? */
1247 if (aParavirtDebug != mHWData->mParavirtDebug)
1248 {
1249 i_setModified(IsModified_MachineData);
1250 mHWData.backup();
1251 mHWData->mParavirtDebug = aParavirtDebug;
1252 }
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1258{
1259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 *aParavirtProvider = mHWData->mParavirtProvider;
1262
1263 return S_OK;
1264}
1265
1266HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1267{
1268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 HRESULT rc = i_checkStateDependency(MutableStateDep);
1271 if (FAILED(rc)) return rc;
1272
1273 if (aParavirtProvider != mHWData->mParavirtProvider)
1274 {
1275 i_setModified(IsModified_MachineData);
1276 mHWData.backup();
1277 mHWData->mParavirtProvider = aParavirtProvider;
1278 }
1279
1280 return S_OK;
1281}
1282
1283HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1284{
1285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1286
1287 *aParavirtProvider = mHWData->mParavirtProvider;
1288 switch (mHWData->mParavirtProvider)
1289 {
1290 case ParavirtProvider_None:
1291 case ParavirtProvider_HyperV:
1292 case ParavirtProvider_KVM:
1293 case ParavirtProvider_Minimal:
1294 break;
1295
1296 /* Resolve dynamic provider types to the effective types. */
1297 default:
1298 {
1299 ComObjPtr<GuestOSType> pGuestOSType;
1300 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1301 pGuestOSType);
1302 if (FAILED(hrc2) || pGuestOSType.isNull())
1303 {
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1309 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1310
1311 switch (mHWData->mParavirtProvider)
1312 {
1313 case ParavirtProvider_Legacy:
1314 {
1315 if (fOsXGuest)
1316 *aParavirtProvider = ParavirtProvider_Minimal;
1317 else
1318 *aParavirtProvider = ParavirtProvider_None;
1319 break;
1320 }
1321
1322 case ParavirtProvider_Default:
1323 {
1324 if (fOsXGuest)
1325 *aParavirtProvider = ParavirtProvider_Minimal;
1326 else if ( mUserData->s.strOsType == "Windows10"
1327 || mUserData->s.strOsType == "Windows10_64"
1328 || mUserData->s.strOsType == "Windows81"
1329 || mUserData->s.strOsType == "Windows81_64"
1330 || mUserData->s.strOsType == "Windows8"
1331 || mUserData->s.strOsType == "Windows8_64"
1332 || mUserData->s.strOsType == "Windows7"
1333 || mUserData->s.strOsType == "Windows7_64"
1334 || mUserData->s.strOsType == "WindowsVista"
1335 || mUserData->s.strOsType == "WindowsVista_64"
1336 || mUserData->s.strOsType == "Windows2012"
1337 || mUserData->s.strOsType == "Windows2012_64"
1338 || mUserData->s.strOsType == "Windows2008"
1339 || mUserData->s.strOsType == "Windows2008_64")
1340 {
1341 *aParavirtProvider = ParavirtProvider_HyperV;
1342 }
1343 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1344 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1345 || mUserData->s.strOsType == "Linux"
1346 || mUserData->s.strOsType == "Linux_64"
1347 || mUserData->s.strOsType == "ArchLinux"
1348 || mUserData->s.strOsType == "ArchLinux_64"
1349 || mUserData->s.strOsType == "Debian"
1350 || mUserData->s.strOsType == "Debian_64"
1351 || mUserData->s.strOsType == "Fedora"
1352 || mUserData->s.strOsType == "Fedora_64"
1353 || mUserData->s.strOsType == "Gentoo"
1354 || mUserData->s.strOsType == "Gentoo_64"
1355 || mUserData->s.strOsType == "Mandriva"
1356 || mUserData->s.strOsType == "Mandriva_64"
1357 || mUserData->s.strOsType == "OpenSUSE"
1358 || mUserData->s.strOsType == "OpenSUSE_64"
1359 || mUserData->s.strOsType == "Oracle"
1360 || mUserData->s.strOsType == "Oracle_64"
1361 || mUserData->s.strOsType == "RedHat"
1362 || mUserData->s.strOsType == "RedHat_64"
1363 || mUserData->s.strOsType == "Turbolinux"
1364 || mUserData->s.strOsType == "Turbolinux_64"
1365 || mUserData->s.strOsType == "Ubuntu"
1366 || mUserData->s.strOsType == "Ubuntu_64"
1367 || mUserData->s.strOsType == "Xandros"
1368 || mUserData->s.strOsType == "Xandros_64")
1369 {
1370 *aParavirtProvider = ParavirtProvider_KVM;
1371 }
1372 else
1373 *aParavirtProvider = ParavirtProvider_None;
1374 break;
1375 }
1376
1377 default: AssertFailedBreak(); /* Shut up MSC. */
1378 }
1379 break;
1380 }
1381 }
1382
1383 Assert( *aParavirtProvider == ParavirtProvider_None
1384 || *aParavirtProvider == ParavirtProvider_Minimal
1385 || *aParavirtProvider == ParavirtProvider_HyperV
1386 || *aParavirtProvider == ParavirtProvider_KVM);
1387 return S_OK;
1388}
1389
1390HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1391{
1392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 aHardwareVersion = mHWData->mHWVersion;
1395
1396 return S_OK;
1397}
1398
1399HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1400{
1401 /* check known version */
1402 Utf8Str hwVersion = aHardwareVersion;
1403 if ( hwVersion.compare("1") != 0
1404 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1405 return setError(E_INVALIDARG,
1406 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = i_checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 i_setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mHWVersion = aHardwareVersion;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1421{
1422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1423
1424 if (!mHWData->mHardwareUUID.isZero())
1425 aHardwareUUID = mHWData->mHardwareUUID;
1426 else
1427 aHardwareUUID = mData->mUuid;
1428
1429 return S_OK;
1430}
1431
1432HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1433{
1434 if (!aHardwareUUID.isValid())
1435 return E_INVALIDARG;
1436
1437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 HRESULT rc = i_checkStateDependency(MutableStateDep);
1440 if (FAILED(rc)) return rc;
1441
1442 i_setModified(IsModified_MachineData);
1443 mHWData.backup();
1444 if (aHardwareUUID == mData->mUuid)
1445 mHWData->mHardwareUUID.clear();
1446 else
1447 mHWData->mHardwareUUID = aHardwareUUID;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aMemorySize = mHWData->mMemorySize;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setMemorySize(ULONG aMemorySize)
1462{
1463 /* check RAM limits */
1464 if ( aMemorySize < MM_RAM_MIN_IN_MB
1465 || aMemorySize > MM_RAM_MAX_IN_MB
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1469 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 HRESULT rc = i_checkStateDependency(MutableStateDep);
1474 if (FAILED(rc)) return rc;
1475
1476 i_setModified(IsModified_MachineData);
1477 mHWData.backup();
1478 mHWData->mMemorySize = aMemorySize;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1484{
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aCPUCount = mHWData->mCPUCount;
1488
1489 return S_OK;
1490}
1491
1492HRESULT Machine::setCPUCount(ULONG aCPUCount)
1493{
1494 /* check CPU limits */
1495 if ( aCPUCount < SchemaDefs::MinCPUCount
1496 || aCPUCount > SchemaDefs::MaxCPUCount
1497 )
1498 return setError(E_INVALIDARG,
1499 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1500 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1505 if (mHWData->mCPUHotPlugEnabled)
1506 {
1507 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1508 {
1509 if (mHWData->mCPUAttached[idx])
1510 return setError(E_INVALIDARG,
1511 tr("There is still a CPU attached to socket %lu."
1512 "Detach the CPU before removing the socket"),
1513 aCPUCount, idx+1);
1514 }
1515 }
1516
1517 HRESULT rc = i_checkStateDependency(MutableStateDep);
1518 if (FAILED(rc)) return rc;
1519
1520 i_setModified(IsModified_MachineData);
1521 mHWData.backup();
1522 mHWData->mCPUCount = aCPUCount;
1523
1524 return S_OK;
1525}
1526
1527HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1528{
1529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1530
1531 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1532
1533 return S_OK;
1534}
1535
1536HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1537{
1538 HRESULT rc = S_OK;
1539
1540 /* check throttle limits */
1541 if ( aCPUExecutionCap < 1
1542 || aCPUExecutionCap > 100
1543 )
1544 return setError(E_INVALIDARG,
1545 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1546 aCPUExecutionCap, 1, 100);
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 alock.release();
1551 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1552 alock.acquire();
1553 if (FAILED(rc)) return rc;
1554
1555 i_setModified(IsModified_MachineData);
1556 mHWData.backup();
1557 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1558
1559 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1560 if (Global::IsOnline(mData->mMachineState))
1561 i_saveSettings(NULL);
1562
1563 return S_OK;
1564}
1565
1566HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1567{
1568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1571
1572 return S_OK;
1573}
1574
1575HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1576{
1577 HRESULT rc = S_OK;
1578
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 rc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(rc)) return rc;
1583
1584 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1585 {
1586 if (aCPUHotPlugEnabled)
1587 {
1588 i_setModified(IsModified_MachineData);
1589 mHWData.backup();
1590
1591 /* Add the amount of CPUs currently attached */
1592 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1593 mHWData->mCPUAttached[i] = true;
1594 }
1595 else
1596 {
1597 /*
1598 * We can disable hotplug only if the amount of maximum CPUs is equal
1599 * to the amount of attached CPUs
1600 */
1601 unsigned cCpusAttached = 0;
1602 unsigned iHighestId = 0;
1603
1604 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1605 {
1606 if (mHWData->mCPUAttached[i])
1607 {
1608 cCpusAttached++;
1609 iHighestId = i;
1610 }
1611 }
1612
1613 if ( (cCpusAttached != mHWData->mCPUCount)
1614 || (iHighestId >= mHWData->mCPUCount))
1615 return setError(E_INVALIDARG,
1616 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1617
1618 i_setModified(IsModified_MachineData);
1619 mHWData.backup();
1620 }
1621 }
1622
1623 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1624
1625 return rc;
1626}
1627
1628HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1629{
1630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1631
1632 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1633
1634 return S_OK;
1635}
1636
1637HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1638{
1639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1642 if (SUCCEEDED(hrc))
1643 {
1644 i_setModified(IsModified_MachineData);
1645 mHWData.backup();
1646 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1647 }
1648 return hrc;
1649}
1650
1651HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1652{
1653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1654 aCPUProfile = mHWData->mCpuProfile;
1655 return S_OK;
1656}
1657
1658HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1659{
1660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1661 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1662 if (SUCCEEDED(hrc))
1663 {
1664 i_setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 /* Empty equals 'host'. */
1667 if (aCPUProfile.isNotEmpty())
1668 mHWData->mCpuProfile = aCPUProfile;
1669 else
1670 mHWData->mCpuProfile = "host";
1671 }
1672 return hrc;
1673}
1674
1675HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1681
1682 return S_OK;
1683#else
1684 NOREF(aEmulatedUSBCardReaderEnabled);
1685 return E_NOTIMPL;
1686#endif
1687}
1688
1689HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1690{
1691#ifdef VBOX_WITH_USB_CARDREADER
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 i_setModified(IsModified_MachineData);
1698 mHWData.backup();
1699 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1700
1701 return S_OK;
1702#else
1703 NOREF(aEmulatedUSBCardReaderEnabled);
1704 return E_NOTIMPL;
1705#endif
1706}
1707
1708HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1709{
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *aHPETEnabled = mHWData->mHPETEnabled;
1713
1714 return S_OK;
1715}
1716
1717HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1718{
1719 HRESULT rc = S_OK;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 rc = i_checkStateDependency(MutableStateDep);
1724 if (FAILED(rc)) return rc;
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728
1729 mHWData->mHPETEnabled = aHPETEnabled;
1730
1731 return rc;
1732}
1733
1734HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1739
1740 return S_OK;
1741}
1742
1743HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1744{
1745 switch (aGraphicsControllerType)
1746 {
1747 case GraphicsControllerType_Null:
1748 case GraphicsControllerType_VBoxVGA:
1749#ifdef VBOX_WITH_VMSVGA
1750 case GraphicsControllerType_VMSVGA:
1751 case GraphicsControllerType_VBoxSVGA:
1752#endif
1753 break;
1754 default:
1755 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1756 }
1757
1758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1759
1760 HRESULT rc = i_checkStateDependency(MutableStateDep);
1761 if (FAILED(rc)) return rc;
1762
1763 i_setModified(IsModified_MachineData);
1764 mHWData.backup();
1765 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1766
1767 return S_OK;
1768}
1769
1770HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1771{
1772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 *aVRAMSize = mHWData->mVRAMSize;
1775
1776 return S_OK;
1777}
1778
1779HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1780{
1781 /* check VRAM limits */
1782 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1783 return setError(E_INVALIDARG,
1784 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1785 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 HRESULT rc = i_checkStateDependency(MutableStateDep);
1790 if (FAILED(rc)) return rc;
1791
1792 i_setModified(IsModified_MachineData);
1793 mHWData.backup();
1794 mHWData->mVRAMSize = aVRAMSize;
1795
1796 return S_OK;
1797}
1798
1799/** @todo this method should not be public */
1800HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1805
1806 return S_OK;
1807}
1808
1809/**
1810 * Set the memory balloon size.
1811 *
1812 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1813 * we have to make sure that we never call IGuest from here.
1814 */
1815HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1816{
1817 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1818#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1819 /* check limits */
1820 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1821 return setError(E_INVALIDARG,
1822 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1823 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1824
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1830
1831 return S_OK;
1832#else
1833 NOREF(aMemoryBalloonSize);
1834 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1835#endif
1836}
1837
1838HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1839{
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1843 return S_OK;
1844}
1845
1846HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1847{
1848#ifdef VBOX_WITH_PAGE_SHARING
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1855 return S_OK;
1856#else
1857 NOREF(aPageFusionEnabled);
1858 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1859#endif
1860}
1861
1862HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1867
1868 return S_OK;
1869}
1870
1871HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1872{
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 HRESULT rc = i_checkStateDependency(MutableStateDep);
1876 if (FAILED(rc)) return rc;
1877
1878 /** @todo check validity! */
1879
1880 i_setModified(IsModified_MachineData);
1881 mHWData.backup();
1882 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1883
1884 return S_OK;
1885}
1886
1887
1888HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1889{
1890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1893
1894 return S_OK;
1895}
1896
1897HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1898{
1899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 HRESULT rc = i_checkStateDependency(MutableStateDep);
1902 if (FAILED(rc)) return rc;
1903
1904 /** @todo check validity! */
1905 i_setModified(IsModified_MachineData);
1906 mHWData.backup();
1907 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1908
1909 return S_OK;
1910}
1911
1912HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1913{
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915
1916 *aMonitorCount = mHWData->mMonitorCount;
1917
1918 return S_OK;
1919}
1920
1921HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1922{
1923 /* make sure monitor count is a sensible number */
1924 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1925 return setError(E_INVALIDARG,
1926 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1927 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1928
1929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1930
1931 HRESULT rc = i_checkStateDependency(MutableStateDep);
1932 if (FAILED(rc)) return rc;
1933
1934 i_setModified(IsModified_MachineData);
1935 mHWData.backup();
1936 mHWData->mMonitorCount = aMonitorCount;
1937
1938 return S_OK;
1939}
1940
1941HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1942{
1943 /* mBIOSSettings is constant during life time, no need to lock */
1944 aBIOSSettings = mBIOSSettings;
1945
1946 return S_OK;
1947}
1948
1949HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1950{
1951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1952
1953 aRecordingSettings = mRecordingSettings;
1954
1955 return S_OK;
1956}
1957
1958HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1959{
1960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 switch (aProperty)
1963 {
1964 case CPUPropertyType_PAE:
1965 *aValue = mHWData->mPAEEnabled;
1966 break;
1967
1968 case CPUPropertyType_LongMode:
1969 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1970 *aValue = TRUE;
1971 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1972 *aValue = FALSE;
1973#if HC_ARCH_BITS == 64
1974 else
1975 *aValue = TRUE;
1976#else
1977 else
1978 {
1979 *aValue = FALSE;
1980
1981 ComObjPtr<GuestOSType> pGuestOSType;
1982 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1983 pGuestOSType);
1984 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1985 {
1986 if (pGuestOSType->i_is64Bit())
1987 {
1988 ComObjPtr<Host> pHost = mParent->i_host();
1989 alock.release();
1990
1991 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1992 if (FAILED(hrc2))
1993 *aValue = FALSE;
1994 }
1995 }
1996 }
1997#endif
1998 break;
1999
2000 case CPUPropertyType_TripleFaultReset:
2001 *aValue = mHWData->mTripleFaultReset;
2002 break;
2003
2004 case CPUPropertyType_APIC:
2005 *aValue = mHWData->mAPIC;
2006 break;
2007
2008 case CPUPropertyType_X2APIC:
2009 *aValue = mHWData->mX2APIC;
2010 break;
2011
2012 case CPUPropertyType_IBPBOnVMExit:
2013 *aValue = mHWData->mIBPBOnVMExit;
2014 break;
2015
2016 case CPUPropertyType_IBPBOnVMEntry:
2017 *aValue = mHWData->mIBPBOnVMEntry;
2018 break;
2019
2020 case CPUPropertyType_SpecCtrl:
2021 *aValue = mHWData->mSpecCtrl;
2022 break;
2023
2024 case CPUPropertyType_SpecCtrlByHost:
2025 *aValue = mHWData->mSpecCtrlByHost;
2026 break;
2027
2028 case CPUPropertyType_HWVirt:
2029 *aValue = mHWData->mNestedHWVirt;
2030 break;
2031
2032 case CPUPropertyType_L1DFlushOnEMTScheduling:
2033 *aValue = mHWData->mL1DFlushOnSched;
2034 break;
2035
2036 case CPUPropertyType_L1DFlushOnVMEntry:
2037 *aValue = mHWData->mL1DFlushOnVMEntry;
2038 break;
2039
2040 case CPUPropertyType_MDSClearOnEMTScheduling:
2041 *aValue = mHWData->mMDSClearOnSched;
2042 break;
2043
2044 case CPUPropertyType_MDSClearOnVMEntry:
2045 *aValue = mHWData->mMDSClearOnVMEntry;
2046 break;
2047
2048 default:
2049 return E_INVALIDARG;
2050 }
2051 return S_OK;
2052}
2053
2054HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2055{
2056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 HRESULT rc = i_checkStateDependency(MutableStateDep);
2059 if (FAILED(rc)) return rc;
2060
2061 switch (aProperty)
2062 {
2063 case CPUPropertyType_PAE:
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mPAEEnabled = !!aValue;
2067 break;
2068
2069 case CPUPropertyType_LongMode:
2070 i_setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2073 break;
2074
2075 case CPUPropertyType_TripleFaultReset:
2076 i_setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mTripleFaultReset = !!aValue;
2079 break;
2080
2081 case CPUPropertyType_APIC:
2082 if (mHWData->mX2APIC)
2083 aValue = TRUE;
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mAPIC = !!aValue;
2087 break;
2088
2089 case CPUPropertyType_X2APIC:
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mX2APIC = !!aValue;
2093 if (aValue)
2094 mHWData->mAPIC = !!aValue;
2095 break;
2096
2097 case CPUPropertyType_IBPBOnVMExit:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mIBPBOnVMExit = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_IBPBOnVMEntry:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mIBPBOnVMEntry = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_SpecCtrl:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mSpecCtrl = !!aValue;
2113 break;
2114
2115 case CPUPropertyType_SpecCtrlByHost:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mSpecCtrlByHost = !!aValue;
2119 break;
2120
2121 case CPUPropertyType_HWVirt:
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mNestedHWVirt = !!aValue;
2125 break;
2126
2127 case CPUPropertyType_L1DFlushOnEMTScheduling:
2128 i_setModified(IsModified_MachineData);
2129 mHWData.backup();
2130 mHWData->mL1DFlushOnSched = !!aValue;
2131 break;
2132
2133 case CPUPropertyType_L1DFlushOnVMEntry:
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mL1DFlushOnVMEntry = !!aValue;
2137 break;
2138
2139 case CPUPropertyType_MDSClearOnEMTScheduling:
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mMDSClearOnSched = !!aValue;
2143 break;
2144
2145 case CPUPropertyType_MDSClearOnVMEntry:
2146 i_setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mMDSClearOnVMEntry = !!aValue;
2149 break;
2150
2151 default:
2152 return E_INVALIDARG;
2153 }
2154 return S_OK;
2155}
2156
2157HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2158 ULONG *aValEcx, ULONG *aValEdx)
2159{
2160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2161 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2162 {
2163 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2164 it != mHWData->mCpuIdLeafList.end();
2165 ++it)
2166 {
2167 if (aOrdinal == 0)
2168 {
2169 const settings::CpuIdLeaf &rLeaf= *it;
2170 *aIdx = rLeaf.idx;
2171 *aSubIdx = rLeaf.idxSub;
2172 *aValEax = rLeaf.uEax;
2173 *aValEbx = rLeaf.uEbx;
2174 *aValEcx = rLeaf.uEcx;
2175 *aValEdx = rLeaf.uEdx;
2176 return S_OK;
2177 }
2178 aOrdinal--;
2179 }
2180 }
2181 return E_INVALIDARG;
2182}
2183
2184HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2185{
2186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 /*
2189 * Search the list.
2190 */
2191 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2192 {
2193 const settings::CpuIdLeaf &rLeaf= *it;
2194 if ( rLeaf.idx == aIdx
2195 && ( aSubIdx == UINT32_MAX
2196 || rLeaf.idxSub == aSubIdx) )
2197 {
2198 *aValEax = rLeaf.uEax;
2199 *aValEbx = rLeaf.uEbx;
2200 *aValEcx = rLeaf.uEcx;
2201 *aValEdx = rLeaf.uEdx;
2202 return S_OK;
2203 }
2204 }
2205
2206 return E_INVALIDARG;
2207}
2208
2209
2210HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2211{
2212 /*
2213 * Validate input before taking locks and checking state.
2214 */
2215 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2216 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2217 if ( aIdx >= UINT32_C(0x20)
2218 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2219 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2220 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2221
2222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2223 HRESULT rc = i_checkStateDependency(MutableStateDep);
2224 if (FAILED(rc)) return rc;
2225
2226 /*
2227 * Impose a maximum number of leaves.
2228 */
2229 if (mHWData->mCpuIdLeafList.size() > 256)
2230 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2231
2232 /*
2233 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2234 */
2235 i_setModified(IsModified_MachineData);
2236 mHWData.backup();
2237
2238 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2239 {
2240 settings::CpuIdLeaf &rLeaf= *it;
2241 if ( rLeaf.idx == aIdx
2242 && ( aSubIdx == UINT32_MAX
2243 || rLeaf.idxSub == aSubIdx) )
2244 it = mHWData->mCpuIdLeafList.erase(it);
2245 else
2246 ++it;
2247 }
2248
2249 settings::CpuIdLeaf NewLeaf;
2250 NewLeaf.idx = aIdx;
2251 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2252 NewLeaf.uEax = aValEax;
2253 NewLeaf.uEbx = aValEbx;
2254 NewLeaf.uEcx = aValEcx;
2255 NewLeaf.uEdx = aValEdx;
2256 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2257 return S_OK;
2258}
2259
2260HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2261{
2262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2263
2264 HRESULT rc = i_checkStateDependency(MutableStateDep);
2265 if (FAILED(rc)) return rc;
2266
2267 /*
2268 * Do the removal.
2269 */
2270 bool fModified = mHWData.isBackedUp();
2271 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2272 {
2273 settings::CpuIdLeaf &rLeaf= *it;
2274 if ( rLeaf.idx == aIdx
2275 && ( aSubIdx == UINT32_MAX
2276 || rLeaf.idxSub == aSubIdx) )
2277 {
2278 if (!fModified)
2279 {
2280 fModified = true;
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 // Start from the beginning, since mHWData.backup() creates
2284 // a new list, causing iterator mixup. This makes sure that
2285 // the settings are not unnecessarily marked as modified,
2286 // at the price of extra list walking.
2287 it = mHWData->mCpuIdLeafList.begin();
2288 }
2289 else
2290 it = mHWData->mCpuIdLeafList.erase(it);
2291 }
2292 else
2293 ++it;
2294 }
2295
2296 return S_OK;
2297}
2298
2299HRESULT Machine::removeAllCPUIDLeaves()
2300{
2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 HRESULT rc = i_checkStateDependency(MutableStateDep);
2304 if (FAILED(rc)) return rc;
2305
2306 if (mHWData->mCpuIdLeafList.size() > 0)
2307 {
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310
2311 mHWData->mCpuIdLeafList.clear();
2312 }
2313
2314 return S_OK;
2315}
2316HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2317{
2318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 switch(aProperty)
2321 {
2322 case HWVirtExPropertyType_Enabled:
2323 *aValue = mHWData->mHWVirtExEnabled;
2324 break;
2325
2326 case HWVirtExPropertyType_VPID:
2327 *aValue = mHWData->mHWVirtExVPIDEnabled;
2328 break;
2329
2330 case HWVirtExPropertyType_NestedPaging:
2331 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2332 break;
2333
2334 case HWVirtExPropertyType_UnrestrictedExecution:
2335 *aValue = mHWData->mHWVirtExUXEnabled;
2336 break;
2337
2338 case HWVirtExPropertyType_LargePages:
2339 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2340#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2341 *aValue = FALSE;
2342#endif
2343 break;
2344
2345 case HWVirtExPropertyType_Force:
2346 *aValue = mHWData->mHWVirtExForceEnabled;
2347 break;
2348
2349 case HWVirtExPropertyType_UseNativeApi:
2350 *aValue = mHWData->mHWVirtExUseNativeApi;
2351 break;
2352
2353 default:
2354 return E_INVALIDARG;
2355 }
2356 return S_OK;
2357}
2358
2359HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2360{
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 HRESULT rc = i_checkStateDependency(MutableStateDep);
2364 if (FAILED(rc)) return rc;
2365
2366 switch (aProperty)
2367 {
2368 case HWVirtExPropertyType_Enabled:
2369 i_setModified(IsModified_MachineData);
2370 mHWData.backup();
2371 mHWData->mHWVirtExEnabled = !!aValue;
2372 break;
2373
2374 case HWVirtExPropertyType_VPID:
2375 i_setModified(IsModified_MachineData);
2376 mHWData.backup();
2377 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2378 break;
2379
2380 case HWVirtExPropertyType_NestedPaging:
2381 i_setModified(IsModified_MachineData);
2382 mHWData.backup();
2383 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2384 break;
2385
2386 case HWVirtExPropertyType_UnrestrictedExecution:
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mHWVirtExUXEnabled = !!aValue;
2390 break;
2391
2392 case HWVirtExPropertyType_LargePages:
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2396 break;
2397
2398 case HWVirtExPropertyType_Force:
2399 i_setModified(IsModified_MachineData);
2400 mHWData.backup();
2401 mHWData->mHWVirtExForceEnabled = !!aValue;
2402 break;
2403
2404 case HWVirtExPropertyType_UseNativeApi:
2405 i_setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mHWVirtExUseNativeApi = !!aValue;
2408 break;
2409
2410 default:
2411 return E_INVALIDARG;
2412 }
2413
2414 return S_OK;
2415}
2416
2417HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2418{
2419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2420
2421 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2422
2423 return S_OK;
2424}
2425
2426HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2427{
2428 /** @todo (r=dmik):
2429 * 1. Allow to change the name of the snapshot folder containing snapshots
2430 * 2. Rename the folder on disk instead of just changing the property
2431 * value (to be smart and not to leave garbage). Note that it cannot be
2432 * done here because the change may be rolled back. Thus, the right
2433 * place is #saveSettings().
2434 */
2435
2436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2437
2438 HRESULT rc = i_checkStateDependency(MutableStateDep);
2439 if (FAILED(rc)) return rc;
2440
2441 if (!mData->mCurrentSnapshot.isNull())
2442 return setError(E_FAIL,
2443 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2444
2445 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2446
2447 if (strSnapshotFolder.isEmpty())
2448 strSnapshotFolder = "Snapshots";
2449 int vrc = i_calculateFullPath(strSnapshotFolder,
2450 strSnapshotFolder);
2451 if (RT_FAILURE(vrc))
2452 return setErrorBoth(E_FAIL, vrc,
2453 tr("Invalid snapshot folder '%s' (%Rrc)"),
2454 strSnapshotFolder.c_str(), vrc);
2455
2456 i_setModified(IsModified_MachineData);
2457 mUserData.backup();
2458
2459 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2460
2461 return S_OK;
2462}
2463
2464HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aMediumAttachments.resize(mMediumAttachments->size());
2469 size_t i = 0;
2470 for (MediumAttachmentList::const_iterator
2471 it = mMediumAttachments->begin();
2472 it != mMediumAttachments->end();
2473 ++it, ++i)
2474 aMediumAttachments[i] = *it;
2475
2476 return S_OK;
2477}
2478
2479HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2480{
2481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2482
2483 Assert(!!mVRDEServer);
2484
2485 aVRDEServer = mVRDEServer;
2486
2487 return S_OK;
2488}
2489
2490HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2491{
2492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 aAudioAdapter = mAudioAdapter;
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2500{
2501#ifdef VBOX_WITH_VUSB
2502 clearError();
2503 MultiResult rc(S_OK);
2504
2505# ifdef VBOX_WITH_USB
2506 rc = mParent->i_host()->i_checkUSBProxyService();
2507 if (FAILED(rc)) return rc;
2508# endif
2509
2510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2511
2512 aUSBControllers.resize(mUSBControllers->size());
2513 size_t i = 0;
2514 for (USBControllerList::const_iterator
2515 it = mUSBControllers->begin();
2516 it != mUSBControllers->end();
2517 ++it, ++i)
2518 aUSBControllers[i] = *it;
2519
2520 return S_OK;
2521#else
2522 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2523 * extended error info to indicate that USB is simply not available
2524 * (w/o treating it as a failure), for example, as in OSE */
2525 NOREF(aUSBControllers);
2526 ReturnComNotImplemented();
2527#endif /* VBOX_WITH_VUSB */
2528}
2529
2530HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2531{
2532#ifdef VBOX_WITH_VUSB
2533 clearError();
2534 MultiResult rc(S_OK);
2535
2536# ifdef VBOX_WITH_USB
2537 rc = mParent->i_host()->i_checkUSBProxyService();
2538 if (FAILED(rc)) return rc;
2539# endif
2540
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 aUSBDeviceFilters = mUSBDeviceFilters;
2544 return rc;
2545#else
2546 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2547 * extended error info to indicate that USB is simply not available
2548 * (w/o treating it as a failure), for example, as in OSE */
2549 NOREF(aUSBDeviceFilters);
2550 ReturnComNotImplemented();
2551#endif /* VBOX_WITH_VUSB */
2552}
2553
2554HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 aSettingsFilePath = mData->m_strConfigFileFull;
2559
2560 return S_OK;
2561}
2562
2563HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2564{
2565 RT_NOREF(aSettingsFilePath);
2566 ReturnComNotImplemented();
2567}
2568
2569HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2574 if (FAILED(rc)) return rc;
2575
2576 if (!mData->pMachineConfigFile->fileExists())
2577 // this is a new machine, and no config file exists yet:
2578 *aSettingsModified = TRUE;
2579 else
2580 *aSettingsModified = (mData->flModifications != 0);
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2586{
2587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2588
2589 *aSessionState = mData->mSession.mState;
2590
2591 return S_OK;
2592}
2593
2594HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2595{
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 aSessionName = mData->mSession.mName;
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 *aSessionPID = mData->mSession.mPID;
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::getState(MachineState_T *aState)
2613{
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 *aState = mData->mMachineState;
2617 Assert(mData->mMachineState != MachineState_Null);
2618
2619 return S_OK;
2620}
2621
2622HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2623{
2624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 aStateFilePath = mSSData->strStateFilePath;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2641{
2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 i_getLogFolder(aLogFolder);
2645
2646 return S_OK;
2647}
2648
2649HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2650{
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 aCurrentSnapshot = mData->mCurrentSnapshot;
2654
2655 return S_OK;
2656}
2657
2658HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2659{
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2663 ? 0
2664 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 /* Note: for machines with no snapshots, we always return FALSE
2674 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2675 * reasons :) */
2676
2677 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2678 ? FALSE
2679 : mData->mCurrentStateModified;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aSharedFolders.resize(mHWData->mSharedFolders.size());
2689 size_t i = 0;
2690 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2691 it = mHWData->mSharedFolders.begin();
2692 it != mHWData->mSharedFolders.end();
2693 ++it, ++i)
2694 aSharedFolders[i] = *it;
2695
2696 return S_OK;
2697}
2698
2699HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2700{
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 *aClipboardMode = mHWData->mClipboardMode;
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2709{
2710 HRESULT rc = S_OK;
2711
2712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 alock.release();
2715 rc = i_onClipboardModeChange(aClipboardMode);
2716 alock.acquire();
2717 if (FAILED(rc)) return rc;
2718
2719 i_setModified(IsModified_MachineData);
2720 mHWData.backup();
2721 mHWData->mClipboardMode = aClipboardMode;
2722
2723 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2724 if (Global::IsOnline(mData->mMachineState))
2725 i_saveSettings(NULL);
2726
2727 return S_OK;
2728}
2729
2730HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 *aDnDMode = mHWData->mDnDMode;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2740{
2741 HRESULT rc = S_OK;
2742
2743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 alock.release();
2746 rc = i_onDnDModeChange(aDnDMode);
2747
2748 alock.acquire();
2749 if (FAILED(rc)) return rc;
2750
2751 i_setModified(IsModified_MachineData);
2752 mHWData.backup();
2753 mHWData->mDnDMode = aDnDMode;
2754
2755 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2756 if (Global::IsOnline(mData->mMachineState))
2757 i_saveSettings(NULL);
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aStorageControllers.resize(mStorageControllers->size());
2767 size_t i = 0;
2768 for (StorageControllerList::const_iterator
2769 it = mStorageControllers->begin();
2770 it != mStorageControllers->end();
2771 ++it, ++i)
2772 aStorageControllers[i] = *it;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aEnabled = mUserData->s.fTeleporterEnabled;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2787{
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 /* Only allow it to be set to true when PoweredOff or Aborted.
2791 (Clearing it is always permitted.) */
2792 if ( aTeleporterEnabled
2793 && mData->mRegistered
2794 && ( !i_isSessionMachine()
2795 || ( mData->mMachineState != MachineState_PoweredOff
2796 && mData->mMachineState != MachineState_Teleported
2797 && mData->mMachineState != MachineState_Aborted
2798 )
2799 )
2800 )
2801 return setError(VBOX_E_INVALID_VM_STATE,
2802 tr("The machine is not powered off (state is %s)"),
2803 Global::stringifyMachineState(mData->mMachineState));
2804
2805 i_setModified(IsModified_MachineData);
2806 mUserData.backup();
2807 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2808
2809 return S_OK;
2810}
2811
2812HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2813{
2814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2817
2818 return S_OK;
2819}
2820
2821HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2822{
2823 if (aTeleporterPort >= _64K)
2824 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2825
2826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2829 if (FAILED(rc)) return rc;
2830
2831 i_setModified(IsModified_MachineData);
2832 mUserData.backup();
2833 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2848{
2849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2852 if (FAILED(rc)) return rc;
2853
2854 i_setModified(IsModified_MachineData);
2855 mUserData.backup();
2856 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2870{
2871 /*
2872 * Hash the password first.
2873 */
2874 com::Utf8Str aT = aTeleporterPassword;
2875
2876 if (!aT.isEmpty())
2877 {
2878 if (VBoxIsPasswordHashed(&aT))
2879 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2880 VBoxHashPassword(&aT);
2881 }
2882
2883 /*
2884 * Do the update.
2885 */
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2888 if (SUCCEEDED(hrc))
2889 {
2890 i_setModified(IsModified_MachineData);
2891 mUserData.backup();
2892 mUserData->s.strTeleporterPassword = aT;
2893 }
2894
2895 return hrc;
2896}
2897
2898HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2907{
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 /** @todo deal with running state change. */
2911 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mUserData.backup();
2916 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2917 return S_OK;
2918}
2919
2920HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2925 return S_OK;
2926}
2927
2928HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
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.strFaultToleranceAddress = aFaultToleranceAddress;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2943{
2944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2947 return S_OK;
2948}
2949
2950HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2951{
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 /** @todo deal with running state change. */
2955 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2956 if (FAILED(rc)) return rc;
2957
2958 i_setModified(IsModified_MachineData);
2959 mUserData.backup();
2960 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2961 return S_OK;
2962}
2963
2964HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2965{
2966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2967
2968 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2969
2970 return S_OK;
2971}
2972
2973HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2974{
2975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 /** @todo deal with running state change. */
2978 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2979 if (FAILED(rc)) return rc;
2980
2981 i_setModified(IsModified_MachineData);
2982 mUserData.backup();
2983 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2984
2985 return S_OK;
2986}
2987
2988HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2989{
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2993 return S_OK;
2994}
2995
2996HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2997{
2998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2999
3000 /** @todo deal with running state change. */
3001 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3002 if (FAILED(rc)) return rc;
3003
3004 i_setModified(IsModified_MachineData);
3005 mUserData.backup();
3006 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3007 return S_OK;
3008}
3009
3010HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3011{
3012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3015
3016 return S_OK;
3017}
3018
3019HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3020{
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 /* Only allow it to be set to true when PoweredOff or Aborted.
3024 (Clearing it is always permitted.) */
3025 if ( aRTCUseUTC
3026 && mData->mRegistered
3027 && ( !i_isSessionMachine()
3028 || ( mData->mMachineState != MachineState_PoweredOff
3029 && mData->mMachineState != MachineState_Teleported
3030 && mData->mMachineState != MachineState_Aborted
3031 )
3032 )
3033 )
3034 return setError(VBOX_E_INVALID_VM_STATE,
3035 tr("The machine is not powered off (state is %s)"),
3036 Global::stringifyMachineState(mData->mMachineState));
3037
3038 i_setModified(IsModified_MachineData);
3039 mUserData.backup();
3040 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3041
3042 return S_OK;
3043}
3044
3045HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3046{
3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3055{
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 HRESULT rc = i_checkStateDependency(MutableStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3069{
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 *aIOCacheSize = mHWData->mIOCacheSize;
3073
3074 return S_OK;
3075}
3076
3077HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3078{
3079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 HRESULT rc = i_checkStateDependency(MutableStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mHWData.backup();
3086 mHWData->mIOCacheSize = aIOCacheSize;
3087
3088 return S_OK;
3089}
3090
3091
3092/**
3093 * @note Locks objects!
3094 */
3095HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3096 LockType_T aLockType)
3097{
3098 /* check the session state */
3099 SessionState_T state;
3100 HRESULT rc = aSession->COMGETTER(State)(&state);
3101 if (FAILED(rc)) return rc;
3102
3103 if (state != SessionState_Unlocked)
3104 return setError(VBOX_E_INVALID_OBJECT_STATE,
3105 tr("The given session is busy"));
3106
3107 // get the client's IInternalSessionControl interface
3108 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3109 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3110 E_INVALIDARG);
3111
3112 // session name (only used in some code paths)
3113 Utf8Str strSessionName;
3114
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 if (!mData->mRegistered)
3118 return setError(E_UNEXPECTED,
3119 tr("The machine '%s' is not registered"),
3120 mUserData->s.strName.c_str());
3121
3122 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3123
3124 SessionState_T oldState = mData->mSession.mState;
3125 /* Hack: in case the session is closing and there is a progress object
3126 * which allows waiting for the session to be closed, take the opportunity
3127 * and do a limited wait (max. 1 second). This helps a lot when the system
3128 * is busy and thus session closing can take a little while. */
3129 if ( mData->mSession.mState == SessionState_Unlocking
3130 && mData->mSession.mProgress)
3131 {
3132 alock.release();
3133 mData->mSession.mProgress->WaitForCompletion(1000);
3134 alock.acquire();
3135 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3136 }
3137
3138 // try again now
3139 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3140 // (i.e. session machine exists)
3141 && (aLockType == LockType_Shared) // caller wants a shared link to the
3142 // existing session that holds the write lock:
3143 )
3144 {
3145 // OK, share the session... we are now dealing with three processes:
3146 // 1) VBoxSVC (where this code runs);
3147 // 2) process C: the caller's client process (who wants a shared session);
3148 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3149
3150 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3151 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3152 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3153 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3154 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3155
3156 /*
3157 * Release the lock before calling the client process. It's safe here
3158 * since the only thing to do after we get the lock again is to add
3159 * the remote control to the list (which doesn't directly influence
3160 * anything).
3161 */
3162 alock.release();
3163
3164 // get the console of the session holding the write lock (this is a remote call)
3165 ComPtr<IConsole> pConsoleW;
3166 if (mData->mSession.mLockType == LockType_VM)
3167 {
3168 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3169 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3170 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3171 if (FAILED(rc))
3172 // the failure may occur w/o any error info (from RPC), so provide one
3173 return setError(VBOX_E_VM_ERROR,
3174 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3175 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3176 }
3177
3178 // share the session machine and W's console with the caller's session
3179 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3180 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3181 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3182
3183 if (FAILED(rc))
3184 // the failure may occur w/o any error info (from RPC), so provide one
3185 return setError(VBOX_E_VM_ERROR,
3186 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3187 alock.acquire();
3188
3189 // need to revalidate the state after acquiring the lock again
3190 if (mData->mSession.mState != SessionState_Locked)
3191 {
3192 pSessionControl->Uninitialize();
3193 return setError(VBOX_E_INVALID_SESSION_STATE,
3194 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3195 mUserData->s.strName.c_str());
3196 }
3197
3198 // add the caller's session to the list
3199 mData->mSession.mRemoteControls.push_back(pSessionControl);
3200 }
3201 else if ( mData->mSession.mState == SessionState_Locked
3202 || mData->mSession.mState == SessionState_Unlocking
3203 )
3204 {
3205 // sharing not permitted, or machine still unlocking:
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3208 mUserData->s.strName.c_str());
3209 }
3210 else
3211 {
3212 // machine is not locked: then write-lock the machine (create the session machine)
3213
3214 // must not be busy
3215 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3216
3217 // get the caller's session PID
3218 RTPROCESS pid = NIL_RTPROCESS;
3219 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3220 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3221 Assert(pid != NIL_RTPROCESS);
3222
3223 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3224
3225 if (fLaunchingVMProcess)
3226 {
3227 if (mData->mSession.mPID == NIL_RTPROCESS)
3228 {
3229 // two or more clients racing for a lock, the one which set the
3230 // session state to Spawning will win, the others will get an
3231 // error as we can't decide here if waiting a little would help
3232 // (only for shared locks this would avoid an error)
3233 return setError(VBOX_E_INVALID_OBJECT_STATE,
3234 tr("The machine '%s' already has a lock request pending"),
3235 mUserData->s.strName.c_str());
3236 }
3237
3238 // this machine is awaiting for a spawning session to be opened:
3239 // then the calling process must be the one that got started by
3240 // LaunchVMProcess()
3241
3242 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3243 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3244
3245#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3246 /* Hardened windows builds spawns three processes when a VM is
3247 launched, the 3rd one is the one that will end up here. */
3248 RTPROCESS ppid;
3249 int rc = RTProcQueryParent(pid, &ppid);
3250 if (RT_SUCCESS(rc))
3251 rc = RTProcQueryParent(ppid, &ppid);
3252 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3253 || rc == VERR_ACCESS_DENIED)
3254 {
3255 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3256 mData->mSession.mPID = pid;
3257 }
3258#endif
3259
3260 if (mData->mSession.mPID != pid)
3261 return setError(E_ACCESSDENIED,
3262 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3263 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3264 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3265 }
3266
3267 // create the mutable SessionMachine from the current machine
3268 ComObjPtr<SessionMachine> sessionMachine;
3269 sessionMachine.createObject();
3270 rc = sessionMachine->init(this);
3271 AssertComRC(rc);
3272
3273 /* NOTE: doing return from this function after this point but
3274 * before the end is forbidden since it may call SessionMachine::uninit()
3275 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3276 * lock while still holding the Machine lock in alock so that a deadlock
3277 * is possible due to the wrong lock order. */
3278
3279 if (SUCCEEDED(rc))
3280 {
3281 /*
3282 * Set the session state to Spawning to protect against subsequent
3283 * attempts to open a session and to unregister the machine after
3284 * we release the lock.
3285 */
3286 SessionState_T origState = mData->mSession.mState;
3287 mData->mSession.mState = SessionState_Spawning;
3288
3289#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3290 /* Get the client token ID to be passed to the client process */
3291 Utf8Str strTokenId;
3292 sessionMachine->i_getTokenId(strTokenId);
3293 Assert(!strTokenId.isEmpty());
3294#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3295 /* Get the client token to be passed to the client process */
3296 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3297 /* The token is now "owned" by pToken, fix refcount */
3298 if (!pToken.isNull())
3299 pToken->Release();
3300#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3301
3302 /*
3303 * Release the lock before calling the client process -- it will call
3304 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3305 * because the state is Spawning, so that LaunchVMProcess() and
3306 * LockMachine() calls will fail. This method, called before we
3307 * acquire the lock again, will fail because of the wrong PID.
3308 *
3309 * Note that mData->mSession.mRemoteControls accessed outside
3310 * the lock may not be modified when state is Spawning, so it's safe.
3311 */
3312 alock.release();
3313
3314 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3315#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3316 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3317#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3318 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3319 /* Now the token is owned by the client process. */
3320 pToken.setNull();
3321#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3322 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3323
3324 /* The failure may occur w/o any error info (from RPC), so provide one */
3325 if (FAILED(rc))
3326 setError(VBOX_E_VM_ERROR,
3327 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3328
3329 // get session name, either to remember or to compare against
3330 // the already known session name.
3331 {
3332 Bstr bstrSessionName;
3333 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3334 if (SUCCEEDED(rc2))
3335 strSessionName = bstrSessionName;
3336 }
3337
3338 if ( SUCCEEDED(rc)
3339 && fLaunchingVMProcess
3340 )
3341 {
3342 /* complete the remote session initialization */
3343
3344 /* get the console from the direct session */
3345 ComPtr<IConsole> console;
3346 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3347 ComAssertComRC(rc);
3348
3349 if (SUCCEEDED(rc) && !console)
3350 {
3351 ComAssert(!!console);
3352 rc = E_FAIL;
3353 }
3354
3355 /* assign machine & console to the remote session */
3356 if (SUCCEEDED(rc))
3357 {
3358 /*
3359 * after LaunchVMProcess(), the first and the only
3360 * entry in remoteControls is that remote session
3361 */
3362 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3363 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3364 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3365
3366 /* The failure may occur w/o any error info (from RPC), so provide one */
3367 if (FAILED(rc))
3368 setError(VBOX_E_VM_ERROR,
3369 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3370 }
3371
3372 if (FAILED(rc))
3373 pSessionControl->Uninitialize();
3374 }
3375
3376 /* acquire the lock again */
3377 alock.acquire();
3378
3379 /* Restore the session state */
3380 mData->mSession.mState = origState;
3381 }
3382
3383 // finalize spawning anyway (this is why we don't return on errors above)
3384 if (fLaunchingVMProcess)
3385 {
3386 Assert(mData->mSession.mName == strSessionName);
3387 /* Note that the progress object is finalized later */
3388 /** @todo Consider checking mData->mSession.mProgress for cancellation
3389 * around here. */
3390
3391 /* We don't reset mSession.mPID here because it is necessary for
3392 * SessionMachine::uninit() to reap the child process later. */
3393
3394 if (FAILED(rc))
3395 {
3396 /* Close the remote session, remove the remote control from the list
3397 * and reset session state to Closed (@note keep the code in sync
3398 * with the relevant part in checkForSpawnFailure()). */
3399
3400 Assert(mData->mSession.mRemoteControls.size() == 1);
3401 if (mData->mSession.mRemoteControls.size() == 1)
3402 {
3403 ErrorInfoKeeper eik;
3404 mData->mSession.mRemoteControls.front()->Uninitialize();
3405 }
3406
3407 mData->mSession.mRemoteControls.clear();
3408 mData->mSession.mState = SessionState_Unlocked;
3409 }
3410 }
3411 else
3412 {
3413 /* memorize PID of the directly opened session */
3414 if (SUCCEEDED(rc))
3415 mData->mSession.mPID = pid;
3416 }
3417
3418 if (SUCCEEDED(rc))
3419 {
3420 mData->mSession.mLockType = aLockType;
3421 /* memorize the direct session control and cache IUnknown for it */
3422 mData->mSession.mDirectControl = pSessionControl;
3423 mData->mSession.mState = SessionState_Locked;
3424 if (!fLaunchingVMProcess)
3425 mData->mSession.mName = strSessionName;
3426 /* associate the SessionMachine with this Machine */
3427 mData->mSession.mMachine = sessionMachine;
3428
3429 /* request an IUnknown pointer early from the remote party for later
3430 * identity checks (it will be internally cached within mDirectControl
3431 * at least on XPCOM) */
3432 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3433 NOREF(unk);
3434 }
3435
3436 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3437 * would break the lock order */
3438 alock.release();
3439
3440 /* uninitialize the created session machine on failure */
3441 if (FAILED(rc))
3442 sessionMachine->uninit();
3443 }
3444
3445 if (SUCCEEDED(rc))
3446 {
3447 /*
3448 * tell the client watcher thread to update the set of
3449 * machines that have open sessions
3450 */
3451 mParent->i_updateClientWatcher();
3452
3453 if (oldState != SessionState_Locked)
3454 /* fire an event */
3455 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3456 }
3457
3458 return rc;
3459}
3460
3461/**
3462 * @note Locks objects!
3463 */
3464HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3465 const com::Utf8Str &aName,
3466 const com::Utf8Str &aEnvironment,
3467 ComPtr<IProgress> &aProgress)
3468{
3469 Utf8Str strFrontend(aName);
3470 /* "emergencystop" doesn't need the session, so skip the checks/interface
3471 * retrieval. This code doesn't quite fit in here, but introducing a
3472 * special API method would be even more effort, and would require explicit
3473 * support by every API client. It's better to hide the feature a bit. */
3474 if (strFrontend != "emergencystop")
3475 CheckComArgNotNull(aSession);
3476
3477 HRESULT rc = S_OK;
3478 if (strFrontend.isEmpty())
3479 {
3480 Bstr bstrFrontend;
3481 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3482 if (FAILED(rc))
3483 return rc;
3484 strFrontend = bstrFrontend;
3485 if (strFrontend.isEmpty())
3486 {
3487 ComPtr<ISystemProperties> systemProperties;
3488 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3489 if (FAILED(rc))
3490 return rc;
3491 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3492 if (FAILED(rc))
3493 return rc;
3494 strFrontend = bstrFrontend;
3495 }
3496 /* paranoia - emergencystop is not a valid default */
3497 if (strFrontend == "emergencystop")
3498 strFrontend = Utf8Str::Empty;
3499 }
3500 /* default frontend: Qt GUI */
3501 if (strFrontend.isEmpty())
3502 strFrontend = "GUI/Qt";
3503
3504 if (strFrontend != "emergencystop")
3505 {
3506 /* check the session state */
3507 SessionState_T state;
3508 rc = aSession->COMGETTER(State)(&state);
3509 if (FAILED(rc))
3510 return rc;
3511
3512 if (state != SessionState_Unlocked)
3513 return setError(VBOX_E_INVALID_OBJECT_STATE,
3514 tr("The given session is busy"));
3515
3516 /* get the IInternalSessionControl interface */
3517 ComPtr<IInternalSessionControl> control(aSession);
3518 ComAssertMsgRet(!control.isNull(),
3519 ("No IInternalSessionControl interface"),
3520 E_INVALIDARG);
3521
3522 /* get the teleporter enable state for the progress object init. */
3523 BOOL fTeleporterEnabled;
3524 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3525 if (FAILED(rc))
3526 return rc;
3527
3528 /* create a progress object */
3529 ComObjPtr<ProgressProxy> progress;
3530 progress.createObject();
3531 rc = progress->init(mParent,
3532 static_cast<IMachine*>(this),
3533 Bstr(tr("Starting VM")).raw(),
3534 TRUE /* aCancelable */,
3535 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3536 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3537 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3538 2 /* uFirstOperationWeight */,
3539 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3540
3541 if (SUCCEEDED(rc))
3542 {
3543 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3544 if (SUCCEEDED(rc))
3545 {
3546 aProgress = progress;
3547
3548 /* signal the client watcher thread */
3549 mParent->i_updateClientWatcher();
3550
3551 /* fire an event */
3552 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3553 }
3554 }
3555 }
3556 else
3557 {
3558 /* no progress object - either instant success or failure */
3559 aProgress = NULL;
3560
3561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3562
3563 if (mData->mSession.mState != SessionState_Locked)
3564 return setError(VBOX_E_INVALID_OBJECT_STATE,
3565 tr("The machine '%s' is not locked by a session"),
3566 mUserData->s.strName.c_str());
3567
3568 /* must have a VM process associated - do not kill normal API clients
3569 * with an open session */
3570 if (!Global::IsOnline(mData->mMachineState))
3571 return setError(VBOX_E_INVALID_OBJECT_STATE,
3572 tr("The machine '%s' does not have a VM process"),
3573 mUserData->s.strName.c_str());
3574
3575 /* forcibly terminate the VM process */
3576 if (mData->mSession.mPID != NIL_RTPROCESS)
3577 RTProcTerminate(mData->mSession.mPID);
3578
3579 /* signal the client watcher thread, as most likely the client has
3580 * been terminated */
3581 mParent->i_updateClientWatcher();
3582 }
3583
3584 return rc;
3585}
3586
3587HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3588{
3589 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3590 return setError(E_INVALIDARG,
3591 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3592 aPosition, SchemaDefs::MaxBootPosition);
3593
3594 if (aDevice == DeviceType_USB)
3595 return setError(E_NOTIMPL,
3596 tr("Booting from USB device is currently not supported"));
3597
3598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3599
3600 HRESULT rc = i_checkStateDependency(MutableStateDep);
3601 if (FAILED(rc)) return rc;
3602
3603 i_setModified(IsModified_MachineData);
3604 mHWData.backup();
3605 mHWData->mBootOrder[aPosition - 1] = aDevice;
3606
3607 return S_OK;
3608}
3609
3610HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3611{
3612 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3613 return setError(E_INVALIDARG,
3614 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3615 aPosition, SchemaDefs::MaxBootPosition);
3616
3617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3618
3619 *aDevice = mHWData->mBootOrder[aPosition - 1];
3620
3621 return S_OK;
3622}
3623
3624HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3625 LONG aControllerPort,
3626 LONG aDevice,
3627 DeviceType_T aType,
3628 const ComPtr<IMedium> &aMedium)
3629{
3630 IMedium *aM = aMedium;
3631 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3632 aName.c_str(), aControllerPort, aDevice, aType, aM));
3633
3634 // request the host lock first, since might be calling Host methods for getting host drives;
3635 // next, protect the media tree all the while we're in here, as well as our member variables
3636 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3637 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3638
3639 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3640 if (FAILED(rc)) return rc;
3641
3642 /// @todo NEWMEDIA implicit machine registration
3643 if (!mData->mRegistered)
3644 return setError(VBOX_E_INVALID_OBJECT_STATE,
3645 tr("Cannot attach storage devices to an unregistered machine"));
3646
3647 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3648
3649 /* Check for an existing controller. */
3650 ComObjPtr<StorageController> ctl;
3651 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3652 if (FAILED(rc)) return rc;
3653
3654 StorageControllerType_T ctrlType;
3655 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3656 if (FAILED(rc))
3657 return setError(E_FAIL,
3658 tr("Could not get type of controller '%s'"),
3659 aName.c_str());
3660
3661 bool fSilent = false;
3662 Utf8Str strReconfig;
3663
3664 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3665 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3666 if ( mData->mMachineState == MachineState_Paused
3667 && strReconfig == "1")
3668 fSilent = true;
3669
3670 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3671 bool fHotplug = false;
3672 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3673 fHotplug = true;
3674
3675 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3676 return setError(VBOX_E_INVALID_VM_STATE,
3677 tr("Controller '%s' does not support hotplugging"),
3678 aName.c_str());
3679
3680 // check that the port and device are not out of range
3681 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3682 if (FAILED(rc)) return rc;
3683
3684 /* check if the device slot is already busy */
3685 MediumAttachment *pAttachTemp;
3686 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3687 aName,
3688 aControllerPort,
3689 aDevice)))
3690 {
3691 Medium *pMedium = pAttachTemp->i_getMedium();
3692 if (pMedium)
3693 {
3694 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3695 return setError(VBOX_E_OBJECT_IN_USE,
3696 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3697 pMedium->i_getLocationFull().c_str(),
3698 aControllerPort,
3699 aDevice,
3700 aName.c_str());
3701 }
3702 else
3703 return setError(VBOX_E_OBJECT_IN_USE,
3704 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3705 aControllerPort, aDevice, aName.c_str());
3706 }
3707
3708 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3709 if (aMedium && medium.isNull())
3710 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3711
3712 AutoCaller mediumCaller(medium);
3713 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3714
3715 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3716
3717 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3718 && !medium.isNull()
3719 )
3720 return setError(VBOX_E_OBJECT_IN_USE,
3721 tr("Medium '%s' is already attached to this virtual machine"),
3722 medium->i_getLocationFull().c_str());
3723
3724 if (!medium.isNull())
3725 {
3726 MediumType_T mtype = medium->i_getType();
3727 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3728 // For DVDs it's not written to the config file, so needs no global config
3729 // version bump. For floppies it's a new attribute "type", which is ignored
3730 // by older VirtualBox version, so needs no global config version bump either.
3731 // For hard disks this type is not accepted.
3732 if (mtype == MediumType_MultiAttach)
3733 {
3734 // This type is new with VirtualBox 4.0 and therefore requires settings
3735 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3736 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3737 // two reasons: The medium type is a property of the media registry tree, which
3738 // can reside in the global config file (for pre-4.0 media); we would therefore
3739 // possibly need to bump the global config version. We don't want to do that though
3740 // because that might make downgrading to pre-4.0 impossible.
3741 // As a result, we can only use these two new types if the medium is NOT in the
3742 // global registry:
3743 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3744 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3745 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3746 )
3747 return setError(VBOX_E_INVALID_OBJECT_STATE,
3748 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3749 "to machines that were created with VirtualBox 4.0 or later"),
3750 medium->i_getLocationFull().c_str());
3751 }
3752 }
3753
3754 bool fIndirect = false;
3755 if (!medium.isNull())
3756 fIndirect = medium->i_isReadOnly();
3757 bool associate = true;
3758
3759 do
3760 {
3761 if ( aType == DeviceType_HardDisk
3762 && mMediumAttachments.isBackedUp())
3763 {
3764 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3765
3766 /* check if the medium was attached to the VM before we started
3767 * changing attachments in which case the attachment just needs to
3768 * be restored */
3769 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3770 {
3771 AssertReturn(!fIndirect, E_FAIL);
3772
3773 /* see if it's the same bus/channel/device */
3774 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3775 {
3776 /* the simplest case: restore the whole attachment
3777 * and return, nothing else to do */
3778 mMediumAttachments->push_back(pAttachTemp);
3779
3780 /* Reattach the medium to the VM. */
3781 if (fHotplug || fSilent)
3782 {
3783 mediumLock.release();
3784 treeLock.release();
3785 alock.release();
3786
3787 MediumLockList *pMediumLockList(new MediumLockList());
3788
3789 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3790 medium /* pToLockWrite */,
3791 false /* fMediumLockWriteAll */,
3792 NULL,
3793 *pMediumLockList);
3794 alock.acquire();
3795 if (FAILED(rc))
3796 delete pMediumLockList;
3797 else
3798 {
3799 mData->mSession.mLockedMedia.Unlock();
3800 alock.release();
3801 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3802 mData->mSession.mLockedMedia.Lock();
3803 alock.acquire();
3804 }
3805 alock.release();
3806
3807 if (SUCCEEDED(rc))
3808 {
3809 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3810 /* Remove lock list in case of error. */
3811 if (FAILED(rc))
3812 {
3813 mData->mSession.mLockedMedia.Unlock();
3814 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3815 mData->mSession.mLockedMedia.Lock();
3816 }
3817 }
3818 }
3819
3820 return S_OK;
3821 }
3822
3823 /* bus/channel/device differ; we need a new attachment object,
3824 * but don't try to associate it again */
3825 associate = false;
3826 break;
3827 }
3828 }
3829
3830 /* go further only if the attachment is to be indirect */
3831 if (!fIndirect)
3832 break;
3833
3834 /* perform the so called smart attachment logic for indirect
3835 * attachments. Note that smart attachment is only applicable to base
3836 * hard disks. */
3837
3838 if (medium->i_getParent().isNull())
3839 {
3840 /* first, investigate the backup copy of the current hard disk
3841 * attachments to make it possible to re-attach existing diffs to
3842 * another device slot w/o losing their contents */
3843 if (mMediumAttachments.isBackedUp())
3844 {
3845 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3846
3847 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3848 uint32_t foundLevel = 0;
3849
3850 for (MediumAttachmentList::const_iterator
3851 it = oldAtts.begin();
3852 it != oldAtts.end();
3853 ++it)
3854 {
3855 uint32_t level = 0;
3856 MediumAttachment *pAttach = *it;
3857 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3858 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3859 if (pMedium.isNull())
3860 continue;
3861
3862 if (pMedium->i_getBase(&level) == medium)
3863 {
3864 /* skip the hard disk if its currently attached (we
3865 * cannot attach the same hard disk twice) */
3866 if (i_findAttachment(*mMediumAttachments.data(),
3867 pMedium))
3868 continue;
3869
3870 /* matched device, channel and bus (i.e. attached to the
3871 * same place) will win and immediately stop the search;
3872 * otherwise the attachment that has the youngest
3873 * descendant of medium will be used
3874 */
3875 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3876 {
3877 /* the simplest case: restore the whole attachment
3878 * and return, nothing else to do */
3879 mMediumAttachments->push_back(*it);
3880
3881 /* Reattach the medium to the VM. */
3882 if (fHotplug || fSilent)
3883 {
3884 mediumLock.release();
3885 treeLock.release();
3886 alock.release();
3887
3888 MediumLockList *pMediumLockList(new MediumLockList());
3889
3890 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3891 medium /* pToLockWrite */,
3892 false /* fMediumLockWriteAll */,
3893 NULL,
3894 *pMediumLockList);
3895 alock.acquire();
3896 if (FAILED(rc))
3897 delete pMediumLockList;
3898 else
3899 {
3900 mData->mSession.mLockedMedia.Unlock();
3901 alock.release();
3902 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3903 mData->mSession.mLockedMedia.Lock();
3904 alock.acquire();
3905 }
3906 alock.release();
3907
3908 if (SUCCEEDED(rc))
3909 {
3910 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3911 /* Remove lock list in case of error. */
3912 if (FAILED(rc))
3913 {
3914 mData->mSession.mLockedMedia.Unlock();
3915 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3916 mData->mSession.mLockedMedia.Lock();
3917 }
3918 }
3919 }
3920
3921 return S_OK;
3922 }
3923 else if ( foundIt == oldAtts.end()
3924 || level > foundLevel /* prefer younger */
3925 )
3926 {
3927 foundIt = it;
3928 foundLevel = level;
3929 }
3930 }
3931 }
3932
3933 if (foundIt != oldAtts.end())
3934 {
3935 /* use the previously attached hard disk */
3936 medium = (*foundIt)->i_getMedium();
3937 mediumCaller.attach(medium);
3938 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3939 mediumLock.attach(medium);
3940 /* not implicit, doesn't require association with this VM */
3941 fIndirect = false;
3942 associate = false;
3943 /* go right to the MediumAttachment creation */
3944 break;
3945 }
3946 }
3947
3948 /* must give up the medium lock and medium tree lock as below we
3949 * go over snapshots, which needs a lock with higher lock order. */
3950 mediumLock.release();
3951 treeLock.release();
3952
3953 /* then, search through snapshots for the best diff in the given
3954 * hard disk's chain to base the new diff on */
3955
3956 ComObjPtr<Medium> base;
3957 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3958 while (snap)
3959 {
3960 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3961
3962 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3963
3964 MediumAttachment *pAttachFound = NULL;
3965 uint32_t foundLevel = 0;
3966
3967 for (MediumAttachmentList::const_iterator
3968 it = snapAtts.begin();
3969 it != snapAtts.end();
3970 ++it)
3971 {
3972 MediumAttachment *pAttach = *it;
3973 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3974 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3975 if (pMedium.isNull())
3976 continue;
3977
3978 uint32_t level = 0;
3979 if (pMedium->i_getBase(&level) == medium)
3980 {
3981 /* matched device, channel and bus (i.e. attached to the
3982 * same place) will win and immediately stop the search;
3983 * otherwise the attachment that has the youngest
3984 * descendant of medium will be used
3985 */
3986 if ( pAttach->i_getDevice() == aDevice
3987 && pAttach->i_getPort() == aControllerPort
3988 && pAttach->i_getControllerName() == aName
3989 )
3990 {
3991 pAttachFound = pAttach;
3992 break;
3993 }
3994 else if ( !pAttachFound
3995 || level > foundLevel /* prefer younger */
3996 )
3997 {
3998 pAttachFound = pAttach;
3999 foundLevel = level;
4000 }
4001 }
4002 }
4003
4004 if (pAttachFound)
4005 {
4006 base = pAttachFound->i_getMedium();
4007 break;
4008 }
4009
4010 snap = snap->i_getParent();
4011 }
4012
4013 /* re-lock medium tree and the medium, as we need it below */
4014 treeLock.acquire();
4015 mediumLock.acquire();
4016
4017 /* found a suitable diff, use it as a base */
4018 if (!base.isNull())
4019 {
4020 medium = base;
4021 mediumCaller.attach(medium);
4022 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4023 mediumLock.attach(medium);
4024 }
4025 }
4026
4027 Utf8Str strFullSnapshotFolder;
4028 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4029
4030 ComObjPtr<Medium> diff;
4031 diff.createObject();
4032 // store this diff in the same registry as the parent
4033 Guid uuidRegistryParent;
4034 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4035 {
4036 // parent image has no registry: this can happen if we're attaching a new immutable
4037 // image that has not yet been attached (medium then points to the base and we're
4038 // creating the diff image for the immutable, and the parent is not yet registered);
4039 // put the parent in the machine registry then
4040 mediumLock.release();
4041 treeLock.release();
4042 alock.release();
4043 i_addMediumToRegistry(medium);
4044 alock.acquire();
4045 treeLock.acquire();
4046 mediumLock.acquire();
4047 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4048 }
4049 rc = diff->init(mParent,
4050 medium->i_getPreferredDiffFormat(),
4051 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4052 uuidRegistryParent,
4053 DeviceType_HardDisk);
4054 if (FAILED(rc)) return rc;
4055
4056 /* Apply the normal locking logic to the entire chain. */
4057 MediumLockList *pMediumLockList(new MediumLockList());
4058 mediumLock.release();
4059 treeLock.release();
4060 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4061 diff /* pToLockWrite */,
4062 false /* fMediumLockWriteAll */,
4063 medium,
4064 *pMediumLockList);
4065 treeLock.acquire();
4066 mediumLock.acquire();
4067 if (SUCCEEDED(rc))
4068 {
4069 mediumLock.release();
4070 treeLock.release();
4071 rc = pMediumLockList->Lock();
4072 treeLock.acquire();
4073 mediumLock.acquire();
4074 if (FAILED(rc))
4075 setError(rc,
4076 tr("Could not lock medium when creating diff '%s'"),
4077 diff->i_getLocationFull().c_str());
4078 else
4079 {
4080 /* will release the lock before the potentially lengthy
4081 * operation, so protect with the special state */
4082 MachineState_T oldState = mData->mMachineState;
4083 i_setMachineState(MachineState_SettingUp);
4084
4085 mediumLock.release();
4086 treeLock.release();
4087 alock.release();
4088
4089 rc = medium->i_createDiffStorage(diff,
4090 medium->i_getPreferredDiffVariant(),
4091 pMediumLockList,
4092 NULL /* aProgress */,
4093 true /* aWait */,
4094 false /* aNotify */);
4095
4096 alock.acquire();
4097 treeLock.acquire();
4098 mediumLock.acquire();
4099
4100 i_setMachineState(oldState);
4101 }
4102 }
4103
4104 /* Unlock the media and free the associated memory. */
4105 delete pMediumLockList;
4106
4107 if (FAILED(rc)) return rc;
4108
4109 /* use the created diff for the actual attachment */
4110 medium = diff;
4111 mediumCaller.attach(medium);
4112 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4113 mediumLock.attach(medium);
4114 }
4115 while (0);
4116
4117 ComObjPtr<MediumAttachment> attachment;
4118 attachment.createObject();
4119 rc = attachment->init(this,
4120 medium,
4121 aName,
4122 aControllerPort,
4123 aDevice,
4124 aType,
4125 fIndirect,
4126 false /* fPassthrough */,
4127 false /* fTempEject */,
4128 false /* fNonRotational */,
4129 false /* fDiscard */,
4130 fHotplug /* fHotPluggable */,
4131 Utf8Str::Empty);
4132 if (FAILED(rc)) return rc;
4133
4134 if (associate && !medium.isNull())
4135 {
4136 // as the last step, associate the medium to the VM
4137 rc = medium->i_addBackReference(mData->mUuid);
4138 // here we can fail because of Deleting, or being in process of creating a Diff
4139 if (FAILED(rc)) return rc;
4140
4141 mediumLock.release();
4142 treeLock.release();
4143 alock.release();
4144 i_addMediumToRegistry(medium);
4145 alock.acquire();
4146 treeLock.acquire();
4147 mediumLock.acquire();
4148 }
4149
4150 /* success: finally remember the attachment */
4151 i_setModified(IsModified_Storage);
4152 mMediumAttachments.backup();
4153 mMediumAttachments->push_back(attachment);
4154
4155 mediumLock.release();
4156 treeLock.release();
4157 alock.release();
4158
4159 if (fHotplug || fSilent)
4160 {
4161 if (!medium.isNull())
4162 {
4163 MediumLockList *pMediumLockList(new MediumLockList());
4164
4165 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4166 medium /* pToLockWrite */,
4167 false /* fMediumLockWriteAll */,
4168 NULL,
4169 *pMediumLockList);
4170 alock.acquire();
4171 if (FAILED(rc))
4172 delete pMediumLockList;
4173 else
4174 {
4175 mData->mSession.mLockedMedia.Unlock();
4176 alock.release();
4177 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4178 mData->mSession.mLockedMedia.Lock();
4179 alock.acquire();
4180 }
4181 alock.release();
4182 }
4183
4184 if (SUCCEEDED(rc))
4185 {
4186 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4187 /* Remove lock list in case of error. */
4188 if (FAILED(rc))
4189 {
4190 mData->mSession.mLockedMedia.Unlock();
4191 mData->mSession.mLockedMedia.Remove(attachment);
4192 mData->mSession.mLockedMedia.Lock();
4193 }
4194 }
4195 }
4196
4197 /* Save modified registries, but skip this machine as it's the caller's
4198 * job to save its settings like all other settings changes. */
4199 mParent->i_unmarkRegistryModified(i_getId());
4200 mParent->i_saveModifiedRegistries();
4201
4202 if (SUCCEEDED(rc))
4203 {
4204 if (fIndirect && medium != aM)
4205 mParent->i_onMediumConfigChanged(medium);
4206 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4207 }
4208
4209 return rc;
4210}
4211
4212HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4213 LONG aDevice)
4214{
4215 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4216 aName.c_str(), aControllerPort, aDevice));
4217
4218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4219
4220 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4221 if (FAILED(rc)) return rc;
4222
4223 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4224
4225 /* Check for an existing controller. */
4226 ComObjPtr<StorageController> ctl;
4227 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4228 if (FAILED(rc)) return rc;
4229
4230 StorageControllerType_T ctrlType;
4231 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4232 if (FAILED(rc))
4233 return setError(E_FAIL,
4234 tr("Could not get type of controller '%s'"),
4235 aName.c_str());
4236
4237 bool fSilent = false;
4238 Utf8Str strReconfig;
4239
4240 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4241 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4242 if ( mData->mMachineState == MachineState_Paused
4243 && strReconfig == "1")
4244 fSilent = true;
4245
4246 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4247 bool fHotplug = false;
4248 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4249 fHotplug = true;
4250
4251 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4252 return setError(VBOX_E_INVALID_VM_STATE,
4253 tr("Controller '%s' does not support hotplugging"),
4254 aName.c_str());
4255
4256 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4257 aName,
4258 aControllerPort,
4259 aDevice);
4260 if (!pAttach)
4261 return setError(VBOX_E_OBJECT_NOT_FOUND,
4262 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4263 aDevice, aControllerPort, aName.c_str());
4264
4265 if (fHotplug && !pAttach->i_getHotPluggable())
4266 return setError(VBOX_E_NOT_SUPPORTED,
4267 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4268 aDevice, aControllerPort, aName.c_str());
4269
4270 /*
4271 * The VM has to detach the device before we delete any implicit diffs.
4272 * If this fails we can roll back without loosing data.
4273 */
4274 if (fHotplug || fSilent)
4275 {
4276 alock.release();
4277 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4278 alock.acquire();
4279 }
4280 if (FAILED(rc)) return rc;
4281
4282 /* If we are here everything went well and we can delete the implicit now. */
4283 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4284
4285 alock.release();
4286
4287 /* Save modified registries, but skip this machine as it's the caller's
4288 * job to save its settings like all other settings changes. */
4289 mParent->i_unmarkRegistryModified(i_getId());
4290 mParent->i_saveModifiedRegistries();
4291
4292 if (SUCCEEDED(rc))
4293 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4294
4295 return rc;
4296}
4297
4298HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4299 LONG aDevice, BOOL aPassthrough)
4300{
4301 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4302 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 /* Check for an existing controller. */
4312 ComObjPtr<StorageController> ctl;
4313 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4314 if (FAILED(rc)) return rc;
4315
4316 StorageControllerType_T ctrlType;
4317 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4318 if (FAILED(rc))
4319 return setError(E_FAIL,
4320 tr("Could not get type of controller '%s'"),
4321 aName.c_str());
4322
4323 bool fSilent = false;
4324 Utf8Str strReconfig;
4325
4326 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4327 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4328 if ( mData->mMachineState == MachineState_Paused
4329 && strReconfig == "1")
4330 fSilent = true;
4331
4332 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4333 bool fHotplug = false;
4334 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4335 fHotplug = true;
4336
4337 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4338 return setError(VBOX_E_INVALID_VM_STATE,
4339 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4340 aName.c_str());
4341
4342 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4343 aName,
4344 aControllerPort,
4345 aDevice);
4346 if (!pAttach)
4347 return setError(VBOX_E_OBJECT_NOT_FOUND,
4348 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4349 aDevice, aControllerPort, aName.c_str());
4350
4351
4352 i_setModified(IsModified_Storage);
4353 mMediumAttachments.backup();
4354
4355 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4356
4357 if (pAttach->i_getType() != DeviceType_DVD)
4358 return setError(E_INVALIDARG,
4359 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4360 aDevice, aControllerPort, aName.c_str());
4361
4362 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4363
4364 pAttach->i_updatePassthrough(!!aPassthrough);
4365
4366 attLock.release();
4367 alock.release();
4368 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4369 if (SUCCEEDED(rc) && fValueChanged)
4370 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4371
4372 return rc;
4373}
4374
4375HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4376 LONG aDevice, BOOL aTemporaryEject)
4377{
4378
4379 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4380 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4381
4382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4383
4384 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4385 if (FAILED(rc)) return rc;
4386
4387 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4388 aName,
4389 aControllerPort,
4390 aDevice);
4391 if (!pAttach)
4392 return setError(VBOX_E_OBJECT_NOT_FOUND,
4393 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4394 aDevice, aControllerPort, aName.c_str());
4395
4396
4397 i_setModified(IsModified_Storage);
4398 mMediumAttachments.backup();
4399
4400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4401
4402 if (pAttach->i_getType() != DeviceType_DVD)
4403 return setError(E_INVALIDARG,
4404 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4405 aDevice, aControllerPort, aName.c_str());
4406 pAttach->i_updateTempEject(!!aTemporaryEject);
4407
4408 return S_OK;
4409}
4410
4411HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4412 LONG aDevice, BOOL aNonRotational)
4413{
4414
4415 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4416 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4417
4418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4419
4420 HRESULT rc = i_checkStateDependency(MutableStateDep);
4421 if (FAILED(rc)) return rc;
4422
4423 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4424
4425 if (Global::IsOnlineOrTransient(mData->mMachineState))
4426 return setError(VBOX_E_INVALID_VM_STATE,
4427 tr("Invalid machine state: %s"),
4428 Global::stringifyMachineState(mData->mMachineState));
4429
4430 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4431 aName,
4432 aControllerPort,
4433 aDevice);
4434 if (!pAttach)
4435 return setError(VBOX_E_OBJECT_NOT_FOUND,
4436 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4437 aDevice, aControllerPort, aName.c_str());
4438
4439
4440 i_setModified(IsModified_Storage);
4441 mMediumAttachments.backup();
4442
4443 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4444
4445 if (pAttach->i_getType() != DeviceType_HardDisk)
4446 return setError(E_INVALIDARG,
4447 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"),
4448 aDevice, aControllerPort, aName.c_str());
4449 pAttach->i_updateNonRotational(!!aNonRotational);
4450
4451 return S_OK;
4452}
4453
4454HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4455 LONG aDevice, BOOL aDiscard)
4456{
4457
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4459 aName.c_str(), aControllerPort, aDevice, aDiscard));
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = i_checkStateDependency(MutableStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4467
4468 if (Global::IsOnlineOrTransient(mData->mMachineState))
4469 return setError(VBOX_E_INVALID_VM_STATE,
4470 tr("Invalid machine state: %s"),
4471 Global::stringifyMachineState(mData->mMachineState));
4472
4473 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4474 aName,
4475 aControllerPort,
4476 aDevice);
4477 if (!pAttach)
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4480 aDevice, aControllerPort, aName.c_str());
4481
4482
4483 i_setModified(IsModified_Storage);
4484 mMediumAttachments.backup();
4485
4486 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4487
4488 if (pAttach->i_getType() != DeviceType_HardDisk)
4489 return setError(E_INVALIDARG,
4490 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"),
4491 aDevice, aControllerPort, aName.c_str());
4492 pAttach->i_updateDiscard(!!aDiscard);
4493
4494 return S_OK;
4495}
4496
4497HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4498 LONG aDevice, BOOL aHotPluggable)
4499{
4500 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4501 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4502
4503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4504
4505 HRESULT rc = i_checkStateDependency(MutableStateDep);
4506 if (FAILED(rc)) return rc;
4507
4508 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4509
4510 if (Global::IsOnlineOrTransient(mData->mMachineState))
4511 return setError(VBOX_E_INVALID_VM_STATE,
4512 tr("Invalid machine state: %s"),
4513 Global::stringifyMachineState(mData->mMachineState));
4514
4515 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (!pAttach)
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524 /* Check for an existing controller. */
4525 ComObjPtr<StorageController> ctl;
4526 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4527 if (FAILED(rc)) return rc;
4528
4529 StorageControllerType_T ctrlType;
4530 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4531 if (FAILED(rc))
4532 return setError(E_FAIL,
4533 tr("Could not get type of controller '%s'"),
4534 aName.c_str());
4535
4536 if (!i_isControllerHotplugCapable(ctrlType))
4537 return setError(VBOX_E_NOT_SUPPORTED,
4538 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4539 aName.c_str());
4540
4541 i_setModified(IsModified_Storage);
4542 mMediumAttachments.backup();
4543
4544 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4545
4546 if (pAttach->i_getType() == DeviceType_Floppy)
4547 return setError(E_INVALIDARG,
4548 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"),
4549 aDevice, aControllerPort, aName.c_str());
4550 pAttach->i_updateHotPluggable(!!aHotPluggable);
4551
4552 return S_OK;
4553}
4554
4555HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4556 LONG aDevice)
4557{
4558 int rc = S_OK;
4559 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4560 aName.c_str(), aControllerPort, aDevice));
4561
4562 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4563
4564 return rc;
4565}
4566
4567HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4568 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4569{
4570 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4571 aName.c_str(), aControllerPort, aDevice));
4572
4573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4574
4575 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4576 if (FAILED(rc)) return rc;
4577
4578 if (Global::IsOnlineOrTransient(mData->mMachineState))
4579 return setError(VBOX_E_INVALID_VM_STATE,
4580 tr("Invalid machine state: %s"),
4581 Global::stringifyMachineState(mData->mMachineState));
4582
4583 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4584 aName,
4585 aControllerPort,
4586 aDevice);
4587 if (!pAttach)
4588 return setError(VBOX_E_OBJECT_NOT_FOUND,
4589 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4590 aDevice, aControllerPort, aName.c_str());
4591
4592
4593 i_setModified(IsModified_Storage);
4594 mMediumAttachments.backup();
4595
4596 IBandwidthGroup *iB = aBandwidthGroup;
4597 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4598 if (aBandwidthGroup && group.isNull())
4599 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4600
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602
4603 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4604 if (strBandwidthGroupOld.isNotEmpty())
4605 {
4606 /* Get the bandwidth group object and release it - this must not fail. */
4607 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4608 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4609 Assert(SUCCEEDED(rc));
4610
4611 pBandwidthGroupOld->i_release();
4612 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4613 }
4614
4615 if (!group.isNull())
4616 {
4617 group->i_reference();
4618 pAttach->i_updateBandwidthGroup(group->i_getName());
4619 }
4620
4621 return S_OK;
4622}
4623
4624HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4625 LONG aControllerPort,
4626 LONG aDevice,
4627 DeviceType_T aType)
4628{
4629 HRESULT rc = S_OK;
4630
4631 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4632 aName.c_str(), aControllerPort, aDevice, aType));
4633
4634 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4635
4636 return rc;
4637}
4638
4639
4640HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4641 LONG aControllerPort,
4642 LONG aDevice,
4643 BOOL aForce)
4644{
4645 int rc = S_OK;
4646 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4647 aName.c_str(), aControllerPort, aForce));
4648
4649 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4650
4651 return rc;
4652}
4653
4654HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4655 LONG aControllerPort,
4656 LONG aDevice,
4657 const ComPtr<IMedium> &aMedium,
4658 BOOL aForce)
4659{
4660 int rc = S_OK;
4661 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4662 aName.c_str(), aControllerPort, aDevice, aForce));
4663
4664 // request the host lock first, since might be calling Host methods for getting host drives;
4665 // next, protect the media tree all the while we're in here, as well as our member variables
4666 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4667 this->lockHandle(),
4668 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4669
4670 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4671 aName,
4672 aControllerPort,
4673 aDevice);
4674 if (pAttach.isNull())
4675 return setError(VBOX_E_OBJECT_NOT_FOUND,
4676 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4677 aDevice, aControllerPort, aName.c_str());
4678
4679 /* Remember previously mounted medium. The medium before taking the
4680 * backup is not necessarily the same thing. */
4681 ComObjPtr<Medium> oldmedium;
4682 oldmedium = pAttach->i_getMedium();
4683
4684 IMedium *iM = aMedium;
4685 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4686 if (aMedium && pMedium.isNull())
4687 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4688
4689 AutoCaller mediumCaller(pMedium);
4690 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4691
4692 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4693 if (pMedium)
4694 {
4695 DeviceType_T mediumType = pAttach->i_getType();
4696 switch (mediumType)
4697 {
4698 case DeviceType_DVD:
4699 case DeviceType_Floppy:
4700 break;
4701
4702 default:
4703 return setError(VBOX_E_INVALID_OBJECT_STATE,
4704 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4705 aControllerPort,
4706 aDevice,
4707 aName.c_str());
4708 }
4709 }
4710
4711 i_setModified(IsModified_Storage);
4712 mMediumAttachments.backup();
4713
4714 {
4715 // The backup operation makes the pAttach reference point to the
4716 // old settings. Re-get the correct reference.
4717 pAttach = i_findAttachment(*mMediumAttachments.data(),
4718 aName,
4719 aControllerPort,
4720 aDevice);
4721 if (!oldmedium.isNull())
4722 oldmedium->i_removeBackReference(mData->mUuid);
4723 if (!pMedium.isNull())
4724 {
4725 pMedium->i_addBackReference(mData->mUuid);
4726
4727 mediumLock.release();
4728 multiLock.release();
4729 i_addMediumToRegistry(pMedium);
4730 multiLock.acquire();
4731 mediumLock.acquire();
4732 }
4733
4734 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4735 pAttach->i_updateMedium(pMedium);
4736 }
4737
4738 i_setModified(IsModified_Storage);
4739
4740 mediumLock.release();
4741 multiLock.release();
4742 rc = i_onMediumChange(pAttach, aForce);
4743 multiLock.acquire();
4744 mediumLock.acquire();
4745
4746 /* On error roll back this change only. */
4747 if (FAILED(rc))
4748 {
4749 if (!pMedium.isNull())
4750 pMedium->i_removeBackReference(mData->mUuid);
4751 pAttach = i_findAttachment(*mMediumAttachments.data(),
4752 aName,
4753 aControllerPort,
4754 aDevice);
4755 /* If the attachment is gone in the meantime, bail out. */
4756 if (pAttach.isNull())
4757 return rc;
4758 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4759 if (!oldmedium.isNull())
4760 oldmedium->i_addBackReference(mData->mUuid);
4761 pAttach->i_updateMedium(oldmedium);
4762 }
4763
4764 mediumLock.release();
4765 multiLock.release();
4766
4767 /* Save modified registries, but skip this machine as it's the caller's
4768 * job to save its settings like all other settings changes. */
4769 mParent->i_unmarkRegistryModified(i_getId());
4770 mParent->i_saveModifiedRegistries();
4771
4772 return rc;
4773}
4774HRESULT Machine::getMedium(const com::Utf8Str &aName,
4775 LONG aControllerPort,
4776 LONG aDevice,
4777 ComPtr<IMedium> &aMedium)
4778{
4779 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4780 aName.c_str(), aControllerPort, aDevice));
4781
4782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4783
4784 aMedium = NULL;
4785
4786 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4787 aName,
4788 aControllerPort,
4789 aDevice);
4790 if (pAttach.isNull())
4791 return setError(VBOX_E_OBJECT_NOT_FOUND,
4792 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4793 aDevice, aControllerPort, aName.c_str());
4794
4795 aMedium = pAttach->i_getMedium();
4796
4797 return S_OK;
4798}
4799
4800HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4801{
4802
4803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4806
4807 return S_OK;
4808}
4809
4810HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4811{
4812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4813
4814 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4815
4816 return S_OK;
4817}
4818
4819HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4820{
4821 /* Do not assert if slot is out of range, just return the advertised
4822 status. testdriver/vbox.py triggers this in logVmInfo. */
4823 if (aSlot >= mNetworkAdapters.size())
4824 return setError(E_INVALIDARG,
4825 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4826 aSlot, mNetworkAdapters.size());
4827
4828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4831
4832 return S_OK;
4833}
4834
4835HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4836{
4837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4838
4839 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4840 size_t i = 0;
4841 for (settings::StringsMap::const_iterator
4842 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4843 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4844 ++it, ++i)
4845 aKeys[i] = it->first;
4846
4847 return S_OK;
4848}
4849
4850 /**
4851 * @note Locks this object for reading.
4852 */
4853HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4854 com::Utf8Str &aValue)
4855{
4856 /* start with nothing found */
4857 aValue = "";
4858
4859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4860
4861 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4862 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4863 // found:
4864 aValue = it->second; // source is a Utf8Str
4865
4866 /* return the result to caller (may be empty) */
4867 return S_OK;
4868}
4869
4870 /**
4871 * @note Locks mParent for writing + this object for writing.
4872 */
4873HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4874{
4875 /* Because control characters in aKey have caused problems in the settings
4876 * they are rejected unless the key should be deleted. */
4877 if (!aValue.isEmpty())
4878 {
4879 for (size_t i = 0; i < aKey.length(); ++i)
4880 {
4881 char ch = aKey[i];
4882 if (RTLocCIsCntrl(ch))
4883 return E_INVALIDARG;
4884 }
4885 }
4886
4887 Utf8Str strOldValue; // empty
4888
4889 // locking note: we only hold the read lock briefly to look up the old value,
4890 // then release it and call the onExtraCanChange callbacks. There is a small
4891 // chance of a race insofar as the callback might be called twice if two callers
4892 // change the same key at the same time, but that's a much better solution
4893 // than the deadlock we had here before. The actual changing of the extradata
4894 // is then performed under the write lock and race-free.
4895
4896 // look up the old value first; if nothing has changed then we need not do anything
4897 {
4898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4899
4900 // For snapshots don't even think about allowing changes, extradata
4901 // is global for a machine, so there is nothing snapshot specific.
4902 if (i_isSnapshotMachine())
4903 return setError(VBOX_E_INVALID_VM_STATE,
4904 tr("Cannot set extradata for a snapshot"));
4905
4906 // check if the right IMachine instance is used
4907 if (mData->mRegistered && !i_isSessionMachine())
4908 return setError(VBOX_E_INVALID_VM_STATE,
4909 tr("Cannot set extradata for an immutable machine"));
4910
4911 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4912 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4913 strOldValue = it->second;
4914 }
4915
4916 bool fChanged;
4917 if ((fChanged = (strOldValue != aValue)))
4918 {
4919 // ask for permission from all listeners outside the locks;
4920 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4921 // lock to copy the list of callbacks to invoke
4922 Bstr error;
4923 Bstr bstrValue(aValue);
4924
4925 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4926 {
4927 const char *sep = error.isEmpty() ? "" : ": ";
4928 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4929 return setError(E_ACCESSDENIED,
4930 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4931 aKey.c_str(),
4932 aValue.c_str(),
4933 sep,
4934 error.raw());
4935 }
4936
4937 // data is changing and change not vetoed: then write it out under the lock
4938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4939
4940 if (aValue.isEmpty())
4941 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4942 else
4943 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4944 // creates a new key if needed
4945
4946 bool fNeedsGlobalSaveSettings = false;
4947 // This saving of settings is tricky: there is no "old state" for the
4948 // extradata items at all (unlike all other settings), so the old/new
4949 // settings comparison would give a wrong result!
4950 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4951
4952 if (fNeedsGlobalSaveSettings)
4953 {
4954 // save the global settings; for that we should hold only the VirtualBox lock
4955 alock.release();
4956 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4957 mParent->i_saveSettings();
4958 }
4959 }
4960
4961 // fire notification outside the lock
4962 if (fChanged)
4963 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4964
4965 return S_OK;
4966}
4967
4968HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4969{
4970 aProgress = NULL;
4971 NOREF(aSettingsFilePath);
4972 ReturnComNotImplemented();
4973}
4974
4975HRESULT Machine::saveSettings()
4976{
4977 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4978
4979 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4980 if (FAILED(rc)) return rc;
4981
4982 /* the settings file path may never be null */
4983 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4984
4985 /* save all VM data excluding snapshots */
4986 bool fNeedsGlobalSaveSettings = false;
4987 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4988 mlock.release();
4989
4990 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4991 {
4992 // save the global settings; for that we should hold only the VirtualBox lock
4993 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4994 rc = mParent->i_saveSettings();
4995 }
4996
4997 return rc;
4998}
4999
5000
5001HRESULT Machine::discardSettings()
5002{
5003 /*
5004 * We need to take the machine list lock here as well as the machine one
5005 * or we'll get into trouble should any media stuff require rolling back.
5006 *
5007 * Details:
5008 *
5009 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5010 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5011 * 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]
5012 * 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
5013 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5014 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5015 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5016 * 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
5017 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5018 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5019 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5020 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5021 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5022 * 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]
5023 * 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] (*)
5024 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5025 * 0:005> k
5026 * # Child-SP RetAddr Call Site
5027 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5028 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5029 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5030 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5031 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5032 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5033 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5034 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5035 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5036 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5037 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5038 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5039 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5040 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5041 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5042 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5043 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5044 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5045 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5046 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5047 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5048 *
5049 */
5050 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5054 if (FAILED(rc)) return rc;
5055
5056 /*
5057 * during this rollback, the session will be notified if data has
5058 * been actually changed
5059 */
5060 i_rollback(true /* aNotify */);
5061
5062 return S_OK;
5063}
5064
5065/** @note Locks objects! */
5066HRESULT Machine::unregister(AutoCaller &autoCaller,
5067 CleanupMode_T aCleanupMode,
5068 std::vector<ComPtr<IMedium> > &aMedia)
5069{
5070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5071
5072 Guid id(i_getId());
5073
5074 if (mData->mSession.mState != SessionState_Unlocked)
5075 return setError(VBOX_E_INVALID_OBJECT_STATE,
5076 tr("Cannot unregister the machine '%s' while it is locked"),
5077 mUserData->s.strName.c_str());
5078
5079 // wait for state dependents to drop to zero
5080 i_ensureNoStateDependencies();
5081
5082 if (!mData->mAccessible)
5083 {
5084 // inaccessible maschines can only be unregistered; uninitialize ourselves
5085 // here because currently there may be no unregistered that are inaccessible
5086 // (this state combination is not supported). Note releasing the caller and
5087 // leaving the lock before calling uninit()
5088 alock.release();
5089 autoCaller.release();
5090
5091 uninit();
5092
5093 mParent->i_unregisterMachine(this, id);
5094 // calls VirtualBox::i_saveSettings()
5095
5096 return S_OK;
5097 }
5098
5099 HRESULT rc = S_OK;
5100
5101 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5102 // discard saved state
5103 if (mData->mMachineState == MachineState_Saved)
5104 {
5105 // add the saved state file to the list of files the caller should delete
5106 Assert(!mSSData->strStateFilePath.isEmpty());
5107 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5108
5109 mSSData->strStateFilePath.setNull();
5110
5111 // unconditionally set the machine state to powered off, we now
5112 // know no session has locked the machine
5113 mData->mMachineState = MachineState_PoweredOff;
5114 }
5115
5116 size_t cSnapshots = 0;
5117 if (mData->mFirstSnapshot)
5118 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5119 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5120 // fail now before we start detaching media
5121 return setError(VBOX_E_INVALID_OBJECT_STATE,
5122 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5123 mUserData->s.strName.c_str(), cSnapshots);
5124
5125 // This list collects the medium objects from all medium attachments
5126 // which we will detach from the machine and its snapshots, in a specific
5127 // order which allows for closing all media without getting "media in use"
5128 // errors, simply by going through the list from the front to the back:
5129 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5130 // and must be closed before the parent media from the snapshots, or closing the parents
5131 // will fail because they still have children);
5132 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5133 // the root ("first") snapshot of the machine.
5134 MediaList llMedia;
5135
5136 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5137 && mMediumAttachments->size()
5138 )
5139 {
5140 // we have media attachments: detach them all and add the Medium objects to our list
5141 if (aCleanupMode != CleanupMode_UnregisterOnly)
5142 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5143 else
5144 return setError(VBOX_E_INVALID_OBJECT_STATE,
5145 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5146 mUserData->s.strName.c_str(), mMediumAttachments->size());
5147 }
5148
5149 if (cSnapshots)
5150 {
5151 // add the media from the medium attachments of the snapshots to llMedia
5152 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5153 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5154 // into the children first
5155
5156 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5157 MachineState_T oldState = mData->mMachineState;
5158 mData->mMachineState = MachineState_DeletingSnapshot;
5159
5160 // make a copy of the first snapshot so the refcount does not drop to 0
5161 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5162 // because of the AutoCaller voodoo)
5163 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5164
5165 // GO!
5166 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5167
5168 mData->mMachineState = oldState;
5169 }
5170
5171 if (FAILED(rc))
5172 {
5173 i_rollbackMedia();
5174 return rc;
5175 }
5176
5177 // commit all the media changes made above
5178 i_commitMedia();
5179
5180 mData->mRegistered = false;
5181
5182 // machine lock no longer needed
5183 alock.release();
5184
5185 // return media to caller
5186 aMedia.resize(llMedia.size());
5187 size_t i = 0;
5188 for (MediaList::const_iterator
5189 it = llMedia.begin();
5190 it != llMedia.end();
5191 ++it, ++i)
5192 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5193
5194 mParent->i_unregisterMachine(this, id);
5195 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5196
5197 return S_OK;
5198}
5199
5200/**
5201 * Task record for deleting a machine config.
5202 */
5203class Machine::DeleteConfigTask
5204 : public Machine::Task
5205{
5206public:
5207 DeleteConfigTask(Machine *m,
5208 Progress *p,
5209 const Utf8Str &t,
5210 const RTCList<ComPtr<IMedium> > &llMediums,
5211 const StringsList &llFilesToDelete)
5212 : Task(m, p, t),
5213 m_llMediums(llMediums),
5214 m_llFilesToDelete(llFilesToDelete)
5215 {}
5216
5217private:
5218 void handler()
5219 {
5220 try
5221 {
5222 m_pMachine->i_deleteConfigHandler(*this);
5223 }
5224 catch (...)
5225 {
5226 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5227 }
5228 }
5229
5230 RTCList<ComPtr<IMedium> > m_llMediums;
5231 StringsList m_llFilesToDelete;
5232
5233 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5234};
5235
5236/**
5237 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5238 * SessionMachine::taskHandler().
5239 *
5240 * @note Locks this object for writing.
5241 *
5242 * @param task
5243 * @return
5244 */
5245void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5246{
5247 LogFlowThisFuncEnter();
5248
5249 AutoCaller autoCaller(this);
5250 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5251 if (FAILED(autoCaller.rc()))
5252 {
5253 /* we might have been uninitialized because the session was accidentally
5254 * closed by the client, so don't assert */
5255 HRESULT rc = setError(E_FAIL,
5256 tr("The session has been accidentally closed"));
5257 task.m_pProgress->i_notifyComplete(rc);
5258 LogFlowThisFuncLeave();
5259 return;
5260 }
5261
5262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 HRESULT rc = S_OK;
5265
5266 try
5267 {
5268 ULONG uLogHistoryCount = 3;
5269 ComPtr<ISystemProperties> systemProperties;
5270 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5271 if (FAILED(rc)) throw rc;
5272
5273 if (!systemProperties.isNull())
5274 {
5275 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5276 if (FAILED(rc)) throw rc;
5277 }
5278
5279 MachineState_T oldState = mData->mMachineState;
5280 i_setMachineState(MachineState_SettingUp);
5281 alock.release();
5282 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5283 {
5284 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5285 {
5286 AutoCaller mac(pMedium);
5287 if (FAILED(mac.rc())) throw mac.rc();
5288 Utf8Str strLocation = pMedium->i_getLocationFull();
5289 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5290 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5291 if (FAILED(rc)) throw rc;
5292 }
5293 if (pMedium->i_isMediumFormatFile())
5294 {
5295 ComPtr<IProgress> pProgress2;
5296 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5297 if (FAILED(rc)) throw rc;
5298 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5299 if (FAILED(rc)) throw rc;
5300 }
5301
5302 /* Close the medium, deliberately without checking the return
5303 * code, and without leaving any trace in the error info, as
5304 * a failure here is a very minor issue, which shouldn't happen
5305 * as above we even managed to delete the medium. */
5306 {
5307 ErrorInfoKeeper eik;
5308 pMedium->Close();
5309 }
5310 }
5311 i_setMachineState(oldState);
5312 alock.acquire();
5313
5314 // delete the files pushed on the task list by Machine::Delete()
5315 // (this includes saved states of the machine and snapshots and
5316 // medium storage files from the IMedium list passed in, and the
5317 // machine XML file)
5318 for (StringsList::const_iterator
5319 it = task.m_llFilesToDelete.begin();
5320 it != task.m_llFilesToDelete.end();
5321 ++it)
5322 {
5323 const Utf8Str &strFile = *it;
5324 LogFunc(("Deleting file %s\n", strFile.c_str()));
5325 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5326 if (FAILED(rc)) throw rc;
5327
5328 int vrc = RTFileDelete(strFile.c_str());
5329 if (RT_FAILURE(vrc))
5330 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5331 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5332 }
5333
5334 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5335 if (FAILED(rc)) throw rc;
5336
5337 /* delete the settings only when the file actually exists */
5338 if (mData->pMachineConfigFile->fileExists())
5339 {
5340 /* Delete any backup or uncommitted XML files. Ignore failures.
5341 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5342 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5343 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5344 RTFileDelete(otherXml.c_str());
5345 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5346 RTFileDelete(otherXml.c_str());
5347
5348 /* delete the Logs folder, nothing important should be left
5349 * there (we don't check for errors because the user might have
5350 * some private files there that we don't want to delete) */
5351 Utf8Str logFolder;
5352 getLogFolder(logFolder);
5353 Assert(logFolder.length());
5354 if (RTDirExists(logFolder.c_str()))
5355 {
5356 /* Delete all VBox.log[.N] files from the Logs folder
5357 * (this must be in sync with the rotation logic in
5358 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5359 * files that may have been created by the GUI. */
5360 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5361 logFolder.c_str(), RTPATH_DELIMITER);
5362 RTFileDelete(log.c_str());
5363 log = Utf8StrFmt("%s%cVBox.png",
5364 logFolder.c_str(), RTPATH_DELIMITER);
5365 RTFileDelete(log.c_str());
5366 for (int i = uLogHistoryCount; i > 0; i--)
5367 {
5368 log = Utf8StrFmt("%s%cVBox.log.%d",
5369 logFolder.c_str(), RTPATH_DELIMITER, i);
5370 RTFileDelete(log.c_str());
5371 log = Utf8StrFmt("%s%cVBox.png.%d",
5372 logFolder.c_str(), RTPATH_DELIMITER, i);
5373 RTFileDelete(log.c_str());
5374 }
5375#if defined(RT_OS_WINDOWS)
5376 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5377 RTFileDelete(log.c_str());
5378 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5379 RTFileDelete(log.c_str());
5380#endif
5381
5382 RTDirRemove(logFolder.c_str());
5383 }
5384
5385 /* delete the Snapshots folder, nothing important should be left
5386 * there (we don't check for errors because the user might have
5387 * some private files there that we don't want to delete) */
5388 Utf8Str strFullSnapshotFolder;
5389 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5390 Assert(!strFullSnapshotFolder.isEmpty());
5391 if (RTDirExists(strFullSnapshotFolder.c_str()))
5392 RTDirRemove(strFullSnapshotFolder.c_str());
5393
5394 // delete the directory that contains the settings file, but only
5395 // if it matches the VM name
5396 Utf8Str settingsDir;
5397 if (i_isInOwnDir(&settingsDir))
5398 RTDirRemove(settingsDir.c_str());
5399 }
5400
5401 alock.release();
5402
5403 mParent->i_saveModifiedRegistries();
5404 }
5405 catch (HRESULT aRC) { rc = aRC; }
5406
5407 task.m_pProgress->i_notifyComplete(rc);
5408
5409 LogFlowThisFuncLeave();
5410}
5411
5412HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5413{
5414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5415
5416 HRESULT rc = i_checkStateDependency(MutableStateDep);
5417 if (FAILED(rc)) return rc;
5418
5419 if (mData->mRegistered)
5420 return setError(VBOX_E_INVALID_VM_STATE,
5421 tr("Cannot delete settings of a registered machine"));
5422
5423 // collect files to delete
5424 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5425 if (mData->pMachineConfigFile->fileExists())
5426 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5427
5428 RTCList<ComPtr<IMedium> > llMediums;
5429 for (size_t i = 0; i < aMedia.size(); ++i)
5430 {
5431 IMedium *pIMedium(aMedia[i]);
5432 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5433 if (pMedium.isNull())
5434 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5435 SafeArray<BSTR> ids;
5436 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5437 if (FAILED(rc)) return rc;
5438 /* At this point the medium should not have any back references
5439 * anymore. If it has it is attached to another VM and *must* not
5440 * deleted. */
5441 if (ids.size() < 1)
5442 llMediums.append(pMedium);
5443 }
5444
5445 ComObjPtr<Progress> pProgress;
5446 pProgress.createObject();
5447 rc = pProgress->init(i_getVirtualBox(),
5448 static_cast<IMachine*>(this) /* aInitiator */,
5449 tr("Deleting files"),
5450 true /* fCancellable */,
5451 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5452 tr("Collecting file inventory"));
5453 if (FAILED(rc))
5454 return rc;
5455
5456 /* create and start the task on a separate thread (note that it will not
5457 * start working until we release alock) */
5458 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5459 rc = pTask->createThread();
5460 if (FAILED(rc))
5461 return rc;
5462
5463 pProgress.queryInterfaceTo(aProgress.asOutParam());
5464
5465 LogFlowFuncLeave();
5466
5467 return S_OK;
5468}
5469
5470HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5471{
5472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5473
5474 ComObjPtr<Snapshot> pSnapshot;
5475 HRESULT rc;
5476
5477 if (aNameOrId.isEmpty())
5478 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5479 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5480 else
5481 {
5482 Guid uuid(aNameOrId);
5483 if (uuid.isValid())
5484 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5485 else
5486 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5487 }
5488 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5489
5490 return rc;
5491}
5492
5493HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5494 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5495{
5496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5497
5498 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5499 if (FAILED(rc)) return rc;
5500
5501 ComObjPtr<SharedFolder> sharedFolder;
5502 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5503 if (SUCCEEDED(rc))
5504 return setError(VBOX_E_OBJECT_IN_USE,
5505 tr("Shared folder named '%s' already exists"),
5506 aName.c_str());
5507
5508 sharedFolder.createObject();
5509 rc = sharedFolder->init(i_getMachine(),
5510 aName,
5511 aHostPath,
5512 !!aWritable,
5513 !!aAutomount,
5514 aAutoMountPoint,
5515 true /* fFailOnError */);
5516 if (FAILED(rc)) return rc;
5517
5518 i_setModified(IsModified_SharedFolders);
5519 mHWData.backup();
5520 mHWData->mSharedFolders.push_back(sharedFolder);
5521
5522 /* inform the direct session if any */
5523 alock.release();
5524 i_onSharedFolderChange();
5525
5526 return S_OK;
5527}
5528
5529HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5530{
5531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5532
5533 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5534 if (FAILED(rc)) return rc;
5535
5536 ComObjPtr<SharedFolder> sharedFolder;
5537 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5538 if (FAILED(rc)) return rc;
5539
5540 i_setModified(IsModified_SharedFolders);
5541 mHWData.backup();
5542 mHWData->mSharedFolders.remove(sharedFolder);
5543
5544 /* inform the direct session if any */
5545 alock.release();
5546 i_onSharedFolderChange();
5547
5548 return S_OK;
5549}
5550
5551HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5552{
5553 /* start with No */
5554 *aCanShow = FALSE;
5555
5556 ComPtr<IInternalSessionControl> directControl;
5557 {
5558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5559
5560 if (mData->mSession.mState != SessionState_Locked)
5561 return setError(VBOX_E_INVALID_VM_STATE,
5562 tr("Machine is not locked for session (session state: %s)"),
5563 Global::stringifySessionState(mData->mSession.mState));
5564
5565 if (mData->mSession.mLockType == LockType_VM)
5566 directControl = mData->mSession.mDirectControl;
5567 }
5568
5569 /* ignore calls made after #OnSessionEnd() is called */
5570 if (!directControl)
5571 return S_OK;
5572
5573 LONG64 dummy;
5574 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5575}
5576
5577HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5578{
5579 ComPtr<IInternalSessionControl> directControl;
5580 {
5581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5582
5583 if (mData->mSession.mState != SessionState_Locked)
5584 return setError(E_FAIL,
5585 tr("Machine is not locked for session (session state: %s)"),
5586 Global::stringifySessionState(mData->mSession.mState));
5587
5588 if (mData->mSession.mLockType == LockType_VM)
5589 directControl = mData->mSession.mDirectControl;
5590 }
5591
5592 /* ignore calls made after #OnSessionEnd() is called */
5593 if (!directControl)
5594 return S_OK;
5595
5596 BOOL dummy;
5597 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5598}
5599
5600#ifdef VBOX_WITH_GUEST_PROPS
5601/**
5602 * Look up a guest property in VBoxSVC's internal structures.
5603 */
5604HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5605 com::Utf8Str &aValue,
5606 LONG64 *aTimestamp,
5607 com::Utf8Str &aFlags) const
5608{
5609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5610
5611 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5612 if (it != mHWData->mGuestProperties.end())
5613 {
5614 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5615 aValue = it->second.strValue;
5616 *aTimestamp = it->second.mTimestamp;
5617 GuestPropWriteFlags(it->second.mFlags, szFlags);
5618 aFlags = Utf8Str(szFlags);
5619 }
5620
5621 return S_OK;
5622}
5623
5624/**
5625 * Query the VM that a guest property belongs to for the property.
5626 * @returns E_ACCESSDENIED if the VM process is not available or not
5627 * currently handling queries and the lookup should then be done in
5628 * VBoxSVC.
5629 */
5630HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5631 com::Utf8Str &aValue,
5632 LONG64 *aTimestamp,
5633 com::Utf8Str &aFlags) const
5634{
5635 HRESULT rc = S_OK;
5636 Bstr bstrValue;
5637 Bstr bstrFlags;
5638
5639 ComPtr<IInternalSessionControl> directControl;
5640 {
5641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5642 if (mData->mSession.mLockType == LockType_VM)
5643 directControl = mData->mSession.mDirectControl;
5644 }
5645
5646 /* ignore calls made after #OnSessionEnd() is called */
5647 if (!directControl)
5648 rc = E_ACCESSDENIED;
5649 else
5650 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5651 0 /* accessMode */,
5652 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5653
5654 aValue = bstrValue;
5655 aFlags = bstrFlags;
5656
5657 return rc;
5658}
5659#endif // VBOX_WITH_GUEST_PROPS
5660
5661HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5662 com::Utf8Str &aValue,
5663 LONG64 *aTimestamp,
5664 com::Utf8Str &aFlags)
5665{
5666#ifndef VBOX_WITH_GUEST_PROPS
5667 ReturnComNotImplemented();
5668#else // VBOX_WITH_GUEST_PROPS
5669
5670 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5671
5672 if (rc == E_ACCESSDENIED)
5673 /* The VM is not running or the service is not (yet) accessible */
5674 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5675 return rc;
5676#endif // VBOX_WITH_GUEST_PROPS
5677}
5678
5679HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5680{
5681 LONG64 dummyTimestamp;
5682 com::Utf8Str dummyFlags;
5683 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5684 return rc;
5685
5686}
5687HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5688{
5689 com::Utf8Str dummyFlags;
5690 com::Utf8Str dummyValue;
5691 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5692 return rc;
5693}
5694
5695#ifdef VBOX_WITH_GUEST_PROPS
5696/**
5697 * Set a guest property in VBoxSVC's internal structures.
5698 */
5699HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5700 const com::Utf8Str &aFlags, bool fDelete)
5701{
5702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5703 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5704 if (FAILED(rc)) return rc;
5705
5706 try
5707 {
5708 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5709 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5710 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5711
5712 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5713 if (it == mHWData->mGuestProperties.end())
5714 {
5715 if (!fDelete)
5716 {
5717 i_setModified(IsModified_MachineData);
5718 mHWData.backupEx();
5719
5720 RTTIMESPEC time;
5721 HWData::GuestProperty prop;
5722 prop.strValue = Bstr(aValue).raw();
5723 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5724 prop.mFlags = fFlags;
5725 mHWData->mGuestProperties[aName] = prop;
5726 }
5727 }
5728 else
5729 {
5730 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5731 {
5732 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5733 }
5734 else
5735 {
5736 i_setModified(IsModified_MachineData);
5737 mHWData.backupEx();
5738
5739 /* The backupEx() operation invalidates our iterator,
5740 * so get a new one. */
5741 it = mHWData->mGuestProperties.find(aName);
5742 Assert(it != mHWData->mGuestProperties.end());
5743
5744 if (!fDelete)
5745 {
5746 RTTIMESPEC time;
5747 it->second.strValue = aValue;
5748 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5749 it->second.mFlags = fFlags;
5750 }
5751 else
5752 mHWData->mGuestProperties.erase(it);
5753 }
5754 }
5755
5756 if (SUCCEEDED(rc))
5757 {
5758 alock.release();
5759
5760 mParent->i_onGuestPropertyChange(mData->mUuid,
5761 Bstr(aName).raw(),
5762 Bstr(aValue).raw(),
5763 Bstr(aFlags).raw());
5764 }
5765 }
5766 catch (std::bad_alloc &)
5767 {
5768 rc = E_OUTOFMEMORY;
5769 }
5770
5771 return rc;
5772}
5773
5774/**
5775 * Set a property on the VM that that property belongs to.
5776 * @returns E_ACCESSDENIED if the VM process is not available or not
5777 * currently handling queries and the setting should then be done in
5778 * VBoxSVC.
5779 */
5780HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5781 const com::Utf8Str &aFlags, bool fDelete)
5782{
5783 HRESULT rc;
5784
5785 try
5786 {
5787 ComPtr<IInternalSessionControl> directControl;
5788 {
5789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5790 if (mData->mSession.mLockType == LockType_VM)
5791 directControl = mData->mSession.mDirectControl;
5792 }
5793
5794 Bstr dummy1; /* will not be changed (setter) */
5795 Bstr dummy2; /* will not be changed (setter) */
5796 LONG64 dummy64;
5797 if (!directControl)
5798 rc = E_ACCESSDENIED;
5799 else
5800 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5801 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5802 fDelete ? 2 : 1 /* accessMode */,
5803 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5804 }
5805 catch (std::bad_alloc &)
5806 {
5807 rc = E_OUTOFMEMORY;
5808 }
5809
5810 return rc;
5811}
5812#endif // VBOX_WITH_GUEST_PROPS
5813
5814HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5815 const com::Utf8Str &aFlags)
5816{
5817#ifndef VBOX_WITH_GUEST_PROPS
5818 ReturnComNotImplemented();
5819#else // VBOX_WITH_GUEST_PROPS
5820 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5821 if (rc == E_ACCESSDENIED)
5822 /* The VM is not running or the service is not (yet) accessible */
5823 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5824 return rc;
5825#endif // VBOX_WITH_GUEST_PROPS
5826}
5827
5828HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5829{
5830 return setGuestProperty(aProperty, aValue, "");
5831}
5832
5833HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5834{
5835#ifndef VBOX_WITH_GUEST_PROPS
5836 ReturnComNotImplemented();
5837#else // VBOX_WITH_GUEST_PROPS
5838 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5839 if (rc == E_ACCESSDENIED)
5840 /* The VM is not running or the service is not (yet) accessible */
5841 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5842 return rc;
5843#endif // VBOX_WITH_GUEST_PROPS
5844}
5845
5846#ifdef VBOX_WITH_GUEST_PROPS
5847/**
5848 * Enumerate the guest properties in VBoxSVC's internal structures.
5849 */
5850HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5851 std::vector<com::Utf8Str> &aNames,
5852 std::vector<com::Utf8Str> &aValues,
5853 std::vector<LONG64> &aTimestamps,
5854 std::vector<com::Utf8Str> &aFlags)
5855{
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857 Utf8Str strPatterns(aPatterns);
5858
5859 /*
5860 * Look for matching patterns and build up a list.
5861 */
5862 HWData::GuestPropertyMap propMap;
5863 for (HWData::GuestPropertyMap::const_iterator
5864 it = mHWData->mGuestProperties.begin();
5865 it != mHWData->mGuestProperties.end();
5866 ++it)
5867 {
5868 if ( strPatterns.isEmpty()
5869 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5870 RTSTR_MAX,
5871 it->first.c_str(),
5872 RTSTR_MAX,
5873 NULL)
5874 )
5875 propMap.insert(*it);
5876 }
5877
5878 alock.release();
5879
5880 /*
5881 * And build up the arrays for returning the property information.
5882 */
5883 size_t cEntries = propMap.size();
5884
5885 aNames.resize(cEntries);
5886 aValues.resize(cEntries);
5887 aTimestamps.resize(cEntries);
5888 aFlags.resize(cEntries);
5889
5890 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5891 size_t i = 0;
5892 for (HWData::GuestPropertyMap::const_iterator
5893 it = propMap.begin();
5894 it != propMap.end();
5895 ++it, ++i)
5896 {
5897 aNames[i] = it->first;
5898 aValues[i] = it->second.strValue;
5899 aTimestamps[i] = it->second.mTimestamp;
5900 GuestPropWriteFlags(it->second.mFlags, szFlags);
5901 aFlags[i] = Utf8Str(szFlags);
5902 }
5903
5904 return S_OK;
5905}
5906
5907/**
5908 * Enumerate the properties managed by a VM.
5909 * @returns E_ACCESSDENIED if the VM process is not available or not
5910 * currently handling queries and the setting should then be done in
5911 * VBoxSVC.
5912 */
5913HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5914 std::vector<com::Utf8Str> &aNames,
5915 std::vector<com::Utf8Str> &aValues,
5916 std::vector<LONG64> &aTimestamps,
5917 std::vector<com::Utf8Str> &aFlags)
5918{
5919 HRESULT rc;
5920 ComPtr<IInternalSessionControl> directControl;
5921 {
5922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5923 if (mData->mSession.mLockType == LockType_VM)
5924 directControl = mData->mSession.mDirectControl;
5925 }
5926
5927 com::SafeArray<BSTR> bNames;
5928 com::SafeArray<BSTR> bValues;
5929 com::SafeArray<LONG64> bTimestamps;
5930 com::SafeArray<BSTR> bFlags;
5931
5932 if (!directControl)
5933 rc = E_ACCESSDENIED;
5934 else
5935 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5936 ComSafeArrayAsOutParam(bNames),
5937 ComSafeArrayAsOutParam(bValues),
5938 ComSafeArrayAsOutParam(bTimestamps),
5939 ComSafeArrayAsOutParam(bFlags));
5940 size_t i;
5941 aNames.resize(bNames.size());
5942 for (i = 0; i < bNames.size(); ++i)
5943 aNames[i] = Utf8Str(bNames[i]);
5944 aValues.resize(bValues.size());
5945 for (i = 0; i < bValues.size(); ++i)
5946 aValues[i] = Utf8Str(bValues[i]);
5947 aTimestamps.resize(bTimestamps.size());
5948 for (i = 0; i < bTimestamps.size(); ++i)
5949 aTimestamps[i] = bTimestamps[i];
5950 aFlags.resize(bFlags.size());
5951 for (i = 0; i < bFlags.size(); ++i)
5952 aFlags[i] = Utf8Str(bFlags[i]);
5953
5954 return rc;
5955}
5956#endif // VBOX_WITH_GUEST_PROPS
5957HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5958 std::vector<com::Utf8Str> &aNames,
5959 std::vector<com::Utf8Str> &aValues,
5960 std::vector<LONG64> &aTimestamps,
5961 std::vector<com::Utf8Str> &aFlags)
5962{
5963#ifndef VBOX_WITH_GUEST_PROPS
5964 ReturnComNotImplemented();
5965#else // VBOX_WITH_GUEST_PROPS
5966
5967 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5968
5969 if (rc == E_ACCESSDENIED)
5970 /* The VM is not running or the service is not (yet) accessible */
5971 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5972 return rc;
5973#endif // VBOX_WITH_GUEST_PROPS
5974}
5975
5976HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5977 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5978{
5979 MediumAttachmentList atts;
5980
5981 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5982 if (FAILED(rc)) return rc;
5983
5984 aMediumAttachments.resize(atts.size());
5985 size_t i = 0;
5986 for (MediumAttachmentList::const_iterator
5987 it = atts.begin();
5988 it != atts.end();
5989 ++it, ++i)
5990 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5991
5992 return S_OK;
5993}
5994
5995HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5996 LONG aControllerPort,
5997 LONG aDevice,
5998 ComPtr<IMediumAttachment> &aAttachment)
5999{
6000 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6001 aName.c_str(), aControllerPort, aDevice));
6002
6003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6004
6005 aAttachment = NULL;
6006
6007 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6008 aName,
6009 aControllerPort,
6010 aDevice);
6011 if (pAttach.isNull())
6012 return setError(VBOX_E_OBJECT_NOT_FOUND,
6013 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6014 aDevice, aControllerPort, aName.c_str());
6015
6016 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6017
6018 return S_OK;
6019}
6020
6021
6022HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6023 StorageBus_T aConnectionType,
6024 ComPtr<IStorageController> &aController)
6025{
6026 if ( (aConnectionType <= StorageBus_Null)
6027 || (aConnectionType > StorageBus_VirtioSCSI))
6028 return setError(E_INVALIDARG,
6029 tr("Invalid connection type: %d"),
6030 aConnectionType);
6031
6032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6033
6034 HRESULT rc = i_checkStateDependency(MutableStateDep);
6035 if (FAILED(rc)) return rc;
6036
6037 /* try to find one with the name first. */
6038 ComObjPtr<StorageController> ctrl;
6039
6040 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6041 if (SUCCEEDED(rc))
6042 return setError(VBOX_E_OBJECT_IN_USE,
6043 tr("Storage controller named '%s' already exists"),
6044 aName.c_str());
6045
6046 ctrl.createObject();
6047
6048 /* get a new instance number for the storage controller */
6049 ULONG ulInstance = 0;
6050 bool fBootable = true;
6051 for (StorageControllerList::const_iterator
6052 it = mStorageControllers->begin();
6053 it != mStorageControllers->end();
6054 ++it)
6055 {
6056 if ((*it)->i_getStorageBus() == aConnectionType)
6057 {
6058 ULONG ulCurInst = (*it)->i_getInstance();
6059
6060 if (ulCurInst >= ulInstance)
6061 ulInstance = ulCurInst + 1;
6062
6063 /* Only one controller of each type can be marked as bootable. */
6064 if ((*it)->i_getBootable())
6065 fBootable = false;
6066 }
6067 }
6068
6069 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6070 if (FAILED(rc)) return rc;
6071
6072 i_setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074 mStorageControllers->push_back(ctrl);
6075
6076 ctrl.queryInterfaceTo(aController.asOutParam());
6077
6078 /* inform the direct session if any */
6079 alock.release();
6080 i_onStorageControllerChange(i_getId(), aName);
6081
6082 return S_OK;
6083}
6084
6085HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6086 ComPtr<IStorageController> &aStorageController)
6087{
6088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 ComObjPtr<StorageController> ctrl;
6091
6092 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6093 if (SUCCEEDED(rc))
6094 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6095
6096 return rc;
6097}
6098
6099HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6100 ULONG aInstance,
6101 ComPtr<IStorageController> &aStorageController)
6102{
6103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6104
6105 for (StorageControllerList::const_iterator
6106 it = mStorageControllers->begin();
6107 it != mStorageControllers->end();
6108 ++it)
6109 {
6110 if ( (*it)->i_getStorageBus() == aConnectionType
6111 && (*it)->i_getInstance() == aInstance)
6112 {
6113 (*it).queryInterfaceTo(aStorageController.asOutParam());
6114 return S_OK;
6115 }
6116 }
6117
6118 return setError(VBOX_E_OBJECT_NOT_FOUND,
6119 tr("Could not find a storage controller with instance number '%lu'"),
6120 aInstance);
6121}
6122
6123HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6124{
6125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6126
6127 HRESULT rc = i_checkStateDependency(MutableStateDep);
6128 if (FAILED(rc)) return rc;
6129
6130 ComObjPtr<StorageController> ctrl;
6131
6132 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6133 if (SUCCEEDED(rc))
6134 {
6135 /* Ensure that only one controller of each type is marked as bootable. */
6136 if (aBootable == TRUE)
6137 {
6138 for (StorageControllerList::const_iterator
6139 it = mStorageControllers->begin();
6140 it != mStorageControllers->end();
6141 ++it)
6142 {
6143 ComObjPtr<StorageController> aCtrl = (*it);
6144
6145 if ( (aCtrl->i_getName() != aName)
6146 && aCtrl->i_getBootable() == TRUE
6147 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6148 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6149 {
6150 aCtrl->i_setBootable(FALSE);
6151 break;
6152 }
6153 }
6154 }
6155
6156 if (SUCCEEDED(rc))
6157 {
6158 ctrl->i_setBootable(aBootable);
6159 i_setModified(IsModified_Storage);
6160 }
6161 }
6162
6163 if (SUCCEEDED(rc))
6164 {
6165 /* inform the direct session if any */
6166 alock.release();
6167 i_onStorageControllerChange(i_getId(), aName);
6168 }
6169
6170 return rc;
6171}
6172
6173HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6174{
6175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 HRESULT rc = i_checkStateDependency(MutableStateDep);
6178 if (FAILED(rc)) return rc;
6179
6180 ComObjPtr<StorageController> ctrl;
6181 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6182 if (FAILED(rc)) return rc;
6183
6184 MediumAttachmentList llDetachedAttachments;
6185 {
6186 /* find all attached devices to the appropriate storage controller and detach them all */
6187 // make a temporary list because detachDevice invalidates iterators into
6188 // mMediumAttachments
6189 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6190
6191 for (MediumAttachmentList::const_iterator
6192 it = llAttachments2.begin();
6193 it != llAttachments2.end();
6194 ++it)
6195 {
6196 MediumAttachment *pAttachTemp = *it;
6197
6198 AutoCaller localAutoCaller(pAttachTemp);
6199 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6200
6201 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6202
6203 if (pAttachTemp->i_getControllerName() == aName)
6204 {
6205 llDetachedAttachments.push_back(pAttachTemp);
6206 rc = i_detachDevice(pAttachTemp, alock, NULL);
6207 if (FAILED(rc)) return rc;
6208 }
6209 }
6210 }
6211
6212 /* send event about detached devices before removing parent controller */
6213 for (MediumAttachmentList::const_iterator
6214 it = llDetachedAttachments.begin();
6215 it != llDetachedAttachments.end();
6216 ++it)
6217 {
6218 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6219 }
6220
6221 /* We can remove it now. */
6222 i_setModified(IsModified_Storage);
6223 mStorageControllers.backup();
6224
6225 ctrl->i_unshare();
6226
6227 mStorageControllers->remove(ctrl);
6228
6229 /* inform the direct session if any */
6230 alock.release();
6231 i_onStorageControllerChange(i_getId(), aName);
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6237 ComPtr<IUSBController> &aController)
6238{
6239 if ( (aType <= USBControllerType_Null)
6240 || (aType >= USBControllerType_Last))
6241 return setError(E_INVALIDARG,
6242 tr("Invalid USB controller type: %d"),
6243 aType);
6244
6245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 HRESULT rc = i_checkStateDependency(MutableStateDep);
6248 if (FAILED(rc)) return rc;
6249
6250 /* try to find one with the same type first. */
6251 ComObjPtr<USBController> ctrl;
6252
6253 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6254 if (SUCCEEDED(rc))
6255 return setError(VBOX_E_OBJECT_IN_USE,
6256 tr("USB controller named '%s' already exists"),
6257 aName.c_str());
6258
6259 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6260 ULONG maxInstances;
6261 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6262 if (FAILED(rc))
6263 return rc;
6264
6265 ULONG cInstances = i_getUSBControllerCountByType(aType);
6266 if (cInstances >= maxInstances)
6267 return setError(E_INVALIDARG,
6268 tr("Too many USB controllers of this type"));
6269
6270 ctrl.createObject();
6271
6272 rc = ctrl->init(this, aName, aType);
6273 if (FAILED(rc)) return rc;
6274
6275 i_setModified(IsModified_USB);
6276 mUSBControllers.backup();
6277 mUSBControllers->push_back(ctrl);
6278
6279 ctrl.queryInterfaceTo(aController.asOutParam());
6280
6281 /* inform the direct session if any */
6282 alock.release();
6283 i_onUSBControllerChange();
6284
6285 return S_OK;
6286}
6287
6288HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6289{
6290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 ComObjPtr<USBController> ctrl;
6293
6294 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6295 if (SUCCEEDED(rc))
6296 ctrl.queryInterfaceTo(aController.asOutParam());
6297
6298 return rc;
6299}
6300
6301HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6302 ULONG *aControllers)
6303{
6304 if ( (aType <= USBControllerType_Null)
6305 || (aType >= USBControllerType_Last))
6306 return setError(E_INVALIDARG,
6307 tr("Invalid USB controller type: %d"),
6308 aType);
6309
6310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6311
6312 ComObjPtr<USBController> ctrl;
6313
6314 *aControllers = i_getUSBControllerCountByType(aType);
6315
6316 return S_OK;
6317}
6318
6319HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6320{
6321
6322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6323
6324 HRESULT rc = i_checkStateDependency(MutableStateDep);
6325 if (FAILED(rc)) return rc;
6326
6327 ComObjPtr<USBController> ctrl;
6328 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6329 if (FAILED(rc)) return rc;
6330
6331 i_setModified(IsModified_USB);
6332 mUSBControllers.backup();
6333
6334 ctrl->i_unshare();
6335
6336 mUSBControllers->remove(ctrl);
6337
6338 /* inform the direct session if any */
6339 alock.release();
6340 i_onUSBControllerChange();
6341
6342 return S_OK;
6343}
6344
6345HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6346 ULONG *aOriginX,
6347 ULONG *aOriginY,
6348 ULONG *aWidth,
6349 ULONG *aHeight,
6350 BOOL *aEnabled)
6351{
6352 uint32_t u32OriginX= 0;
6353 uint32_t u32OriginY= 0;
6354 uint32_t u32Width = 0;
6355 uint32_t u32Height = 0;
6356 uint16_t u16Flags = 0;
6357
6358 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6359 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6360 if (RT_FAILURE(vrc))
6361 {
6362#ifdef RT_OS_WINDOWS
6363 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6364 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6365 * So just assign fEnable to TRUE again.
6366 * The right fix would be to change GUI API wrappers to make sure that parameters
6367 * are changed only if API succeeds.
6368 */
6369 *aEnabled = TRUE;
6370#endif
6371 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6372 tr("Saved guest size is not available (%Rrc)"),
6373 vrc);
6374 }
6375
6376 *aOriginX = u32OriginX;
6377 *aOriginY = u32OriginY;
6378 *aWidth = u32Width;
6379 *aHeight = u32Height;
6380 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6381
6382 return S_OK;
6383}
6384
6385HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6386 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6387{
6388 if (aScreenId != 0)
6389 return E_NOTIMPL;
6390
6391 if ( aBitmapFormat != BitmapFormat_BGR0
6392 && aBitmapFormat != BitmapFormat_BGRA
6393 && aBitmapFormat != BitmapFormat_RGBA
6394 && aBitmapFormat != BitmapFormat_PNG)
6395 return setError(E_NOTIMPL,
6396 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6397
6398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6399
6400 uint8_t *pu8Data = NULL;
6401 uint32_t cbData = 0;
6402 uint32_t u32Width = 0;
6403 uint32_t u32Height = 0;
6404
6405 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6406
6407 if (RT_FAILURE(vrc))
6408 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6409 tr("Saved thumbnail data is not available (%Rrc)"),
6410 vrc);
6411
6412 HRESULT hr = S_OK;
6413
6414 *aWidth = u32Width;
6415 *aHeight = u32Height;
6416
6417 if (cbData > 0)
6418 {
6419 /* Convert pixels to the format expected by the API caller. */
6420 if (aBitmapFormat == BitmapFormat_BGR0)
6421 {
6422 /* [0] B, [1] G, [2] R, [3] 0. */
6423 aData.resize(cbData);
6424 memcpy(&aData.front(), pu8Data, cbData);
6425 }
6426 else if (aBitmapFormat == BitmapFormat_BGRA)
6427 {
6428 /* [0] B, [1] G, [2] R, [3] A. */
6429 aData.resize(cbData);
6430 for (uint32_t i = 0; i < cbData; i += 4)
6431 {
6432 aData[i] = pu8Data[i];
6433 aData[i + 1] = pu8Data[i + 1];
6434 aData[i + 2] = pu8Data[i + 2];
6435 aData[i + 3] = 0xff;
6436 }
6437 }
6438 else if (aBitmapFormat == BitmapFormat_RGBA)
6439 {
6440 /* [0] R, [1] G, [2] B, [3] A. */
6441 aData.resize(cbData);
6442 for (uint32_t i = 0; i < cbData; i += 4)
6443 {
6444 aData[i] = pu8Data[i + 2];
6445 aData[i + 1] = pu8Data[i + 1];
6446 aData[i + 2] = pu8Data[i];
6447 aData[i + 3] = 0xff;
6448 }
6449 }
6450 else if (aBitmapFormat == BitmapFormat_PNG)
6451 {
6452 uint8_t *pu8PNG = NULL;
6453 uint32_t cbPNG = 0;
6454 uint32_t cxPNG = 0;
6455 uint32_t cyPNG = 0;
6456
6457 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6458
6459 if (RT_SUCCESS(vrc))
6460 {
6461 aData.resize(cbPNG);
6462 if (cbPNG)
6463 memcpy(&aData.front(), pu8PNG, cbPNG);
6464 }
6465 else
6466 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6467 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6468 vrc);
6469
6470 RTMemFree(pu8PNG);
6471 }
6472 }
6473
6474 freeSavedDisplayScreenshot(pu8Data);
6475
6476 return hr;
6477}
6478
6479HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6480 ULONG *aWidth,
6481 ULONG *aHeight,
6482 std::vector<BitmapFormat_T> &aBitmapFormats)
6483{
6484 if (aScreenId != 0)
6485 return E_NOTIMPL;
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 uint8_t *pu8Data = NULL;
6490 uint32_t cbData = 0;
6491 uint32_t u32Width = 0;
6492 uint32_t u32Height = 0;
6493
6494 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6495
6496 if (RT_FAILURE(vrc))
6497 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6498 tr("Saved screenshot data is not available (%Rrc)"),
6499 vrc);
6500
6501 *aWidth = u32Width;
6502 *aHeight = u32Height;
6503 aBitmapFormats.resize(1);
6504 aBitmapFormats[0] = BitmapFormat_PNG;
6505
6506 freeSavedDisplayScreenshot(pu8Data);
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6512 BitmapFormat_T aBitmapFormat,
6513 ULONG *aWidth,
6514 ULONG *aHeight,
6515 std::vector<BYTE> &aData)
6516{
6517 if (aScreenId != 0)
6518 return E_NOTIMPL;
6519
6520 if (aBitmapFormat != BitmapFormat_PNG)
6521 return E_NOTIMPL;
6522
6523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6524
6525 uint8_t *pu8Data = NULL;
6526 uint32_t cbData = 0;
6527 uint32_t u32Width = 0;
6528 uint32_t u32Height = 0;
6529
6530 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6531
6532 if (RT_FAILURE(vrc))
6533 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6534 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6535 vrc);
6536
6537 *aWidth = u32Width;
6538 *aHeight = u32Height;
6539
6540 aData.resize(cbData);
6541 if (cbData)
6542 memcpy(&aData.front(), pu8Data, cbData);
6543
6544 freeSavedDisplayScreenshot(pu8Data);
6545
6546 return S_OK;
6547}
6548
6549HRESULT Machine::hotPlugCPU(ULONG aCpu)
6550{
6551 HRESULT rc = S_OK;
6552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6553
6554 if (!mHWData->mCPUHotPlugEnabled)
6555 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6556
6557 if (aCpu >= mHWData->mCPUCount)
6558 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6559
6560 if (mHWData->mCPUAttached[aCpu])
6561 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6562
6563 alock.release();
6564 rc = i_onCPUChange(aCpu, false);
6565 alock.acquire();
6566 if (FAILED(rc)) return rc;
6567
6568 i_setModified(IsModified_MachineData);
6569 mHWData.backup();
6570 mHWData->mCPUAttached[aCpu] = true;
6571
6572 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6573 if (Global::IsOnline(mData->mMachineState))
6574 i_saveSettings(NULL);
6575
6576 return S_OK;
6577}
6578
6579HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6580{
6581 HRESULT rc = S_OK;
6582
6583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6584
6585 if (!mHWData->mCPUHotPlugEnabled)
6586 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6587
6588 if (aCpu >= SchemaDefs::MaxCPUCount)
6589 return setError(E_INVALIDARG,
6590 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6591 SchemaDefs::MaxCPUCount);
6592
6593 if (!mHWData->mCPUAttached[aCpu])
6594 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6595
6596 /* CPU 0 can't be detached */
6597 if (aCpu == 0)
6598 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6599
6600 alock.release();
6601 rc = i_onCPUChange(aCpu, true);
6602 alock.acquire();
6603 if (FAILED(rc)) return rc;
6604
6605 i_setModified(IsModified_MachineData);
6606 mHWData.backup();
6607 mHWData->mCPUAttached[aCpu] = false;
6608
6609 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6610 if (Global::IsOnline(mData->mMachineState))
6611 i_saveSettings(NULL);
6612
6613 return S_OK;
6614}
6615
6616HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6617{
6618 *aAttached = false;
6619
6620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6621
6622 /* If hotplug is enabled the CPU is always enabled. */
6623 if (!mHWData->mCPUHotPlugEnabled)
6624 {
6625 if (aCpu < mHWData->mCPUCount)
6626 *aAttached = true;
6627 }
6628 else
6629 {
6630 if (aCpu < SchemaDefs::MaxCPUCount)
6631 *aAttached = mHWData->mCPUAttached[aCpu];
6632 }
6633
6634 return S_OK;
6635}
6636
6637HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6638{
6639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6640
6641 Utf8Str log = i_getLogFilename(aIdx);
6642 if (!RTFileExists(log.c_str()))
6643 log.setNull();
6644 aFilename = log;
6645
6646 return S_OK;
6647}
6648
6649HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6650{
6651 if (aSize < 0)
6652 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6653
6654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6655
6656 HRESULT rc = S_OK;
6657 Utf8Str log = i_getLogFilename(aIdx);
6658
6659 /* do not unnecessarily hold the lock while doing something which does
6660 * not need the lock and potentially takes a long time. */
6661 alock.release();
6662
6663 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6664 * keeps the SOAP reply size under 1M for the webservice (we're using
6665 * base64 encoded strings for binary data for years now, avoiding the
6666 * expansion of each byte array element to approx. 25 bytes of XML. */
6667 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6668 aData.resize(cbData);
6669
6670 RTFILE LogFile;
6671 int vrc = RTFileOpen(&LogFile, log.c_str(),
6672 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6673 if (RT_SUCCESS(vrc))
6674 {
6675 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6676 if (RT_SUCCESS(vrc))
6677 aData.resize(cbData);
6678 else
6679 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6680 tr("Could not read log file '%s' (%Rrc)"),
6681 log.c_str(), vrc);
6682 RTFileClose(LogFile);
6683 }
6684 else
6685 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6686 tr("Could not open log file '%s' (%Rrc)"),
6687 log.c_str(), vrc);
6688
6689 if (FAILED(rc))
6690 aData.resize(0);
6691
6692 return rc;
6693}
6694
6695
6696/**
6697 * Currently this method doesn't attach device to the running VM,
6698 * just makes sure it's plugged on next VM start.
6699 */
6700HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6701{
6702 // lock scope
6703 {
6704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 HRESULT rc = i_checkStateDependency(MutableStateDep);
6707 if (FAILED(rc)) return rc;
6708
6709 ChipsetType_T aChipset = ChipsetType_PIIX3;
6710 COMGETTER(ChipsetType)(&aChipset);
6711
6712 if (aChipset != ChipsetType_ICH9)
6713 {
6714 return setError(E_INVALIDARG,
6715 tr("Host PCI attachment only supported with ICH9 chipset"));
6716 }
6717
6718 // check if device with this host PCI address already attached
6719 for (HWData::PCIDeviceAssignmentList::const_iterator
6720 it = mHWData->mPCIDeviceAssignments.begin();
6721 it != mHWData->mPCIDeviceAssignments.end();
6722 ++it)
6723 {
6724 LONG iHostAddress = -1;
6725 ComPtr<PCIDeviceAttachment> pAttach;
6726 pAttach = *it;
6727 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6728 if (iHostAddress == aHostAddress)
6729 return setError(E_INVALIDARG,
6730 tr("Device with host PCI address already attached to this VM"));
6731 }
6732
6733 ComObjPtr<PCIDeviceAttachment> pda;
6734 char name[32];
6735
6736 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6737 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6738 pda.createObject();
6739 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6740 i_setModified(IsModified_MachineData);
6741 mHWData.backup();
6742 mHWData->mPCIDeviceAssignments.push_back(pda);
6743 }
6744
6745 return S_OK;
6746}
6747
6748/**
6749 * Currently this method doesn't detach device from the running VM,
6750 * just makes sure it's not plugged on next VM start.
6751 */
6752HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6753{
6754 ComObjPtr<PCIDeviceAttachment> pAttach;
6755 bool fRemoved = false;
6756 HRESULT rc;
6757
6758 // lock scope
6759 {
6760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6761
6762 rc = i_checkStateDependency(MutableStateDep);
6763 if (FAILED(rc)) return rc;
6764
6765 for (HWData::PCIDeviceAssignmentList::const_iterator
6766 it = mHWData->mPCIDeviceAssignments.begin();
6767 it != mHWData->mPCIDeviceAssignments.end();
6768 ++it)
6769 {
6770 LONG iHostAddress = -1;
6771 pAttach = *it;
6772 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6773 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6774 {
6775 i_setModified(IsModified_MachineData);
6776 mHWData.backup();
6777 mHWData->mPCIDeviceAssignments.remove(pAttach);
6778 fRemoved = true;
6779 break;
6780 }
6781 }
6782 }
6783
6784
6785 /* Fire event outside of the lock */
6786 if (fRemoved)
6787 {
6788 Assert(!pAttach.isNull());
6789 ComPtr<IEventSource> es;
6790 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6791 Assert(SUCCEEDED(rc));
6792 Bstr mid;
6793 rc = this->COMGETTER(Id)(mid.asOutParam());
6794 Assert(SUCCEEDED(rc));
6795 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6796 }
6797
6798 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6799 tr("No host PCI device %08x attached"),
6800 aHostAddress
6801 );
6802}
6803
6804HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6805{
6806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6809 size_t i = 0;
6810 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6811 it = mHWData->mPCIDeviceAssignments.begin();
6812 it != mHWData->mPCIDeviceAssignments.end();
6813 ++it, ++i)
6814 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6815
6816 return S_OK;
6817}
6818
6819HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6820{
6821 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6827{
6828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6831
6832 return S_OK;
6833}
6834
6835HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6836{
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if (SUCCEEDED(hrc))
6840 {
6841 hrc = mHWData.backupEx();
6842 if (SUCCEEDED(hrc))
6843 {
6844 i_setModified(IsModified_MachineData);
6845 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6846 }
6847 }
6848 return hrc;
6849}
6850
6851HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6852{
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6855 return S_OK;
6856}
6857
6858HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6859{
6860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6861 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6862 if (SUCCEEDED(hrc))
6863 {
6864 hrc = mHWData.backupEx();
6865 if (SUCCEEDED(hrc))
6866 {
6867 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6868 if (SUCCEEDED(hrc))
6869 i_setModified(IsModified_MachineData);
6870 }
6871 }
6872 return hrc;
6873}
6874
6875HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6876{
6877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6878
6879 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6880
6881 return S_OK;
6882}
6883
6884HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6885{
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6888 if (SUCCEEDED(hrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 i_setModified(IsModified_MachineData);
6894 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6895 }
6896 }
6897 return hrc;
6898}
6899
6900HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6901{
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6905
6906 return S_OK;
6907}
6908
6909HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6910{
6911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6912
6913 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6914 if ( SUCCEEDED(hrc)
6915 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6916 {
6917 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6918 int vrc;
6919
6920 if (aAutostartEnabled)
6921 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6922 else
6923 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6924
6925 if (RT_SUCCESS(vrc))
6926 {
6927 hrc = mHWData.backupEx();
6928 if (SUCCEEDED(hrc))
6929 {
6930 i_setModified(IsModified_MachineData);
6931 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6932 }
6933 }
6934 else if (vrc == VERR_NOT_SUPPORTED)
6935 hrc = setError(VBOX_E_NOT_SUPPORTED,
6936 tr("The VM autostart feature is not supported on this platform"));
6937 else if (vrc == VERR_PATH_NOT_FOUND)
6938 hrc = setError(E_FAIL,
6939 tr("The path to the autostart database is not set"));
6940 else
6941 hrc = setError(E_UNEXPECTED,
6942 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6943 aAutostartEnabled ? "Adding" : "Removing",
6944 mUserData->s.strName.c_str(), vrc);
6945 }
6946 return hrc;
6947}
6948
6949HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6950{
6951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6952
6953 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6954
6955 return S_OK;
6956}
6957
6958HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6959{
6960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6961 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6962 if (SUCCEEDED(hrc))
6963 {
6964 hrc = mHWData.backupEx();
6965 if (SUCCEEDED(hrc))
6966 {
6967 i_setModified(IsModified_MachineData);
6968 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6969 }
6970 }
6971 return hrc;
6972}
6973
6974HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6975{
6976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6977
6978 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6979
6980 return S_OK;
6981}
6982
6983HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6984{
6985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6987 if ( SUCCEEDED(hrc)
6988 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6989 {
6990 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6991 int vrc;
6992
6993 if (aAutostopType != AutostopType_Disabled)
6994 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6995 else
6996 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6997
6998 if (RT_SUCCESS(vrc))
6999 {
7000 hrc = mHWData.backupEx();
7001 if (SUCCEEDED(hrc))
7002 {
7003 i_setModified(IsModified_MachineData);
7004 mHWData->mAutostart.enmAutostopType = aAutostopType;
7005 }
7006 }
7007 else if (vrc == VERR_NOT_SUPPORTED)
7008 hrc = setError(VBOX_E_NOT_SUPPORTED,
7009 tr("The VM autostop feature is not supported on this platform"));
7010 else if (vrc == VERR_PATH_NOT_FOUND)
7011 hrc = setError(E_FAIL,
7012 tr("The path to the autostart database is not set"));
7013 else
7014 hrc = setError(E_UNEXPECTED,
7015 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7016 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7017 mUserData->s.strName.c_str(), vrc);
7018 }
7019 return hrc;
7020}
7021
7022HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7023{
7024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7025
7026 aDefaultFrontend = mHWData->mDefaultFrontend;
7027
7028 return S_OK;
7029}
7030
7031HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7032{
7033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7034 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7035 if (SUCCEEDED(hrc))
7036 {
7037 hrc = mHWData.backupEx();
7038 if (SUCCEEDED(hrc))
7039 {
7040 i_setModified(IsModified_MachineData);
7041 mHWData->mDefaultFrontend = aDefaultFrontend;
7042 }
7043 }
7044 return hrc;
7045}
7046
7047HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7048{
7049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 size_t cbIcon = mUserData->s.ovIcon.size();
7051 aIcon.resize(cbIcon);
7052 if (cbIcon)
7053 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7054 return S_OK;
7055}
7056
7057HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 i_setModified(IsModified_MachineData);
7064 mUserData.backup();
7065 size_t cbIcon = aIcon.size();
7066 mUserData->s.ovIcon.resize(cbIcon);
7067 if (cbIcon)
7068 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7069 }
7070 return hrc;
7071}
7072
7073HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7074{
7075#ifdef VBOX_WITH_USB
7076 *aUSBProxyAvailable = true;
7077#else
7078 *aUSBProxyAvailable = false;
7079#endif
7080 return S_OK;
7081}
7082
7083HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
7084{
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 *aVMProcessPriority = mUserData->s.enmVMPriority;
7088
7089 return S_OK;
7090}
7091
7092HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
7093{
7094 RT_NOREF(aVMProcessPriority);
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7097 if (SUCCEEDED(hrc))
7098 {
7099 hrc = mUserData.backupEx();
7100 if (SUCCEEDED(hrc))
7101 {
7102 i_setModified(IsModified_MachineData);
7103 mUserData->s.enmVMPriority = aVMProcessPriority;
7104 }
7105 }
7106 alock.release();
7107 if (SUCCEEDED(hrc))
7108 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7109 return hrc;
7110}
7111
7112HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7113 ComPtr<IProgress> &aProgress)
7114{
7115 ComObjPtr<Progress> pP;
7116 Progress *ppP = pP;
7117 IProgress *iP = static_cast<IProgress *>(ppP);
7118 IProgress **pProgress = &iP;
7119
7120 IMachine *pTarget = aTarget;
7121
7122 /* Convert the options. */
7123 RTCList<CloneOptions_T> optList;
7124 if (aOptions.size())
7125 for (size_t i = 0; i < aOptions.size(); ++i)
7126 optList.append(aOptions[i]);
7127
7128 if (optList.contains(CloneOptions_Link))
7129 {
7130 if (!i_isSnapshotMachine())
7131 return setError(E_INVALIDARG,
7132 tr("Linked clone can only be created from a snapshot"));
7133 if (aMode != CloneMode_MachineState)
7134 return setError(E_INVALIDARG,
7135 tr("Linked clone can only be created for a single machine state"));
7136 }
7137 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7138
7139 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7140
7141 HRESULT rc = pWorker->start(pProgress);
7142
7143 pP = static_cast<Progress *>(*pProgress);
7144 pP.queryInterfaceTo(aProgress.asOutParam());
7145
7146 return rc;
7147
7148}
7149
7150HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7151 const com::Utf8Str &aType,
7152 ComPtr<IProgress> &aProgress)
7153{
7154 LogFlowThisFuncEnter();
7155
7156 ComObjPtr<Progress> progress;
7157
7158 progress.createObject();
7159
7160 HRESULT rc = S_OK;
7161 Utf8Str targetPath = aTargetPath;
7162 Utf8Str type = aType;
7163
7164 /* Initialize our worker task */
7165 MachineMoveVM* task = NULL;
7166 try
7167 {
7168 task = new MachineMoveVM(this, targetPath, type, progress);
7169 }
7170 catch(...)
7171 {
7172 delete task;
7173 return rc;
7174 }
7175
7176 /*
7177 * task pointer will be owned by the ThreadTask class.
7178 * There is no need to call operator "delete" in the end.
7179 */
7180 rc = task->init();
7181 if (SUCCEEDED(rc))
7182 {
7183 rc = task->createThread();
7184 if (FAILED(rc))
7185 {
7186 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7187 }
7188
7189 /* Return progress to the caller */
7190 progress.queryInterfaceTo(aProgress.asOutParam());
7191 }
7192
7193 LogFlowThisFuncLeave();
7194 return rc;
7195
7196}
7197
7198HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7199{
7200 NOREF(aProgress);
7201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 // This check should always fail.
7204 HRESULT rc = i_checkStateDependency(MutableStateDep);
7205 if (FAILED(rc)) return rc;
7206
7207 AssertFailedReturn(E_NOTIMPL);
7208}
7209
7210HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7211{
7212 NOREF(aSavedStateFile);
7213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7214
7215 // This check should always fail.
7216 HRESULT rc = i_checkStateDependency(MutableStateDep);
7217 if (FAILED(rc)) return rc;
7218
7219 AssertFailedReturn(E_NOTIMPL);
7220}
7221
7222HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7223{
7224 NOREF(aFRemoveFile);
7225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 // This check should always fail.
7228 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7229 if (FAILED(rc)) return rc;
7230
7231 AssertFailedReturn(E_NOTIMPL);
7232}
7233
7234// public methods for internal purposes
7235/////////////////////////////////////////////////////////////////////////////
7236
7237/**
7238 * Adds the given IsModified_* flag to the dirty flags of the machine.
7239 * This must be called either during i_loadSettings or under the machine write lock.
7240 * @param fl Flag
7241 * @param fAllowStateModification If state modifications are allowed.
7242 */
7243void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7244{
7245 mData->flModifications |= fl;
7246 if (fAllowStateModification && i_isStateModificationAllowed())
7247 mData->mCurrentStateModified = true;
7248}
7249
7250/**
7251 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7252 * care of the write locking.
7253 *
7254 * @param fModification The flag to add.
7255 * @param fAllowStateModification If state modifications are allowed.
7256 */
7257void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7258{
7259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7260 i_setModified(fModification, fAllowStateModification);
7261}
7262
7263/**
7264 * Saves the registry entry of this machine to the given configuration node.
7265 *
7266 * @param data Machine registry data.
7267 *
7268 * @note locks this object for reading.
7269 */
7270HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7271{
7272 AutoLimitedCaller autoCaller(this);
7273 AssertComRCReturnRC(autoCaller.rc());
7274
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276
7277 data.uuid = mData->mUuid;
7278 data.strSettingsFile = mData->m_strConfigFile;
7279
7280 return S_OK;
7281}
7282
7283/**
7284 * Calculates the absolute path of the given path taking the directory of the
7285 * machine settings file as the current directory.
7286 *
7287 * @param strPath Path to calculate the absolute path for.
7288 * @param aResult Where to put the result (used only on success, can be the
7289 * same Utf8Str instance as passed in @a aPath).
7290 * @return IPRT result.
7291 *
7292 * @note Locks this object for reading.
7293 */
7294int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7295{
7296 AutoCaller autoCaller(this);
7297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7298
7299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7300
7301 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7302
7303 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7304
7305 strSettingsDir.stripFilename();
7306 char szFolder[RTPATH_MAX];
7307 size_t cbFolder = sizeof(szFolder);
7308 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7309 if (RT_SUCCESS(vrc))
7310 aResult = szFolder;
7311
7312 return vrc;
7313}
7314
7315/**
7316 * Copies strSource to strTarget, making it relative to the machine folder
7317 * if it is a subdirectory thereof, or simply copying it otherwise.
7318 *
7319 * @param strSource Path to evaluate and copy.
7320 * @param strTarget Buffer to receive target path.
7321 *
7322 * @note Locks this object for reading.
7323 */
7324void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7325 Utf8Str &strTarget)
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturn(autoCaller.rc(), (void)0);
7329
7330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7331
7332 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7333 // use strTarget as a temporary buffer to hold the machine settings dir
7334 strTarget = mData->m_strConfigFileFull;
7335 strTarget.stripFilename();
7336 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7337 {
7338 // is relative: then append what's left
7339 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7340 // for empty paths (only possible for subdirs) use "." to avoid
7341 // triggering default settings for not present config attributes.
7342 if (strTarget.isEmpty())
7343 strTarget = ".";
7344 }
7345 else
7346 // is not relative: then overwrite
7347 strTarget = strSource;
7348}
7349
7350/**
7351 * Returns the full path to the machine's log folder in the
7352 * \a aLogFolder argument.
7353 */
7354void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7355{
7356 AutoCaller autoCaller(this);
7357 AssertComRCReturnVoid(autoCaller.rc());
7358
7359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7360
7361 char szTmp[RTPATH_MAX];
7362 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7363 if (RT_SUCCESS(vrc))
7364 {
7365 if (szTmp[0] && !mUserData.isNull())
7366 {
7367 char szTmp2[RTPATH_MAX];
7368 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7369 if (RT_SUCCESS(vrc))
7370 aLogFolder = Utf8StrFmt("%s%c%s",
7371 szTmp2,
7372 RTPATH_DELIMITER,
7373 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7374 }
7375 else
7376 vrc = VERR_PATH_IS_RELATIVE;
7377 }
7378
7379 if (RT_FAILURE(vrc))
7380 {
7381 // fallback if VBOX_USER_LOGHOME is not set or invalid
7382 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7383 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7384 aLogFolder.append(RTPATH_DELIMITER);
7385 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7386 }
7387}
7388
7389/**
7390 * Returns the full path to the machine's log file for an given index.
7391 */
7392Utf8Str Machine::i_getLogFilename(ULONG idx)
7393{
7394 Utf8Str logFolder;
7395 getLogFolder(logFolder);
7396 Assert(logFolder.length());
7397
7398 Utf8Str log;
7399 if (idx == 0)
7400 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7401#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7402 else if (idx == 1)
7403 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7404 else
7405 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7406#else
7407 else
7408 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7409#endif
7410 return log;
7411}
7412
7413/**
7414 * Returns the full path to the machine's hardened log file.
7415 */
7416Utf8Str Machine::i_getHardeningLogFilename(void)
7417{
7418 Utf8Str strFilename;
7419 getLogFolder(strFilename);
7420 Assert(strFilename.length());
7421 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7422 return strFilename;
7423}
7424
7425
7426/**
7427 * Composes a unique saved state filename based on the current system time. The filename is
7428 * granular to the second so this will work so long as no more than one snapshot is taken on
7429 * a machine per second.
7430 *
7431 * Before version 4.1, we used this formula for saved state files:
7432 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7433 * which no longer works because saved state files can now be shared between the saved state of the
7434 * "saved" machine and an online snapshot, and the following would cause problems:
7435 * 1) save machine
7436 * 2) create online snapshot from that machine state --> reusing saved state file
7437 * 3) save machine again --> filename would be reused, breaking the online snapshot
7438 *
7439 * So instead we now use a timestamp.
7440 *
7441 * @param strStateFilePath
7442 */
7443
7444void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7445{
7446 AutoCaller autoCaller(this);
7447 AssertComRCReturnVoid(autoCaller.rc());
7448
7449 {
7450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7451 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7452 }
7453
7454 RTTIMESPEC ts;
7455 RTTimeNow(&ts);
7456 RTTIME time;
7457 RTTimeExplode(&time, &ts);
7458
7459 strStateFilePath += RTPATH_DELIMITER;
7460 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7461 time.i32Year, time.u8Month, time.u8MonthDay,
7462 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7463}
7464
7465/**
7466 * Returns whether at least one USB controller is present for the VM.
7467 */
7468bool Machine::i_isUSBControllerPresent()
7469{
7470 AutoCaller autoCaller(this);
7471 AssertComRCReturn(autoCaller.rc(), false);
7472
7473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7474
7475 return (mUSBControllers->size() > 0);
7476}
7477
7478/**
7479 * @note Locks this object for writing, calls the client process
7480 * (inside the lock).
7481 */
7482HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7483 const Utf8Str &strFrontend,
7484 const Utf8Str &strEnvironment,
7485 ProgressProxy *aProgress)
7486{
7487 LogFlowThisFuncEnter();
7488
7489 AssertReturn(aControl, E_FAIL);
7490 AssertReturn(aProgress, E_FAIL);
7491 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7492
7493 AutoCaller autoCaller(this);
7494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7495
7496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7497
7498 if (!mData->mRegistered)
7499 return setError(E_UNEXPECTED,
7500 tr("The machine '%s' is not registered"),
7501 mUserData->s.strName.c_str());
7502
7503 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7504
7505 /* The process started when launching a VM with separate UI/VM processes is always
7506 * the UI process, i.e. needs special handling as it won't claim the session. */
7507 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7508
7509 if (fSeparate)
7510 {
7511 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7512 return setError(VBOX_E_INVALID_OBJECT_STATE,
7513 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7514 mUserData->s.strName.c_str());
7515 }
7516 else
7517 {
7518 if ( mData->mSession.mState == SessionState_Locked
7519 || mData->mSession.mState == SessionState_Spawning
7520 || mData->mSession.mState == SessionState_Unlocking)
7521 return setError(VBOX_E_INVALID_OBJECT_STATE,
7522 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7523 mUserData->s.strName.c_str());
7524
7525 /* may not be busy */
7526 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7527 }
7528
7529 /* get the path to the executable */
7530 char szPath[RTPATH_MAX];
7531 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7532 size_t cchBufLeft = strlen(szPath);
7533 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7534 szPath[cchBufLeft] = 0;
7535 char *pszNamePart = szPath + cchBufLeft;
7536 cchBufLeft = sizeof(szPath) - cchBufLeft;
7537
7538 int vrc = VINF_SUCCESS;
7539 RTPROCESS pid = NIL_RTPROCESS;
7540
7541 RTENV env = RTENV_DEFAULT;
7542
7543 if (!strEnvironment.isEmpty())
7544 {
7545 char *newEnvStr = NULL;
7546
7547 do
7548 {
7549 /* clone the current environment */
7550 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7551 AssertRCBreakStmt(vrc2, vrc = vrc2);
7552
7553 newEnvStr = RTStrDup(strEnvironment.c_str());
7554 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7555
7556 /* put new variables to the environment
7557 * (ignore empty variable names here since RTEnv API
7558 * intentionally doesn't do that) */
7559 char *var = newEnvStr;
7560 for (char *p = newEnvStr; *p; ++p)
7561 {
7562 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7563 {
7564 *p = '\0';
7565 if (*var)
7566 {
7567 char *val = strchr(var, '=');
7568 if (val)
7569 {
7570 *val++ = '\0';
7571 vrc2 = RTEnvSetEx(env, var, val);
7572 }
7573 else
7574 vrc2 = RTEnvUnsetEx(env, var);
7575 if (RT_FAILURE(vrc2))
7576 break;
7577 }
7578 var = p + 1;
7579 }
7580 }
7581 if (RT_SUCCESS(vrc2) && *var)
7582 vrc2 = RTEnvPutEx(env, var);
7583
7584 AssertRCBreakStmt(vrc2, vrc = vrc2);
7585 }
7586 while (0);
7587
7588 if (newEnvStr != NULL)
7589 RTStrFree(newEnvStr);
7590 }
7591
7592 /* Hardening logging */
7593#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7594 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7595 {
7596 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7597 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7598 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7599 {
7600 Utf8Str strStartupLogDir = strHardeningLogFile;
7601 strStartupLogDir.stripFilename();
7602 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7603 file without stripping the file. */
7604 }
7605 strSupHardeningLogArg.append(strHardeningLogFile);
7606
7607 /* Remove legacy log filename to avoid confusion. */
7608 Utf8Str strOldStartupLogFile;
7609 getLogFolder(strOldStartupLogFile);
7610 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7611 RTFileDelete(strOldStartupLogFile.c_str());
7612 }
7613 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7614#else
7615 const char *pszSupHardeningLogArg = NULL;
7616#endif
7617
7618 Utf8Str strCanonicalName;
7619
7620#ifdef VBOX_WITH_QTGUI
7621 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7622 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7623 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7624 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7625 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7626 {
7627 strCanonicalName = "GUI/Qt";
7628# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7629 /* Modify the base path so that we don't need to use ".." below. */
7630 RTPathStripTrailingSlash(szPath);
7631 RTPathStripFilename(szPath);
7632 cchBufLeft = strlen(szPath);
7633 pszNamePart = szPath + cchBufLeft;
7634 cchBufLeft = sizeof(szPath) - cchBufLeft;
7635
7636# define OSX_APP_NAME "VirtualBoxVM"
7637# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7638
7639 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7640 if ( strAppOverride.contains(".")
7641 || strAppOverride.contains("/")
7642 || strAppOverride.contains("\\")
7643 || strAppOverride.contains(":"))
7644 strAppOverride.setNull();
7645 Utf8Str strAppPath;
7646 if (!strAppOverride.isEmpty())
7647 {
7648 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7649 Utf8Str strFullPath(szPath);
7650 strFullPath.append(strAppPath);
7651 /* there is a race, but people using this deserve the failure */
7652 if (!RTFileExists(strFullPath.c_str()))
7653 strAppOverride.setNull();
7654 }
7655 if (strAppOverride.isEmpty())
7656 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7657 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7658 strcpy(pszNamePart, strAppPath.c_str());
7659# else
7660 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7661 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7662 strcpy(pszNamePart, s_szVirtualBox_exe);
7663# endif
7664
7665 Utf8Str idStr = mData->mUuid.toString();
7666 const char *apszArgs[] =
7667 {
7668 szPath,
7669 "--comment", mUserData->s.strName.c_str(),
7670 "--startvm", idStr.c_str(),
7671 "--no-startvm-errormsgbox",
7672 NULL, /* For "--separate". */
7673 NULL, /* For "--sup-startup-log". */
7674 NULL
7675 };
7676 unsigned iArg = 6;
7677 if (fSeparate)
7678 apszArgs[iArg++] = "--separate";
7679 apszArgs[iArg++] = pszSupHardeningLogArg;
7680
7681 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7682 }
7683#else /* !VBOX_WITH_QTGUI */
7684 if (0)
7685 ;
7686#endif /* VBOX_WITH_QTGUI */
7687
7688 else
7689
7690#ifdef VBOX_WITH_VBOXSDL
7691 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7692 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7693 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7694 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7695 {
7696 strCanonicalName = "GUI/SDL";
7697 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7698 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7699 strcpy(pszNamePart, s_szVBoxSDL_exe);
7700
7701 Utf8Str idStr = mData->mUuid.toString();
7702 const char *apszArgs[] =
7703 {
7704 szPath,
7705 "--comment", mUserData->s.strName.c_str(),
7706 "--startvm", idStr.c_str(),
7707 NULL, /* For "--separate". */
7708 NULL, /* For "--sup-startup-log". */
7709 NULL
7710 };
7711 unsigned iArg = 5;
7712 if (fSeparate)
7713 apszArgs[iArg++] = "--separate";
7714 apszArgs[iArg++] = pszSupHardeningLogArg;
7715
7716 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7717 }
7718#else /* !VBOX_WITH_VBOXSDL */
7719 if (0)
7720 ;
7721#endif /* !VBOX_WITH_VBOXSDL */
7722
7723 else
7724
7725#ifdef VBOX_WITH_HEADLESS
7726 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7727 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7728 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7729 )
7730 {
7731 strCanonicalName = "headless";
7732 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7733 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7734 * and a VM works even if the server has not been installed.
7735 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7736 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7737 * differently in 4.0 and 3.x.
7738 */
7739 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7740 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7741 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7742
7743 Utf8Str idStr = mData->mUuid.toString();
7744 const char *apszArgs[] =
7745 {
7746 szPath,
7747 "--comment", mUserData->s.strName.c_str(),
7748 "--startvm", idStr.c_str(),
7749 "--vrde", "config",
7750 NULL, /* For "--capture". */
7751 NULL, /* For "--sup-startup-log". */
7752 NULL
7753 };
7754 unsigned iArg = 7;
7755 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7756 apszArgs[iArg++] = "--capture";
7757 apszArgs[iArg++] = pszSupHardeningLogArg;
7758
7759# ifdef RT_OS_WINDOWS
7760 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7761# else
7762 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7763# endif
7764 }
7765#else /* !VBOX_WITH_HEADLESS */
7766 if (0)
7767 ;
7768#endif /* !VBOX_WITH_HEADLESS */
7769 else
7770 {
7771 RTEnvDestroy(env);
7772 return setError(E_INVALIDARG,
7773 tr("Invalid frontend name: '%s'"),
7774 strFrontend.c_str());
7775 }
7776
7777 RTEnvDestroy(env);
7778
7779 if (RT_FAILURE(vrc))
7780 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7781 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7782 mUserData->s.strName.c_str(), vrc);
7783
7784 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7785
7786 if (!fSeparate)
7787 {
7788 /*
7789 * Note that we don't release the lock here before calling the client,
7790 * because it doesn't need to call us back if called with a NULL argument.
7791 * Releasing the lock here is dangerous because we didn't prepare the
7792 * launch data yet, but the client we've just started may happen to be
7793 * too fast and call LockMachine() that will fail (because of PID, etc.),
7794 * so that the Machine will never get out of the Spawning session state.
7795 */
7796
7797 /* inform the session that it will be a remote one */
7798 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7799#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7800 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7801#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7802 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7803#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7804 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7805
7806 if (FAILED(rc))
7807 {
7808 /* restore the session state */
7809 mData->mSession.mState = SessionState_Unlocked;
7810 alock.release();
7811 mParent->i_addProcessToReap(pid);
7812 /* The failure may occur w/o any error info (from RPC), so provide one */
7813 return setError(VBOX_E_VM_ERROR,
7814 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7815 }
7816
7817 /* attach launch data to the machine */
7818 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7819 mData->mSession.mRemoteControls.push_back(aControl);
7820 mData->mSession.mProgress = aProgress;
7821 mData->mSession.mPID = pid;
7822 mData->mSession.mState = SessionState_Spawning;
7823 Assert(strCanonicalName.isNotEmpty());
7824 mData->mSession.mName = strCanonicalName;
7825 }
7826 else
7827 {
7828 /* For separate UI process we declare the launch as completed instantly, as the
7829 * actual headless VM start may or may not come. No point in remembering anything
7830 * yet, as what matters for us is when the headless VM gets started. */
7831 aProgress->i_notifyComplete(S_OK);
7832 }
7833
7834 alock.release();
7835 mParent->i_addProcessToReap(pid);
7836
7837 LogFlowThisFuncLeave();
7838 return S_OK;
7839}
7840
7841/**
7842 * Returns @c true if the given session machine instance has an open direct
7843 * session (and optionally also for direct sessions which are closing) and
7844 * returns the session control machine instance if so.
7845 *
7846 * Note that when the method returns @c false, the arguments remain unchanged.
7847 *
7848 * @param aMachine Session machine object.
7849 * @param aControl Direct session control object (optional).
7850 * @param aRequireVM If true then only allow VM sessions.
7851 * @param aAllowClosing If true then additionally a session which is currently
7852 * being closed will also be allowed.
7853 *
7854 * @note locks this object for reading.
7855 */
7856bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7857 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7858 bool aRequireVM /*= false*/,
7859 bool aAllowClosing /*= false*/)
7860{
7861 AutoLimitedCaller autoCaller(this);
7862 AssertComRCReturn(autoCaller.rc(), false);
7863
7864 /* just return false for inaccessible machines */
7865 if (getObjectState().getState() != ObjectState::Ready)
7866 return false;
7867
7868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7869
7870 if ( ( mData->mSession.mState == SessionState_Locked
7871 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7872 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7873 )
7874 {
7875 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7876
7877 aMachine = mData->mSession.mMachine;
7878
7879 if (aControl != NULL)
7880 *aControl = mData->mSession.mDirectControl;
7881
7882 return true;
7883 }
7884
7885 return false;
7886}
7887
7888/**
7889 * Returns @c true if the given machine has an spawning direct session.
7890 *
7891 * @note locks this object for reading.
7892 */
7893bool Machine::i_isSessionSpawning()
7894{
7895 AutoLimitedCaller autoCaller(this);
7896 AssertComRCReturn(autoCaller.rc(), false);
7897
7898 /* just return false for inaccessible machines */
7899 if (getObjectState().getState() != ObjectState::Ready)
7900 return false;
7901
7902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7903
7904 if (mData->mSession.mState == SessionState_Spawning)
7905 return true;
7906
7907 return false;
7908}
7909
7910/**
7911 * Called from the client watcher thread to check for unexpected client process
7912 * death during Session_Spawning state (e.g. before it successfully opened a
7913 * direct session).
7914 *
7915 * On Win32 and on OS/2, this method is called only when we've got the
7916 * direct client's process termination notification, so it always returns @c
7917 * true.
7918 *
7919 * On other platforms, this method returns @c true if the client process is
7920 * terminated and @c false if it's still alive.
7921 *
7922 * @note Locks this object for writing.
7923 */
7924bool Machine::i_checkForSpawnFailure()
7925{
7926 AutoCaller autoCaller(this);
7927 if (!autoCaller.isOk())
7928 {
7929 /* nothing to do */
7930 LogFlowThisFunc(("Already uninitialized!\n"));
7931 return true;
7932 }
7933
7934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7935
7936 if (mData->mSession.mState != SessionState_Spawning)
7937 {
7938 /* nothing to do */
7939 LogFlowThisFunc(("Not spawning any more!\n"));
7940 return true;
7941 }
7942
7943 HRESULT rc = S_OK;
7944
7945 /* PID not yet initialized, skip check. */
7946 if (mData->mSession.mPID == NIL_RTPROCESS)
7947 return false;
7948
7949 RTPROCSTATUS status;
7950 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7951
7952 if (vrc != VERR_PROCESS_RUNNING)
7953 {
7954 Utf8Str strExtraInfo;
7955
7956#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7957 /* If the startup logfile exists and is of non-zero length, tell the
7958 user to look there for more details to encourage them to attach it
7959 when reporting startup issues. */
7960 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7961 uint64_t cbStartupLogFile = 0;
7962 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7963 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7964 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7965#endif
7966
7967 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7968 rc = setError(E_FAIL,
7969 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7970 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7971 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7972 rc = setError(E_FAIL,
7973 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7974 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7975 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7976 rc = setError(E_FAIL,
7977 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7978 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7979 else
7980 rc = setErrorBoth(E_FAIL, vrc,
7981 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7982 i_getName().c_str(), vrc, strExtraInfo.c_str());
7983 }
7984
7985 if (FAILED(rc))
7986 {
7987 /* Close the remote session, remove the remote control from the list
7988 * and reset session state to Closed (@note keep the code in sync with
7989 * the relevant part in LockMachine()). */
7990
7991 Assert(mData->mSession.mRemoteControls.size() == 1);
7992 if (mData->mSession.mRemoteControls.size() == 1)
7993 {
7994 ErrorInfoKeeper eik;
7995 mData->mSession.mRemoteControls.front()->Uninitialize();
7996 }
7997
7998 mData->mSession.mRemoteControls.clear();
7999 mData->mSession.mState = SessionState_Unlocked;
8000
8001 /* finalize the progress after setting the state */
8002 if (!mData->mSession.mProgress.isNull())
8003 {
8004 mData->mSession.mProgress->notifyComplete(rc);
8005 mData->mSession.mProgress.setNull();
8006 }
8007
8008 mData->mSession.mPID = NIL_RTPROCESS;
8009
8010 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8011 return true;
8012 }
8013
8014 return false;
8015}
8016
8017/**
8018 * Checks whether the machine can be registered. If so, commits and saves
8019 * all settings.
8020 *
8021 * @note Must be called from mParent's write lock. Locks this object and
8022 * children for writing.
8023 */
8024HRESULT Machine::i_prepareRegister()
8025{
8026 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8027
8028 AutoLimitedCaller autoCaller(this);
8029 AssertComRCReturnRC(autoCaller.rc());
8030
8031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8032
8033 /* wait for state dependents to drop to zero */
8034 i_ensureNoStateDependencies();
8035
8036 if (!mData->mAccessible)
8037 return setError(VBOX_E_INVALID_OBJECT_STATE,
8038 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8039 mUserData->s.strName.c_str(),
8040 mData->mUuid.toString().c_str());
8041
8042 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8043
8044 if (mData->mRegistered)
8045 return setError(VBOX_E_INVALID_OBJECT_STATE,
8046 tr("The machine '%s' with UUID {%s} is already registered"),
8047 mUserData->s.strName.c_str(),
8048 mData->mUuid.toString().c_str());
8049
8050 HRESULT rc = S_OK;
8051
8052 // Ensure the settings are saved. If we are going to be registered and
8053 // no config file exists yet, create it by calling i_saveSettings() too.
8054 if ( (mData->flModifications)
8055 || (!mData->pMachineConfigFile->fileExists())
8056 )
8057 {
8058 rc = i_saveSettings(NULL);
8059 // no need to check whether VirtualBox.xml needs saving too since
8060 // we can't have a machine XML file rename pending
8061 if (FAILED(rc)) return rc;
8062 }
8063
8064 /* more config checking goes here */
8065
8066 if (SUCCEEDED(rc))
8067 {
8068 /* we may have had implicit modifications we want to fix on success */
8069 i_commit();
8070
8071 mData->mRegistered = true;
8072 }
8073 else
8074 {
8075 /* we may have had implicit modifications we want to cancel on failure*/
8076 i_rollback(false /* aNotify */);
8077 }
8078
8079 return rc;
8080}
8081
8082/**
8083 * Increases the number of objects dependent on the machine state or on the
8084 * registered state. Guarantees that these two states will not change at least
8085 * until #i_releaseStateDependency() is called.
8086 *
8087 * Depending on the @a aDepType value, additional state checks may be made.
8088 * These checks will set extended error info on failure. See
8089 * #i_checkStateDependency() for more info.
8090 *
8091 * If this method returns a failure, the dependency is not added and the caller
8092 * is not allowed to rely on any particular machine state or registration state
8093 * value and may return the failed result code to the upper level.
8094 *
8095 * @param aDepType Dependency type to add.
8096 * @param aState Current machine state (NULL if not interested).
8097 * @param aRegistered Current registered state (NULL if not interested).
8098 *
8099 * @note Locks this object for writing.
8100 */
8101HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8102 MachineState_T *aState /* = NULL */,
8103 BOOL *aRegistered /* = NULL */)
8104{
8105 AutoCaller autoCaller(this);
8106 AssertComRCReturnRC(autoCaller.rc());
8107
8108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8109
8110 HRESULT rc = i_checkStateDependency(aDepType);
8111 if (FAILED(rc)) return rc;
8112
8113 {
8114 if (mData->mMachineStateChangePending != 0)
8115 {
8116 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8117 * drop to zero so don't add more. It may make sense to wait a bit
8118 * and retry before reporting an error (since the pending state
8119 * transition should be really quick) but let's just assert for
8120 * now to see if it ever happens on practice. */
8121
8122 AssertFailed();
8123
8124 return setError(E_ACCESSDENIED,
8125 tr("Machine state change is in progress. Please retry the operation later."));
8126 }
8127
8128 ++mData->mMachineStateDeps;
8129 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8130 }
8131
8132 if (aState)
8133 *aState = mData->mMachineState;
8134 if (aRegistered)
8135 *aRegistered = mData->mRegistered;
8136
8137 return S_OK;
8138}
8139
8140/**
8141 * Decreases the number of objects dependent on the machine state.
8142 * Must always complete the #i_addStateDependency() call after the state
8143 * dependency is no more necessary.
8144 */
8145void Machine::i_releaseStateDependency()
8146{
8147 AutoCaller autoCaller(this);
8148 AssertComRCReturnVoid(autoCaller.rc());
8149
8150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8151
8152 /* releaseStateDependency() w/o addStateDependency()? */
8153 AssertReturnVoid(mData->mMachineStateDeps != 0);
8154 -- mData->mMachineStateDeps;
8155
8156 if (mData->mMachineStateDeps == 0)
8157 {
8158 /* inform i_ensureNoStateDependencies() that there are no more deps */
8159 if (mData->mMachineStateChangePending != 0)
8160 {
8161 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8162 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8163 }
8164 }
8165}
8166
8167Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8168{
8169 /* start with nothing found */
8170 Utf8Str strResult("");
8171
8172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8173
8174 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8175 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8176 // found:
8177 strResult = it->second; // source is a Utf8Str
8178
8179 return strResult;
8180}
8181
8182// protected methods
8183/////////////////////////////////////////////////////////////////////////////
8184
8185/**
8186 * Performs machine state checks based on the @a aDepType value. If a check
8187 * fails, this method will set extended error info, otherwise it will return
8188 * S_OK. It is supposed, that on failure, the caller will immediately return
8189 * the return value of this method to the upper level.
8190 *
8191 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8192 *
8193 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8194 * current state of this machine object allows to change settings of the
8195 * machine (i.e. the machine is not registered, or registered but not running
8196 * and not saved). It is useful to call this method from Machine setters
8197 * before performing any change.
8198 *
8199 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8200 * as for MutableStateDep except that if the machine is saved, S_OK is also
8201 * returned. This is useful in setters which allow changing machine
8202 * properties when it is in the saved state.
8203 *
8204 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8205 * if the current state of this machine object allows to change runtime
8206 * changeable settings of the machine (i.e. the machine is not registered, or
8207 * registered but either running or not running and not saved). It is useful
8208 * to call this method from Machine setters before performing any changes to
8209 * runtime changeable settings.
8210 *
8211 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8212 * the same as for MutableOrRunningStateDep except that if the machine is
8213 * saved, S_OK is also returned. This is useful in setters which allow
8214 * changing runtime and saved state changeable machine properties.
8215 *
8216 * @param aDepType Dependency type to check.
8217 *
8218 * @note Non Machine based classes should use #i_addStateDependency() and
8219 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8220 * template.
8221 *
8222 * @note This method must be called from under this object's read or write
8223 * lock.
8224 */
8225HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8226{
8227 switch (aDepType)
8228 {
8229 case AnyStateDep:
8230 {
8231 break;
8232 }
8233 case MutableStateDep:
8234 {
8235 if ( mData->mRegistered
8236 && ( !i_isSessionMachine()
8237 || ( mData->mMachineState != MachineState_Aborted
8238 && mData->mMachineState != MachineState_Teleported
8239 && mData->mMachineState != MachineState_PoweredOff
8240 )
8241 )
8242 )
8243 return setError(VBOX_E_INVALID_VM_STATE,
8244 tr("The machine is not mutable (state is %s)"),
8245 Global::stringifyMachineState(mData->mMachineState));
8246 break;
8247 }
8248 case MutableOrSavedStateDep:
8249 {
8250 if ( mData->mRegistered
8251 && ( !i_isSessionMachine()
8252 || ( mData->mMachineState != MachineState_Aborted
8253 && mData->mMachineState != MachineState_Teleported
8254 && mData->mMachineState != MachineState_Saved
8255 && mData->mMachineState != MachineState_PoweredOff
8256 )
8257 )
8258 )
8259 return setError(VBOX_E_INVALID_VM_STATE,
8260 tr("The machine is not mutable or saved (state is %s)"),
8261 Global::stringifyMachineState(mData->mMachineState));
8262 break;
8263 }
8264 case MutableOrRunningStateDep:
8265 {
8266 if ( mData->mRegistered
8267 && ( !i_isSessionMachine()
8268 || ( mData->mMachineState != MachineState_Aborted
8269 && mData->mMachineState != MachineState_Teleported
8270 && mData->mMachineState != MachineState_PoweredOff
8271 && !Global::IsOnline(mData->mMachineState)
8272 )
8273 )
8274 )
8275 return setError(VBOX_E_INVALID_VM_STATE,
8276 tr("The machine is not mutable or running (state is %s)"),
8277 Global::stringifyMachineState(mData->mMachineState));
8278 break;
8279 }
8280 case MutableOrSavedOrRunningStateDep:
8281 {
8282 if ( mData->mRegistered
8283 && ( !i_isSessionMachine()
8284 || ( mData->mMachineState != MachineState_Aborted
8285 && mData->mMachineState != MachineState_Teleported
8286 && mData->mMachineState != MachineState_Saved
8287 && mData->mMachineState != MachineState_PoweredOff
8288 && !Global::IsOnline(mData->mMachineState)
8289 )
8290 )
8291 )
8292 return setError(VBOX_E_INVALID_VM_STATE,
8293 tr("The machine is not mutable, saved or running (state is %s)"),
8294 Global::stringifyMachineState(mData->mMachineState));
8295 break;
8296 }
8297 }
8298
8299 return S_OK;
8300}
8301
8302/**
8303 * Helper to initialize all associated child objects and allocate data
8304 * structures.
8305 *
8306 * This method must be called as a part of the object's initialization procedure
8307 * (usually done in the #init() method).
8308 *
8309 * @note Must be called only from #init() or from #i_registeredInit().
8310 */
8311HRESULT Machine::initDataAndChildObjects()
8312{
8313 AutoCaller autoCaller(this);
8314 AssertComRCReturnRC(autoCaller.rc());
8315 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8316 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8317
8318 AssertReturn(!mData->mAccessible, E_FAIL);
8319
8320 /* allocate data structures */
8321 mSSData.allocate();
8322 mUserData.allocate();
8323 mHWData.allocate();
8324 mMediumAttachments.allocate();
8325 mStorageControllers.allocate();
8326 mUSBControllers.allocate();
8327
8328 /* initialize mOSTypeId */
8329 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8330
8331/** @todo r=bird: init() methods never fails, right? Why don't we make them
8332 * return void then! */
8333
8334 /* create associated BIOS settings object */
8335 unconst(mBIOSSettings).createObject();
8336 mBIOSSettings->init(this);
8337
8338 /* create associated record settings object */
8339 unconst(mRecordingSettings).createObject();
8340 mRecordingSettings->init(this);
8341
8342 /* create an associated VRDE object (default is disabled) */
8343 unconst(mVRDEServer).createObject();
8344 mVRDEServer->init(this);
8345
8346 /* create associated serial port objects */
8347 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8348 {
8349 unconst(mSerialPorts[slot]).createObject();
8350 mSerialPorts[slot]->init(this, slot);
8351 }
8352
8353 /* create associated parallel port objects */
8354 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8355 {
8356 unconst(mParallelPorts[slot]).createObject();
8357 mParallelPorts[slot]->init(this, slot);
8358 }
8359
8360 /* create the audio adapter object (always present, default is disabled) */
8361 unconst(mAudioAdapter).createObject();
8362 mAudioAdapter->init(this);
8363
8364 /* create the USB device filters object (always present) */
8365 unconst(mUSBDeviceFilters).createObject();
8366 mUSBDeviceFilters->init(this);
8367
8368 /* create associated network adapter objects */
8369 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8371 {
8372 unconst(mNetworkAdapters[slot]).createObject();
8373 mNetworkAdapters[slot]->init(this, slot);
8374 }
8375
8376 /* create the bandwidth control */
8377 unconst(mBandwidthControl).createObject();
8378 mBandwidthControl->init(this);
8379
8380 return S_OK;
8381}
8382
8383/**
8384 * Helper to uninitialize all associated child objects and to free all data
8385 * structures.
8386 *
8387 * This method must be called as a part of the object's uninitialization
8388 * procedure (usually done in the #uninit() method).
8389 *
8390 * @note Must be called only from #uninit() or from #i_registeredInit().
8391 */
8392void Machine::uninitDataAndChildObjects()
8393{
8394 AutoCaller autoCaller(this);
8395 AssertComRCReturnVoid(autoCaller.rc());
8396 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8397 || getObjectState().getState() == ObjectState::Limited);
8398
8399 /* tell all our other child objects we've been uninitialized */
8400 if (mBandwidthControl)
8401 {
8402 mBandwidthControl->uninit();
8403 unconst(mBandwidthControl).setNull();
8404 }
8405
8406 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8407 {
8408 if (mNetworkAdapters[slot])
8409 {
8410 mNetworkAdapters[slot]->uninit();
8411 unconst(mNetworkAdapters[slot]).setNull();
8412 }
8413 }
8414
8415 if (mUSBDeviceFilters)
8416 {
8417 mUSBDeviceFilters->uninit();
8418 unconst(mUSBDeviceFilters).setNull();
8419 }
8420
8421 if (mAudioAdapter)
8422 {
8423 mAudioAdapter->uninit();
8424 unconst(mAudioAdapter).setNull();
8425 }
8426
8427 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8428 {
8429 if (mParallelPorts[slot])
8430 {
8431 mParallelPorts[slot]->uninit();
8432 unconst(mParallelPorts[slot]).setNull();
8433 }
8434 }
8435
8436 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8437 {
8438 if (mSerialPorts[slot])
8439 {
8440 mSerialPorts[slot]->uninit();
8441 unconst(mSerialPorts[slot]).setNull();
8442 }
8443 }
8444
8445 if (mVRDEServer)
8446 {
8447 mVRDEServer->uninit();
8448 unconst(mVRDEServer).setNull();
8449 }
8450
8451 if (mBIOSSettings)
8452 {
8453 mBIOSSettings->uninit();
8454 unconst(mBIOSSettings).setNull();
8455 }
8456
8457 if (mRecordingSettings)
8458 {
8459 mRecordingSettings->uninit();
8460 unconst(mRecordingSettings).setNull();
8461 }
8462
8463 /* Deassociate media (only when a real Machine or a SnapshotMachine
8464 * instance is uninitialized; SessionMachine instances refer to real
8465 * Machine media). This is necessary for a clean re-initialization of
8466 * the VM after successfully re-checking the accessibility state. Note
8467 * that in case of normal Machine or SnapshotMachine uninitialization (as
8468 * a result of unregistering or deleting the snapshot), outdated media
8469 * attachments will already be uninitialized and deleted, so this
8470 * code will not affect them. */
8471 if ( !mMediumAttachments.isNull()
8472 && !i_isSessionMachine()
8473 )
8474 {
8475 for (MediumAttachmentList::const_iterator
8476 it = mMediumAttachments->begin();
8477 it != mMediumAttachments->end();
8478 ++it)
8479 {
8480 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8481 if (pMedium.isNull())
8482 continue;
8483 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8484 AssertComRC(rc);
8485 }
8486 }
8487
8488 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8489 {
8490 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8491 if (mData->mFirstSnapshot)
8492 {
8493 // snapshots tree is protected by machine write lock; strictly
8494 // this isn't necessary here since we're deleting the entire
8495 // machine, but otherwise we assert in Snapshot::uninit()
8496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8497 mData->mFirstSnapshot->uninit();
8498 mData->mFirstSnapshot.setNull();
8499 }
8500
8501 mData->mCurrentSnapshot.setNull();
8502 }
8503
8504 /* free data structures (the essential mData structure is not freed here
8505 * since it may be still in use) */
8506 mMediumAttachments.free();
8507 mStorageControllers.free();
8508 mUSBControllers.free();
8509 mHWData.free();
8510 mUserData.free();
8511 mSSData.free();
8512}
8513
8514/**
8515 * Returns a pointer to the Machine object for this machine that acts like a
8516 * parent for complex machine data objects such as shared folders, etc.
8517 *
8518 * For primary Machine objects and for SnapshotMachine objects, returns this
8519 * object's pointer itself. For SessionMachine objects, returns the peer
8520 * (primary) machine pointer.
8521 */
8522Machine *Machine::i_getMachine()
8523{
8524 if (i_isSessionMachine())
8525 return (Machine*)mPeer;
8526 return this;
8527}
8528
8529/**
8530 * Makes sure that there are no machine state dependents. If necessary, waits
8531 * for the number of dependents to drop to zero.
8532 *
8533 * Make sure this method is called from under this object's write lock to
8534 * guarantee that no new dependents may be added when this method returns
8535 * control to the caller.
8536 *
8537 * @note Locks this object for writing. The lock will be released while waiting
8538 * (if necessary).
8539 *
8540 * @warning To be used only in methods that change the machine state!
8541 */
8542void Machine::i_ensureNoStateDependencies()
8543{
8544 AssertReturnVoid(isWriteLockOnCurrentThread());
8545
8546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8547
8548 /* Wait for all state dependents if necessary */
8549 if (mData->mMachineStateDeps != 0)
8550 {
8551 /* lazy semaphore creation */
8552 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8553 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8554
8555 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8556 mData->mMachineStateDeps));
8557
8558 ++mData->mMachineStateChangePending;
8559
8560 /* reset the semaphore before waiting, the last dependent will signal
8561 * it */
8562 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8563
8564 alock.release();
8565
8566 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8567
8568 alock.acquire();
8569
8570 -- mData->mMachineStateChangePending;
8571 }
8572}
8573
8574/**
8575 * Changes the machine state and informs callbacks.
8576 *
8577 * This method is not intended to fail so it either returns S_OK or asserts (and
8578 * returns a failure).
8579 *
8580 * @note Locks this object for writing.
8581 */
8582HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8583{
8584 LogFlowThisFuncEnter();
8585 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8586 Assert(aMachineState != MachineState_Null);
8587
8588 AutoCaller autoCaller(this);
8589 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8590
8591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8592
8593 /* wait for state dependents to drop to zero */
8594 i_ensureNoStateDependencies();
8595
8596 MachineState_T const enmOldState = mData->mMachineState;
8597 if (enmOldState != aMachineState)
8598 {
8599 mData->mMachineState = aMachineState;
8600 RTTimeNow(&mData->mLastStateChange);
8601
8602#ifdef VBOX_WITH_DTRACE_R3_MAIN
8603 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8604#endif
8605 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8606 }
8607
8608 LogFlowThisFuncLeave();
8609 return S_OK;
8610}
8611
8612/**
8613 * Searches for a shared folder with the given logical name
8614 * in the collection of shared folders.
8615 *
8616 * @param aName logical name of the shared folder
8617 * @param aSharedFolder where to return the found object
8618 * @param aSetError whether to set the error info if the folder is
8619 * not found
8620 * @return
8621 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8622 *
8623 * @note
8624 * must be called from under the object's lock!
8625 */
8626HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8627 ComObjPtr<SharedFolder> &aSharedFolder,
8628 bool aSetError /* = false */)
8629{
8630 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8631 for (HWData::SharedFolderList::const_iterator
8632 it = mHWData->mSharedFolders.begin();
8633 it != mHWData->mSharedFolders.end();
8634 ++it)
8635 {
8636 SharedFolder *pSF = *it;
8637 AutoCaller autoCaller(pSF);
8638 if (pSF->i_getName() == aName)
8639 {
8640 aSharedFolder = pSF;
8641 rc = S_OK;
8642 break;
8643 }
8644 }
8645
8646 if (aSetError && FAILED(rc))
8647 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8648
8649 return rc;
8650}
8651
8652/**
8653 * Initializes all machine instance data from the given settings structures
8654 * from XML. The exception is the machine UUID which needs special handling
8655 * depending on the caller's use case, so the caller needs to set that herself.
8656 *
8657 * This gets called in several contexts during machine initialization:
8658 *
8659 * -- When machine XML exists on disk already and needs to be loaded into memory,
8660 * for example, from #i_registeredInit() to load all registered machines on
8661 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8662 * attached to the machine should be part of some media registry already.
8663 *
8664 * -- During OVF import, when a machine config has been constructed from an
8665 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8666 * ensure that the media listed as attachments in the config (which have
8667 * been imported from the OVF) receive the correct registry ID.
8668 *
8669 * -- During VM cloning.
8670 *
8671 * @param config Machine settings from XML.
8672 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8673 * for each attached medium in the config.
8674 * @return
8675 */
8676HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8677 const Guid *puuidRegistry)
8678{
8679 // copy name, description, OS type, teleporter, UTC etc.
8680 mUserData->s = config.machineUserData;
8681
8682 // look up the object by Id to check it is valid
8683 ComObjPtr<GuestOSType> pGuestOSType;
8684 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8685 if (!pGuestOSType.isNull())
8686 mUserData->s.strOsType = pGuestOSType->i_id();
8687
8688 // stateFile (optional)
8689 if (config.strStateFile.isEmpty())
8690 mSSData->strStateFilePath.setNull();
8691 else
8692 {
8693 Utf8Str stateFilePathFull(config.strStateFile);
8694 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8695 if (RT_FAILURE(vrc))
8696 return setErrorBoth(E_FAIL, vrc,
8697 tr("Invalid saved state file path '%s' (%Rrc)"),
8698 config.strStateFile.c_str(),
8699 vrc);
8700 mSSData->strStateFilePath = stateFilePathFull;
8701 }
8702
8703 // snapshot folder needs special processing so set it again
8704 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8705 if (FAILED(rc)) return rc;
8706
8707 /* Copy the extra data items (config may or may not be the same as
8708 * mData->pMachineConfigFile) if necessary. When loading the XML files
8709 * from disk they are the same, but not for OVF import. */
8710 if (mData->pMachineConfigFile != &config)
8711 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8712
8713 /* currentStateModified (optional, default is true) */
8714 mData->mCurrentStateModified = config.fCurrentStateModified;
8715
8716 mData->mLastStateChange = config.timeLastStateChange;
8717
8718 /*
8719 * note: all mUserData members must be assigned prior this point because
8720 * we need to commit changes in order to let mUserData be shared by all
8721 * snapshot machine instances.
8722 */
8723 mUserData.commitCopy();
8724
8725 // machine registry, if present (must be loaded before snapshots)
8726 if (config.canHaveOwnMediaRegistry())
8727 {
8728 // determine machine folder
8729 Utf8Str strMachineFolder = i_getSettingsFileFull();
8730 strMachineFolder.stripFilename();
8731 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8732 config.mediaRegistry,
8733 strMachineFolder);
8734 if (FAILED(rc)) return rc;
8735 }
8736
8737 /* Snapshot node (optional) */
8738 size_t cRootSnapshots;
8739 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8740 {
8741 // there must be only one root snapshot
8742 Assert(cRootSnapshots == 1);
8743
8744 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8745
8746 rc = i_loadSnapshot(snap,
8747 config.uuidCurrentSnapshot,
8748 NULL); // no parent == first snapshot
8749 if (FAILED(rc)) return rc;
8750 }
8751
8752 // hardware data
8753 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8754 if (FAILED(rc)) return rc;
8755
8756 /*
8757 * NOTE: the assignment below must be the last thing to do,
8758 * otherwise it will be not possible to change the settings
8759 * somewhere in the code above because all setters will be
8760 * blocked by i_checkStateDependency(MutableStateDep).
8761 */
8762
8763 /* set the machine state to Aborted or Saved when appropriate */
8764 if (config.fAborted)
8765 {
8766 mSSData->strStateFilePath.setNull();
8767
8768 /* no need to use i_setMachineState() during init() */
8769 mData->mMachineState = MachineState_Aborted;
8770 }
8771 else if (!mSSData->strStateFilePath.isEmpty())
8772 {
8773 /* no need to use i_setMachineState() during init() */
8774 mData->mMachineState = MachineState_Saved;
8775 }
8776
8777 // after loading settings, we are no longer different from the XML on disk
8778 mData->flModifications = 0;
8779
8780 return S_OK;
8781}
8782
8783/**
8784 * Recursively loads all snapshots starting from the given.
8785 *
8786 * @param data snapshot settings.
8787 * @param aCurSnapshotId Current snapshot ID from the settings file.
8788 * @param aParentSnapshot Parent snapshot.
8789 */
8790HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8791 const Guid &aCurSnapshotId,
8792 Snapshot *aParentSnapshot)
8793{
8794 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8795 AssertReturn(!i_isSessionMachine(), E_FAIL);
8796
8797 HRESULT rc = S_OK;
8798
8799 Utf8Str strStateFile;
8800 if (!data.strStateFile.isEmpty())
8801 {
8802 /* optional */
8803 strStateFile = data.strStateFile;
8804 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8805 if (RT_FAILURE(vrc))
8806 return setErrorBoth(E_FAIL, vrc,
8807 tr("Invalid saved state file path '%s' (%Rrc)"),
8808 strStateFile.c_str(),
8809 vrc);
8810 }
8811
8812 /* create a snapshot machine object */
8813 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8814 pSnapshotMachine.createObject();
8815 rc = pSnapshotMachine->initFromSettings(this,
8816 data.hardware,
8817 &data.debugging,
8818 &data.autostart,
8819 data.uuid.ref(),
8820 strStateFile);
8821 if (FAILED(rc)) return rc;
8822
8823 /* create a snapshot object */
8824 ComObjPtr<Snapshot> pSnapshot;
8825 pSnapshot.createObject();
8826 /* initialize the snapshot */
8827 rc = pSnapshot->init(mParent, // VirtualBox object
8828 data.uuid,
8829 data.strName,
8830 data.strDescription,
8831 data.timestamp,
8832 pSnapshotMachine,
8833 aParentSnapshot);
8834 if (FAILED(rc)) return rc;
8835
8836 /* memorize the first snapshot if necessary */
8837 if (!mData->mFirstSnapshot)
8838 mData->mFirstSnapshot = pSnapshot;
8839
8840 /* memorize the current snapshot when appropriate */
8841 if ( !mData->mCurrentSnapshot
8842 && pSnapshot->i_getId() == aCurSnapshotId
8843 )
8844 mData->mCurrentSnapshot = pSnapshot;
8845
8846 // now create the children
8847 for (settings::SnapshotsList::const_iterator
8848 it = data.llChildSnapshots.begin();
8849 it != data.llChildSnapshots.end();
8850 ++it)
8851 {
8852 const settings::Snapshot &childData = *it;
8853 // recurse
8854 rc = i_loadSnapshot(childData,
8855 aCurSnapshotId,
8856 pSnapshot); // parent = the one we created above
8857 if (FAILED(rc)) return rc;
8858 }
8859
8860 return rc;
8861}
8862
8863/**
8864 * Loads settings into mHWData.
8865 *
8866 * @param puuidRegistry Registry ID.
8867 * @param puuidSnapshot Snapshot ID
8868 * @param data Reference to the hardware settings.
8869 * @param pDbg Pointer to the debugging settings.
8870 * @param pAutostart Pointer to the autostart settings.
8871 */
8872HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8873 const Guid *puuidSnapshot,
8874 const settings::Hardware &data,
8875 const settings::Debugging *pDbg,
8876 const settings::Autostart *pAutostart)
8877{
8878 AssertReturn(!i_isSessionMachine(), E_FAIL);
8879
8880 HRESULT rc = S_OK;
8881
8882 try
8883 {
8884 ComObjPtr<GuestOSType> pGuestOSType;
8885 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8886
8887 /* The hardware version attribute (optional). */
8888 mHWData->mHWVersion = data.strVersion;
8889 mHWData->mHardwareUUID = data.uuid;
8890
8891 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8892 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8893 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8894 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8895 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8896 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8897 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8898 mHWData->mPAEEnabled = data.fPAE;
8899 mHWData->mLongMode = data.enmLongMode;
8900 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8901 mHWData->mAPIC = data.fAPIC;
8902 mHWData->mX2APIC = data.fX2APIC;
8903 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8904 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8905 mHWData->mSpecCtrl = data.fSpecCtrl;
8906 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8907 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8908 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8909 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8910 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8911 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8912 mHWData->mCPUCount = data.cCPUs;
8913 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8914 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8915 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8916 mHWData->mCpuProfile = data.strCpuProfile;
8917
8918 // cpu
8919 if (mHWData->mCPUHotPlugEnabled)
8920 {
8921 for (settings::CpuList::const_iterator
8922 it = data.llCpus.begin();
8923 it != data.llCpus.end();
8924 ++it)
8925 {
8926 const settings::Cpu &cpu = *it;
8927
8928 mHWData->mCPUAttached[cpu.ulId] = true;
8929 }
8930 }
8931
8932 // cpuid leafs
8933 for (settings::CpuIdLeafsList::const_iterator
8934 it = data.llCpuIdLeafs.begin();
8935 it != data.llCpuIdLeafs.end();
8936 ++it)
8937 {
8938 const settings::CpuIdLeaf &rLeaf= *it;
8939 if ( rLeaf.idx < UINT32_C(0x20)
8940 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8941 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8942 mHWData->mCpuIdLeafList.push_back(rLeaf);
8943 /* else: just ignore */
8944 }
8945
8946 mHWData->mMemorySize = data.ulMemorySizeMB;
8947 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8948
8949 // boot order
8950 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8951 {
8952 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8953 if (it == data.mapBootOrder.end())
8954 mHWData->mBootOrder[i] = DeviceType_Null;
8955 else
8956 mHWData->mBootOrder[i] = it->second;
8957 }
8958
8959 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8960 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8961 mHWData->mMonitorCount = data.cMonitors;
8962 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8963 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8964 mHWData->mFirmwareType = data.firmwareType;
8965 mHWData->mPointingHIDType = data.pointingHIDType;
8966 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8967 mHWData->mChipsetType = data.chipsetType;
8968 mHWData->mParavirtProvider = data.paravirtProvider;
8969 mHWData->mParavirtDebug = data.strParavirtDebug;
8970 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8971 mHWData->mHPETEnabled = data.fHPETEnabled;
8972
8973 /* VRDEServer */
8974 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8975 if (FAILED(rc)) return rc;
8976
8977 /* BIOS */
8978 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8979 if (FAILED(rc)) return rc;
8980
8981 /* Recording settings */
8982 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8983 if (FAILED(rc)) return rc;
8984
8985 // Bandwidth control (must come before network adapters)
8986 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8987 if (FAILED(rc)) return rc;
8988
8989 /* USB controllers */
8990 for (settings::USBControllerList::const_iterator
8991 it = data.usbSettings.llUSBControllers.begin();
8992 it != data.usbSettings.llUSBControllers.end();
8993 ++it)
8994 {
8995 const settings::USBController &settingsCtrl = *it;
8996 ComObjPtr<USBController> newCtrl;
8997
8998 newCtrl.createObject();
8999 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9000 mUSBControllers->push_back(newCtrl);
9001 }
9002
9003 /* USB device filters */
9004 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9005 if (FAILED(rc)) return rc;
9006
9007 // network adapters (establish array size first and apply defaults, to
9008 // ensure reading the same settings as we saved, since the list skips
9009 // adapters having defaults)
9010 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9011 size_t oldCount = mNetworkAdapters.size();
9012 if (newCount > oldCount)
9013 {
9014 mNetworkAdapters.resize(newCount);
9015 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9016 {
9017 unconst(mNetworkAdapters[slot]).createObject();
9018 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9019 }
9020 }
9021 else if (newCount < oldCount)
9022 mNetworkAdapters.resize(newCount);
9023 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9024 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9025 for (settings::NetworkAdaptersList::const_iterator
9026 it = data.llNetworkAdapters.begin();
9027 it != data.llNetworkAdapters.end();
9028 ++it)
9029 {
9030 const settings::NetworkAdapter &nic = *it;
9031
9032 /* slot uniqueness is guaranteed by XML Schema */
9033 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9034 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9035 if (FAILED(rc)) return rc;
9036 }
9037
9038 // serial ports (establish defaults first, to ensure reading the same
9039 // settings as we saved, since the list skips ports having defaults)
9040 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9041 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9042 for (settings::SerialPortsList::const_iterator
9043 it = data.llSerialPorts.begin();
9044 it != data.llSerialPorts.end();
9045 ++it)
9046 {
9047 const settings::SerialPort &s = *it;
9048
9049 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9050 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9051 if (FAILED(rc)) return rc;
9052 }
9053
9054 // parallel ports (establish defaults first, to ensure reading the same
9055 // settings as we saved, since the list skips ports having defaults)
9056 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9057 mParallelPorts[i]->i_applyDefaults();
9058 for (settings::ParallelPortsList::const_iterator
9059 it = data.llParallelPorts.begin();
9060 it != data.llParallelPorts.end();
9061 ++it)
9062 {
9063 const settings::ParallelPort &p = *it;
9064
9065 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9066 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9067 if (FAILED(rc)) return rc;
9068 }
9069
9070 /* AudioAdapter */
9071 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9072 if (FAILED(rc)) return rc;
9073
9074 /* storage controllers */
9075 rc = i_loadStorageControllers(data.storage,
9076 puuidRegistry,
9077 puuidSnapshot);
9078 if (FAILED(rc)) return rc;
9079
9080 /* Shared folders */
9081 for (settings::SharedFoldersList::const_iterator
9082 it = data.llSharedFolders.begin();
9083 it != data.llSharedFolders.end();
9084 ++it)
9085 {
9086 const settings::SharedFolder &sf = *it;
9087
9088 ComObjPtr<SharedFolder> sharedFolder;
9089 /* Check for double entries. Not allowed! */
9090 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9091 if (SUCCEEDED(rc))
9092 return setError(VBOX_E_OBJECT_IN_USE,
9093 tr("Shared folder named '%s' already exists"),
9094 sf.strName.c_str());
9095
9096 /* Create the new shared folder. Don't break on error. This will be
9097 * reported when the machine starts. */
9098 sharedFolder.createObject();
9099 rc = sharedFolder->init(i_getMachine(),
9100 sf.strName,
9101 sf.strHostPath,
9102 RT_BOOL(sf.fWritable),
9103 RT_BOOL(sf.fAutoMount),
9104 sf.strAutoMountPoint,
9105 false /* fFailOnError */);
9106 if (FAILED(rc)) return rc;
9107 mHWData->mSharedFolders.push_back(sharedFolder);
9108 }
9109
9110 // Clipboard
9111 mHWData->mClipboardMode = data.clipboardMode;
9112
9113 // drag'n'drop
9114 mHWData->mDnDMode = data.dndMode;
9115
9116 // guest settings
9117 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9118
9119 // IO settings
9120 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9121 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9122
9123 // Host PCI devices
9124 for (settings::HostPCIDeviceAttachmentList::const_iterator
9125 it = data.pciAttachments.begin();
9126 it != data.pciAttachments.end();
9127 ++it)
9128 {
9129 const settings::HostPCIDeviceAttachment &hpda = *it;
9130 ComObjPtr<PCIDeviceAttachment> pda;
9131
9132 pda.createObject();
9133 pda->i_loadSettings(this, hpda);
9134 mHWData->mPCIDeviceAssignments.push_back(pda);
9135 }
9136
9137 /*
9138 * (The following isn't really real hardware, but it lives in HWData
9139 * for reasons of convenience.)
9140 */
9141
9142#ifdef VBOX_WITH_GUEST_PROPS
9143 /* Guest properties (optional) */
9144
9145 /* Only load transient guest properties for configs which have saved
9146 * state, because there shouldn't be any for powered off VMs. The same
9147 * logic applies for snapshots, as offline snapshots shouldn't have
9148 * any such properties. They confuse the code in various places.
9149 * Note: can't rely on the machine state, as it isn't set yet. */
9150 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9151 /* apologies for the hacky unconst() usage, but this needs hacking
9152 * actually inconsistent settings into consistency, otherwise there
9153 * will be some corner cases where the inconsistency survives
9154 * surprisingly long without getting fixed, especially for snapshots
9155 * as there are no config changes. */
9156 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9157 for (settings::GuestPropertiesList::iterator
9158 it = llGuestProperties.begin();
9159 it != llGuestProperties.end();
9160 /*nothing*/)
9161 {
9162 const settings::GuestProperty &prop = *it;
9163 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9164 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9165 if ( fSkipTransientGuestProperties
9166 && ( fFlags & GUEST_PROP_F_TRANSIENT
9167 || fFlags & GUEST_PROP_F_TRANSRESET))
9168 {
9169 it = llGuestProperties.erase(it);
9170 continue;
9171 }
9172 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9173 mHWData->mGuestProperties[prop.strName] = property;
9174 ++it;
9175 }
9176#endif /* VBOX_WITH_GUEST_PROPS defined */
9177
9178 rc = i_loadDebugging(pDbg);
9179 if (FAILED(rc))
9180 return rc;
9181
9182 mHWData->mAutostart = *pAutostart;
9183
9184 /* default frontend */
9185 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9186 }
9187 catch (std::bad_alloc &)
9188 {
9189 return E_OUTOFMEMORY;
9190 }
9191
9192 AssertComRC(rc);
9193 return rc;
9194}
9195
9196/**
9197 * Called from i_loadHardware() to load the debugging settings of the
9198 * machine.
9199 *
9200 * @param pDbg Pointer to the settings.
9201 */
9202HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9203{
9204 mHWData->mDebugging = *pDbg;
9205 /* no more processing currently required, this will probably change. */
9206 return S_OK;
9207}
9208
9209/**
9210 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9211 *
9212 * @param data storage settings.
9213 * @param puuidRegistry media registry ID to set media to or NULL;
9214 * see Machine::i_loadMachineDataFromSettings()
9215 * @param puuidSnapshot snapshot ID
9216 * @return
9217 */
9218HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9219 const Guid *puuidRegistry,
9220 const Guid *puuidSnapshot)
9221{
9222 AssertReturn(!i_isSessionMachine(), E_FAIL);
9223
9224 HRESULT rc = S_OK;
9225
9226 for (settings::StorageControllersList::const_iterator
9227 it = data.llStorageControllers.begin();
9228 it != data.llStorageControllers.end();
9229 ++it)
9230 {
9231 const settings::StorageController &ctlData = *it;
9232
9233 ComObjPtr<StorageController> pCtl;
9234 /* Try to find one with the name first. */
9235 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9236 if (SUCCEEDED(rc))
9237 return setError(VBOX_E_OBJECT_IN_USE,
9238 tr("Storage controller named '%s' already exists"),
9239 ctlData.strName.c_str());
9240
9241 pCtl.createObject();
9242 rc = pCtl->init(this,
9243 ctlData.strName,
9244 ctlData.storageBus,
9245 ctlData.ulInstance,
9246 ctlData.fBootable);
9247 if (FAILED(rc)) return rc;
9248
9249 mStorageControllers->push_back(pCtl);
9250
9251 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9252 if (FAILED(rc)) return rc;
9253
9254 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9255 if (FAILED(rc)) return rc;
9256
9257 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9258 if (FAILED(rc)) return rc;
9259
9260 /* Load the attached devices now. */
9261 rc = i_loadStorageDevices(pCtl,
9262 ctlData,
9263 puuidRegistry,
9264 puuidSnapshot);
9265 if (FAILED(rc)) return rc;
9266 }
9267
9268 return S_OK;
9269}
9270
9271/**
9272 * Called from i_loadStorageControllers for a controller's devices.
9273 *
9274 * @param aStorageController
9275 * @param data
9276 * @param puuidRegistry media registry ID to set media to or NULL; see
9277 * Machine::i_loadMachineDataFromSettings()
9278 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9279 * @return
9280 */
9281HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9282 const settings::StorageController &data,
9283 const Guid *puuidRegistry,
9284 const Guid *puuidSnapshot)
9285{
9286 HRESULT rc = S_OK;
9287
9288 /* paranoia: detect duplicate attachments */
9289 for (settings::AttachedDevicesList::const_iterator
9290 it = data.llAttachedDevices.begin();
9291 it != data.llAttachedDevices.end();
9292 ++it)
9293 {
9294 const settings::AttachedDevice &ad = *it;
9295
9296 for (settings::AttachedDevicesList::const_iterator it2 = it;
9297 it2 != data.llAttachedDevices.end();
9298 ++it2)
9299 {
9300 if (it == it2)
9301 continue;
9302
9303 const settings::AttachedDevice &ad2 = *it2;
9304
9305 if ( ad.lPort == ad2.lPort
9306 && ad.lDevice == ad2.lDevice)
9307 {
9308 return setError(E_FAIL,
9309 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9310 aStorageController->i_getName().c_str(),
9311 ad.lPort,
9312 ad.lDevice,
9313 mUserData->s.strName.c_str());
9314 }
9315 }
9316 }
9317
9318 for (settings::AttachedDevicesList::const_iterator
9319 it = data.llAttachedDevices.begin();
9320 it != data.llAttachedDevices.end();
9321 ++it)
9322 {
9323 const settings::AttachedDevice &dev = *it;
9324 ComObjPtr<Medium> medium;
9325
9326 switch (dev.deviceType)
9327 {
9328 case DeviceType_Floppy:
9329 case DeviceType_DVD:
9330 if (dev.strHostDriveSrc.isNotEmpty())
9331 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9332 false /* fRefresh */, medium);
9333 else
9334 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9335 dev.uuid,
9336 false /* fRefresh */,
9337 false /* aSetError */,
9338 medium);
9339 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9340 // This is not an error. The host drive or UUID might have vanished, so just go
9341 // ahead without this removeable medium attachment
9342 rc = S_OK;
9343 break;
9344
9345 case DeviceType_HardDisk:
9346 {
9347 /* find a hard disk by UUID */
9348 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9349 if (FAILED(rc))
9350 {
9351 if (i_isSnapshotMachine())
9352 {
9353 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9354 // so the user knows that the bad disk is in a snapshot somewhere
9355 com::ErrorInfo info;
9356 return setError(E_FAIL,
9357 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9358 puuidSnapshot->raw(),
9359 info.getText().raw());
9360 }
9361 else
9362 return rc;
9363 }
9364
9365 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9366
9367 if (medium->i_getType() == MediumType_Immutable)
9368 {
9369 if (i_isSnapshotMachine())
9370 return setError(E_FAIL,
9371 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9372 "of the virtual machine '%s' ('%s')"),
9373 medium->i_getLocationFull().c_str(),
9374 dev.uuid.raw(),
9375 puuidSnapshot->raw(),
9376 mUserData->s.strName.c_str(),
9377 mData->m_strConfigFileFull.c_str());
9378
9379 return setError(E_FAIL,
9380 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9381 medium->i_getLocationFull().c_str(),
9382 dev.uuid.raw(),
9383 mUserData->s.strName.c_str(),
9384 mData->m_strConfigFileFull.c_str());
9385 }
9386
9387 if (medium->i_getType() == MediumType_MultiAttach)
9388 {
9389 if (i_isSnapshotMachine())
9390 return setError(E_FAIL,
9391 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9392 "of the virtual machine '%s' ('%s')"),
9393 medium->i_getLocationFull().c_str(),
9394 dev.uuid.raw(),
9395 puuidSnapshot->raw(),
9396 mUserData->s.strName.c_str(),
9397 mData->m_strConfigFileFull.c_str());
9398
9399 return setError(E_FAIL,
9400 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9401 medium->i_getLocationFull().c_str(),
9402 dev.uuid.raw(),
9403 mUserData->s.strName.c_str(),
9404 mData->m_strConfigFileFull.c_str());
9405 }
9406
9407 if ( !i_isSnapshotMachine()
9408 && medium->i_getChildren().size() != 0
9409 )
9410 return setError(E_FAIL,
9411 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9412 "because it has %d differencing child hard disks"),
9413 medium->i_getLocationFull().c_str(),
9414 dev.uuid.raw(),
9415 mUserData->s.strName.c_str(),
9416 mData->m_strConfigFileFull.c_str(),
9417 medium->i_getChildren().size());
9418
9419 if (i_findAttachment(*mMediumAttachments.data(),
9420 medium))
9421 return setError(E_FAIL,
9422 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9423 medium->i_getLocationFull().c_str(),
9424 dev.uuid.raw(),
9425 mUserData->s.strName.c_str(),
9426 mData->m_strConfigFileFull.c_str());
9427
9428 break;
9429 }
9430
9431 default:
9432 return setError(E_FAIL,
9433 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9434 medium->i_getLocationFull().c_str(),
9435 mUserData->s.strName.c_str(),
9436 mData->m_strConfigFileFull.c_str());
9437 }
9438
9439 if (FAILED(rc))
9440 break;
9441
9442 /* Bandwidth groups are loaded at this point. */
9443 ComObjPtr<BandwidthGroup> pBwGroup;
9444
9445 if (!dev.strBwGroup.isEmpty())
9446 {
9447 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9448 if (FAILED(rc))
9449 return setError(E_FAIL,
9450 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9451 medium->i_getLocationFull().c_str(),
9452 dev.strBwGroup.c_str(),
9453 mUserData->s.strName.c_str(),
9454 mData->m_strConfigFileFull.c_str());
9455 pBwGroup->i_reference();
9456 }
9457
9458 const Utf8Str controllerName = aStorageController->i_getName();
9459 ComObjPtr<MediumAttachment> pAttachment;
9460 pAttachment.createObject();
9461 rc = pAttachment->init(this,
9462 medium,
9463 controllerName,
9464 dev.lPort,
9465 dev.lDevice,
9466 dev.deviceType,
9467 false,
9468 dev.fPassThrough,
9469 dev.fTempEject,
9470 dev.fNonRotational,
9471 dev.fDiscard,
9472 dev.fHotPluggable,
9473 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9474 if (FAILED(rc)) break;
9475
9476 /* associate the medium with this machine and snapshot */
9477 if (!medium.isNull())
9478 {
9479 AutoCaller medCaller(medium);
9480 if (FAILED(medCaller.rc())) return medCaller.rc();
9481 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9482
9483 if (i_isSnapshotMachine())
9484 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9485 else
9486 rc = medium->i_addBackReference(mData->mUuid);
9487 /* If the medium->addBackReference fails it sets an appropriate
9488 * error message, so no need to do any guesswork here. */
9489
9490 if (puuidRegistry)
9491 // caller wants registry ID to be set on all attached media (OVF import case)
9492 medium->i_addRegistry(*puuidRegistry);
9493 }
9494
9495 if (FAILED(rc))
9496 break;
9497
9498 /* back up mMediumAttachments to let registeredInit() properly rollback
9499 * on failure (= limited accessibility) */
9500 i_setModified(IsModified_Storage);
9501 mMediumAttachments.backup();
9502 mMediumAttachments->push_back(pAttachment);
9503 }
9504
9505 return rc;
9506}
9507
9508/**
9509 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9510 *
9511 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9512 * @param aSnapshot where to return the found snapshot
9513 * @param aSetError true to set extended error info on failure
9514 */
9515HRESULT Machine::i_findSnapshotById(const Guid &aId,
9516 ComObjPtr<Snapshot> &aSnapshot,
9517 bool aSetError /* = false */)
9518{
9519 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9520
9521 if (!mData->mFirstSnapshot)
9522 {
9523 if (aSetError)
9524 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9525 return E_FAIL;
9526 }
9527
9528 if (aId.isZero())
9529 aSnapshot = mData->mFirstSnapshot;
9530 else
9531 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9532
9533 if (!aSnapshot)
9534 {
9535 if (aSetError)
9536 return setError(E_FAIL,
9537 tr("Could not find a snapshot with UUID {%s}"),
9538 aId.toString().c_str());
9539 return E_FAIL;
9540 }
9541
9542 return S_OK;
9543}
9544
9545/**
9546 * Returns the snapshot with the given name or fails of no such snapshot.
9547 *
9548 * @param strName snapshot name to find
9549 * @param aSnapshot where to return the found snapshot
9550 * @param aSetError true to set extended error info on failure
9551 */
9552HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9553 ComObjPtr<Snapshot> &aSnapshot,
9554 bool aSetError /* = false */)
9555{
9556 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9557
9558 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9559
9560 if (!mData->mFirstSnapshot)
9561 {
9562 if (aSetError)
9563 return setError(VBOX_E_OBJECT_NOT_FOUND,
9564 tr("This machine does not have any snapshots"));
9565 return VBOX_E_OBJECT_NOT_FOUND;
9566 }
9567
9568 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9569
9570 if (!aSnapshot)
9571 {
9572 if (aSetError)
9573 return setError(VBOX_E_OBJECT_NOT_FOUND,
9574 tr("Could not find a snapshot named '%s'"), strName.c_str());
9575 return VBOX_E_OBJECT_NOT_FOUND;
9576 }
9577
9578 return S_OK;
9579}
9580
9581/**
9582 * Returns a storage controller object with the given name.
9583 *
9584 * @param aName storage controller name to find
9585 * @param aStorageController where to return the found storage controller
9586 * @param aSetError true to set extended error info on failure
9587 */
9588HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9589 ComObjPtr<StorageController> &aStorageController,
9590 bool aSetError /* = false */)
9591{
9592 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9593
9594 for (StorageControllerList::const_iterator
9595 it = mStorageControllers->begin();
9596 it != mStorageControllers->end();
9597 ++it)
9598 {
9599 if ((*it)->i_getName() == aName)
9600 {
9601 aStorageController = (*it);
9602 return S_OK;
9603 }
9604 }
9605
9606 if (aSetError)
9607 return setError(VBOX_E_OBJECT_NOT_FOUND,
9608 tr("Could not find a storage controller named '%s'"),
9609 aName.c_str());
9610 return VBOX_E_OBJECT_NOT_FOUND;
9611}
9612
9613/**
9614 * Returns a USB controller object with the given name.
9615 *
9616 * @param aName USB controller name to find
9617 * @param aUSBController where to return the found USB controller
9618 * @param aSetError true to set extended error info on failure
9619 */
9620HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9621 ComObjPtr<USBController> &aUSBController,
9622 bool aSetError /* = false */)
9623{
9624 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9625
9626 for (USBControllerList::const_iterator
9627 it = mUSBControllers->begin();
9628 it != mUSBControllers->end();
9629 ++it)
9630 {
9631 if ((*it)->i_getName() == aName)
9632 {
9633 aUSBController = (*it);
9634 return S_OK;
9635 }
9636 }
9637
9638 if (aSetError)
9639 return setError(VBOX_E_OBJECT_NOT_FOUND,
9640 tr("Could not find a storage controller named '%s'"),
9641 aName.c_str());
9642 return VBOX_E_OBJECT_NOT_FOUND;
9643}
9644
9645/**
9646 * Returns the number of USB controller instance of the given type.
9647 *
9648 * @param enmType USB controller type.
9649 */
9650ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9651{
9652 ULONG cCtrls = 0;
9653
9654 for (USBControllerList::const_iterator
9655 it = mUSBControllers->begin();
9656 it != mUSBControllers->end();
9657 ++it)
9658 {
9659 if ((*it)->i_getControllerType() == enmType)
9660 cCtrls++;
9661 }
9662
9663 return cCtrls;
9664}
9665
9666HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9667 MediumAttachmentList &atts)
9668{
9669 AutoCaller autoCaller(this);
9670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9671
9672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9673
9674 for (MediumAttachmentList::const_iterator
9675 it = mMediumAttachments->begin();
9676 it != mMediumAttachments->end();
9677 ++it)
9678 {
9679 const ComObjPtr<MediumAttachment> &pAtt = *it;
9680 // should never happen, but deal with NULL pointers in the list.
9681 AssertContinue(!pAtt.isNull());
9682
9683 // getControllerName() needs caller+read lock
9684 AutoCaller autoAttCaller(pAtt);
9685 if (FAILED(autoAttCaller.rc()))
9686 {
9687 atts.clear();
9688 return autoAttCaller.rc();
9689 }
9690 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9691
9692 if (pAtt->i_getControllerName() == aName)
9693 atts.push_back(pAtt);
9694 }
9695
9696 return S_OK;
9697}
9698
9699
9700/**
9701 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9702 * file if the machine name was changed and about creating a new settings file
9703 * if this is a new machine.
9704 *
9705 * @note Must be never called directly but only from #saveSettings().
9706 */
9707HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9708{
9709 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9710
9711 HRESULT rc = S_OK;
9712
9713 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9714
9715 /// @todo need to handle primary group change, too
9716
9717 /* attempt to rename the settings file if machine name is changed */
9718 if ( mUserData->s.fNameSync
9719 && mUserData.isBackedUp()
9720 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9721 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9722 )
9723 {
9724 bool dirRenamed = false;
9725 bool fileRenamed = false;
9726
9727 Utf8Str configFile, newConfigFile;
9728 Utf8Str configFilePrev, newConfigFilePrev;
9729 Utf8Str configDir, newConfigDir;
9730
9731 do
9732 {
9733 int vrc = VINF_SUCCESS;
9734
9735 Utf8Str name = mUserData.backedUpData()->s.strName;
9736 Utf8Str newName = mUserData->s.strName;
9737 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9738 if (group == "/")
9739 group.setNull();
9740 Utf8Str newGroup = mUserData->s.llGroups.front();
9741 if (newGroup == "/")
9742 newGroup.setNull();
9743
9744 configFile = mData->m_strConfigFileFull;
9745
9746 /* first, rename the directory if it matches the group and machine name */
9747 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9748 group.c_str(), RTPATH_DELIMITER, name.c_str());
9749 /** @todo hack, make somehow use of ComposeMachineFilename */
9750 if (mUserData->s.fDirectoryIncludesUUID)
9751 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9752 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9753 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9754 /** @todo hack, make somehow use of ComposeMachineFilename */
9755 if (mUserData->s.fDirectoryIncludesUUID)
9756 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9757 configDir = configFile;
9758 configDir.stripFilename();
9759 newConfigDir = configDir;
9760 if ( configDir.length() >= groupPlusName.length()
9761 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9762 groupPlusName.c_str()))
9763 {
9764 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9765 Utf8Str newConfigBaseDir(newConfigDir);
9766 newConfigDir.append(newGroupPlusName);
9767 /* consistency: use \ if appropriate on the platform */
9768 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9769 /* new dir and old dir cannot be equal here because of 'if'
9770 * above and because name != newName */
9771 Assert(configDir != newConfigDir);
9772 if (!fSettingsFileIsNew)
9773 {
9774 /* perform real rename only if the machine is not new */
9775 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9776 if ( vrc == VERR_FILE_NOT_FOUND
9777 || vrc == VERR_PATH_NOT_FOUND)
9778 {
9779 /* create the parent directory, then retry renaming */
9780 Utf8Str parent(newConfigDir);
9781 parent.stripFilename();
9782 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9783 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9784 }
9785 if (RT_FAILURE(vrc))
9786 {
9787 rc = setErrorBoth(E_FAIL, vrc,
9788 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9789 configDir.c_str(),
9790 newConfigDir.c_str(),
9791 vrc);
9792 break;
9793 }
9794 /* delete subdirectories which are no longer needed */
9795 Utf8Str dir(configDir);
9796 dir.stripFilename();
9797 while (dir != newConfigBaseDir && dir != ".")
9798 {
9799 vrc = RTDirRemove(dir.c_str());
9800 if (RT_FAILURE(vrc))
9801 break;
9802 dir.stripFilename();
9803 }
9804 dirRenamed = true;
9805 }
9806 }
9807
9808 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9809 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9810
9811 /* then try to rename the settings file itself */
9812 if (newConfigFile != configFile)
9813 {
9814 /* get the path to old settings file in renamed directory */
9815 configFile = Utf8StrFmt("%s%c%s",
9816 newConfigDir.c_str(),
9817 RTPATH_DELIMITER,
9818 RTPathFilename(configFile.c_str()));
9819 if (!fSettingsFileIsNew)
9820 {
9821 /* perform real rename only if the machine is not new */
9822 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9823 if (RT_FAILURE(vrc))
9824 {
9825 rc = setErrorBoth(E_FAIL, vrc,
9826 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9827 configFile.c_str(),
9828 newConfigFile.c_str(),
9829 vrc);
9830 break;
9831 }
9832 fileRenamed = true;
9833 configFilePrev = configFile;
9834 configFilePrev += "-prev";
9835 newConfigFilePrev = newConfigFile;
9836 newConfigFilePrev += "-prev";
9837 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9838 }
9839 }
9840
9841 // update m_strConfigFileFull amd mConfigFile
9842 mData->m_strConfigFileFull = newConfigFile;
9843 // compute the relative path too
9844 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9845
9846 // store the old and new so that VirtualBox::i_saveSettings() can update
9847 // the media registry
9848 if ( mData->mRegistered
9849 && (configDir != newConfigDir || configFile != newConfigFile))
9850 {
9851 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9852
9853 if (pfNeedsGlobalSaveSettings)
9854 *pfNeedsGlobalSaveSettings = true;
9855 }
9856
9857 // in the saved state file path, replace the old directory with the new directory
9858 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9859 {
9860 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9861 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9862 }
9863
9864 // and do the same thing for the saved state file paths of all the online snapshots
9865 if (mData->mFirstSnapshot)
9866 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9867 newConfigDir.c_str());
9868 }
9869 while (0);
9870
9871 if (FAILED(rc))
9872 {
9873 /* silently try to rename everything back */
9874 if (fileRenamed)
9875 {
9876 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9877 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9878 }
9879 if (dirRenamed)
9880 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9881 }
9882
9883 if (FAILED(rc)) return rc;
9884 }
9885
9886 if (fSettingsFileIsNew)
9887 {
9888 /* create a virgin config file */
9889 int vrc = VINF_SUCCESS;
9890
9891 /* ensure the settings directory exists */
9892 Utf8Str path(mData->m_strConfigFileFull);
9893 path.stripFilename();
9894 if (!RTDirExists(path.c_str()))
9895 {
9896 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9897 if (RT_FAILURE(vrc))
9898 {
9899 return setErrorBoth(E_FAIL, vrc,
9900 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9901 path.c_str(),
9902 vrc);
9903 }
9904 }
9905
9906 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9907 path = Utf8Str(mData->m_strConfigFileFull);
9908 RTFILE f = NIL_RTFILE;
9909 vrc = RTFileOpen(&f, path.c_str(),
9910 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9911 if (RT_FAILURE(vrc))
9912 return setErrorBoth(E_FAIL, vrc,
9913 tr("Could not create the settings file '%s' (%Rrc)"),
9914 path.c_str(),
9915 vrc);
9916 RTFileClose(f);
9917 }
9918
9919 return rc;
9920}
9921
9922/**
9923 * Saves and commits machine data, user data and hardware data.
9924 *
9925 * Note that on failure, the data remains uncommitted.
9926 *
9927 * @a aFlags may combine the following flags:
9928 *
9929 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9930 * Used when saving settings after an operation that makes them 100%
9931 * correspond to the settings from the current snapshot.
9932 * - SaveS_Force: settings will be saved without doing a deep compare of the
9933 * settings structures. This is used when this is called because snapshots
9934 * have changed to avoid the overhead of the deep compare.
9935 *
9936 * @note Must be called from under this object's write lock. Locks children for
9937 * writing.
9938 *
9939 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9940 * initialized to false and that will be set to true by this function if
9941 * the caller must invoke VirtualBox::i_saveSettings() because the global
9942 * settings have changed. This will happen if a machine rename has been
9943 * saved and the global machine and media registries will therefore need
9944 * updating.
9945 * @param aFlags Flags.
9946 */
9947HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9948 int aFlags /*= 0*/)
9949{
9950 LogFlowThisFuncEnter();
9951
9952 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9953
9954 /* make sure child objects are unable to modify the settings while we are
9955 * saving them */
9956 i_ensureNoStateDependencies();
9957
9958 AssertReturn(!i_isSnapshotMachine(),
9959 E_FAIL);
9960
9961 if (!mData->mAccessible)
9962 return setError(VBOX_E_INVALID_VM_STATE,
9963 tr("The machine is not accessible, so cannot save settings"));
9964
9965 HRESULT rc = S_OK;
9966 bool fNeedsWrite = false;
9967
9968 /* First, prepare to save settings. It will care about renaming the
9969 * settings directory and file if the machine name was changed and about
9970 * creating a new settings file if this is a new machine. */
9971 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9972 if (FAILED(rc)) return rc;
9973
9974 // keep a pointer to the current settings structures
9975 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9976 settings::MachineConfigFile *pNewConfig = NULL;
9977
9978 try
9979 {
9980 // make a fresh one to have everyone write stuff into
9981 pNewConfig = new settings::MachineConfigFile(NULL);
9982 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9983
9984 // now go and copy all the settings data from COM to the settings structures
9985 // (this calls i_saveSettings() on all the COM objects in the machine)
9986 i_copyMachineDataToSettings(*pNewConfig);
9987
9988 if (aFlags & SaveS_ResetCurStateModified)
9989 {
9990 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9991 mData->mCurrentStateModified = FALSE;
9992 fNeedsWrite = true; // always, no need to compare
9993 }
9994 else if (aFlags & SaveS_Force)
9995 {
9996 fNeedsWrite = true; // always, no need to compare
9997 }
9998 else
9999 {
10000 if (!mData->mCurrentStateModified)
10001 {
10002 // do a deep compare of the settings that we just saved with the settings
10003 // previously stored in the config file; this invokes MachineConfigFile::operator==
10004 // which does a deep compare of all the settings, which is expensive but less expensive
10005 // than writing out XML in vain
10006 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10007
10008 // could still be modified if any settings changed
10009 mData->mCurrentStateModified = fAnySettingsChanged;
10010
10011 fNeedsWrite = fAnySettingsChanged;
10012 }
10013 else
10014 fNeedsWrite = true;
10015 }
10016
10017 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10018
10019 if (fNeedsWrite)
10020 // now spit it all out!
10021 pNewConfig->write(mData->m_strConfigFileFull);
10022
10023 mData->pMachineConfigFile = pNewConfig;
10024 delete pOldConfig;
10025 i_commit();
10026
10027 // after saving settings, we are no longer different from the XML on disk
10028 mData->flModifications = 0;
10029 }
10030 catch (HRESULT err)
10031 {
10032 // we assume that error info is set by the thrower
10033 rc = err;
10034
10035 // restore old config
10036 delete pNewConfig;
10037 mData->pMachineConfigFile = pOldConfig;
10038 }
10039 catch (...)
10040 {
10041 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10042 }
10043
10044 if (fNeedsWrite)
10045 {
10046 /* Fire the data change event, even on failure (since we've already
10047 * committed all data). This is done only for SessionMachines because
10048 * mutable Machine instances are always not registered (i.e. private
10049 * to the client process that creates them) and thus don't need to
10050 * inform callbacks. */
10051 if (i_isSessionMachine())
10052 mParent->i_onMachineDataChange(mData->mUuid);
10053 }
10054
10055 LogFlowThisFunc(("rc=%08X\n", rc));
10056 LogFlowThisFuncLeave();
10057 return rc;
10058}
10059
10060/**
10061 * Implementation for saving the machine settings into the given
10062 * settings::MachineConfigFile instance. This copies machine extradata
10063 * from the previous machine config file in the instance data, if any.
10064 *
10065 * This gets called from two locations:
10066 *
10067 * -- Machine::i_saveSettings(), during the regular XML writing;
10068 *
10069 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10070 * exported to OVF and we write the VirtualBox proprietary XML
10071 * into a <vbox:Machine> tag.
10072 *
10073 * This routine fills all the fields in there, including snapshots, *except*
10074 * for the following:
10075 *
10076 * -- fCurrentStateModified. There is some special logic associated with that.
10077 *
10078 * The caller can then call MachineConfigFile::write() or do something else
10079 * with it.
10080 *
10081 * Caller must hold the machine lock!
10082 *
10083 * This throws XML errors and HRESULT, so the caller must have a catch block!
10084 */
10085void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10086{
10087 // deep copy extradata, being extra careful with self assignment (the STL
10088 // map assignment on Mac OS X clang based Xcode isn't checking)
10089 if (&config != mData->pMachineConfigFile)
10090 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10091
10092 config.uuid = mData->mUuid;
10093
10094 // copy name, description, OS type, teleport, UTC etc.
10095 config.machineUserData = mUserData->s;
10096
10097 if ( mData->mMachineState == MachineState_Saved
10098 || mData->mMachineState == MachineState_Restoring
10099 // when doing certain snapshot operations we may or may not have
10100 // a saved state in the current state, so keep everything as is
10101 || ( ( mData->mMachineState == MachineState_Snapshotting
10102 || mData->mMachineState == MachineState_DeletingSnapshot
10103 || mData->mMachineState == MachineState_RestoringSnapshot)
10104 && (!mSSData->strStateFilePath.isEmpty())
10105 )
10106 )
10107 {
10108 Assert(!mSSData->strStateFilePath.isEmpty());
10109 /* try to make the file name relative to the settings file dir */
10110 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10111 }
10112 else
10113 {
10114 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10115 config.strStateFile.setNull();
10116 }
10117
10118 if (mData->mCurrentSnapshot)
10119 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10120 else
10121 config.uuidCurrentSnapshot.clear();
10122
10123 config.timeLastStateChange = mData->mLastStateChange;
10124 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10125 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10126
10127 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10128 if (FAILED(rc)) throw rc;
10129
10130 // save machine's media registry if this is VirtualBox 4.0 or later
10131 if (config.canHaveOwnMediaRegistry())
10132 {
10133 // determine machine folder
10134 Utf8Str strMachineFolder = i_getSettingsFileFull();
10135 strMachineFolder.stripFilename();
10136 mParent->i_saveMediaRegistry(config.mediaRegistry,
10137 i_getId(), // only media with registry ID == machine UUID
10138 strMachineFolder);
10139 // this throws HRESULT
10140 }
10141
10142 // save snapshots
10143 rc = i_saveAllSnapshots(config);
10144 if (FAILED(rc)) throw rc;
10145}
10146
10147/**
10148 * Saves all snapshots of the machine into the given machine config file. Called
10149 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10150 * @param config
10151 * @return
10152 */
10153HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10154{
10155 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10156
10157 HRESULT rc = S_OK;
10158
10159 try
10160 {
10161 config.llFirstSnapshot.clear();
10162
10163 if (mData->mFirstSnapshot)
10164 {
10165 // the settings use a list for "the first snapshot"
10166 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10167
10168 // get reference to the snapshot on the list and work on that
10169 // element straight in the list to avoid excessive copying later
10170 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10171 if (FAILED(rc)) throw rc;
10172 }
10173
10174// if (mType == IsSessionMachine)
10175// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10176
10177 }
10178 catch (HRESULT err)
10179 {
10180 /* we assume that error info is set by the thrower */
10181 rc = err;
10182 }
10183 catch (...)
10184 {
10185 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10186 }
10187
10188 return rc;
10189}
10190
10191/**
10192 * Saves the VM hardware configuration. It is assumed that the
10193 * given node is empty.
10194 *
10195 * @param data Reference to the settings object for the hardware config.
10196 * @param pDbg Pointer to the settings object for the debugging config
10197 * which happens to live in mHWData.
10198 * @param pAutostart Pointer to the settings object for the autostart config
10199 * which happens to live in mHWData.
10200 */
10201HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10202 settings::Autostart *pAutostart)
10203{
10204 HRESULT rc = S_OK;
10205
10206 try
10207 {
10208 /* The hardware version attribute (optional).
10209 Automatically upgrade from 1 to current default hardware version
10210 when there is no saved state. (ugly!) */
10211 if ( mHWData->mHWVersion == "1"
10212 && mSSData->strStateFilePath.isEmpty()
10213 )
10214 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10215
10216 data.strVersion = mHWData->mHWVersion;
10217 data.uuid = mHWData->mHardwareUUID;
10218
10219 // CPU
10220 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10221 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10222 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10223 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10224 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10225 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10226 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10227 data.fPAE = !!mHWData->mPAEEnabled;
10228 data.enmLongMode = mHWData->mLongMode;
10229 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10230 data.fAPIC = !!mHWData->mAPIC;
10231 data.fX2APIC = !!mHWData->mX2APIC;
10232 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10233 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10234 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10235 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10236 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10237 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10238 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10239 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10240 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10241 data.cCPUs = mHWData->mCPUCount;
10242 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10243 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10244 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10245 data.strCpuProfile = mHWData->mCpuProfile;
10246
10247 data.llCpus.clear();
10248 if (data.fCpuHotPlug)
10249 {
10250 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10251 {
10252 if (mHWData->mCPUAttached[idx])
10253 {
10254 settings::Cpu cpu;
10255 cpu.ulId = idx;
10256 data.llCpus.push_back(cpu);
10257 }
10258 }
10259 }
10260
10261 /* Standard and Extended CPUID leafs. */
10262 data.llCpuIdLeafs.clear();
10263 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10264
10265 // memory
10266 data.ulMemorySizeMB = mHWData->mMemorySize;
10267 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10268
10269 // firmware
10270 data.firmwareType = mHWData->mFirmwareType;
10271
10272 // HID
10273 data.pointingHIDType = mHWData->mPointingHIDType;
10274 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10275
10276 // chipset
10277 data.chipsetType = mHWData->mChipsetType;
10278
10279 // paravirt
10280 data.paravirtProvider = mHWData->mParavirtProvider;
10281 data.strParavirtDebug = mHWData->mParavirtDebug;
10282
10283 // emulated USB card reader
10284 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10285
10286 // HPET
10287 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10288
10289 // boot order
10290 data.mapBootOrder.clear();
10291 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10292 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10293
10294 // display
10295 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10296 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10297 data.cMonitors = mHWData->mMonitorCount;
10298 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10299 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10300
10301 /* VRDEServer settings (optional) */
10302 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10303 if (FAILED(rc)) throw rc;
10304
10305 /* BIOS settings (required) */
10306 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10307 if (FAILED(rc)) throw rc;
10308
10309 /* Recording settings (required) */
10310 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10311 if (FAILED(rc)) throw rc;
10312
10313 /* USB Controller (required) */
10314 data.usbSettings.llUSBControllers.clear();
10315 for (USBControllerList::const_iterator
10316 it = mUSBControllers->begin();
10317 it != mUSBControllers->end();
10318 ++it)
10319 {
10320 ComObjPtr<USBController> ctrl = *it;
10321 settings::USBController settingsCtrl;
10322
10323 settingsCtrl.strName = ctrl->i_getName();
10324 settingsCtrl.enmType = ctrl->i_getControllerType();
10325
10326 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10327 }
10328
10329 /* USB device filters (required) */
10330 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10331 if (FAILED(rc)) throw rc;
10332
10333 /* Network adapters (required) */
10334 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10335 data.llNetworkAdapters.clear();
10336 /* Write out only the nominal number of network adapters for this
10337 * chipset type. Since Machine::commit() hasn't been called there
10338 * may be extra NIC settings in the vector. */
10339 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10340 {
10341 settings::NetworkAdapter nic;
10342 nic.ulSlot = (uint32_t)slot;
10343 /* paranoia check... must not be NULL, but must not crash either. */
10344 if (mNetworkAdapters[slot])
10345 {
10346 if (mNetworkAdapters[slot]->i_hasDefaults())
10347 continue;
10348
10349 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10350 if (FAILED(rc)) throw rc;
10351
10352 data.llNetworkAdapters.push_back(nic);
10353 }
10354 }
10355
10356 /* Serial ports */
10357 data.llSerialPorts.clear();
10358 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10359 {
10360 if (mSerialPorts[slot]->i_hasDefaults())
10361 continue;
10362
10363 settings::SerialPort s;
10364 s.ulSlot = slot;
10365 rc = mSerialPorts[slot]->i_saveSettings(s);
10366 if (FAILED(rc)) return rc;
10367
10368 data.llSerialPorts.push_back(s);
10369 }
10370
10371 /* Parallel ports */
10372 data.llParallelPorts.clear();
10373 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10374 {
10375 if (mParallelPorts[slot]->i_hasDefaults())
10376 continue;
10377
10378 settings::ParallelPort p;
10379 p.ulSlot = slot;
10380 rc = mParallelPorts[slot]->i_saveSettings(p);
10381 if (FAILED(rc)) return rc;
10382
10383 data.llParallelPorts.push_back(p);
10384 }
10385
10386 /* Audio adapter */
10387 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10388 if (FAILED(rc)) return rc;
10389
10390 rc = i_saveStorageControllers(data.storage);
10391 if (FAILED(rc)) return rc;
10392
10393 /* Shared folders */
10394 data.llSharedFolders.clear();
10395 for (HWData::SharedFolderList::const_iterator
10396 it = mHWData->mSharedFolders.begin();
10397 it != mHWData->mSharedFolders.end();
10398 ++it)
10399 {
10400 SharedFolder *pSF = *it;
10401 AutoCaller sfCaller(pSF);
10402 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10403 settings::SharedFolder sf;
10404 sf.strName = pSF->i_getName();
10405 sf.strHostPath = pSF->i_getHostPath();
10406 sf.fWritable = !!pSF->i_isWritable();
10407 sf.fAutoMount = !!pSF->i_isAutoMounted();
10408 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10409
10410 data.llSharedFolders.push_back(sf);
10411 }
10412
10413 // clipboard
10414 data.clipboardMode = mHWData->mClipboardMode;
10415
10416 // drag'n'drop
10417 data.dndMode = mHWData->mDnDMode;
10418
10419 /* Guest */
10420 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10421
10422 // IO settings
10423 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10424 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10425
10426 /* BandwidthControl (required) */
10427 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10428 if (FAILED(rc)) throw rc;
10429
10430 /* Host PCI devices */
10431 data.pciAttachments.clear();
10432 for (HWData::PCIDeviceAssignmentList::const_iterator
10433 it = mHWData->mPCIDeviceAssignments.begin();
10434 it != mHWData->mPCIDeviceAssignments.end();
10435 ++it)
10436 {
10437 ComObjPtr<PCIDeviceAttachment> pda = *it;
10438 settings::HostPCIDeviceAttachment hpda;
10439
10440 rc = pda->i_saveSettings(hpda);
10441 if (FAILED(rc)) throw rc;
10442
10443 data.pciAttachments.push_back(hpda);
10444 }
10445
10446 // guest properties
10447 data.llGuestProperties.clear();
10448#ifdef VBOX_WITH_GUEST_PROPS
10449 for (HWData::GuestPropertyMap::const_iterator
10450 it = mHWData->mGuestProperties.begin();
10451 it != mHWData->mGuestProperties.end();
10452 ++it)
10453 {
10454 HWData::GuestProperty property = it->second;
10455
10456 /* Remove transient guest properties at shutdown unless we
10457 * are saving state. Note that restoring snapshot intentionally
10458 * keeps them, they will be removed if appropriate once the final
10459 * machine state is set (as crashes etc. need to work). */
10460 if ( ( mData->mMachineState == MachineState_PoweredOff
10461 || mData->mMachineState == MachineState_Aborted
10462 || mData->mMachineState == MachineState_Teleported)
10463 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10464 continue;
10465 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10466 prop.strName = it->first;
10467 prop.strValue = property.strValue;
10468 prop.timestamp = property.mTimestamp;
10469 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10470 GuestPropWriteFlags(property.mFlags, szFlags);
10471 prop.strFlags = szFlags;
10472
10473 data.llGuestProperties.push_back(prop);
10474 }
10475
10476 /* I presume this doesn't require a backup(). */
10477 mData->mGuestPropertiesModified = FALSE;
10478#endif /* VBOX_WITH_GUEST_PROPS defined */
10479
10480 *pDbg = mHWData->mDebugging;
10481 *pAutostart = mHWData->mAutostart;
10482
10483 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10484 }
10485 catch (std::bad_alloc &)
10486 {
10487 return E_OUTOFMEMORY;
10488 }
10489
10490 AssertComRC(rc);
10491 return rc;
10492}
10493
10494/**
10495 * Saves the storage controller configuration.
10496 *
10497 * @param data storage settings.
10498 */
10499HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10500{
10501 data.llStorageControllers.clear();
10502
10503 for (StorageControllerList::const_iterator
10504 it = mStorageControllers->begin();
10505 it != mStorageControllers->end();
10506 ++it)
10507 {
10508 HRESULT rc;
10509 ComObjPtr<StorageController> pCtl = *it;
10510
10511 settings::StorageController ctl;
10512 ctl.strName = pCtl->i_getName();
10513 ctl.controllerType = pCtl->i_getControllerType();
10514 ctl.storageBus = pCtl->i_getStorageBus();
10515 ctl.ulInstance = pCtl->i_getInstance();
10516 ctl.fBootable = pCtl->i_getBootable();
10517
10518 /* Save the port count. */
10519 ULONG portCount;
10520 rc = pCtl->COMGETTER(PortCount)(&portCount);
10521 ComAssertComRCRet(rc, rc);
10522 ctl.ulPortCount = portCount;
10523
10524 /* Save fUseHostIOCache */
10525 BOOL fUseHostIOCache;
10526 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10527 ComAssertComRCRet(rc, rc);
10528 ctl.fUseHostIOCache = !!fUseHostIOCache;
10529
10530 /* save the devices now. */
10531 rc = i_saveStorageDevices(pCtl, ctl);
10532 ComAssertComRCRet(rc, rc);
10533
10534 data.llStorageControllers.push_back(ctl);
10535 }
10536
10537 return S_OK;
10538}
10539
10540/**
10541 * Saves the hard disk configuration.
10542 */
10543HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10544 settings::StorageController &data)
10545{
10546 MediumAttachmentList atts;
10547
10548 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10549 if (FAILED(rc)) return rc;
10550
10551 data.llAttachedDevices.clear();
10552 for (MediumAttachmentList::const_iterator
10553 it = atts.begin();
10554 it != atts.end();
10555 ++it)
10556 {
10557 settings::AttachedDevice dev;
10558 IMediumAttachment *iA = *it;
10559 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10560 Medium *pMedium = pAttach->i_getMedium();
10561
10562 dev.deviceType = pAttach->i_getType();
10563 dev.lPort = pAttach->i_getPort();
10564 dev.lDevice = pAttach->i_getDevice();
10565 dev.fPassThrough = pAttach->i_getPassthrough();
10566 dev.fHotPluggable = pAttach->i_getHotPluggable();
10567 if (pMedium)
10568 {
10569 if (pMedium->i_isHostDrive())
10570 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10571 else
10572 dev.uuid = pMedium->i_getId();
10573 dev.fTempEject = pAttach->i_getTempEject();
10574 dev.fNonRotational = pAttach->i_getNonRotational();
10575 dev.fDiscard = pAttach->i_getDiscard();
10576 }
10577
10578 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10579
10580 data.llAttachedDevices.push_back(dev);
10581 }
10582
10583 return S_OK;
10584}
10585
10586/**
10587 * Saves machine state settings as defined by aFlags
10588 * (SaveSTS_* values).
10589 *
10590 * @param aFlags Combination of SaveSTS_* flags.
10591 *
10592 * @note Locks objects for writing.
10593 */
10594HRESULT Machine::i_saveStateSettings(int aFlags)
10595{
10596 if (aFlags == 0)
10597 return S_OK;
10598
10599 AutoCaller autoCaller(this);
10600 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10601
10602 /* This object's write lock is also necessary to serialize file access
10603 * (prevent concurrent reads and writes) */
10604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10605
10606 HRESULT rc = S_OK;
10607
10608 Assert(mData->pMachineConfigFile);
10609
10610 try
10611 {
10612 if (aFlags & SaveSTS_CurStateModified)
10613 mData->pMachineConfigFile->fCurrentStateModified = true;
10614
10615 if (aFlags & SaveSTS_StateFilePath)
10616 {
10617 if (!mSSData->strStateFilePath.isEmpty())
10618 /* try to make the file name relative to the settings file dir */
10619 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10620 else
10621 mData->pMachineConfigFile->strStateFile.setNull();
10622 }
10623
10624 if (aFlags & SaveSTS_StateTimeStamp)
10625 {
10626 Assert( mData->mMachineState != MachineState_Aborted
10627 || mSSData->strStateFilePath.isEmpty());
10628
10629 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10630
10631 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10632/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10633 }
10634
10635 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10636 }
10637 catch (...)
10638 {
10639 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10640 }
10641
10642 return rc;
10643}
10644
10645/**
10646 * Ensures that the given medium is added to a media registry. If this machine
10647 * was created with 4.0 or later, then the machine registry is used. Otherwise
10648 * the global VirtualBox media registry is used.
10649 *
10650 * Caller must NOT hold machine lock, media tree or any medium locks!
10651 *
10652 * @param pMedium
10653 */
10654void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10655{
10656 /* Paranoia checks: do not hold machine or media tree locks. */
10657 AssertReturnVoid(!isWriteLockOnCurrentThread());
10658 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10659
10660 ComObjPtr<Medium> pBase;
10661 {
10662 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10663 pBase = pMedium->i_getBase();
10664 }
10665
10666 /* Paranoia checks: do not hold medium locks. */
10667 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10668 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10669
10670 // decide which medium registry to use now that the medium is attached:
10671 Guid uuid;
10672 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10673 if (fCanHaveOwnMediaRegistry)
10674 // machine XML is VirtualBox 4.0 or higher:
10675 uuid = i_getId(); // machine UUID
10676 else
10677 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10678
10679 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10680 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10681 if (pMedium->i_addRegistry(uuid))
10682 mParent->i_markRegistryModified(uuid);
10683
10684 /* For more complex hard disk structures it can happen that the base
10685 * medium isn't yet associated with any medium registry. Do that now. */
10686 if (pMedium != pBase)
10687 {
10688 /* Tree lock needed by Medium::addRegistry when recursing. */
10689 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10690 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10691 {
10692 treeLock.release();
10693 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10694 treeLock.acquire();
10695 }
10696 if (pBase->i_addRegistryRecursive(uuid))
10697 {
10698 treeLock.release();
10699 mParent->i_markRegistryModified(uuid);
10700 }
10701 }
10702}
10703
10704/**
10705 * Creates differencing hard disks for all normal hard disks attached to this
10706 * machine and a new set of attachments to refer to created disks.
10707 *
10708 * Used when taking a snapshot or when deleting the current state. Gets called
10709 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10710 *
10711 * This method assumes that mMediumAttachments contains the original hard disk
10712 * attachments it needs to create diffs for. On success, these attachments will
10713 * be replaced with the created diffs.
10714 *
10715 * Attachments with non-normal hard disks are left as is.
10716 *
10717 * If @a aOnline is @c false then the original hard disks that require implicit
10718 * diffs will be locked for reading. Otherwise it is assumed that they are
10719 * already locked for writing (when the VM was started). Note that in the latter
10720 * case it is responsibility of the caller to lock the newly created diffs for
10721 * writing if this method succeeds.
10722 *
10723 * @param aProgress Progress object to run (must contain at least as
10724 * many operations left as the number of hard disks
10725 * attached).
10726 * @param aWeight Weight of this operation.
10727 * @param aOnline Whether the VM was online prior to this operation.
10728 *
10729 * @note The progress object is not marked as completed, neither on success nor
10730 * on failure. This is a responsibility of the caller.
10731 *
10732 * @note Locks this object and the media tree for writing.
10733 */
10734HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10735 ULONG aWeight,
10736 bool aOnline)
10737{
10738 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10739
10740 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10741 AssertReturn(!!pProgressControl, E_INVALIDARG);
10742
10743 AutoCaller autoCaller(this);
10744 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10745
10746 AutoMultiWriteLock2 alock(this->lockHandle(),
10747 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10748
10749 /* must be in a protective state because we release the lock below */
10750 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10751 || mData->mMachineState == MachineState_OnlineSnapshotting
10752 || mData->mMachineState == MachineState_LiveSnapshotting
10753 || mData->mMachineState == MachineState_RestoringSnapshot
10754 || mData->mMachineState == MachineState_DeletingSnapshot
10755 , E_FAIL);
10756
10757 HRESULT rc = S_OK;
10758
10759 // use appropriate locked media map (online or offline)
10760 MediumLockListMap lockedMediaOffline;
10761 MediumLockListMap *lockedMediaMap;
10762 if (aOnline)
10763 lockedMediaMap = &mData->mSession.mLockedMedia;
10764 else
10765 lockedMediaMap = &lockedMediaOffline;
10766
10767 try
10768 {
10769 if (!aOnline)
10770 {
10771 /* lock all attached hard disks early to detect "in use"
10772 * situations before creating actual diffs */
10773 for (MediumAttachmentList::const_iterator
10774 it = mMediumAttachments->begin();
10775 it != mMediumAttachments->end();
10776 ++it)
10777 {
10778 MediumAttachment *pAtt = *it;
10779 if (pAtt->i_getType() == DeviceType_HardDisk)
10780 {
10781 Medium *pMedium = pAtt->i_getMedium();
10782 Assert(pMedium);
10783
10784 MediumLockList *pMediumLockList(new MediumLockList());
10785 alock.release();
10786 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10787 NULL /* pToLockWrite */,
10788 false /* fMediumLockWriteAll */,
10789 NULL,
10790 *pMediumLockList);
10791 alock.acquire();
10792 if (FAILED(rc))
10793 {
10794 delete pMediumLockList;
10795 throw rc;
10796 }
10797 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10798 if (FAILED(rc))
10799 {
10800 throw setError(rc,
10801 tr("Collecting locking information for all attached media failed"));
10802 }
10803 }
10804 }
10805
10806 /* Now lock all media. If this fails, nothing is locked. */
10807 alock.release();
10808 rc = lockedMediaMap->Lock();
10809 alock.acquire();
10810 if (FAILED(rc))
10811 {
10812 throw setError(rc,
10813 tr("Locking of attached media failed"));
10814 }
10815 }
10816
10817 /* remember the current list (note that we don't use backup() since
10818 * mMediumAttachments may be already backed up) */
10819 MediumAttachmentList atts = *mMediumAttachments.data();
10820
10821 /* start from scratch */
10822 mMediumAttachments->clear();
10823
10824 /* go through remembered attachments and create diffs for normal hard
10825 * disks and attach them */
10826 for (MediumAttachmentList::const_iterator
10827 it = atts.begin();
10828 it != atts.end();
10829 ++it)
10830 {
10831 MediumAttachment *pAtt = *it;
10832
10833 DeviceType_T devType = pAtt->i_getType();
10834 Medium *pMedium = pAtt->i_getMedium();
10835
10836 if ( devType != DeviceType_HardDisk
10837 || pMedium == NULL
10838 || pMedium->i_getType() != MediumType_Normal)
10839 {
10840 /* copy the attachment as is */
10841
10842 /** @todo the progress object created in SessionMachine::TakeSnaphot
10843 * only expects operations for hard disks. Later other
10844 * device types need to show up in the progress as well. */
10845 if (devType == DeviceType_HardDisk)
10846 {
10847 if (pMedium == NULL)
10848 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10849 aWeight); // weight
10850 else
10851 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10852 pMedium->i_getBase()->i_getName().c_str()).raw(),
10853 aWeight); // weight
10854 }
10855
10856 mMediumAttachments->push_back(pAtt);
10857 continue;
10858 }
10859
10860 /* need a diff */
10861 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10862 pMedium->i_getBase()->i_getName().c_str()).raw(),
10863 aWeight); // weight
10864
10865 Utf8Str strFullSnapshotFolder;
10866 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10867
10868 ComObjPtr<Medium> diff;
10869 diff.createObject();
10870 // store the diff in the same registry as the parent
10871 // (this cannot fail here because we can't create implicit diffs for
10872 // unregistered images)
10873 Guid uuidRegistryParent;
10874 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10875 Assert(fInRegistry); NOREF(fInRegistry);
10876 rc = diff->init(mParent,
10877 pMedium->i_getPreferredDiffFormat(),
10878 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10879 uuidRegistryParent,
10880 DeviceType_HardDisk);
10881 if (FAILED(rc)) throw rc;
10882
10883 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10884 * the push_back? Looks like we're going to release medium with the
10885 * wrong kind of lock (general issue with if we fail anywhere at all)
10886 * and an orphaned VDI in the snapshots folder. */
10887
10888 /* update the appropriate lock list */
10889 MediumLockList *pMediumLockList;
10890 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10891 AssertComRCThrowRC(rc);
10892 if (aOnline)
10893 {
10894 alock.release();
10895 /* The currently attached medium will be read-only, change
10896 * the lock type to read. */
10897 rc = pMediumLockList->Update(pMedium, false);
10898 alock.acquire();
10899 AssertComRCThrowRC(rc);
10900 }
10901
10902 /* release the locks before the potentially lengthy operation */
10903 alock.release();
10904 rc = pMedium->i_createDiffStorage(diff,
10905 pMedium->i_getPreferredDiffVariant(),
10906 pMediumLockList,
10907 NULL /* aProgress */,
10908 true /* aWait */,
10909 false /* aNotify */);
10910 alock.acquire();
10911 if (FAILED(rc)) throw rc;
10912
10913 /* actual lock list update is done in Machine::i_commitMedia */
10914
10915 rc = diff->i_addBackReference(mData->mUuid);
10916 AssertComRCThrowRC(rc);
10917
10918 /* add a new attachment */
10919 ComObjPtr<MediumAttachment> attachment;
10920 attachment.createObject();
10921 rc = attachment->init(this,
10922 diff,
10923 pAtt->i_getControllerName(),
10924 pAtt->i_getPort(),
10925 pAtt->i_getDevice(),
10926 DeviceType_HardDisk,
10927 true /* aImplicit */,
10928 false /* aPassthrough */,
10929 false /* aTempEject */,
10930 pAtt->i_getNonRotational(),
10931 pAtt->i_getDiscard(),
10932 pAtt->i_getHotPluggable(),
10933 pAtt->i_getBandwidthGroup());
10934 if (FAILED(rc)) throw rc;
10935
10936 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10937 AssertComRCThrowRC(rc);
10938 mMediumAttachments->push_back(attachment);
10939 }
10940 }
10941 catch (HRESULT aRC) { rc = aRC; }
10942
10943 /* unlock all hard disks we locked when there is no VM */
10944 if (!aOnline)
10945 {
10946 ErrorInfoKeeper eik;
10947
10948 HRESULT rc1 = lockedMediaMap->Clear();
10949 AssertComRC(rc1);
10950 }
10951
10952 return rc;
10953}
10954
10955/**
10956 * Deletes implicit differencing hard disks created either by
10957 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10958 * mMediumAttachments.
10959 *
10960 * Note that to delete hard disks created by #attachDevice() this method is
10961 * called from #i_rollbackMedia() when the changes are rolled back.
10962 *
10963 * @note Locks this object and the media tree for writing.
10964 */
10965HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10966{
10967 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10968
10969 AutoCaller autoCaller(this);
10970 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10971
10972 AutoMultiWriteLock2 alock(this->lockHandle(),
10973 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10974
10975 /* We absolutely must have backed up state. */
10976 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10977
10978 /* Check if there are any implicitly created diff images. */
10979 bool fImplicitDiffs = false;
10980 for (MediumAttachmentList::const_iterator
10981 it = mMediumAttachments->begin();
10982 it != mMediumAttachments->end();
10983 ++it)
10984 {
10985 const ComObjPtr<MediumAttachment> &pAtt = *it;
10986 if (pAtt->i_isImplicit())
10987 {
10988 fImplicitDiffs = true;
10989 break;
10990 }
10991 }
10992 /* If there is nothing to do, leave early. This saves lots of image locking
10993 * effort. It also avoids a MachineStateChanged event without real reason.
10994 * This is important e.g. when loading a VM config, because there should be
10995 * no events. Otherwise API clients can become thoroughly confused for
10996 * inaccessible VMs (the code for loading VM configs uses this method for
10997 * cleanup if the config makes no sense), as they take such events as an
10998 * indication that the VM is alive, and they would force the VM config to
10999 * be reread, leading to an endless loop. */
11000 if (!fImplicitDiffs)
11001 return S_OK;
11002
11003 HRESULT rc = S_OK;
11004 MachineState_T oldState = mData->mMachineState;
11005
11006 /* will release the lock before the potentially lengthy operation,
11007 * so protect with the special state (unless already protected) */
11008 if ( oldState != MachineState_Snapshotting
11009 && oldState != MachineState_OnlineSnapshotting
11010 && oldState != MachineState_LiveSnapshotting
11011 && oldState != MachineState_RestoringSnapshot
11012 && oldState != MachineState_DeletingSnapshot
11013 && oldState != MachineState_DeletingSnapshotOnline
11014 && oldState != MachineState_DeletingSnapshotPaused
11015 )
11016 i_setMachineState(MachineState_SettingUp);
11017
11018 // use appropriate locked media map (online or offline)
11019 MediumLockListMap lockedMediaOffline;
11020 MediumLockListMap *lockedMediaMap;
11021 if (aOnline)
11022 lockedMediaMap = &mData->mSession.mLockedMedia;
11023 else
11024 lockedMediaMap = &lockedMediaOffline;
11025
11026 try
11027 {
11028 if (!aOnline)
11029 {
11030 /* lock all attached hard disks early to detect "in use"
11031 * situations before deleting actual diffs */
11032 for (MediumAttachmentList::const_iterator
11033 it = mMediumAttachments->begin();
11034 it != mMediumAttachments->end();
11035 ++it)
11036 {
11037 MediumAttachment *pAtt = *it;
11038 if (pAtt->i_getType() == DeviceType_HardDisk)
11039 {
11040 Medium *pMedium = pAtt->i_getMedium();
11041 Assert(pMedium);
11042
11043 MediumLockList *pMediumLockList(new MediumLockList());
11044 alock.release();
11045 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11046 NULL /* pToLockWrite */,
11047 false /* fMediumLockWriteAll */,
11048 NULL,
11049 *pMediumLockList);
11050 alock.acquire();
11051
11052 if (FAILED(rc))
11053 {
11054 delete pMediumLockList;
11055 throw rc;
11056 }
11057
11058 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11059 if (FAILED(rc))
11060 throw rc;
11061 }
11062 }
11063
11064 if (FAILED(rc))
11065 throw rc;
11066 } // end of offline
11067
11068 /* Lock lists are now up to date and include implicitly created media */
11069
11070 /* Go through remembered attachments and delete all implicitly created
11071 * diffs and fix up the attachment information */
11072 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11073 MediumAttachmentList implicitAtts;
11074 for (MediumAttachmentList::const_iterator
11075 it = mMediumAttachments->begin();
11076 it != mMediumAttachments->end();
11077 ++it)
11078 {
11079 ComObjPtr<MediumAttachment> pAtt = *it;
11080 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11081 if (pMedium.isNull())
11082 continue;
11083
11084 // Implicit attachments go on the list for deletion and back references are removed.
11085 if (pAtt->i_isImplicit())
11086 {
11087 /* Deassociate and mark for deletion */
11088 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11089 rc = pMedium->i_removeBackReference(mData->mUuid);
11090 if (FAILED(rc))
11091 throw rc;
11092 implicitAtts.push_back(pAtt);
11093 continue;
11094 }
11095
11096 /* Was this medium attached before? */
11097 if (!i_findAttachment(oldAtts, pMedium))
11098 {
11099 /* no: de-associate */
11100 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11101 rc = pMedium->i_removeBackReference(mData->mUuid);
11102 if (FAILED(rc))
11103 throw rc;
11104 continue;
11105 }
11106 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11107 }
11108
11109 /* If there are implicit attachments to delete, throw away the lock
11110 * map contents (which will unlock all media) since the medium
11111 * attachments will be rolled back. Below we need to completely
11112 * recreate the lock map anyway since it is infinitely complex to
11113 * do this incrementally (would need reconstructing each attachment
11114 * change, which would be extremely hairy). */
11115 if (implicitAtts.size() != 0)
11116 {
11117 ErrorInfoKeeper eik;
11118
11119 HRESULT rc1 = lockedMediaMap->Clear();
11120 AssertComRC(rc1);
11121 }
11122
11123 /* rollback hard disk changes */
11124 mMediumAttachments.rollback();
11125
11126 MultiResult mrc(S_OK);
11127
11128 // Delete unused implicit diffs.
11129 if (implicitAtts.size() != 0)
11130 {
11131 alock.release();
11132
11133 for (MediumAttachmentList::const_iterator
11134 it = implicitAtts.begin();
11135 it != implicitAtts.end();
11136 ++it)
11137 {
11138 // Remove medium associated with this attachment.
11139 ComObjPtr<MediumAttachment> pAtt = *it;
11140 Assert(pAtt);
11141 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11142 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11143 Assert(pMedium);
11144
11145 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11146 // continue on delete failure, just collect error messages
11147 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11148 pMedium->i_getLocationFull().c_str() ));
11149 mrc = rc;
11150 }
11151 // Clear the list of deleted implicit attachments now, while not
11152 // holding the lock, as it will ultimately trigger Medium::uninit()
11153 // calls which assume that the media tree lock isn't held.
11154 implicitAtts.clear();
11155
11156 alock.acquire();
11157
11158 /* if there is a VM recreate media lock map as mentioned above,
11159 * otherwise it is a waste of time and we leave things unlocked */
11160 if (aOnline)
11161 {
11162 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11163 /* must never be NULL, but better safe than sorry */
11164 if (!pMachine.isNull())
11165 {
11166 alock.release();
11167 rc = mData->mSession.mMachine->i_lockMedia();
11168 alock.acquire();
11169 if (FAILED(rc))
11170 throw rc;
11171 }
11172 }
11173 }
11174 }
11175 catch (HRESULT aRC) {rc = aRC;}
11176
11177 if (mData->mMachineState == MachineState_SettingUp)
11178 i_setMachineState(oldState);
11179
11180 /* unlock all hard disks we locked when there is no VM */
11181 if (!aOnline)
11182 {
11183 ErrorInfoKeeper eik;
11184
11185 HRESULT rc1 = lockedMediaMap->Clear();
11186 AssertComRC(rc1);
11187 }
11188
11189 return rc;
11190}
11191
11192
11193/**
11194 * Looks through the given list of media attachments for one with the given parameters
11195 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11196 * can be searched as well if needed.
11197 *
11198 * @param ll
11199 * @param aControllerName
11200 * @param aControllerPort
11201 * @param aDevice
11202 * @return
11203 */
11204MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11205 const Utf8Str &aControllerName,
11206 LONG aControllerPort,
11207 LONG aDevice)
11208{
11209 for (MediumAttachmentList::const_iterator
11210 it = ll.begin();
11211 it != ll.end();
11212 ++it)
11213 {
11214 MediumAttachment *pAttach = *it;
11215 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
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 pMedium
11229 * @return
11230 */
11231MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11232 ComObjPtr<Medium> pMedium)
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 == pMedium)
11242 return pAttach;
11243 }
11244
11245 return NULL;
11246}
11247
11248/**
11249 * Looks through the given list of media attachments for one with the given parameters
11250 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11251 * can be searched as well if needed.
11252 *
11253 * @param ll
11254 * @param id
11255 * @return
11256 */
11257MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11258 Guid &id)
11259{
11260 for (MediumAttachmentList::const_iterator
11261 it = ll.begin();
11262 it != ll.end();
11263 ++it)
11264 {
11265 MediumAttachment *pAttach = *it;
11266 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11267 if (pMediumThis->i_getId() == id)
11268 return pAttach;
11269 }
11270
11271 return NULL;
11272}
11273
11274/**
11275 * Main implementation for Machine::DetachDevice. This also gets called
11276 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11277 *
11278 * @param pAttach Medium attachment to detach.
11279 * @param writeLock Machine write lock which the caller must have locked once.
11280 * This may be released temporarily in here.
11281 * @param pSnapshot If NULL, then the detachment is for the current machine.
11282 * Otherwise this is for a SnapshotMachine, and this must be
11283 * its snapshot.
11284 * @return
11285 */
11286HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11287 AutoWriteLock &writeLock,
11288 Snapshot *pSnapshot)
11289{
11290 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11291 DeviceType_T mediumType = pAttach->i_getType();
11292
11293 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11294
11295 if (pAttach->i_isImplicit())
11296 {
11297 /* attempt to implicitly delete the implicitly created diff */
11298
11299 /// @todo move the implicit flag from MediumAttachment to Medium
11300 /// and forbid any hard disk operation when it is implicit. Or maybe
11301 /// a special media state for it to make it even more simple.
11302
11303 Assert(mMediumAttachments.isBackedUp());
11304
11305 /* will release the lock before the potentially lengthy operation, so
11306 * protect with the special state */
11307 MachineState_T oldState = mData->mMachineState;
11308 i_setMachineState(MachineState_SettingUp);
11309
11310 writeLock.release();
11311
11312 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11313 true /*aWait*/,
11314 false /*aNotify*/);
11315
11316 writeLock.acquire();
11317
11318 i_setMachineState(oldState);
11319
11320 if (FAILED(rc)) return rc;
11321 }
11322
11323 i_setModified(IsModified_Storage);
11324 mMediumAttachments.backup();
11325 mMediumAttachments->remove(pAttach);
11326
11327 if (!oldmedium.isNull())
11328 {
11329 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11330 if (pSnapshot)
11331 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11332 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11333 else if (mediumType != DeviceType_HardDisk)
11334 oldmedium->i_removeBackReference(mData->mUuid);
11335 }
11336
11337 return S_OK;
11338}
11339
11340/**
11341 * Goes thru all media of the given list and
11342 *
11343 * 1) calls i_detachDevice() on each of them for this machine and
11344 * 2) adds all Medium objects found in the process to the given list,
11345 * depending on cleanupMode.
11346 *
11347 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11348 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11349 * media to the list.
11350 *
11351 * This gets called from Machine::Unregister, both for the actual Machine and
11352 * the SnapshotMachine objects that might be found in the snapshots.
11353 *
11354 * Requires caller and locking. The machine lock must be passed in because it
11355 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11356 *
11357 * @param writeLock Machine lock from top-level caller; this gets passed to
11358 * i_detachDevice.
11359 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11360 * object if called for a SnapshotMachine.
11361 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11362 * added to llMedia; if Full, then all media get added;
11363 * otherwise no media get added.
11364 * @param llMedia Caller's list to receive Medium objects which got detached so
11365 * caller can close() them, depending on cleanupMode.
11366 * @return
11367 */
11368HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11369 Snapshot *pSnapshot,
11370 CleanupMode_T cleanupMode,
11371 MediaList &llMedia)
11372{
11373 Assert(isWriteLockOnCurrentThread());
11374
11375 HRESULT rc;
11376
11377 // make a temporary list because i_detachDevice invalidates iterators into
11378 // mMediumAttachments
11379 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11380
11381 for (MediumAttachmentList::iterator
11382 it = llAttachments2.begin();
11383 it != llAttachments2.end();
11384 ++it)
11385 {
11386 ComObjPtr<MediumAttachment> &pAttach = *it;
11387 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11388
11389 if (!pMedium.isNull())
11390 {
11391 AutoCaller mac(pMedium);
11392 if (FAILED(mac.rc())) return mac.rc();
11393 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11394 DeviceType_T devType = pMedium->i_getDeviceType();
11395 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11396 && devType == DeviceType_HardDisk)
11397 || (cleanupMode == CleanupMode_Full)
11398 )
11399 {
11400 llMedia.push_back(pMedium);
11401 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11402 /* Not allowed to keep this lock as below we need the parent
11403 * medium lock, and the lock order is parent to child. */
11404 lock.release();
11405 /*
11406 * Search for medias which are not attached to any machine, but
11407 * in the chain to an attached disk. Mediums are only consided
11408 * if they are:
11409 * - have only one child
11410 * - no references to any machines
11411 * - are of normal medium type
11412 */
11413 while (!pParent.isNull())
11414 {
11415 AutoCaller mac1(pParent);
11416 if (FAILED(mac1.rc())) return mac1.rc();
11417 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11418 if (pParent->i_getChildren().size() == 1)
11419 {
11420 if ( pParent->i_getMachineBackRefCount() == 0
11421 && pParent->i_getType() == MediumType_Normal
11422 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11423 llMedia.push_back(pParent);
11424 }
11425 else
11426 break;
11427 pParent = pParent->i_getParent();
11428 }
11429 }
11430 }
11431
11432 // real machine: then we need to use the proper method
11433 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11434
11435 if (FAILED(rc))
11436 return rc;
11437 }
11438
11439 return S_OK;
11440}
11441
11442/**
11443 * Perform deferred hard disk detachments.
11444 *
11445 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11446 * changed (not backed up).
11447 *
11448 * If @a aOnline is @c true then this method will also unlock the old hard
11449 * disks for which the new implicit diffs were created and will lock these new
11450 * diffs for writing.
11451 *
11452 * @param aOnline Whether the VM was online prior to this operation.
11453 *
11454 * @note Locks this object for writing!
11455 */
11456void Machine::i_commitMedia(bool aOnline /*= false*/)
11457{
11458 AutoCaller autoCaller(this);
11459 AssertComRCReturnVoid(autoCaller.rc());
11460
11461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11462
11463 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11464
11465 HRESULT rc = S_OK;
11466
11467 /* no attach/detach operations -- nothing to do */
11468 if (!mMediumAttachments.isBackedUp())
11469 return;
11470
11471 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11472 bool fMediaNeedsLocking = false;
11473
11474 /* enumerate new attachments */
11475 for (MediumAttachmentList::const_iterator
11476 it = mMediumAttachments->begin();
11477 it != mMediumAttachments->end();
11478 ++it)
11479 {
11480 MediumAttachment *pAttach = *it;
11481
11482 pAttach->i_commit();
11483
11484 Medium *pMedium = pAttach->i_getMedium();
11485 bool fImplicit = pAttach->i_isImplicit();
11486
11487 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11488 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11489 fImplicit));
11490
11491 /** @todo convert all this Machine-based voodoo to MediumAttachment
11492 * based commit logic. */
11493 if (fImplicit)
11494 {
11495 /* convert implicit attachment to normal */
11496 pAttach->i_setImplicit(false);
11497
11498 if ( aOnline
11499 && pMedium
11500 && pAttach->i_getType() == DeviceType_HardDisk
11501 )
11502 {
11503 /* update the appropriate lock list */
11504 MediumLockList *pMediumLockList;
11505 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11506 AssertComRC(rc);
11507 if (pMediumLockList)
11508 {
11509 /* unlock if there's a need to change the locking */
11510 if (!fMediaNeedsLocking)
11511 {
11512 rc = mData->mSession.mLockedMedia.Unlock();
11513 AssertComRC(rc);
11514 fMediaNeedsLocking = true;
11515 }
11516 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11517 AssertComRC(rc);
11518 rc = pMediumLockList->Append(pMedium, true);
11519 AssertComRC(rc);
11520 }
11521 }
11522
11523 continue;
11524 }
11525
11526 if (pMedium)
11527 {
11528 /* was this medium attached before? */
11529 for (MediumAttachmentList::iterator
11530 oldIt = oldAtts.begin();
11531 oldIt != oldAtts.end();
11532 ++oldIt)
11533 {
11534 MediumAttachment *pOldAttach = *oldIt;
11535 if (pOldAttach->i_getMedium() == pMedium)
11536 {
11537 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11538
11539 /* yes: remove from old to avoid de-association */
11540 oldAtts.erase(oldIt);
11541 break;
11542 }
11543 }
11544 }
11545 }
11546
11547 /* enumerate remaining old attachments and de-associate from the
11548 * current machine state */
11549 for (MediumAttachmentList::const_iterator
11550 it = oldAtts.begin();
11551 it != oldAtts.end();
11552 ++it)
11553 {
11554 MediumAttachment *pAttach = *it;
11555 Medium *pMedium = pAttach->i_getMedium();
11556
11557 /* Detach only hard disks, since DVD/floppy media is detached
11558 * instantly in MountMedium. */
11559 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11560 {
11561 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11562
11563 /* now de-associate from the current machine state */
11564 rc = pMedium->i_removeBackReference(mData->mUuid);
11565 AssertComRC(rc);
11566
11567 if (aOnline)
11568 {
11569 /* unlock since medium is not used anymore */
11570 MediumLockList *pMediumLockList;
11571 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11572 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11573 {
11574 /* this happens for online snapshots, there the attachment
11575 * is changing, but only to a diff image created under
11576 * the old one, so there is no separate lock list */
11577 Assert(!pMediumLockList);
11578 }
11579 else
11580 {
11581 AssertComRC(rc);
11582 if (pMediumLockList)
11583 {
11584 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11585 AssertComRC(rc);
11586 }
11587 }
11588 }
11589 }
11590 }
11591
11592 /* take media locks again so that the locking state is consistent */
11593 if (fMediaNeedsLocking)
11594 {
11595 Assert(aOnline);
11596 rc = mData->mSession.mLockedMedia.Lock();
11597 AssertComRC(rc);
11598 }
11599
11600 /* commit the hard disk changes */
11601 mMediumAttachments.commit();
11602
11603 if (i_isSessionMachine())
11604 {
11605 /*
11606 * Update the parent machine to point to the new owner.
11607 * This is necessary because the stored parent will point to the
11608 * session machine otherwise and cause crashes or errors later
11609 * when the session machine gets invalid.
11610 */
11611 /** @todo Change the MediumAttachment class to behave like any other
11612 * class in this regard by creating peer MediumAttachment
11613 * objects for session machines and share the data with the peer
11614 * machine.
11615 */
11616 for (MediumAttachmentList::const_iterator
11617 it = mMediumAttachments->begin();
11618 it != mMediumAttachments->end();
11619 ++it)
11620 (*it)->i_updateParentMachine(mPeer);
11621
11622 /* attach new data to the primary machine and reshare it */
11623 mPeer->mMediumAttachments.attach(mMediumAttachments);
11624 }
11625
11626 return;
11627}
11628
11629/**
11630 * Perform deferred deletion of implicitly created diffs.
11631 *
11632 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11633 * changed (not backed up).
11634 *
11635 * @note Locks this object for writing!
11636 */
11637void Machine::i_rollbackMedia()
11638{
11639 AutoCaller autoCaller(this);
11640 AssertComRCReturnVoid(autoCaller.rc());
11641
11642 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11643 LogFlowThisFunc(("Entering rollbackMedia\n"));
11644
11645 HRESULT rc = S_OK;
11646
11647 /* no attach/detach operations -- nothing to do */
11648 if (!mMediumAttachments.isBackedUp())
11649 return;
11650
11651 /* enumerate new attachments */
11652 for (MediumAttachmentList::const_iterator
11653 it = mMediumAttachments->begin();
11654 it != mMediumAttachments->end();
11655 ++it)
11656 {
11657 MediumAttachment *pAttach = *it;
11658 /* Fix up the backrefs for DVD/floppy media. */
11659 if (pAttach->i_getType() != DeviceType_HardDisk)
11660 {
11661 Medium *pMedium = pAttach->i_getMedium();
11662 if (pMedium)
11663 {
11664 rc = pMedium->i_removeBackReference(mData->mUuid);
11665 AssertComRC(rc);
11666 }
11667 }
11668
11669 (*it)->i_rollback();
11670
11671 pAttach = *it;
11672 /* Fix up the backrefs for DVD/floppy media. */
11673 if (pAttach->i_getType() != DeviceType_HardDisk)
11674 {
11675 Medium *pMedium = pAttach->i_getMedium();
11676 if (pMedium)
11677 {
11678 rc = pMedium->i_addBackReference(mData->mUuid);
11679 AssertComRC(rc);
11680 }
11681 }
11682 }
11683
11684 /** @todo convert all this Machine-based voodoo to MediumAttachment
11685 * based rollback logic. */
11686 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11687
11688 return;
11689}
11690
11691/**
11692 * Returns true if the settings file is located in the directory named exactly
11693 * as the machine; this means, among other things, that the machine directory
11694 * should be auto-renamed.
11695 *
11696 * @param aSettingsDir if not NULL, the full machine settings file directory
11697 * name will be assigned there.
11698 *
11699 * @note Doesn't lock anything.
11700 * @note Not thread safe (must be called from this object's lock).
11701 */
11702bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11703{
11704 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11705 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11706 if (aSettingsDir)
11707 *aSettingsDir = strMachineDirName;
11708 strMachineDirName.stripPath(); // vmname
11709 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11710 strConfigFileOnly.stripPath() // vmname.vbox
11711 .stripSuffix(); // vmname
11712 /** @todo hack, make somehow use of ComposeMachineFilename */
11713 if (mUserData->s.fDirectoryIncludesUUID)
11714 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11715
11716 AssertReturn(!strMachineDirName.isEmpty(), false);
11717 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11718
11719 return strMachineDirName == strConfigFileOnly;
11720}
11721
11722/**
11723 * Discards all changes to machine settings.
11724 *
11725 * @param aNotify Whether to notify the direct session about changes or not.
11726 *
11727 * @note Locks objects for writing!
11728 */
11729void Machine::i_rollback(bool aNotify)
11730{
11731 AutoCaller autoCaller(this);
11732 AssertComRCReturn(autoCaller.rc(), (void)0);
11733
11734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11735
11736 if (!mStorageControllers.isNull())
11737 {
11738 if (mStorageControllers.isBackedUp())
11739 {
11740 /* unitialize all new devices (absent in the backed up list). */
11741 StorageControllerList *backedList = mStorageControllers.backedUpData();
11742 for (StorageControllerList::const_iterator
11743 it = mStorageControllers->begin();
11744 it != mStorageControllers->end();
11745 ++it)
11746 {
11747 if ( std::find(backedList->begin(), backedList->end(), *it)
11748 == backedList->end()
11749 )
11750 {
11751 (*it)->uninit();
11752 }
11753 }
11754
11755 /* restore the list */
11756 mStorageControllers.rollback();
11757 }
11758
11759 /* rollback any changes to devices after restoring the list */
11760 if (mData->flModifications & IsModified_Storage)
11761 {
11762 for (StorageControllerList::const_iterator
11763 it = mStorageControllers->begin();
11764 it != mStorageControllers->end();
11765 ++it)
11766 {
11767 (*it)->i_rollback();
11768 }
11769 }
11770 }
11771
11772 if (!mUSBControllers.isNull())
11773 {
11774 if (mUSBControllers.isBackedUp())
11775 {
11776 /* unitialize all new devices (absent in the backed up list). */
11777 USBControllerList *backedList = mUSBControllers.backedUpData();
11778 for (USBControllerList::const_iterator
11779 it = mUSBControllers->begin();
11780 it != mUSBControllers->end();
11781 ++it)
11782 {
11783 if ( std::find(backedList->begin(), backedList->end(), *it)
11784 == backedList->end()
11785 )
11786 {
11787 (*it)->uninit();
11788 }
11789 }
11790
11791 /* restore the list */
11792 mUSBControllers.rollback();
11793 }
11794
11795 /* rollback any changes to devices after restoring the list */
11796 if (mData->flModifications & IsModified_USB)
11797 {
11798 for (USBControllerList::const_iterator
11799 it = mUSBControllers->begin();
11800 it != mUSBControllers->end();
11801 ++it)
11802 {
11803 (*it)->i_rollback();
11804 }
11805 }
11806 }
11807
11808 mUserData.rollback();
11809
11810 mHWData.rollback();
11811
11812 if (mData->flModifications & IsModified_Storage)
11813 i_rollbackMedia();
11814
11815 if (mBIOSSettings)
11816 mBIOSSettings->i_rollback();
11817
11818 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11819 mRecordingSettings->i_rollback();
11820
11821 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11822 mVRDEServer->i_rollback();
11823
11824 if (mAudioAdapter)
11825 mAudioAdapter->i_rollback();
11826
11827 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11828 mUSBDeviceFilters->i_rollback();
11829
11830 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11831 mBandwidthControl->i_rollback();
11832
11833 if (!mHWData.isNull())
11834 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11835 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11836 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11837 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11838
11839 if (mData->flModifications & IsModified_NetworkAdapters)
11840 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11841 if ( mNetworkAdapters[slot]
11842 && mNetworkAdapters[slot]->i_isModified())
11843 {
11844 mNetworkAdapters[slot]->i_rollback();
11845 networkAdapters[slot] = mNetworkAdapters[slot];
11846 }
11847
11848 if (mData->flModifications & IsModified_SerialPorts)
11849 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11850 if ( mSerialPorts[slot]
11851 && mSerialPorts[slot]->i_isModified())
11852 {
11853 mSerialPorts[slot]->i_rollback();
11854 serialPorts[slot] = mSerialPorts[slot];
11855 }
11856
11857 if (mData->flModifications & IsModified_ParallelPorts)
11858 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11859 if ( mParallelPorts[slot]
11860 && mParallelPorts[slot]->i_isModified())
11861 {
11862 mParallelPorts[slot]->i_rollback();
11863 parallelPorts[slot] = mParallelPorts[slot];
11864 }
11865
11866 if (aNotify)
11867 {
11868 /* inform the direct session about changes */
11869
11870 ComObjPtr<Machine> that = this;
11871 uint32_t flModifications = mData->flModifications;
11872 alock.release();
11873
11874 if (flModifications & IsModified_SharedFolders)
11875 that->i_onSharedFolderChange();
11876
11877 if (flModifications & IsModified_VRDEServer)
11878 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11879 if (flModifications & IsModified_USB)
11880 that->i_onUSBControllerChange();
11881
11882 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11883 if (networkAdapters[slot])
11884 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11885 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11886 if (serialPorts[slot])
11887 that->i_onSerialPortChange(serialPorts[slot]);
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11889 if (parallelPorts[slot])
11890 that->i_onParallelPortChange(parallelPorts[slot]);
11891
11892 if (flModifications & IsModified_Storage)
11893 {
11894 for (StorageControllerList::const_iterator
11895 it = mStorageControllers->begin();
11896 it != mStorageControllers->end();
11897 ++it)
11898 {
11899 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11900 }
11901 }
11902
11903
11904#if 0
11905 if (flModifications & IsModified_BandwidthControl)
11906 that->onBandwidthControlChange();
11907#endif
11908 }
11909}
11910
11911/**
11912 * Commits all the changes to machine settings.
11913 *
11914 * Note that this operation is supposed to never fail.
11915 *
11916 * @note Locks this object and children for writing.
11917 */
11918void Machine::i_commit()
11919{
11920 AutoCaller autoCaller(this);
11921 AssertComRCReturnVoid(autoCaller.rc());
11922
11923 AutoCaller peerCaller(mPeer);
11924 AssertComRCReturnVoid(peerCaller.rc());
11925
11926 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11927
11928 /*
11929 * use safe commit to ensure Snapshot machines (that share mUserData)
11930 * will still refer to a valid memory location
11931 */
11932 mUserData.commitCopy();
11933
11934 mHWData.commit();
11935
11936 if (mMediumAttachments.isBackedUp())
11937 i_commitMedia(Global::IsOnline(mData->mMachineState));
11938
11939 mBIOSSettings->i_commit();
11940 mRecordingSettings->i_commit();
11941 mVRDEServer->i_commit();
11942 mAudioAdapter->i_commit();
11943 mUSBDeviceFilters->i_commit();
11944 mBandwidthControl->i_commit();
11945
11946 /* Since mNetworkAdapters is a list which might have been changed (resized)
11947 * without using the Backupable<> template we need to handle the copying
11948 * of the list entries manually, including the creation of peers for the
11949 * new objects. */
11950 bool commitNetworkAdapters = false;
11951 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11952 if (mPeer)
11953 {
11954 /* commit everything, even the ones which will go away */
11955 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11956 mNetworkAdapters[slot]->i_commit();
11957 /* copy over the new entries, creating a peer and uninit the original */
11958 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11959 for (size_t slot = 0; slot < newSize; slot++)
11960 {
11961 /* look if this adapter has a peer device */
11962 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11963 if (!peer)
11964 {
11965 /* no peer means the adapter is a newly created one;
11966 * create a peer owning data this data share it with */
11967 peer.createObject();
11968 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11969 }
11970 mPeer->mNetworkAdapters[slot] = peer;
11971 }
11972 /* uninit any no longer needed network adapters */
11973 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11974 mNetworkAdapters[slot]->uninit();
11975 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11976 {
11977 if (mPeer->mNetworkAdapters[slot])
11978 mPeer->mNetworkAdapters[slot]->uninit();
11979 }
11980 /* Keep the original network adapter count until this point, so that
11981 * discarding a chipset type change will not lose settings. */
11982 mNetworkAdapters.resize(newSize);
11983 mPeer->mNetworkAdapters.resize(newSize);
11984 }
11985 else
11986 {
11987 /* we have no peer (our parent is the newly created machine);
11988 * just commit changes to the network adapters */
11989 commitNetworkAdapters = true;
11990 }
11991 if (commitNetworkAdapters)
11992 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11993 mNetworkAdapters[slot]->i_commit();
11994
11995 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11996 mSerialPorts[slot]->i_commit();
11997 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11998 mParallelPorts[slot]->i_commit();
11999
12000 bool commitStorageControllers = false;
12001
12002 if (mStorageControllers.isBackedUp())
12003 {
12004 mStorageControllers.commit();
12005
12006 if (mPeer)
12007 {
12008 /* Commit all changes to new controllers (this will reshare data with
12009 * peers for those who have peers) */
12010 StorageControllerList *newList = new StorageControllerList();
12011 for (StorageControllerList::const_iterator
12012 it = mStorageControllers->begin();
12013 it != mStorageControllers->end();
12014 ++it)
12015 {
12016 (*it)->i_commit();
12017
12018 /* look if this controller has a peer device */
12019 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12020 if (!peer)
12021 {
12022 /* no peer means the device is a newly created one;
12023 * create a peer owning data this device share it with */
12024 peer.createObject();
12025 peer->init(mPeer, *it, true /* aReshare */);
12026 }
12027 else
12028 {
12029 /* remove peer from the old list */
12030 mPeer->mStorageControllers->remove(peer);
12031 }
12032 /* and add it to the new list */
12033 newList->push_back(peer);
12034 }
12035
12036 /* uninit old peer's controllers that are left */
12037 for (StorageControllerList::const_iterator
12038 it = mPeer->mStorageControllers->begin();
12039 it != mPeer->mStorageControllers->end();
12040 ++it)
12041 {
12042 (*it)->uninit();
12043 }
12044
12045 /* attach new list of controllers to our peer */
12046 mPeer->mStorageControllers.attach(newList);
12047 }
12048 else
12049 {
12050 /* we have no peer (our parent is the newly created machine);
12051 * just commit changes to devices */
12052 commitStorageControllers = true;
12053 }
12054 }
12055 else
12056 {
12057 /* the list of controllers itself is not changed,
12058 * just commit changes to controllers themselves */
12059 commitStorageControllers = true;
12060 }
12061
12062 if (commitStorageControllers)
12063 {
12064 for (StorageControllerList::const_iterator
12065 it = mStorageControllers->begin();
12066 it != mStorageControllers->end();
12067 ++it)
12068 {
12069 (*it)->i_commit();
12070 }
12071 }
12072
12073 bool commitUSBControllers = false;
12074
12075 if (mUSBControllers.isBackedUp())
12076 {
12077 mUSBControllers.commit();
12078
12079 if (mPeer)
12080 {
12081 /* Commit all changes to new controllers (this will reshare data with
12082 * peers for those who have peers) */
12083 USBControllerList *newList = new USBControllerList();
12084 for (USBControllerList::const_iterator
12085 it = mUSBControllers->begin();
12086 it != mUSBControllers->end();
12087 ++it)
12088 {
12089 (*it)->i_commit();
12090
12091 /* look if this controller has a peer device */
12092 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12093 if (!peer)
12094 {
12095 /* no peer means the device is a newly created one;
12096 * create a peer owning data this device share it with */
12097 peer.createObject();
12098 peer->init(mPeer, *it, true /* aReshare */);
12099 }
12100 else
12101 {
12102 /* remove peer from the old list */
12103 mPeer->mUSBControllers->remove(peer);
12104 }
12105 /* and add it to the new list */
12106 newList->push_back(peer);
12107 }
12108
12109 /* uninit old peer's controllers that are left */
12110 for (USBControllerList::const_iterator
12111 it = mPeer->mUSBControllers->begin();
12112 it != mPeer->mUSBControllers->end();
12113 ++it)
12114 {
12115 (*it)->uninit();
12116 }
12117
12118 /* attach new list of controllers to our peer */
12119 mPeer->mUSBControllers.attach(newList);
12120 }
12121 else
12122 {
12123 /* we have no peer (our parent is the newly created machine);
12124 * just commit changes to devices */
12125 commitUSBControllers = true;
12126 }
12127 }
12128 else
12129 {
12130 /* the list of controllers itself is not changed,
12131 * just commit changes to controllers themselves */
12132 commitUSBControllers = true;
12133 }
12134
12135 if (commitUSBControllers)
12136 {
12137 for (USBControllerList::const_iterator
12138 it = mUSBControllers->begin();
12139 it != mUSBControllers->end();
12140 ++it)
12141 {
12142 (*it)->i_commit();
12143 }
12144 }
12145
12146 if (i_isSessionMachine())
12147 {
12148 /* attach new data to the primary machine and reshare it */
12149 mPeer->mUserData.attach(mUserData);
12150 mPeer->mHWData.attach(mHWData);
12151 /* mmMediumAttachments is reshared by fixupMedia */
12152 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12153 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12154 }
12155}
12156
12157/**
12158 * Copies all the hardware data from the given machine.
12159 *
12160 * Currently, only called when the VM is being restored from a snapshot. In
12161 * particular, this implies that the VM is not running during this method's
12162 * call.
12163 *
12164 * @note This method must be called from under this object's lock.
12165 *
12166 * @note This method doesn't call #i_commit(), so all data remains backed up and
12167 * unsaved.
12168 */
12169void Machine::i_copyFrom(Machine *aThat)
12170{
12171 AssertReturnVoid(!i_isSnapshotMachine());
12172 AssertReturnVoid(aThat->i_isSnapshotMachine());
12173
12174 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12175
12176 mHWData.assignCopy(aThat->mHWData);
12177
12178 // create copies of all shared folders (mHWData after attaching a copy
12179 // contains just references to original objects)
12180 for (HWData::SharedFolderList::iterator
12181 it = mHWData->mSharedFolders.begin();
12182 it != mHWData->mSharedFolders.end();
12183 ++it)
12184 {
12185 ComObjPtr<SharedFolder> folder;
12186 folder.createObject();
12187 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12188 AssertComRC(rc);
12189 *it = folder;
12190 }
12191
12192 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12193 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12194 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12195 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12196 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12197 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12198
12199 /* create private copies of all controllers */
12200 mStorageControllers.backup();
12201 mStorageControllers->clear();
12202 for (StorageControllerList::const_iterator
12203 it = aThat->mStorageControllers->begin();
12204 it != aThat->mStorageControllers->end();
12205 ++it)
12206 {
12207 ComObjPtr<StorageController> ctrl;
12208 ctrl.createObject();
12209 ctrl->initCopy(this, *it);
12210 mStorageControllers->push_back(ctrl);
12211 }
12212
12213 /* create private copies of all USB controllers */
12214 mUSBControllers.backup();
12215 mUSBControllers->clear();
12216 for (USBControllerList::const_iterator
12217 it = aThat->mUSBControllers->begin();
12218 it != aThat->mUSBControllers->end();
12219 ++it)
12220 {
12221 ComObjPtr<USBController> ctrl;
12222 ctrl.createObject();
12223 ctrl->initCopy(this, *it);
12224 mUSBControllers->push_back(ctrl);
12225 }
12226
12227 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12228 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12229 {
12230 if (mNetworkAdapters[slot].isNotNull())
12231 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12232 else
12233 {
12234 unconst(mNetworkAdapters[slot]).createObject();
12235 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12236 }
12237 }
12238 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12239 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12240 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12241 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12242}
12243
12244/**
12245 * Returns whether the given storage controller is hotplug capable.
12246 *
12247 * @returns true if the controller supports hotplugging
12248 * false otherwise.
12249 * @param enmCtrlType The controller type to check for.
12250 */
12251bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12252{
12253 ComPtr<ISystemProperties> systemProperties;
12254 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12255 if (FAILED(rc))
12256 return false;
12257
12258 BOOL aHotplugCapable = FALSE;
12259 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12260
12261 return RT_BOOL(aHotplugCapable);
12262}
12263
12264#ifdef VBOX_WITH_RESOURCE_USAGE_API
12265
12266void Machine::i_getDiskList(MediaList &list)
12267{
12268 for (MediumAttachmentList::const_iterator
12269 it = mMediumAttachments->begin();
12270 it != mMediumAttachments->end();
12271 ++it)
12272 {
12273 MediumAttachment *pAttach = *it;
12274 /* just in case */
12275 AssertContinue(pAttach);
12276
12277 AutoCaller localAutoCallerA(pAttach);
12278 if (FAILED(localAutoCallerA.rc())) continue;
12279
12280 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12281
12282 if (pAttach->i_getType() == DeviceType_HardDisk)
12283 list.push_back(pAttach->i_getMedium());
12284 }
12285}
12286
12287void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12288{
12289 AssertReturnVoid(isWriteLockOnCurrentThread());
12290 AssertPtrReturnVoid(aCollector);
12291
12292 pm::CollectorHAL *hal = aCollector->getHAL();
12293 /* Create sub metrics */
12294 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12295 "Percentage of processor time spent in user mode by the VM process.");
12296 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12297 "Percentage of processor time spent in kernel mode by the VM process.");
12298 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12299 "Size of resident portion of VM process in memory.");
12300 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12301 "Actual size of all VM disks combined.");
12302 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12303 "Network receive rate.");
12304 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12305 "Network transmit rate.");
12306 /* Create and register base metrics */
12307 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12308 cpuLoadUser, cpuLoadKernel);
12309 aCollector->registerBaseMetric(cpuLoad);
12310 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12311 ramUsageUsed);
12312 aCollector->registerBaseMetric(ramUsage);
12313 MediaList disks;
12314 i_getDiskList(disks);
12315 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12316 diskUsageUsed);
12317 aCollector->registerBaseMetric(diskUsage);
12318
12319 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12320 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12321 new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12323 new pm::AggregateMin()));
12324 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12325 new pm::AggregateMax()));
12326 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12327 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12328 new pm::AggregateAvg()));
12329 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12330 new pm::AggregateMin()));
12331 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12332 new pm::AggregateMax()));
12333
12334 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12335 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12336 new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12338 new pm::AggregateMin()));
12339 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12340 new pm::AggregateMax()));
12341
12342 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12343 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12344 new pm::AggregateAvg()));
12345 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12346 new pm::AggregateMin()));
12347 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12348 new pm::AggregateMax()));
12349
12350
12351 /* Guest metrics collector */
12352 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12353 aCollector->registerGuest(mCollectorGuest);
12354 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12355
12356 /* Create sub metrics */
12357 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12358 "Percentage of processor time spent in user mode as seen by the guest.");
12359 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12360 "Percentage of processor time spent in kernel mode as seen by the guest.");
12361 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12362 "Percentage of processor time spent idling as seen by the guest.");
12363
12364 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12365 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12366 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12367 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12368 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12369 pm::SubMetric *guestMemCache = new pm::SubMetric(
12370 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12371
12372 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12373 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12374
12375 /* Create and register base metrics */
12376 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12377 machineNetRx, machineNetTx);
12378 aCollector->registerBaseMetric(machineNetRate);
12379
12380 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12381 guestLoadUser, guestLoadKernel, guestLoadIdle);
12382 aCollector->registerBaseMetric(guestCpuLoad);
12383
12384 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12385 guestMemTotal, guestMemFree,
12386 guestMemBalloon, guestMemShared,
12387 guestMemCache, guestPagedTotal);
12388 aCollector->registerBaseMetric(guestCpuMem);
12389
12390 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12391 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12392 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12393 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12394
12395 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12396 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12397 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12399
12400 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12401 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12404
12405 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12406 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12407 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12409
12410 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12411 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12412 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12413 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12414
12415 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12416 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12417 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12418 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12419
12420 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12421 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12422 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12423 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12424
12425 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12426 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12427 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12428 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12429
12430 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12431 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12432 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12433 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12434
12435 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12436 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12437 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12438 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12439
12440 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12441 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12442 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12443 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12444}
12445
12446void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12447{
12448 AssertReturnVoid(isWriteLockOnCurrentThread());
12449
12450 if (aCollector)
12451 {
12452 aCollector->unregisterMetricsFor(aMachine);
12453 aCollector->unregisterBaseMetricsFor(aMachine);
12454 }
12455}
12456
12457#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12458
12459
12460////////////////////////////////////////////////////////////////////////////////
12461
12462DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12463
12464HRESULT SessionMachine::FinalConstruct()
12465{
12466 LogFlowThisFunc(("\n"));
12467
12468 mClientToken = NULL;
12469
12470 return BaseFinalConstruct();
12471}
12472
12473void SessionMachine::FinalRelease()
12474{
12475 LogFlowThisFunc(("\n"));
12476
12477 Assert(!mClientToken);
12478 /* paranoia, should not hang around any more */
12479 if (mClientToken)
12480 {
12481 delete mClientToken;
12482 mClientToken = NULL;
12483 }
12484
12485 uninit(Uninit::Unexpected);
12486
12487 BaseFinalRelease();
12488}
12489
12490/**
12491 * @note Must be called only by Machine::LockMachine() from its own write lock.
12492 */
12493HRESULT SessionMachine::init(Machine *aMachine)
12494{
12495 LogFlowThisFuncEnter();
12496 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12497
12498 AssertReturn(aMachine, E_INVALIDARG);
12499
12500 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12501
12502 /* Enclose the state transition NotReady->InInit->Ready */
12503 AutoInitSpan autoInitSpan(this);
12504 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12505
12506 HRESULT rc = S_OK;
12507
12508 RT_ZERO(mAuthLibCtx);
12509
12510 /* create the machine client token */
12511 try
12512 {
12513 mClientToken = new ClientToken(aMachine, this);
12514 if (!mClientToken->isReady())
12515 {
12516 delete mClientToken;
12517 mClientToken = NULL;
12518 rc = E_FAIL;
12519 }
12520 }
12521 catch (std::bad_alloc &)
12522 {
12523 rc = E_OUTOFMEMORY;
12524 }
12525 if (FAILED(rc))
12526 return rc;
12527
12528 /* memorize the peer Machine */
12529 unconst(mPeer) = aMachine;
12530 /* share the parent pointer */
12531 unconst(mParent) = aMachine->mParent;
12532
12533 /* take the pointers to data to share */
12534 mData.share(aMachine->mData);
12535 mSSData.share(aMachine->mSSData);
12536
12537 mUserData.share(aMachine->mUserData);
12538 mHWData.share(aMachine->mHWData);
12539 mMediumAttachments.share(aMachine->mMediumAttachments);
12540
12541 mStorageControllers.allocate();
12542 for (StorageControllerList::const_iterator
12543 it = aMachine->mStorageControllers->begin();
12544 it != aMachine->mStorageControllers->end();
12545 ++it)
12546 {
12547 ComObjPtr<StorageController> ctl;
12548 ctl.createObject();
12549 ctl->init(this, *it);
12550 mStorageControllers->push_back(ctl);
12551 }
12552
12553 mUSBControllers.allocate();
12554 for (USBControllerList::const_iterator
12555 it = aMachine->mUSBControllers->begin();
12556 it != aMachine->mUSBControllers->end();
12557 ++it)
12558 {
12559 ComObjPtr<USBController> ctl;
12560 ctl.createObject();
12561 ctl->init(this, *it);
12562 mUSBControllers->push_back(ctl);
12563 }
12564
12565 unconst(mBIOSSettings).createObject();
12566 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12567 unconst(mRecordingSettings).createObject();
12568 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12569 /* create another VRDEServer object that will be mutable */
12570 unconst(mVRDEServer).createObject();
12571 mVRDEServer->init(this, aMachine->mVRDEServer);
12572 /* create another audio adapter object that will be mutable */
12573 unconst(mAudioAdapter).createObject();
12574 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12575 /* create a list of serial ports that will be mutable */
12576 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12577 {
12578 unconst(mSerialPorts[slot]).createObject();
12579 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12580 }
12581 /* create a list of parallel ports that will be mutable */
12582 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12583 {
12584 unconst(mParallelPorts[slot]).createObject();
12585 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12586 }
12587
12588 /* create another USB device filters object that will be mutable */
12589 unconst(mUSBDeviceFilters).createObject();
12590 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12591
12592 /* create a list of network adapters that will be mutable */
12593 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12594 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12595 {
12596 unconst(mNetworkAdapters[slot]).createObject();
12597 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12598 }
12599
12600 /* create another bandwidth control object that will be mutable */
12601 unconst(mBandwidthControl).createObject();
12602 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12603
12604 /* default is to delete saved state on Saved -> PoweredOff transition */
12605 mRemoveSavedState = true;
12606
12607 /* Confirm a successful initialization when it's the case */
12608 autoInitSpan.setSucceeded();
12609
12610 miNATNetworksStarted = 0;
12611
12612 LogFlowThisFuncLeave();
12613 return rc;
12614}
12615
12616/**
12617 * Uninitializes this session object. If the reason is other than
12618 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12619 * or the client watcher code.
12620 *
12621 * @param aReason uninitialization reason
12622 *
12623 * @note Locks mParent + this object for writing.
12624 */
12625void SessionMachine::uninit(Uninit::Reason aReason)
12626{
12627 LogFlowThisFuncEnter();
12628 LogFlowThisFunc(("reason=%d\n", aReason));
12629
12630 /*
12631 * Strongly reference ourselves to prevent this object deletion after
12632 * mData->mSession.mMachine.setNull() below (which can release the last
12633 * reference and call the destructor). Important: this must be done before
12634 * accessing any members (and before AutoUninitSpan that does it as well).
12635 * This self reference will be released as the very last step on return.
12636 */
12637 ComObjPtr<SessionMachine> selfRef;
12638 if (aReason != Uninit::Unexpected)
12639 selfRef = this;
12640
12641 /* Enclose the state transition Ready->InUninit->NotReady */
12642 AutoUninitSpan autoUninitSpan(this);
12643 if (autoUninitSpan.uninitDone())
12644 {
12645 LogFlowThisFunc(("Already uninitialized\n"));
12646 LogFlowThisFuncLeave();
12647 return;
12648 }
12649
12650 if (autoUninitSpan.initFailed())
12651 {
12652 /* We've been called by init() because it's failed. It's not really
12653 * necessary (nor it's safe) to perform the regular uninit sequence
12654 * below, the following is enough.
12655 */
12656 LogFlowThisFunc(("Initialization failed.\n"));
12657 /* destroy the machine client token */
12658 if (mClientToken)
12659 {
12660 delete mClientToken;
12661 mClientToken = NULL;
12662 }
12663 uninitDataAndChildObjects();
12664 mData.free();
12665 unconst(mParent) = NULL;
12666 unconst(mPeer) = NULL;
12667 LogFlowThisFuncLeave();
12668 return;
12669 }
12670
12671 MachineState_T lastState;
12672 {
12673 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12674 lastState = mData->mMachineState;
12675 }
12676 NOREF(lastState);
12677
12678#ifdef VBOX_WITH_USB
12679 // release all captured USB devices, but do this before requesting the locks below
12680 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12681 {
12682 /* Console::captureUSBDevices() is called in the VM process only after
12683 * setting the machine state to Starting or Restoring.
12684 * Console::detachAllUSBDevices() will be called upon successful
12685 * termination. So, we need to release USB devices only if there was
12686 * an abnormal termination of a running VM.
12687 *
12688 * This is identical to SessionMachine::DetachAllUSBDevices except
12689 * for the aAbnormal argument. */
12690 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12691 AssertComRC(rc);
12692 NOREF(rc);
12693
12694 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12695 if (service)
12696 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12697 }
12698#endif /* VBOX_WITH_USB */
12699
12700 // we need to lock this object in uninit() because the lock is shared
12701 // with mPeer (as well as data we modify below). mParent lock is needed
12702 // by several calls to it.
12703 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12704
12705#ifdef VBOX_WITH_RESOURCE_USAGE_API
12706 /*
12707 * It is safe to call Machine::i_unregisterMetrics() here because
12708 * PerformanceCollector::samplerCallback no longer accesses guest methods
12709 * holding the lock.
12710 */
12711 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12712 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12713 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12714 if (mCollectorGuest)
12715 {
12716 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12717 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12718 mCollectorGuest = NULL;
12719 }
12720#endif
12721
12722 if (aReason == Uninit::Abnormal)
12723 {
12724 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12725
12726 /* reset the state to Aborted */
12727 if (mData->mMachineState != MachineState_Aborted)
12728 i_setMachineState(MachineState_Aborted);
12729 }
12730
12731 // any machine settings modified?
12732 if (mData->flModifications)
12733 {
12734 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12735 i_rollback(false /* aNotify */);
12736 }
12737
12738 mData->mSession.mPID = NIL_RTPROCESS;
12739
12740 if (aReason == Uninit::Unexpected)
12741 {
12742 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12743 * client watcher thread to update the set of machines that have open
12744 * sessions. */
12745 mParent->i_updateClientWatcher();
12746 }
12747
12748 /* uninitialize all remote controls */
12749 if (mData->mSession.mRemoteControls.size())
12750 {
12751 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12752 mData->mSession.mRemoteControls.size()));
12753
12754 /* Always restart a the beginning, since the iterator is invalidated
12755 * by using erase(). */
12756 for (Data::Session::RemoteControlList::iterator
12757 it = mData->mSession.mRemoteControls.begin();
12758 it != mData->mSession.mRemoteControls.end();
12759 it = mData->mSession.mRemoteControls.begin())
12760 {
12761 ComPtr<IInternalSessionControl> pControl = *it;
12762 mData->mSession.mRemoteControls.erase(it);
12763 multilock.release();
12764 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12765 HRESULT rc = pControl->Uninitialize();
12766 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12767 if (FAILED(rc))
12768 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12769 multilock.acquire();
12770 }
12771 mData->mSession.mRemoteControls.clear();
12772 }
12773
12774 /* Remove all references to the NAT network service. The service will stop
12775 * if all references (also from other VMs) are removed. */
12776 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12777 {
12778 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12779 {
12780 BOOL enabled;
12781 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12782 if ( FAILED(hrc)
12783 || !enabled)
12784 continue;
12785
12786 NetworkAttachmentType_T type;
12787 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12788 if ( SUCCEEDED(hrc)
12789 && type == NetworkAttachmentType_NATNetwork)
12790 {
12791 Bstr name;
12792 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12793 if (SUCCEEDED(hrc))
12794 {
12795 multilock.release();
12796 Utf8Str strName(name);
12797 LogRel(("VM '%s' stops using NAT network '%s'\n",
12798 mUserData->s.strName.c_str(), strName.c_str()));
12799 mParent->i_natNetworkRefDec(strName);
12800 multilock.acquire();
12801 }
12802 }
12803 }
12804 }
12805
12806 /*
12807 * An expected uninitialization can come only from #i_checkForDeath().
12808 * Otherwise it means that something's gone really wrong (for example,
12809 * the Session implementation has released the VirtualBox reference
12810 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12811 * etc). However, it's also possible, that the client releases the IPC
12812 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12813 * but the VirtualBox release event comes first to the server process.
12814 * This case is practically possible, so we should not assert on an
12815 * unexpected uninit, just log a warning.
12816 */
12817
12818 if (aReason == Uninit::Unexpected)
12819 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12820
12821 if (aReason != Uninit::Normal)
12822 {
12823 mData->mSession.mDirectControl.setNull();
12824 }
12825 else
12826 {
12827 /* this must be null here (see #OnSessionEnd()) */
12828 Assert(mData->mSession.mDirectControl.isNull());
12829 Assert(mData->mSession.mState == SessionState_Unlocking);
12830 Assert(!mData->mSession.mProgress.isNull());
12831 }
12832 if (mData->mSession.mProgress)
12833 {
12834 if (aReason == Uninit::Normal)
12835 mData->mSession.mProgress->i_notifyComplete(S_OK);
12836 else
12837 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12838 COM_IIDOF(ISession),
12839 getComponentName(),
12840 tr("The VM session was aborted"));
12841 mData->mSession.mProgress.setNull();
12842 }
12843
12844 if (mConsoleTaskData.mProgress)
12845 {
12846 Assert(aReason == Uninit::Abnormal);
12847 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12848 COM_IIDOF(ISession),
12849 getComponentName(),
12850 tr("The VM session was aborted"));
12851 mConsoleTaskData.mProgress.setNull();
12852 }
12853
12854 /* remove the association between the peer machine and this session machine */
12855 Assert( (SessionMachine*)mData->mSession.mMachine == this
12856 || aReason == Uninit::Unexpected);
12857
12858 /* reset the rest of session data */
12859 mData->mSession.mLockType = LockType_Null;
12860 mData->mSession.mMachine.setNull();
12861 mData->mSession.mState = SessionState_Unlocked;
12862 mData->mSession.mName.setNull();
12863
12864 /* destroy the machine client token before leaving the exclusive lock */
12865 if (mClientToken)
12866 {
12867 delete mClientToken;
12868 mClientToken = NULL;
12869 }
12870
12871 /* fire an event */
12872 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12873
12874 uninitDataAndChildObjects();
12875
12876 /* free the essential data structure last */
12877 mData.free();
12878
12879 /* release the exclusive lock before setting the below two to NULL */
12880 multilock.release();
12881
12882 unconst(mParent) = NULL;
12883 unconst(mPeer) = NULL;
12884
12885 AuthLibUnload(&mAuthLibCtx);
12886
12887 LogFlowThisFuncLeave();
12888}
12889
12890// util::Lockable interface
12891////////////////////////////////////////////////////////////////////////////////
12892
12893/**
12894 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12895 * with the primary Machine instance (mPeer).
12896 */
12897RWLockHandle *SessionMachine::lockHandle() const
12898{
12899 AssertReturn(mPeer != NULL, NULL);
12900 return mPeer->lockHandle();
12901}
12902
12903// IInternalMachineControl methods
12904////////////////////////////////////////////////////////////////////////////////
12905
12906/**
12907 * Passes collected guest statistics to performance collector object
12908 */
12909HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12910 ULONG aCpuKernel, ULONG aCpuIdle,
12911 ULONG aMemTotal, ULONG aMemFree,
12912 ULONG aMemBalloon, ULONG aMemShared,
12913 ULONG aMemCache, ULONG aPageTotal,
12914 ULONG aAllocVMM, ULONG aFreeVMM,
12915 ULONG aBalloonedVMM, ULONG aSharedVMM,
12916 ULONG aVmNetRx, ULONG aVmNetTx)
12917{
12918#ifdef VBOX_WITH_RESOURCE_USAGE_API
12919 if (mCollectorGuest)
12920 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12921 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12922 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12923 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12924
12925 return S_OK;
12926#else
12927 NOREF(aValidStats);
12928 NOREF(aCpuUser);
12929 NOREF(aCpuKernel);
12930 NOREF(aCpuIdle);
12931 NOREF(aMemTotal);
12932 NOREF(aMemFree);
12933 NOREF(aMemBalloon);
12934 NOREF(aMemShared);
12935 NOREF(aMemCache);
12936 NOREF(aPageTotal);
12937 NOREF(aAllocVMM);
12938 NOREF(aFreeVMM);
12939 NOREF(aBalloonedVMM);
12940 NOREF(aSharedVMM);
12941 NOREF(aVmNetRx);
12942 NOREF(aVmNetTx);
12943 return E_NOTIMPL;
12944#endif
12945}
12946
12947////////////////////////////////////////////////////////////////////////////////
12948//
12949// SessionMachine task records
12950//
12951////////////////////////////////////////////////////////////////////////////////
12952
12953/**
12954 * Task record for saving the machine state.
12955 */
12956class SessionMachine::SaveStateTask
12957 : public Machine::Task
12958{
12959public:
12960 SaveStateTask(SessionMachine *m,
12961 Progress *p,
12962 const Utf8Str &t,
12963 Reason_T enmReason,
12964 const Utf8Str &strStateFilePath)
12965 : Task(m, p, t),
12966 m_enmReason(enmReason),
12967 m_strStateFilePath(strStateFilePath)
12968 {}
12969
12970private:
12971 void handler()
12972 {
12973 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12974 }
12975
12976 Reason_T m_enmReason;
12977 Utf8Str m_strStateFilePath;
12978
12979 friend class SessionMachine;
12980};
12981
12982/**
12983 * Task thread implementation for SessionMachine::SaveState(), called from
12984 * SessionMachine::taskHandler().
12985 *
12986 * @note Locks this object for writing.
12987 *
12988 * @param task
12989 * @return
12990 */
12991void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12992{
12993 LogFlowThisFuncEnter();
12994
12995 AutoCaller autoCaller(this);
12996 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12997 if (FAILED(autoCaller.rc()))
12998 {
12999 /* we might have been uninitialized because the session was accidentally
13000 * closed by the client, so don't assert */
13001 HRESULT rc = setError(E_FAIL,
13002 tr("The session has been accidentally closed"));
13003 task.m_pProgress->i_notifyComplete(rc);
13004 LogFlowThisFuncLeave();
13005 return;
13006 }
13007
13008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13009
13010 HRESULT rc = S_OK;
13011
13012 try
13013 {
13014 ComPtr<IInternalSessionControl> directControl;
13015 if (mData->mSession.mLockType == LockType_VM)
13016 directControl = mData->mSession.mDirectControl;
13017 if (directControl.isNull())
13018 throw setError(VBOX_E_INVALID_VM_STATE,
13019 tr("Trying to save state without a running VM"));
13020 alock.release();
13021 BOOL fSuspendedBySave;
13022 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13023 Assert(!fSuspendedBySave);
13024 alock.acquire();
13025
13026 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13027 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13028 throw E_FAIL);
13029
13030 if (SUCCEEDED(rc))
13031 {
13032 mSSData->strStateFilePath = task.m_strStateFilePath;
13033
13034 /* save all VM settings */
13035 rc = i_saveSettings(NULL);
13036 // no need to check whether VirtualBox.xml needs saving also since
13037 // we can't have a name change pending at this point
13038 }
13039 else
13040 {
13041 // On failure, set the state to the state we had at the beginning.
13042 i_setMachineState(task.m_machineStateBackup);
13043 i_updateMachineStateOnClient();
13044
13045 // Delete the saved state file (might have been already created).
13046 // No need to check whether this is shared with a snapshot here
13047 // because we certainly created a fresh saved state file here.
13048 RTFileDelete(task.m_strStateFilePath.c_str());
13049 }
13050 }
13051 catch (HRESULT aRC) { rc = aRC; }
13052
13053 task.m_pProgress->i_notifyComplete(rc);
13054
13055 LogFlowThisFuncLeave();
13056}
13057
13058/**
13059 * @note Locks this object for writing.
13060 */
13061HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13062{
13063 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13064}
13065
13066HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13067{
13068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13069
13070 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13071 if (FAILED(rc)) return rc;
13072
13073 if ( mData->mMachineState != MachineState_Running
13074 && mData->mMachineState != MachineState_Paused
13075 )
13076 return setError(VBOX_E_INVALID_VM_STATE,
13077 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13078 Global::stringifyMachineState(mData->mMachineState));
13079
13080 ComObjPtr<Progress> pProgress;
13081 pProgress.createObject();
13082 rc = pProgress->init(i_getVirtualBox(),
13083 static_cast<IMachine *>(this) /* aInitiator */,
13084 tr("Saving the execution state of the virtual machine"),
13085 FALSE /* aCancelable */);
13086 if (FAILED(rc))
13087 return rc;
13088
13089 Utf8Str strStateFilePath;
13090 i_composeSavedStateFilename(strStateFilePath);
13091
13092 /* create and start the task on a separate thread (note that it will not
13093 * start working until we release alock) */
13094 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13095 rc = pTask->createThread();
13096 if (FAILED(rc))
13097 return rc;
13098
13099 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13100 i_setMachineState(MachineState_Saving);
13101 i_updateMachineStateOnClient();
13102
13103 pProgress.queryInterfaceTo(aProgress.asOutParam());
13104
13105 return S_OK;
13106}
13107
13108/**
13109 * @note Locks this object for writing.
13110 */
13111HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13112{
13113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13114
13115 HRESULT rc = i_checkStateDependency(MutableStateDep);
13116 if (FAILED(rc)) return rc;
13117
13118 if ( mData->mMachineState != MachineState_PoweredOff
13119 && mData->mMachineState != MachineState_Teleported
13120 && mData->mMachineState != MachineState_Aborted
13121 )
13122 return setError(VBOX_E_INVALID_VM_STATE,
13123 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13124 Global::stringifyMachineState(mData->mMachineState));
13125
13126 com::Utf8Str stateFilePathFull;
13127 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13128 if (RT_FAILURE(vrc))
13129 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13130 tr("Invalid saved state file path '%s' (%Rrc)"),
13131 aSavedStateFile.c_str(),
13132 vrc);
13133
13134 mSSData->strStateFilePath = stateFilePathFull;
13135
13136 /* The below i_setMachineState() will detect the state transition and will
13137 * update the settings file */
13138
13139 return i_setMachineState(MachineState_Saved);
13140}
13141
13142/**
13143 * @note Locks this object for writing.
13144 */
13145HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13146{
13147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13148
13149 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13150 if (FAILED(rc)) return rc;
13151
13152 if (mData->mMachineState != MachineState_Saved)
13153 return setError(VBOX_E_INVALID_VM_STATE,
13154 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13155 Global::stringifyMachineState(mData->mMachineState));
13156
13157 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13158
13159 /*
13160 * Saved -> PoweredOff transition will be detected in the SessionMachine
13161 * and properly handled.
13162 */
13163 rc = i_setMachineState(MachineState_PoweredOff);
13164 return rc;
13165}
13166
13167
13168/**
13169 * @note Locks the same as #i_setMachineState() does.
13170 */
13171HRESULT SessionMachine::updateState(MachineState_T aState)
13172{
13173 return i_setMachineState(aState);
13174}
13175
13176/**
13177 * @note Locks this object for writing.
13178 */
13179HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13180{
13181 IProgress *pProgress(aProgress);
13182
13183 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13184
13185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13186
13187 if (mData->mSession.mState != SessionState_Locked)
13188 return VBOX_E_INVALID_OBJECT_STATE;
13189
13190 if (!mData->mSession.mProgress.isNull())
13191 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13192
13193 /* If we didn't reference the NAT network service yet, add a reference to
13194 * force a start */
13195 if (miNATNetworksStarted < 1)
13196 {
13197 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13198 {
13199 BOOL enabled;
13200 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13201 if ( FAILED(hrc)
13202 || !enabled)
13203 continue;
13204
13205 NetworkAttachmentType_T type;
13206 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13207 if ( SUCCEEDED(hrc)
13208 && type == NetworkAttachmentType_NATNetwork)
13209 {
13210 Bstr name;
13211 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13212 if (SUCCEEDED(hrc))
13213 {
13214 Utf8Str strName(name);
13215 LogRel(("VM '%s' starts using NAT network '%s'\n",
13216 mUserData->s.strName.c_str(), strName.c_str()));
13217 mPeer->lockHandle()->unlockWrite();
13218 mParent->i_natNetworkRefInc(strName);
13219#ifdef RT_LOCK_STRICT
13220 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13221#else
13222 mPeer->lockHandle()->lockWrite();
13223#endif
13224 }
13225 }
13226 }
13227 miNATNetworksStarted++;
13228 }
13229
13230 LogFlowThisFunc(("returns S_OK.\n"));
13231 return S_OK;
13232}
13233
13234/**
13235 * @note Locks this object for writing.
13236 */
13237HRESULT SessionMachine::endPowerUp(LONG aResult)
13238{
13239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13240
13241 if (mData->mSession.mState != SessionState_Locked)
13242 return VBOX_E_INVALID_OBJECT_STATE;
13243
13244 /* Finalize the LaunchVMProcess progress object. */
13245 if (mData->mSession.mProgress)
13246 {
13247 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13248 mData->mSession.mProgress.setNull();
13249 }
13250
13251 if (SUCCEEDED((HRESULT)aResult))
13252 {
13253#ifdef VBOX_WITH_RESOURCE_USAGE_API
13254 /* The VM has been powered up successfully, so it makes sense
13255 * now to offer the performance metrics for a running machine
13256 * object. Doing it earlier wouldn't be safe. */
13257 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13258 mData->mSession.mPID);
13259#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13260 }
13261
13262 return S_OK;
13263}
13264
13265/**
13266 * @note Locks this object for writing.
13267 */
13268HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13269{
13270 LogFlowThisFuncEnter();
13271
13272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13273
13274 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13275 E_FAIL);
13276
13277 /* create a progress object to track operation completion */
13278 ComObjPtr<Progress> pProgress;
13279 pProgress.createObject();
13280 pProgress->init(i_getVirtualBox(),
13281 static_cast<IMachine *>(this) /* aInitiator */,
13282 tr("Stopping the virtual machine"),
13283 FALSE /* aCancelable */);
13284
13285 /* fill in the console task data */
13286 mConsoleTaskData.mLastState = mData->mMachineState;
13287 mConsoleTaskData.mProgress = pProgress;
13288
13289 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13290 i_setMachineState(MachineState_Stopping);
13291
13292 pProgress.queryInterfaceTo(aProgress.asOutParam());
13293
13294 return S_OK;
13295}
13296
13297/**
13298 * @note Locks this object for writing.
13299 */
13300HRESULT SessionMachine::endPoweringDown(LONG aResult,
13301 const com::Utf8Str &aErrMsg)
13302{
13303 LogFlowThisFuncEnter();
13304
13305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13306
13307 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13308 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13309 && mConsoleTaskData.mLastState != MachineState_Null,
13310 E_FAIL);
13311
13312 /*
13313 * On failure, set the state to the state we had when BeginPoweringDown()
13314 * was called (this is expected by Console::PowerDown() and the associated
13315 * task). On success the VM process already changed the state to
13316 * MachineState_PoweredOff, so no need to do anything.
13317 */
13318 if (FAILED(aResult))
13319 i_setMachineState(mConsoleTaskData.mLastState);
13320
13321 /* notify the progress object about operation completion */
13322 Assert(mConsoleTaskData.mProgress);
13323 if (SUCCEEDED(aResult))
13324 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13325 else
13326 {
13327 if (aErrMsg.length())
13328 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13329 COM_IIDOF(ISession),
13330 getComponentName(),
13331 aErrMsg.c_str());
13332 else
13333 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13334 }
13335
13336 /* clear out the temporary saved state data */
13337 mConsoleTaskData.mLastState = MachineState_Null;
13338 mConsoleTaskData.mProgress.setNull();
13339
13340 LogFlowThisFuncLeave();
13341 return S_OK;
13342}
13343
13344
13345/**
13346 * Goes through the USB filters of the given machine to see if the given
13347 * device matches any filter or not.
13348 *
13349 * @note Locks the same as USBController::hasMatchingFilter() does.
13350 */
13351HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13352 BOOL *aMatched,
13353 ULONG *aMaskedInterfaces)
13354{
13355 LogFlowThisFunc(("\n"));
13356
13357#ifdef VBOX_WITH_USB
13358 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13359#else
13360 NOREF(aDevice);
13361 NOREF(aMaskedInterfaces);
13362 *aMatched = FALSE;
13363#endif
13364
13365 return S_OK;
13366}
13367
13368/**
13369 * @note Locks the same as Host::captureUSBDevice() does.
13370 */
13371HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13372{
13373 LogFlowThisFunc(("\n"));
13374
13375#ifdef VBOX_WITH_USB
13376 /* if captureDeviceForVM() fails, it must have set extended error info */
13377 clearError();
13378 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13379 if (FAILED(rc)) return rc;
13380
13381 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13382 AssertReturn(service, E_FAIL);
13383 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13384#else
13385 NOREF(aId);
13386 return E_NOTIMPL;
13387#endif
13388}
13389
13390/**
13391 * @note Locks the same as Host::detachUSBDevice() does.
13392 */
13393HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13394 BOOL aDone)
13395{
13396 LogFlowThisFunc(("\n"));
13397
13398#ifdef VBOX_WITH_USB
13399 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13400 AssertReturn(service, E_FAIL);
13401 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13402#else
13403 NOREF(aId);
13404 NOREF(aDone);
13405 return E_NOTIMPL;
13406#endif
13407}
13408
13409/**
13410 * Inserts all machine filters to the USB proxy service and then calls
13411 * Host::autoCaptureUSBDevices().
13412 *
13413 * Called by Console from the VM process upon VM startup.
13414 *
13415 * @note Locks what called methods lock.
13416 */
13417HRESULT SessionMachine::autoCaptureUSBDevices()
13418{
13419 LogFlowThisFunc(("\n"));
13420
13421#ifdef VBOX_WITH_USB
13422 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13423 AssertComRC(rc);
13424 NOREF(rc);
13425
13426 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13427 AssertReturn(service, E_FAIL);
13428 return service->autoCaptureDevicesForVM(this);
13429#else
13430 return S_OK;
13431#endif
13432}
13433
13434/**
13435 * Removes all machine filters from the USB proxy service and then calls
13436 * Host::detachAllUSBDevices().
13437 *
13438 * Called by Console from the VM process upon normal VM termination or by
13439 * SessionMachine::uninit() upon abnormal VM termination (from under the
13440 * Machine/SessionMachine lock).
13441 *
13442 * @note Locks what called methods lock.
13443 */
13444HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13445{
13446 LogFlowThisFunc(("\n"));
13447
13448#ifdef VBOX_WITH_USB
13449 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13450 AssertComRC(rc);
13451 NOREF(rc);
13452
13453 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13454 AssertReturn(service, E_FAIL);
13455 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13456#else
13457 NOREF(aDone);
13458 return S_OK;
13459#endif
13460}
13461
13462/**
13463 * @note Locks this object for writing.
13464 */
13465HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13466 ComPtr<IProgress> &aProgress)
13467{
13468 LogFlowThisFuncEnter();
13469
13470 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13471 /*
13472 * We don't assert below because it might happen that a non-direct session
13473 * informs us it is closed right after we've been uninitialized -- it's ok.
13474 */
13475
13476 /* get IInternalSessionControl interface */
13477 ComPtr<IInternalSessionControl> control(aSession);
13478
13479 ComAssertRet(!control.isNull(), E_INVALIDARG);
13480
13481 /* Creating a Progress object requires the VirtualBox lock, and
13482 * thus locking it here is required by the lock order rules. */
13483 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13484
13485 if (control == mData->mSession.mDirectControl)
13486 {
13487 /* The direct session is being normally closed by the client process
13488 * ----------------------------------------------------------------- */
13489
13490 /* go to the closing state (essential for all open*Session() calls and
13491 * for #i_checkForDeath()) */
13492 Assert(mData->mSession.mState == SessionState_Locked);
13493 mData->mSession.mState = SessionState_Unlocking;
13494
13495 /* set direct control to NULL to release the remote instance */
13496 mData->mSession.mDirectControl.setNull();
13497 LogFlowThisFunc(("Direct control is set to NULL\n"));
13498
13499 if (mData->mSession.mProgress)
13500 {
13501 /* finalize the progress, someone might wait if a frontend
13502 * closes the session before powering on the VM. */
13503 mData->mSession.mProgress->notifyComplete(E_FAIL,
13504 COM_IIDOF(ISession),
13505 getComponentName(),
13506 tr("The VM session was closed before any attempt to power it on"));
13507 mData->mSession.mProgress.setNull();
13508 }
13509
13510 /* Create the progress object the client will use to wait until
13511 * #i_checkForDeath() is called to uninitialize this session object after
13512 * it releases the IPC semaphore.
13513 * Note! Because we're "reusing" mProgress here, this must be a proxy
13514 * object just like for LaunchVMProcess. */
13515 Assert(mData->mSession.mProgress.isNull());
13516 ComObjPtr<ProgressProxy> progress;
13517 progress.createObject();
13518 ComPtr<IUnknown> pPeer(mPeer);
13519 progress->init(mParent, pPeer,
13520 Bstr(tr("Closing session")).raw(),
13521 FALSE /* aCancelable */);
13522 progress.queryInterfaceTo(aProgress.asOutParam());
13523 mData->mSession.mProgress = progress;
13524 }
13525 else
13526 {
13527 /* the remote session is being normally closed */
13528 bool found = false;
13529 for (Data::Session::RemoteControlList::iterator
13530 it = mData->mSession.mRemoteControls.begin();
13531 it != mData->mSession.mRemoteControls.end();
13532 ++it)
13533 {
13534 if (control == *it)
13535 {
13536 found = true;
13537 // This MUST be erase(it), not remove(*it) as the latter
13538 // triggers a very nasty use after free due to the place where
13539 // the value "lives".
13540 mData->mSession.mRemoteControls.erase(it);
13541 break;
13542 }
13543 }
13544 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13545 E_INVALIDARG);
13546 }
13547
13548 /* signal the client watcher thread, because the client is going away */
13549 mParent->i_updateClientWatcher();
13550
13551 LogFlowThisFuncLeave();
13552 return S_OK;
13553}
13554
13555HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13556 std::vector<com::Utf8Str> &aValues,
13557 std::vector<LONG64> &aTimestamps,
13558 std::vector<com::Utf8Str> &aFlags)
13559{
13560 LogFlowThisFunc(("\n"));
13561
13562#ifdef VBOX_WITH_GUEST_PROPS
13563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13564
13565 size_t cEntries = mHWData->mGuestProperties.size();
13566 aNames.resize(cEntries);
13567 aValues.resize(cEntries);
13568 aTimestamps.resize(cEntries);
13569 aFlags.resize(cEntries);
13570
13571 size_t i = 0;
13572 for (HWData::GuestPropertyMap::const_iterator
13573 it = mHWData->mGuestProperties.begin();
13574 it != mHWData->mGuestProperties.end();
13575 ++it, ++i)
13576 {
13577 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13578 aNames[i] = it->first;
13579 aValues[i] = it->second.strValue;
13580 aTimestamps[i] = it->second.mTimestamp;
13581
13582 /* If it is NULL, keep it NULL. */
13583 if (it->second.mFlags)
13584 {
13585 GuestPropWriteFlags(it->second.mFlags, szFlags);
13586 aFlags[i] = szFlags;
13587 }
13588 else
13589 aFlags[i] = "";
13590 }
13591 return S_OK;
13592#else
13593 ReturnComNotImplemented();
13594#endif
13595}
13596
13597HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13598 const com::Utf8Str &aValue,
13599 LONG64 aTimestamp,
13600 const com::Utf8Str &aFlags)
13601{
13602 LogFlowThisFunc(("\n"));
13603
13604#ifdef VBOX_WITH_GUEST_PROPS
13605 try
13606 {
13607 /*
13608 * Convert input up front.
13609 */
13610 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13611 if (aFlags.length())
13612 {
13613 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13614 AssertRCReturn(vrc, E_INVALIDARG);
13615 }
13616
13617 /*
13618 * Now grab the object lock, validate the state and do the update.
13619 */
13620
13621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13622
13623 if (!Global::IsOnline(mData->mMachineState))
13624 {
13625 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13626 VBOX_E_INVALID_VM_STATE);
13627 }
13628
13629 i_setModified(IsModified_MachineData);
13630 mHWData.backup();
13631
13632 bool fDelete = !aValue.length();
13633 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13634 if (it != mHWData->mGuestProperties.end())
13635 {
13636 if (!fDelete)
13637 {
13638 it->second.strValue = aValue;
13639 it->second.mTimestamp = aTimestamp;
13640 it->second.mFlags = fFlags;
13641 }
13642 else
13643 mHWData->mGuestProperties.erase(it);
13644
13645 mData->mGuestPropertiesModified = TRUE;
13646 }
13647 else if (!fDelete)
13648 {
13649 HWData::GuestProperty prop;
13650 prop.strValue = aValue;
13651 prop.mTimestamp = aTimestamp;
13652 prop.mFlags = fFlags;
13653
13654 mHWData->mGuestProperties[aName] = prop;
13655 mData->mGuestPropertiesModified = TRUE;
13656 }
13657
13658 alock.release();
13659
13660 mParent->i_onGuestPropertyChange(mData->mUuid,
13661 Bstr(aName).raw(),
13662 Bstr(aValue).raw(),
13663 Bstr(aFlags).raw());
13664 }
13665 catch (...)
13666 {
13667 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13668 }
13669 return S_OK;
13670#else
13671 ReturnComNotImplemented();
13672#endif
13673}
13674
13675
13676HRESULT SessionMachine::lockMedia()
13677{
13678 AutoMultiWriteLock2 alock(this->lockHandle(),
13679 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13680
13681 AssertReturn( mData->mMachineState == MachineState_Starting
13682 || mData->mMachineState == MachineState_Restoring
13683 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13684
13685 clearError();
13686 alock.release();
13687 return i_lockMedia();
13688}
13689
13690HRESULT SessionMachine::unlockMedia()
13691{
13692 HRESULT hrc = i_unlockMedia();
13693 return hrc;
13694}
13695
13696HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13697 ComPtr<IMediumAttachment> &aNewAttachment)
13698{
13699 // request the host lock first, since might be calling Host methods for getting host drives;
13700 // next, protect the media tree all the while we're in here, as well as our member variables
13701 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13702 this->lockHandle(),
13703 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13704
13705 IMediumAttachment *iAttach = aAttachment;
13706 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13707
13708 Utf8Str ctrlName;
13709 LONG lPort;
13710 LONG lDevice;
13711 bool fTempEject;
13712 {
13713 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13714
13715 /* Need to query the details first, as the IMediumAttachment reference
13716 * might be to the original settings, which we are going to change. */
13717 ctrlName = pAttach->i_getControllerName();
13718 lPort = pAttach->i_getPort();
13719 lDevice = pAttach->i_getDevice();
13720 fTempEject = pAttach->i_getTempEject();
13721 }
13722
13723 if (!fTempEject)
13724 {
13725 /* Remember previously mounted medium. The medium before taking the
13726 * backup is not necessarily the same thing. */
13727 ComObjPtr<Medium> oldmedium;
13728 oldmedium = pAttach->i_getMedium();
13729
13730 i_setModified(IsModified_Storage);
13731 mMediumAttachments.backup();
13732
13733 // The backup operation makes the pAttach reference point to the
13734 // old settings. Re-get the correct reference.
13735 pAttach = i_findAttachment(*mMediumAttachments.data(),
13736 ctrlName,
13737 lPort,
13738 lDevice);
13739
13740 {
13741 AutoCaller autoAttachCaller(this);
13742 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13743
13744 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13745 if (!oldmedium.isNull())
13746 oldmedium->i_removeBackReference(mData->mUuid);
13747
13748 pAttach->i_updateMedium(NULL);
13749 pAttach->i_updateEjected();
13750 }
13751
13752 i_setModified(IsModified_Storage);
13753 }
13754 else
13755 {
13756 {
13757 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13758 pAttach->i_updateEjected();
13759 }
13760 }
13761
13762 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13763
13764 return S_OK;
13765}
13766
13767HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13768 com::Utf8Str &aResult)
13769{
13770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13771
13772 HRESULT hr = S_OK;
13773
13774 if (!mAuthLibCtx.hAuthLibrary)
13775 {
13776 /* Load the external authentication library. */
13777 Bstr authLibrary;
13778 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13779
13780 Utf8Str filename = authLibrary;
13781
13782 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13783 if (RT_FAILURE(vrc))
13784 hr = setErrorBoth(E_FAIL, vrc,
13785 tr("Could not load the external authentication library '%s' (%Rrc)"),
13786 filename.c_str(), vrc);
13787 }
13788
13789 /* The auth library might need the machine lock. */
13790 alock.release();
13791
13792 if (FAILED(hr))
13793 return hr;
13794
13795 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13796 {
13797 enum VRDEAuthParams
13798 {
13799 parmUuid = 1,
13800 parmGuestJudgement,
13801 parmUser,
13802 parmPassword,
13803 parmDomain,
13804 parmClientId
13805 };
13806
13807 AuthResult result = AuthResultAccessDenied;
13808
13809 Guid uuid(aAuthParams[parmUuid]);
13810 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13811 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13812
13813 result = AuthLibAuthenticate(&mAuthLibCtx,
13814 uuid.raw(), guestJudgement,
13815 aAuthParams[parmUser].c_str(),
13816 aAuthParams[parmPassword].c_str(),
13817 aAuthParams[parmDomain].c_str(),
13818 u32ClientId);
13819
13820 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13821 size_t cbPassword = aAuthParams[parmPassword].length();
13822 if (cbPassword)
13823 {
13824 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13825 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13826 }
13827
13828 if (result == AuthResultAccessGranted)
13829 aResult = "granted";
13830 else
13831 aResult = "denied";
13832
13833 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13834 aAuthParams[parmUser].c_str(), aResult.c_str()));
13835 }
13836 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13837 {
13838 enum VRDEAuthDisconnectParams
13839 {
13840 parmUuid = 1,
13841 parmClientId
13842 };
13843
13844 Guid uuid(aAuthParams[parmUuid]);
13845 uint32_t u32ClientId = 0;
13846 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13847 }
13848 else
13849 {
13850 hr = E_INVALIDARG;
13851 }
13852
13853 return hr;
13854}
13855
13856// public methods only for internal purposes
13857/////////////////////////////////////////////////////////////////////////////
13858
13859#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13860/**
13861 * Called from the client watcher thread to check for expected or unexpected
13862 * death of the client process that has a direct session to this machine.
13863 *
13864 * On Win32 and on OS/2, this method is called only when we've got the
13865 * mutex (i.e. the client has either died or terminated normally) so it always
13866 * returns @c true (the client is terminated, the session machine is
13867 * uninitialized).
13868 *
13869 * On other platforms, the method returns @c true if the client process has
13870 * terminated normally or abnormally and the session machine was uninitialized,
13871 * and @c false if the client process is still alive.
13872 *
13873 * @note Locks this object for writing.
13874 */
13875bool SessionMachine::i_checkForDeath()
13876{
13877 Uninit::Reason reason;
13878 bool terminated = false;
13879
13880 /* Enclose autoCaller with a block because calling uninit() from under it
13881 * will deadlock. */
13882 {
13883 AutoCaller autoCaller(this);
13884 if (!autoCaller.isOk())
13885 {
13886 /* return true if not ready, to cause the client watcher to exclude
13887 * the corresponding session from watching */
13888 LogFlowThisFunc(("Already uninitialized!\n"));
13889 return true;
13890 }
13891
13892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13893
13894 /* Determine the reason of death: if the session state is Closing here,
13895 * everything is fine. Otherwise it means that the client did not call
13896 * OnSessionEnd() before it released the IPC semaphore. This may happen
13897 * either because the client process has abnormally terminated, or
13898 * because it simply forgot to call ISession::Close() before exiting. We
13899 * threat the latter also as an abnormal termination (see
13900 * Session::uninit() for details). */
13901 reason = mData->mSession.mState == SessionState_Unlocking ?
13902 Uninit::Normal :
13903 Uninit::Abnormal;
13904
13905 if (mClientToken)
13906 terminated = mClientToken->release();
13907 } /* AutoCaller block */
13908
13909 if (terminated)
13910 uninit(reason);
13911
13912 return terminated;
13913}
13914
13915void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13916{
13917 LogFlowThisFunc(("\n"));
13918
13919 strTokenId.setNull();
13920
13921 AutoCaller autoCaller(this);
13922 AssertComRCReturnVoid(autoCaller.rc());
13923
13924 Assert(mClientToken);
13925 if (mClientToken)
13926 mClientToken->getId(strTokenId);
13927}
13928#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13929IToken *SessionMachine::i_getToken()
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), NULL);
13935
13936 Assert(mClientToken);
13937 if (mClientToken)
13938 return mClientToken->getToken();
13939 else
13940 return NULL;
13941}
13942#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13943
13944Machine::ClientToken *SessionMachine::i_getClientToken()
13945{
13946 LogFlowThisFunc(("\n"));
13947
13948 AutoCaller autoCaller(this);
13949 AssertComRCReturn(autoCaller.rc(), NULL);
13950
13951 return mClientToken;
13952}
13953
13954
13955/**
13956 * @note Locks this object for reading.
13957 */
13958HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
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 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13977}
13978
13979/**
13980 * @note Locks this object for reading.
13981 */
13982HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13983 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13984 IN_BSTR aGuestIp, LONG aGuestPort)
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 if (mData->mSession.mLockType == LockType_VM)
13995 directControl = mData->mSession.mDirectControl;
13996 }
13997
13998 /* ignore notifications sent after #OnSessionEnd() is called */
13999 if (!directControl)
14000 return S_OK;
14001 /*
14002 * instead acting like callback we ask IVirtualBox deliver corresponding event
14003 */
14004
14005 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14006 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14007 return S_OK;
14008}
14009
14010/**
14011 * @note Locks this object for reading.
14012 */
14013HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14014{
14015 LogFlowThisFunc(("\n"));
14016
14017 AutoCaller autoCaller(this);
14018 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14019
14020 ComPtr<IInternalSessionControl> directControl;
14021 {
14022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14023 if (mData->mSession.mLockType == LockType_VM)
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* ignore notifications sent after #OnSessionEnd() is called */
14028 if (!directControl)
14029 return S_OK;
14030
14031 return directControl->OnAudioAdapterChange(audioAdapter);
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 if (mData->mSession.mLockType == LockType_VM)
14048 directControl = mData->mSession.mDirectControl;
14049 }
14050
14051 /* ignore notifications sent after #OnSessionEnd() is called */
14052 if (!directControl)
14053 return S_OK;
14054
14055 return directControl->OnSerialPortChange(serialPort);
14056}
14057
14058/**
14059 * @note Locks this object for reading.
14060 */
14061HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14062{
14063 LogFlowThisFunc(("\n"));
14064
14065 AutoCaller autoCaller(this);
14066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14067
14068 ComPtr<IInternalSessionControl> directControl;
14069 {
14070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14071 if (mData->mSession.mLockType == LockType_VM)
14072 directControl = mData->mSession.mDirectControl;
14073 }
14074
14075 /* ignore notifications sent after #OnSessionEnd() is called */
14076 if (!directControl)
14077 return S_OK;
14078
14079 return directControl->OnParallelPortChange(parallelPort);
14080}
14081
14082/**
14083 * @note Locks this object for reading.
14084 */
14085HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
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_onStorageControllerChanged(aMachineId, aControllerName);
14100
14101 /* ignore notifications sent after #OnSessionEnd() is called */
14102 if (!directControl)
14103 return S_OK;
14104
14105 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14106}
14107
14108/**
14109 * @note Locks this object for reading.
14110 */
14111HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14112{
14113 LogFlowThisFunc(("\n"));
14114
14115 AutoCaller autoCaller(this);
14116 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14117
14118 ComPtr<IInternalSessionControl> directControl;
14119 {
14120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14121 if (mData->mSession.mLockType == LockType_VM)
14122 directControl = mData->mSession.mDirectControl;
14123 }
14124
14125 mParent->i_onMediumChanged(aAttachment);
14126
14127 /* ignore notifications sent after #OnSessionEnd() is called */
14128 if (!directControl)
14129 return S_OK;
14130
14131 return directControl->OnMediumChange(aAttachment, aForce);
14132}
14133
14134HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14140
14141 ComPtr<IInternalSessionControl> directControl;
14142 {
14143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14144 if (mData->mSession.mLockType == LockType_VM)
14145 directControl = mData->mSession.mDirectControl;
14146 }
14147
14148 /* ignore notifications sent after #OnSessionEnd() is called */
14149 if (!directControl)
14150 return S_OK;
14151
14152 return directControl->OnVMProcessPriorityChange(aPriority);
14153}
14154
14155/**
14156 * @note Locks this object for reading.
14157 */
14158HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnCPUChange(aCPU, aRemove);
14177}
14178
14179HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14180{
14181 LogFlowThisFunc(("\n"));
14182
14183 AutoCaller autoCaller(this);
14184 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14185
14186 ComPtr<IInternalSessionControl> directControl;
14187 {
14188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14189 if (mData->mSession.mLockType == LockType_VM)
14190 directControl = mData->mSession.mDirectControl;
14191 }
14192
14193 /* ignore notifications sent after #OnSessionEnd() is called */
14194 if (!directControl)
14195 return S_OK;
14196
14197 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14198}
14199
14200/**
14201 * @note Locks this object for reading.
14202 */
14203HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14204{
14205 LogFlowThisFunc(("\n"));
14206
14207 AutoCaller autoCaller(this);
14208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14209
14210 ComPtr<IInternalSessionControl> directControl;
14211 {
14212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14213 if (mData->mSession.mLockType == LockType_VM)
14214 directControl = mData->mSession.mDirectControl;
14215 }
14216
14217 /* ignore notifications sent after #OnSessionEnd() is called */
14218 if (!directControl)
14219 return S_OK;
14220
14221 return directControl->OnVRDEServerChange(aRestart);
14222}
14223
14224/**
14225 * @note Locks this object for reading.
14226 */
14227HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14228{
14229 LogFlowThisFunc(("\n"));
14230
14231 AutoCaller autoCaller(this);
14232 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14233
14234 ComPtr<IInternalSessionControl> directControl;
14235 {
14236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14237 if (mData->mSession.mLockType == LockType_VM)
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnRecordingChange(aEnable);
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::i_onUSBControllerChange()
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 if (mData->mSession.mLockType == LockType_VM)
14262 directControl = mData->mSession.mDirectControl;
14263 }
14264
14265 /* ignore notifications sent after #OnSessionEnd() is called */
14266 if (!directControl)
14267 return S_OK;
14268
14269 return directControl->OnUSBControllerChange();
14270}
14271
14272/**
14273 * @note Locks this object for reading.
14274 */
14275HRESULT SessionMachine::i_onSharedFolderChange()
14276{
14277 LogFlowThisFunc(("\n"));
14278
14279 AutoCaller autoCaller(this);
14280 AssertComRCReturnRC(autoCaller.rc());
14281
14282 ComPtr<IInternalSessionControl> directControl;
14283 {
14284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14285 if (mData->mSession.mLockType == LockType_VM)
14286 directControl = mData->mSession.mDirectControl;
14287 }
14288
14289 /* ignore notifications sent after #OnSessionEnd() is called */
14290 if (!directControl)
14291 return S_OK;
14292
14293 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14294}
14295
14296/**
14297 * @note Locks this object for reading.
14298 */
14299HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturnRC(autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 if (mData->mSession.mLockType == LockType_VM)
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnClipboardModeChange(aClipboardMode);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturnRC(autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 if (mData->mSession.mLockType == LockType_VM)
14334 directControl = mData->mSession.mDirectControl;
14335 }
14336
14337 /* ignore notifications sent after #OnSessionEnd() is called */
14338 if (!directControl)
14339 return S_OK;
14340
14341 return directControl->OnDnDModeChange(aDnDMode);
14342}
14343
14344/**
14345 * @note Locks this object for reading.
14346 */
14347HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14348{
14349 LogFlowThisFunc(("\n"));
14350
14351 AutoCaller autoCaller(this);
14352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14353
14354 ComPtr<IInternalSessionControl> directControl;
14355 {
14356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14357 if (mData->mSession.mLockType == LockType_VM)
14358 directControl = mData->mSession.mDirectControl;
14359 }
14360
14361 /* ignore notifications sent after #OnSessionEnd() is called */
14362 if (!directControl)
14363 return S_OK;
14364
14365 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14366}
14367
14368/**
14369 * @note Locks this object for reading.
14370 */
14371HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* ignore notifications sent after #OnSessionEnd() is called */
14386 if (!directControl)
14387 return S_OK;
14388
14389 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14390}
14391
14392/**
14393 * Returns @c true if this machine's USB controller reports it has a matching
14394 * filter for the given USB device and @c false otherwise.
14395 *
14396 * @note locks this object for reading.
14397 */
14398bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14399{
14400 AutoCaller autoCaller(this);
14401 /* silently return if not ready -- this method may be called after the
14402 * direct machine session has been called */
14403 if (!autoCaller.isOk())
14404 return false;
14405
14406#ifdef VBOX_WITH_USB
14407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14408
14409 switch (mData->mMachineState)
14410 {
14411 case MachineState_Starting:
14412 case MachineState_Restoring:
14413 case MachineState_TeleportingIn:
14414 case MachineState_Paused:
14415 case MachineState_Running:
14416 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14417 * elsewhere... */
14418 alock.release();
14419 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14420 default: break;
14421 }
14422#else
14423 NOREF(aDevice);
14424 NOREF(aMaskedIfs);
14425#endif
14426 return false;
14427}
14428
14429/**
14430 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14431 */
14432HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14433 IVirtualBoxErrorInfo *aError,
14434 ULONG aMaskedIfs,
14435 const com::Utf8Str &aCaptureFilename)
14436{
14437 LogFlowThisFunc(("\n"));
14438
14439 AutoCaller autoCaller(this);
14440
14441 /* This notification may happen after the machine object has been
14442 * uninitialized (the session was closed), so don't assert. */
14443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14444
14445 ComPtr<IInternalSessionControl> directControl;
14446 {
14447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14448 if (mData->mSession.mLockType == LockType_VM)
14449 directControl = mData->mSession.mDirectControl;
14450 }
14451
14452 /* fail on notifications sent after #OnSessionEnd() is called, it is
14453 * expected by the caller */
14454 if (!directControl)
14455 return E_FAIL;
14456
14457 /* No locks should be held at this point. */
14458 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14459 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14460
14461 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14462}
14463
14464/**
14465 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14466 */
14467HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14468 IVirtualBoxErrorInfo *aError)
14469{
14470 LogFlowThisFunc(("\n"));
14471
14472 AutoCaller autoCaller(this);
14473
14474 /* This notification may happen after the machine object has been
14475 * uninitialized (the session was closed), so don't assert. */
14476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14477
14478 ComPtr<IInternalSessionControl> directControl;
14479 {
14480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14481 if (mData->mSession.mLockType == LockType_VM)
14482 directControl = mData->mSession.mDirectControl;
14483 }
14484
14485 /* fail on notifications sent after #OnSessionEnd() is called, it is
14486 * expected by the caller */
14487 if (!directControl)
14488 return E_FAIL;
14489
14490 /* No locks should be held at this point. */
14491 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14492 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14493
14494 return directControl->OnUSBDeviceDetach(aId, aError);
14495}
14496
14497// protected methods
14498/////////////////////////////////////////////////////////////////////////////
14499
14500/**
14501 * Deletes the given file if it is no longer in use by either the current machine state
14502 * (if the machine is "saved") or any of the machine's snapshots.
14503 *
14504 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14505 * but is different for each SnapshotMachine. When calling this, the order of calling this
14506 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14507 * is therefore critical. I know, it's all rather messy.
14508 *
14509 * @param strStateFile
14510 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14511 * the test for whether the saved state file is in use.
14512 */
14513void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14514 Snapshot *pSnapshotToIgnore)
14515{
14516 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14517 if ( (strStateFile.isNotEmpty())
14518 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14519 )
14520 // ... and it must also not be shared with other snapshots
14521 if ( !mData->mFirstSnapshot
14522 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14523 // this checks the SnapshotMachine's state file paths
14524 )
14525 RTFileDelete(strStateFile.c_str());
14526}
14527
14528/**
14529 * Locks the attached media.
14530 *
14531 * All attached hard disks are locked for writing and DVD/floppy are locked for
14532 * reading. Parents of attached hard disks (if any) are locked for reading.
14533 *
14534 * This method also performs accessibility check of all media it locks: if some
14535 * media is inaccessible, the method will return a failure and a bunch of
14536 * extended error info objects per each inaccessible medium.
14537 *
14538 * Note that this method is atomic: if it returns a success, all media are
14539 * locked as described above; on failure no media is locked at all (all
14540 * succeeded individual locks will be undone).
14541 *
14542 * The caller is responsible for doing the necessary state sanity checks.
14543 *
14544 * The locks made by this method must be undone by calling #unlockMedia() when
14545 * no more needed.
14546 */
14547HRESULT SessionMachine::i_lockMedia()
14548{
14549 AutoCaller autoCaller(this);
14550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14551
14552 AutoMultiWriteLock2 alock(this->lockHandle(),
14553 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14554
14555 /* bail out if trying to lock things with already set up locking */
14556 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14557
14558 MultiResult mrc(S_OK);
14559
14560 /* Collect locking information for all medium objects attached to the VM. */
14561 for (MediumAttachmentList::const_iterator
14562 it = mMediumAttachments->begin();
14563 it != mMediumAttachments->end();
14564 ++it)
14565 {
14566 MediumAttachment *pAtt = *it;
14567 DeviceType_T devType = pAtt->i_getType();
14568 Medium *pMedium = pAtt->i_getMedium();
14569
14570 MediumLockList *pMediumLockList(new MediumLockList());
14571 // There can be attachments without a medium (floppy/dvd), and thus
14572 // it's impossible to create a medium lock list. It still makes sense
14573 // to have the empty medium lock list in the map in case a medium is
14574 // attached later.
14575 if (pMedium != NULL)
14576 {
14577 MediumType_T mediumType = pMedium->i_getType();
14578 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14579 || mediumType == MediumType_Shareable;
14580 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14581
14582 alock.release();
14583 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14584 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14585 false /* fMediumLockWriteAll */,
14586 NULL,
14587 *pMediumLockList);
14588 alock.acquire();
14589 if (FAILED(mrc))
14590 {
14591 delete pMediumLockList;
14592 mData->mSession.mLockedMedia.Clear();
14593 break;
14594 }
14595 }
14596
14597 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14598 if (FAILED(rc))
14599 {
14600 mData->mSession.mLockedMedia.Clear();
14601 mrc = setError(rc,
14602 tr("Collecting locking information for all attached media failed"));
14603 break;
14604 }
14605 }
14606
14607 if (SUCCEEDED(mrc))
14608 {
14609 /* Now lock all media. If this fails, nothing is locked. */
14610 alock.release();
14611 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14612 alock.acquire();
14613 if (FAILED(rc))
14614 {
14615 mrc = setError(rc,
14616 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14617 }
14618 }
14619
14620 return mrc;
14621}
14622
14623/**
14624 * Undoes the locks made by by #lockMedia().
14625 */
14626HRESULT SessionMachine::i_unlockMedia()
14627{
14628 AutoCaller autoCaller(this);
14629 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14630
14631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14632
14633 /* we may be holding important error info on the current thread;
14634 * preserve it */
14635 ErrorInfoKeeper eik;
14636
14637 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14638 AssertComRC(rc);
14639 return rc;
14640}
14641
14642/**
14643 * Helper to change the machine state (reimplementation).
14644 *
14645 * @note Locks this object for writing.
14646 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14647 * it can cause crashes in random places due to unexpectedly committing
14648 * the current settings. The caller is responsible for that. The call
14649 * to saveStateSettings is fine, because this method does not commit.
14650 */
14651HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14652{
14653 LogFlowThisFuncEnter();
14654 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14655
14656 AutoCaller autoCaller(this);
14657 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14658
14659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14660
14661 MachineState_T oldMachineState = mData->mMachineState;
14662
14663 AssertMsgReturn(oldMachineState != aMachineState,
14664 ("oldMachineState=%s, aMachineState=%s\n",
14665 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14666 E_FAIL);
14667
14668 HRESULT rc = S_OK;
14669
14670 int stsFlags = 0;
14671 bool deleteSavedState = false;
14672
14673 /* detect some state transitions */
14674
14675 if ( ( oldMachineState == MachineState_Saved
14676 && aMachineState == MachineState_Restoring)
14677 || ( ( oldMachineState == MachineState_PoweredOff
14678 || oldMachineState == MachineState_Teleported
14679 || oldMachineState == MachineState_Aborted
14680 )
14681 && ( aMachineState == MachineState_TeleportingIn
14682 || aMachineState == MachineState_Starting
14683 )
14684 )
14685 )
14686 {
14687 /* The EMT thread is about to start */
14688
14689 /* Nothing to do here for now... */
14690
14691 /// @todo NEWMEDIA don't let mDVDDrive and other children
14692 /// change anything when in the Starting/Restoring state
14693 }
14694 else if ( ( oldMachineState == MachineState_Running
14695 || oldMachineState == MachineState_Paused
14696 || oldMachineState == MachineState_Teleporting
14697 || oldMachineState == MachineState_OnlineSnapshotting
14698 || oldMachineState == MachineState_LiveSnapshotting
14699 || oldMachineState == MachineState_Stuck
14700 || oldMachineState == MachineState_Starting
14701 || oldMachineState == MachineState_Stopping
14702 || oldMachineState == MachineState_Saving
14703 || oldMachineState == MachineState_Restoring
14704 || oldMachineState == MachineState_TeleportingPausedVM
14705 || oldMachineState == MachineState_TeleportingIn
14706 )
14707 && ( aMachineState == MachineState_PoweredOff
14708 || aMachineState == MachineState_Saved
14709 || aMachineState == MachineState_Teleported
14710 || aMachineState == MachineState_Aborted
14711 )
14712 )
14713 {
14714 /* The EMT thread has just stopped, unlock attached media. Note that as
14715 * opposed to locking that is done from Console, we do unlocking here
14716 * because the VM process may have aborted before having a chance to
14717 * properly unlock all media it locked. */
14718
14719 unlockMedia();
14720 }
14721
14722 if (oldMachineState == MachineState_Restoring)
14723 {
14724 if (aMachineState != MachineState_Saved)
14725 {
14726 /*
14727 * delete the saved state file once the machine has finished
14728 * restoring from it (note that Console sets the state from
14729 * Restoring to Saved if the VM couldn't restore successfully,
14730 * to give the user an ability to fix an error and retry --
14731 * we keep the saved state file in this case)
14732 */
14733 deleteSavedState = true;
14734 }
14735 }
14736 else if ( oldMachineState == MachineState_Saved
14737 && ( aMachineState == MachineState_PoweredOff
14738 || aMachineState == MachineState_Aborted
14739 || aMachineState == MachineState_Teleported
14740 )
14741 )
14742 {
14743 /*
14744 * delete the saved state after SessionMachine::ForgetSavedState() is called
14745 * or if the VM process (owning a direct VM session) crashed while the
14746 * VM was Saved
14747 */
14748
14749 /// @todo (dmik)
14750 // Not sure that deleting the saved state file just because of the
14751 // client death before it attempted to restore the VM is a good
14752 // thing. But when it crashes we need to go to the Aborted state
14753 // which cannot have the saved state file associated... The only
14754 // way to fix this is to make the Aborted condition not a VM state
14755 // but a bool flag: i.e., when a crash occurs, set it to true and
14756 // change the state to PoweredOff or Saved depending on the
14757 // saved state presence.
14758
14759 deleteSavedState = true;
14760 mData->mCurrentStateModified = TRUE;
14761 stsFlags |= SaveSTS_CurStateModified;
14762 }
14763
14764 if ( aMachineState == MachineState_Starting
14765 || aMachineState == MachineState_Restoring
14766 || aMachineState == MachineState_TeleportingIn
14767 )
14768 {
14769 /* set the current state modified flag to indicate that the current
14770 * state is no more identical to the state in the
14771 * current snapshot */
14772 if (!mData->mCurrentSnapshot.isNull())
14773 {
14774 mData->mCurrentStateModified = TRUE;
14775 stsFlags |= SaveSTS_CurStateModified;
14776 }
14777 }
14778
14779 if (deleteSavedState)
14780 {
14781 if (mRemoveSavedState)
14782 {
14783 Assert(!mSSData->strStateFilePath.isEmpty());
14784
14785 // it is safe to delete the saved state file if ...
14786 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14787 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14788 // ... none of the snapshots share the saved state file
14789 )
14790 RTFileDelete(mSSData->strStateFilePath.c_str());
14791 }
14792
14793 mSSData->strStateFilePath.setNull();
14794 stsFlags |= SaveSTS_StateFilePath;
14795 }
14796
14797 /* redirect to the underlying peer machine */
14798 mPeer->i_setMachineState(aMachineState);
14799
14800 if ( oldMachineState != MachineState_RestoringSnapshot
14801 && ( aMachineState == MachineState_PoweredOff
14802 || aMachineState == MachineState_Teleported
14803 || aMachineState == MachineState_Aborted
14804 || aMachineState == MachineState_Saved))
14805 {
14806 /* the machine has stopped execution
14807 * (or the saved state file was adopted) */
14808 stsFlags |= SaveSTS_StateTimeStamp;
14809 }
14810
14811 if ( ( oldMachineState == MachineState_PoweredOff
14812 || oldMachineState == MachineState_Aborted
14813 || oldMachineState == MachineState_Teleported
14814 )
14815 && aMachineState == MachineState_Saved)
14816 {
14817 /* the saved state file was adopted */
14818 Assert(!mSSData->strStateFilePath.isEmpty());
14819 stsFlags |= SaveSTS_StateFilePath;
14820 }
14821
14822#ifdef VBOX_WITH_GUEST_PROPS
14823 if ( aMachineState == MachineState_PoweredOff
14824 || aMachineState == MachineState_Aborted
14825 || aMachineState == MachineState_Teleported)
14826 {
14827 /* Make sure any transient guest properties get removed from the
14828 * property store on shutdown. */
14829 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14830
14831 /* remove it from the settings representation */
14832 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14833 for (settings::GuestPropertiesList::iterator
14834 it = llGuestProperties.begin();
14835 it != llGuestProperties.end();
14836 /*nothing*/)
14837 {
14838 const settings::GuestProperty &prop = *it;
14839 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14840 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14841 {
14842 it = llGuestProperties.erase(it);
14843 fNeedsSaving = true;
14844 }
14845 else
14846 {
14847 ++it;
14848 }
14849 }
14850
14851 /* Additionally remove it from the HWData representation. Required to
14852 * keep everything in sync, as this is what the API keeps using. */
14853 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14854 for (HWData::GuestPropertyMap::iterator
14855 it = llHWGuestProperties.begin();
14856 it != llHWGuestProperties.end();
14857 /*nothing*/)
14858 {
14859 uint32_t fFlags = it->second.mFlags;
14860 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14861 {
14862 /* iterator where we need to continue after the erase call
14863 * (C++03 is a fact still, and it doesn't return the iterator
14864 * which would allow continuing) */
14865 HWData::GuestPropertyMap::iterator it2 = it;
14866 ++it2;
14867 llHWGuestProperties.erase(it);
14868 it = it2;
14869 fNeedsSaving = true;
14870 }
14871 else
14872 {
14873 ++it;
14874 }
14875 }
14876
14877 if (fNeedsSaving)
14878 {
14879 mData->mCurrentStateModified = TRUE;
14880 stsFlags |= SaveSTS_CurStateModified;
14881 }
14882 }
14883#endif /* VBOX_WITH_GUEST_PROPS */
14884
14885 rc = i_saveStateSettings(stsFlags);
14886
14887 if ( ( oldMachineState != MachineState_PoweredOff
14888 && oldMachineState != MachineState_Aborted
14889 && oldMachineState != MachineState_Teleported
14890 )
14891 && ( aMachineState == MachineState_PoweredOff
14892 || aMachineState == MachineState_Aborted
14893 || aMachineState == MachineState_Teleported
14894 )
14895 )
14896 {
14897 /* we've been shut down for any reason */
14898 /* no special action so far */
14899 }
14900
14901 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14902 LogFlowThisFuncLeave();
14903 return rc;
14904}
14905
14906/**
14907 * Sends the current machine state value to the VM process.
14908 *
14909 * @note Locks this object for reading, then calls a client process.
14910 */
14911HRESULT SessionMachine::i_updateMachineStateOnClient()
14912{
14913 AutoCaller autoCaller(this);
14914 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14915
14916 ComPtr<IInternalSessionControl> directControl;
14917 {
14918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14919 AssertReturn(!!mData, E_FAIL);
14920 if (mData->mSession.mLockType == LockType_VM)
14921 directControl = mData->mSession.mDirectControl;
14922
14923 /* directControl may be already set to NULL here in #OnSessionEnd()
14924 * called too early by the direct session process while there is still
14925 * some operation (like deleting the snapshot) in progress. The client
14926 * process in this case is waiting inside Session::close() for the
14927 * "end session" process object to complete, while #uninit() called by
14928 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14929 * operation to complete. For now, we accept this inconsistent behavior
14930 * and simply do nothing here. */
14931
14932 if (mData->mSession.mState == SessionState_Unlocking)
14933 return S_OK;
14934 }
14935
14936 /* ignore notifications sent after #OnSessionEnd() is called */
14937 if (!directControl)
14938 return S_OK;
14939
14940 return directControl->UpdateMachineState(mData->mMachineState);
14941}
14942
14943
14944/*static*/
14945HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14946{
14947 va_list args;
14948 va_start(args, pcszMsg);
14949 HRESULT rc = setErrorInternal(aResultCode,
14950 getStaticClassIID(),
14951 getStaticComponentName(),
14952 Utf8Str(pcszMsg, args),
14953 false /* aWarning */,
14954 true /* aLogIt */);
14955 va_end(args);
14956 return rc;
14957}
14958
14959
14960HRESULT Machine::updateState(MachineState_T aState)
14961{
14962 NOREF(aState);
14963 ReturnComNotImplemented();
14964}
14965
14966HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14967{
14968 NOREF(aProgress);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::endPowerUp(LONG aResult)
14973{
14974 NOREF(aResult);
14975 ReturnComNotImplemented();
14976}
14977
14978HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14979{
14980 NOREF(aProgress);
14981 ReturnComNotImplemented();
14982}
14983
14984HRESULT Machine::endPoweringDown(LONG aResult,
14985 const com::Utf8Str &aErrMsg)
14986{
14987 NOREF(aResult);
14988 NOREF(aErrMsg);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14993 BOOL *aMatched,
14994 ULONG *aMaskedInterfaces)
14995{
14996 NOREF(aDevice);
14997 NOREF(aMatched);
14998 NOREF(aMaskedInterfaces);
14999 ReturnComNotImplemented();
15000
15001}
15002
15003HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15004{
15005 NOREF(aId); NOREF(aCaptureFilename);
15006 ReturnComNotImplemented();
15007}
15008
15009HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15010 BOOL aDone)
15011{
15012 NOREF(aId);
15013 NOREF(aDone);
15014 ReturnComNotImplemented();
15015}
15016
15017HRESULT Machine::autoCaptureUSBDevices()
15018{
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15023{
15024 NOREF(aDone);
15025 ReturnComNotImplemented();
15026}
15027
15028HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15029 ComPtr<IProgress> &aProgress)
15030{
15031 NOREF(aSession);
15032 NOREF(aProgress);
15033 ReturnComNotImplemented();
15034}
15035
15036HRESULT Machine::finishOnlineMergeMedium()
15037{
15038 ReturnComNotImplemented();
15039}
15040
15041HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15042 std::vector<com::Utf8Str> &aValues,
15043 std::vector<LONG64> &aTimestamps,
15044 std::vector<com::Utf8Str> &aFlags)
15045{
15046 NOREF(aNames);
15047 NOREF(aValues);
15048 NOREF(aTimestamps);
15049 NOREF(aFlags);
15050 ReturnComNotImplemented();
15051}
15052
15053HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15054 const com::Utf8Str &aValue,
15055 LONG64 aTimestamp,
15056 const com::Utf8Str &aFlags)
15057{
15058 NOREF(aName);
15059 NOREF(aValue);
15060 NOREF(aTimestamp);
15061 NOREF(aFlags);
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::lockMedia()
15066{
15067 ReturnComNotImplemented();
15068}
15069
15070HRESULT Machine::unlockMedia()
15071{
15072 ReturnComNotImplemented();
15073}
15074
15075HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15076 ComPtr<IMediumAttachment> &aNewAttachment)
15077{
15078 NOREF(aAttachment);
15079 NOREF(aNewAttachment);
15080 ReturnComNotImplemented();
15081}
15082
15083HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15084 ULONG aCpuUser,
15085 ULONG aCpuKernel,
15086 ULONG aCpuIdle,
15087 ULONG aMemTotal,
15088 ULONG aMemFree,
15089 ULONG aMemBalloon,
15090 ULONG aMemShared,
15091 ULONG aMemCache,
15092 ULONG aPagedTotal,
15093 ULONG aMemAllocTotal,
15094 ULONG aMemFreeTotal,
15095 ULONG aMemBalloonTotal,
15096 ULONG aMemSharedTotal,
15097 ULONG aVmNetRx,
15098 ULONG aVmNetTx)
15099{
15100 NOREF(aValidStats);
15101 NOREF(aCpuUser);
15102 NOREF(aCpuKernel);
15103 NOREF(aCpuIdle);
15104 NOREF(aMemTotal);
15105 NOREF(aMemFree);
15106 NOREF(aMemBalloon);
15107 NOREF(aMemShared);
15108 NOREF(aMemCache);
15109 NOREF(aPagedTotal);
15110 NOREF(aMemAllocTotal);
15111 NOREF(aMemFreeTotal);
15112 NOREF(aMemBalloonTotal);
15113 NOREF(aMemSharedTotal);
15114 NOREF(aVmNetRx);
15115 NOREF(aVmNetTx);
15116 ReturnComNotImplemented();
15117}
15118
15119HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15120 com::Utf8Str &aResult)
15121{
15122 NOREF(aAuthParams);
15123 NOREF(aResult);
15124 ReturnComNotImplemented();
15125}
15126
15127com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15128{
15129 com::Utf8Str strControllerName = "Unknown";
15130 switch (aBusType)
15131 {
15132 case StorageBus_IDE:
15133 {
15134 strControllerName = "IDE";
15135 break;
15136 }
15137 case StorageBus_SATA:
15138 {
15139 strControllerName = "SATA";
15140 break;
15141 }
15142 case StorageBus_SCSI:
15143 {
15144 strControllerName = "SCSI";
15145 break;
15146 }
15147 case StorageBus_Floppy:
15148 {
15149 strControllerName = "Floppy";
15150 break;
15151 }
15152 case StorageBus_SAS:
15153 {
15154 strControllerName = "SAS";
15155 break;
15156 }
15157 case StorageBus_USB:
15158 {
15159 strControllerName = "USB";
15160 break;
15161 }
15162 default:
15163 break;
15164 }
15165 return strControllerName;
15166}
15167
15168HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15169{
15170 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15171
15172 AutoCaller autoCaller(this);
15173 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15174
15175 HRESULT rc = S_OK;
15176
15177 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15178 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15179 rc = getUSBDeviceFilters(usbDeviceFilters);
15180 if (FAILED(rc)) return rc;
15181
15182 NOREF(aFlags);
15183 com::Utf8Str osTypeId;
15184 ComObjPtr<GuestOSType> osType = NULL;
15185
15186 /* Get the guest os type as a string from the VB. */
15187 rc = getOSTypeId(osTypeId);
15188 if (FAILED(rc)) return rc;
15189
15190 /* Get the os type obj that coresponds, can be used to get
15191 * the defaults for this guest OS. */
15192 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15193 if (FAILED(rc)) return rc;
15194
15195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15196
15197 /* Let the OS type select 64-bit ness. */
15198 mHWData->mLongMode = osType->i_is64Bit()
15199 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15200
15201 /* Let the OS type enable the X2APIC */
15202 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15203
15204 /* This one covers IOAPICEnabled. */
15205 mBIOSSettings->i_applyDefaults(osType);
15206
15207 /* Initialize default record settings. */
15208 mRecordingSettings->i_applyDefaults();
15209
15210 /* Initialize default BIOS settings here */
15211 /* Hardware virtualization must be ON by default */
15212 mHWData->mAPIC = true;
15213 mHWData->mHWVirtExEnabled = true;
15214
15215 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15216 if (FAILED(rc)) return rc;
15217
15218 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15219 if (FAILED(rc)) return rc;
15220
15221 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15222 if (FAILED(rc)) return rc;
15223
15224 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15225 if (FAILED(rc)) return rc;
15226
15227 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15228 if (FAILED(rc)) return rc;
15229
15230 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15231 if (FAILED(rc)) return rc;
15232
15233 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15237 if (FAILED(rc)) return rc;
15238
15239 BOOL mRTCUseUTC;
15240 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15241 if (FAILED(rc)) return rc;
15242
15243 setRTCUseUTC(mRTCUseUTC);
15244 if (FAILED(rc)) return rc;
15245
15246 /* the setter does more than just the assignment, so use it */
15247 ChipsetType_T enmChipsetType;
15248 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15249 if (FAILED(rc)) return rc;
15250
15251 rc = COMSETTER(ChipsetType)(enmChipsetType);
15252 if (FAILED(rc)) return rc;
15253
15254 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15255 if (FAILED(rc)) return rc;
15256
15257 /* Apply network adapters defaults */
15258 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15259 mNetworkAdapters[slot]->i_applyDefaults(osType);
15260
15261 /* Apply serial port defaults */
15262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15263 mSerialPorts[slot]->i_applyDefaults(osType);
15264
15265 /* Apply parallel port defaults - not OS dependent*/
15266 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15267 mParallelPorts[slot]->i_applyDefaults();
15268
15269 /* Audio stuff. */
15270 AudioControllerType_T audioController;
15271 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15275 if (FAILED(rc)) return rc;
15276
15277 AudioCodecType_T audioCodec;
15278 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15279 if (FAILED(rc)) return rc;
15280
15281 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15282 if (FAILED(rc)) return rc;
15283
15284 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15285 if (FAILED(rc)) return rc;
15286
15287 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15288 if (FAILED(rc)) return rc;
15289
15290 /* Storage Controllers */
15291 StorageControllerType_T hdStorageControllerType;
15292 StorageBus_T hdStorageBusType;
15293 StorageControllerType_T dvdStorageControllerType;
15294 StorageBus_T dvdStorageBusType;
15295 BOOL recommendedFloppy;
15296 ComPtr<IStorageController> floppyController;
15297 ComPtr<IStorageController> hdController;
15298 ComPtr<IStorageController> dvdController;
15299 Utf8Str strFloppyName, strDVDName, strHDName;
15300
15301 /* GUI auto generates controller names using bus type. Do the same*/
15302 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15303
15304 /* Floppy recommended? add one. */
15305 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15306 if (FAILED(rc)) return rc;
15307 if (recommendedFloppy)
15308 {
15309 rc = addStorageController(strFloppyName,
15310 StorageBus_Floppy,
15311 floppyController);
15312 if (FAILED(rc)) return rc;
15313 }
15314
15315 /* Setup one DVD storage controller. */
15316 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15317 if (FAILED(rc)) return rc;
15318
15319 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15320 if (FAILED(rc)) return rc;
15321
15322 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15323
15324 rc = addStorageController(strDVDName,
15325 dvdStorageBusType,
15326 dvdController);
15327 if (FAILED(rc)) return rc;
15328
15329 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15330 if (FAILED(rc)) return rc;
15331
15332 /* Setup one HDD storage controller. */
15333 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15334 if (FAILED(rc)) return rc;
15335
15336 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15337 if (FAILED(rc)) return rc;
15338
15339 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15340
15341 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15342 {
15343 rc = addStorageController(strHDName,
15344 hdStorageBusType,
15345 hdController);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15349 if (FAILED(rc)) return rc;
15350 }
15351 else
15352 {
15353 /* The HD controller is the same as DVD: */
15354 hdController = dvdController;
15355 }
15356
15357 /* Limit the AHCI port count if it's used because windows has trouble with
15358 * too many ports and other guest (OS X in particular) may take extra long
15359 * boot: */
15360
15361 // pParent = static_cast<Medium*>(aP)
15362 IStorageController *temp = hdController;
15363 ComObjPtr<StorageController> storageController;
15364 storageController = static_cast<StorageController *>(temp);
15365
15366 // tempHDController = aHDController;
15367 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15368 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15369 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15370 storageController->COMSETTER(PortCount)(1);
15371
15372 /* USB stuff */
15373
15374 bool ohciEnabled = false;
15375
15376 ComPtr<IUSBController> usbController;
15377 BOOL recommendedUSB3;
15378 BOOL recommendedUSB;
15379 BOOL usbProxyAvailable;
15380
15381 getUSBProxyAvailable(&usbProxyAvailable);
15382 if (FAILED(rc)) return rc;
15383
15384 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15385 if (FAILED(rc)) return rc;
15386 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15387 if (FAILED(rc)) return rc;
15388
15389 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15390 {
15391#ifdef VBOX_WITH_EXTPACK
15392 /* USB 3.0 is only available if the proper ExtPack is installed. */
15393 ExtPackManager *aManager = mParent->i_getExtPackManager();
15394 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15395 {
15396 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15397 if (FAILED(rc)) return rc;
15398
15399 /* xHci includes OHCI */
15400 ohciEnabled = true;
15401 }
15402#endif
15403 }
15404 if ( !ohciEnabled
15405 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15406 {
15407 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15408 if (FAILED(rc)) return rc;
15409 ohciEnabled = true;
15410
15411#ifdef VBOX_WITH_EXTPACK
15412 /* USB 2.0 is only available if the proper ExtPack is installed.
15413 * Note. Configuring EHCI here and providing messages about
15414 * the missing extpack isn't exactly clean, but it is a
15415 * necessary evil to patch over legacy compatability issues
15416 * introduced by the new distribution model. */
15417 ExtPackManager *manager = mParent->i_getExtPackManager();
15418 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15419 {
15420 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15421 if (FAILED(rc)) return rc;
15422 }
15423#endif
15424 }
15425
15426 /* Set recommended human interface device types: */
15427 BOOL recommendedUSBHID;
15428 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15429 if (FAILED(rc)) return rc;
15430
15431 if (recommendedUSBHID)
15432 {
15433 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15434 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15435 if (!ohciEnabled && !usbDeviceFilters.isNull())
15436 {
15437 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15438 if (FAILED(rc)) return rc;
15439 }
15440 }
15441
15442 BOOL recommendedUSBTablet;
15443 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15444 if (FAILED(rc)) return rc;
15445
15446 if (recommendedUSBTablet)
15447 {
15448 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15449 if (!ohciEnabled && !usbDeviceFilters.isNull())
15450 {
15451 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15452 if (FAILED(rc)) return rc;
15453 }
15454 }
15455 return S_OK;
15456}
15457
15458/* This isn't handled entirely by the wrapper generator yet. */
15459#ifdef VBOX_WITH_XPCOM
15460NS_DECL_CLASSINFO(SessionMachine)
15461NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15462
15463NS_DECL_CLASSINFO(SnapshotMachine)
15464NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15465#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