VirtualBox

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

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

Main: bugref:6913: Added generation of some medium events. Contains some fixes for VBoxSVC crash in the MacOS

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 530.1 KB
Line 
1/* $Id: MachineImpl.cpp 76298 2018-12-19 18:17:50Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2018 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/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48#include "ExtPackManagerImpl.h"
49
50// generated header
51#include "VBoxEvents.h"
52
53#ifdef VBOX_WITH_USB
54# include "USBProxyService.h"
55#endif
56
57#include "AutoCaller.h"
58#include "HashedPw.h"
59#include "Performance.h"
60
61#include <iprt/asm.h>
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/env.h>
65#include <iprt/lockvalidator.h>
66#include <iprt/process.h>
67#include <iprt/cpp/utils.h>
68#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
69#include <iprt/sha.h>
70#include <iprt/string.h>
71#include <iprt/ctype.h>
72
73#include <VBox/com/array.h>
74#include <VBox/com/list.h>
75
76#include <VBox/err.h>
77#include <VBox/param.h>
78#include <VBox/settings.h>
79#include <VBox/vmm/ssm.h>
80
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/array.h>
84#endif
85
86#include "VBox/com/MultiResult.h"
87
88#include <algorithm>
89
90#ifdef VBOX_WITH_DTRACE_R3_MAIN
91# include "dtrace/VBoxAPI.h"
92#endif
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mLockType = LockType_Null;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mHWVirtExEnabled = true;
171 mHWVirtExNestedPagingEnabled = true;
172#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
173 mHWVirtExLargePagesEnabled = true;
174#else
175 /* Not supported on 32 bits hosts. */
176 mHWVirtExLargePagesEnabled = false;
177#endif
178 mHWVirtExVPIDEnabled = true;
179 mHWVirtExUXEnabled = true;
180 mHWVirtExForceEnabled = false;
181 mHWVirtExUseNativeApi = false;
182#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
183 mPAEEnabled = true;
184#else
185 mPAEEnabled = false;
186#endif
187 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
188 mTripleFaultReset = false;
189 mAPIC = true;
190 mX2APIC = false;
191 mIBPBOnVMExit = false;
192 mIBPBOnVMEntry = false;
193 mSpecCtrl = false;
194 mSpecCtrlByHost = false;
195 mNestedHWVirt = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199 mCpuProfile = "host";
200
201 /* default boot order: floppy - DVD - HDD */
202 mBootOrder[0] = DeviceType_Floppy;
203 mBootOrder[1] = DeviceType_DVD;
204 mBootOrder[2] = DeviceType_HardDisk;
205 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
206 mBootOrder[i] = DeviceType_Null;
207
208 mClipboardMode = ClipboardMode_Disabled;
209 mDnDMode = DnDMode_Disabled;
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine class
231/////////////////////////////////////////////////////////////////////////////
232
233// constructor / destructor
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::Machine() :
237#ifdef VBOX_WITH_RESOURCE_USAGE_API
238 mCollectorGuest(NULL),
239#endif
240 mPeer(NULL),
241 mParent(NULL),
242 mSerialPorts(),
243 mParallelPorts(),
244 uRegistryNeedsSaving(0)
245{}
246
247Machine::~Machine()
248{}
249
250HRESULT Machine::FinalConstruct()
251{
252 LogFlowThisFunc(("\n"));
253 return BaseFinalConstruct();
254}
255
256void Machine::FinalRelease()
257{
258 LogFlowThisFunc(("\n"));
259 uninit();
260 BaseFinalRelease();
261}
262
263/**
264 * Initializes a new machine instance; this init() variant creates a new, empty machine.
265 * This gets called from VirtualBox::CreateMachine().
266 *
267 * @param aParent Associated parent object
268 * @param strConfigFile Local file system path to the VM settings file (can
269 * be relative to the VirtualBox config directory).
270 * @param strName name for the machine
271 * @param llGroups list of groups for the machine
272 * @param strOsType OS Type string (stored as is if aOsType is NULL).
273 * @param aOsType OS Type of this machine or NULL.
274 * @param aId UUID for the new machine.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
277 * scheme (includes the UUID).
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 const Utf8Str &strOsType,
286 GuestOSType *aOsType,
287 const Guid &aId,
288 bool fForceOverwrite,
289 bool fDirectoryIncludesUUID)
290{
291 LogFlowThisFuncEnter();
292 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
293
294 /* Enclose the state transition NotReady->InInit->Ready */
295 AutoInitSpan autoInitSpan(this);
296 AssertReturn(autoInitSpan.isOk(), E_FAIL);
297
298 HRESULT rc = initImpl(aParent, strConfigFile);
299 if (FAILED(rc)) return rc;
300
301 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
302 if (FAILED(rc)) return rc;
303
304 if (SUCCEEDED(rc))
305 {
306 // create an empty machine config
307 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
308
309 rc = initDataAndChildObjects();
310 }
311
312 if (SUCCEEDED(rc))
313 {
314 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
315 mData->mAccessible = TRUE;
316
317 unconst(mData->mUuid) = aId;
318
319 mUserData->s.strName = strName;
320
321 if (llGroups.size())
322 mUserData->s.llGroups = llGroups;
323
324 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
325 // the "name sync" flag determines whether the machine directory gets renamed along
326 // with the machine file; say so if the settings file name is the same as the
327 // settings file parent directory (machine directory)
328 mUserData->s.fNameSync = i_isInOwnDir();
329
330 // initialize the default snapshots folder
331 rc = COMSETTER(SnapshotFolder)(NULL);
332 AssertComRC(rc);
333
334 if (aOsType)
335 {
336 /* Store OS type */
337 mUserData->s.strOsType = aOsType->i_id();
338
339 /* Let the OS type select 64-bit ness. */
340 mHWData->mLongMode = aOsType->i_is64Bit()
341 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
342
343 /* Let the OS type enable the X2APIC */
344 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
345 }
346 else if (!strOsType.isEmpty())
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = strOsType;
350
351 /* No guest OS type object. Pick some plausible defaults which the
352 * host can handle. There's no way to know or validate anything. */
353 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
354 mHWData->mX2APIC = false;
355 }
356
357 /* Apply BIOS defaults. */
358 mBIOSSettings->i_applyDefaults(aOsType);
359
360 /* Apply record defaults. */
361 mRecordingSettings->i_applyDefaults();
362
363 /* Apply network adapters defaults */
364 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
365 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
366
367 /* Apply serial port defaults */
368 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
369 mSerialPorts[slot]->i_applyDefaults(aOsType);
370
371 /* Apply parallel port defaults */
372 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
373 mParallelPorts[slot]->i_applyDefaults();
374
375 /* At this point the changing of the current state modification
376 * flag is allowed. */
377 i_allowStateModification();
378
379 /* commit all changes made during the initialization */
380 i_commit();
381 }
382
383 /* Confirm a successful initialization when it's the case */
384 if (SUCCEEDED(rc))
385 {
386 if (mData->mAccessible)
387 autoInitSpan.setSucceeded();
388 else
389 autoInitSpan.setLimited();
390 }
391
392 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
393 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
394 mData->mRegistered,
395 mData->mAccessible,
396 rc));
397
398 LogFlowThisFuncLeave();
399
400 return rc;
401}
402
403/**
404 * Initializes a new instance with data from machine XML (formerly Init_Registered).
405 * Gets called in two modes:
406 *
407 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
408 * UUID is specified and we mark the machine as "registered";
409 *
410 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
411 * and the machine remains unregistered until RegisterMachine() is called.
412 *
413 * @param aParent Associated parent object
414 * @param strConfigFile Local file system path to the VM settings file (can
415 * be relative to the VirtualBox config directory).
416 * @param aId UUID of the machine or NULL (see above).
417 *
418 * @return Success indicator. if not S_OK, the machine object is invalid
419 */
420HRESULT Machine::initFromSettings(VirtualBox *aParent,
421 const Utf8Str &strConfigFile,
422 const Guid *aId)
423{
424 LogFlowThisFuncEnter();
425 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
426
427 /* Enclose the state transition NotReady->InInit->Ready */
428 AutoInitSpan autoInitSpan(this);
429 AssertReturn(autoInitSpan.isOk(), E_FAIL);
430
431 HRESULT rc = initImpl(aParent, strConfigFile);
432 if (FAILED(rc)) return rc;
433
434 if (aId)
435 {
436 // loading a registered VM:
437 unconst(mData->mUuid) = *aId;
438 mData->mRegistered = TRUE;
439 // now load the settings from XML:
440 rc = i_registeredInit();
441 // this calls initDataAndChildObjects() and loadSettings()
442 }
443 else
444 {
445 // opening an unregistered VM (VirtualBox::OpenMachine()):
446 rc = initDataAndChildObjects();
447
448 if (SUCCEEDED(rc))
449 {
450 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
451 mData->mAccessible = TRUE;
452
453 try
454 {
455 // load and parse machine XML; this will throw on XML or logic errors
456 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
457
458 // reject VM UUID duplicates, they can happen if someone
459 // tries to register an already known VM config again
460 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
461 true /* fPermitInaccessible */,
462 false /* aDoSetError */,
463 NULL) != VBOX_E_OBJECT_NOT_FOUND)
464 {
465 throw setError(E_FAIL,
466 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
467 mData->m_strConfigFile.c_str());
468 }
469
470 // use UUID from machine config
471 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
472
473 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
474 NULL /* puuidRegistry */);
475 if (FAILED(rc)) throw rc;
476
477 /* At this point the changing of the current state modification
478 * flag is allowed. */
479 i_allowStateModification();
480
481 i_commit();
482 }
483 catch (HRESULT err)
484 {
485 /* we assume that error info is set by the thrower */
486 rc = err;
487 }
488 catch (...)
489 {
490 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
491 }
492 }
493 }
494
495 /* Confirm a successful initialization when it's the case */
496 if (SUCCEEDED(rc))
497 {
498 if (mData->mAccessible)
499 autoInitSpan.setSucceeded();
500 else
501 {
502 autoInitSpan.setLimited();
503
504 // uninit media from this machine's media registry, or else
505 // reloading the settings will fail
506 mParent->i_unregisterMachineMedia(i_getId());
507 }
508 }
509
510 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
511 "rc=%08X\n",
512 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
513 mData->mRegistered, mData->mAccessible, rc));
514
515 LogFlowThisFuncLeave();
516
517 return rc;
518}
519
520/**
521 * Initializes a new instance from a machine config that is already in memory
522 * (import OVF case). Since we are importing, the UUID in the machine
523 * config is ignored and we always generate a fresh one.
524 *
525 * @param aParent Associated parent object.
526 * @param strName Name for the new machine; this overrides what is specified in config.
527 * @param strSettingsFilename File name of .vbox file.
528 * @param config Machine configuration loaded and parsed from XML.
529 *
530 * @return Success indicator. if not S_OK, the machine object is invalid
531 */
532HRESULT Machine::init(VirtualBox *aParent,
533 const Utf8Str &strName,
534 const Utf8Str &strSettingsFilename,
535 const settings::MachineConfigFile &config)
536{
537 LogFlowThisFuncEnter();
538
539 /* Enclose the state transition NotReady->InInit->Ready */
540 AutoInitSpan autoInitSpan(this);
541 AssertReturn(autoInitSpan.isOk(), E_FAIL);
542
543 HRESULT rc = initImpl(aParent, strSettingsFilename);
544 if (FAILED(rc)) return rc;
545
546 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
547 if (FAILED(rc)) return rc;
548
549 rc = initDataAndChildObjects();
550
551 if (SUCCEEDED(rc))
552 {
553 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
554 mData->mAccessible = TRUE;
555
556 // create empty machine config for instance data
557 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
558
559 // generate fresh UUID, ignore machine config
560 unconst(mData->mUuid).create();
561
562 rc = i_loadMachineDataFromSettings(config,
563 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
564
565 // override VM name as well, it may be different
566 mUserData->s.strName = strName;
567
568 if (SUCCEEDED(rc))
569 {
570 /* At this point the changing of the current state modification
571 * flag is allowed. */
572 i_allowStateModification();
573
574 /* commit all changes made during the initialization */
575 i_commit();
576 }
577 }
578
579 /* Confirm a successful initialization when it's the case */
580 if (SUCCEEDED(rc))
581 {
582 if (mData->mAccessible)
583 autoInitSpan.setSucceeded();
584 else
585 {
586 /* Ignore all errors from unregistering, they would destroy
587- * the more interesting error information we already have,
588- * pinpointing the issue with the VM config. */
589 ErrorInfoKeeper eik;
590
591 autoInitSpan.setLimited();
592
593 // uninit media from this machine's media registry, or else
594 // reloading the settings will fail
595 mParent->i_unregisterMachineMedia(i_getId());
596 }
597 }
598
599 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
600 "rc=%08X\n",
601 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
602 mData->mRegistered, mData->mAccessible, rc));
603
604 LogFlowThisFuncLeave();
605
606 return rc;
607}
608
609/**
610 * Shared code between the various init() implementations.
611 * @param aParent The VirtualBox object.
612 * @param strConfigFile Settings file.
613 * @return
614 */
615HRESULT Machine::initImpl(VirtualBox *aParent,
616 const Utf8Str &strConfigFile)
617{
618 LogFlowThisFuncEnter();
619
620 AssertReturn(aParent, E_INVALIDARG);
621 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
622
623 HRESULT rc = S_OK;
624
625 /* share the parent weakly */
626 unconst(mParent) = aParent;
627
628 /* allocate the essential machine data structure (the rest will be
629 * allocated later by initDataAndChildObjects() */
630 mData.allocate();
631
632 /* memorize the config file name (as provided) */
633 mData->m_strConfigFile = strConfigFile;
634
635 /* get the full file name */
636 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
637 if (RT_FAILURE(vrc1))
638 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
639 tr("Invalid machine settings file name '%s' (%Rrc)"),
640 strConfigFile.c_str(),
641 vrc1);
642
643 LogFlowThisFuncLeave();
644
645 return rc;
646}
647
648/**
649 * Tries to create a machine settings file in the path stored in the machine
650 * instance data. Used when a new machine is created to fail gracefully if
651 * the settings file could not be written (e.g. because machine dir is read-only).
652 * @return
653 */
654HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
655{
656 HRESULT rc = S_OK;
657
658 // when we create a new machine, we must be able to create the settings file
659 RTFILE f = NIL_RTFILE;
660 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
661 if ( RT_SUCCESS(vrc)
662 || vrc == VERR_SHARING_VIOLATION
663 )
664 {
665 if (RT_SUCCESS(vrc))
666 RTFileClose(f);
667 if (!fForceOverwrite)
668 rc = setError(VBOX_E_FILE_ERROR,
669 tr("Machine settings file '%s' already exists"),
670 mData->m_strConfigFileFull.c_str());
671 else
672 {
673 /* try to delete the config file, as otherwise the creation
674 * of a new settings file will fail. */
675 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
676 if (RT_FAILURE(vrc2))
677 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
678 tr("Could not delete the existing settings file '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(), vrc2);
680 }
681 }
682 else if ( vrc != VERR_FILE_NOT_FOUND
683 && vrc != VERR_PATH_NOT_FOUND
684 )
685 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
686 tr("Invalid machine settings file name '%s' (%Rrc)"),
687 mData->m_strConfigFileFull.c_str(),
688 vrc);
689 return rc;
690}
691
692/**
693 * Initializes the registered machine by loading the settings file.
694 * This method is separated from #init() in order to make it possible to
695 * retry the operation after VirtualBox startup instead of refusing to
696 * startup the whole VirtualBox server in case if the settings file of some
697 * registered VM is invalid or inaccessible.
698 *
699 * @note Must be always called from this object's write lock
700 * (unless called from #init() that doesn't need any locking).
701 * @note Locks the mUSBController method for writing.
702 * @note Subclasses must not call this method.
703 */
704HRESULT Machine::i_registeredInit()
705{
706 AssertReturn(!i_isSessionMachine(), E_FAIL);
707 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
708 AssertReturn(mData->mUuid.isValid(), E_FAIL);
709 AssertReturn(!mData->mAccessible, E_FAIL);
710
711 HRESULT rc = initDataAndChildObjects();
712
713 if (SUCCEEDED(rc))
714 {
715 /* Temporarily reset the registered flag in order to let setters
716 * potentially called from loadSettings() succeed (isMutable() used in
717 * all setters will return FALSE for a Machine instance if mRegistered
718 * is TRUE). */
719 mData->mRegistered = FALSE;
720
721 try
722 {
723 // load and parse machine XML; this will throw on XML or logic errors
724 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
725
726 if (mData->mUuid != mData->pMachineConfigFile->uuid)
727 throw setError(E_FAIL,
728 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
729 mData->pMachineConfigFile->uuid.raw(),
730 mData->m_strConfigFileFull.c_str(),
731 mData->mUuid.toString().c_str(),
732 mParent->i_settingsFilePath().c_str());
733
734 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
735 NULL /* const Guid *puuidRegistry */);
736 if (FAILED(rc)) throw rc;
737 }
738 catch (HRESULT err)
739 {
740 /* we assume that error info is set by the thrower */
741 rc = err;
742 }
743 catch (...)
744 {
745 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
746 }
747
748 /* Restore the registered flag (even on failure) */
749 mData->mRegistered = TRUE;
750 }
751
752 if (SUCCEEDED(rc))
753 {
754 /* Set mAccessible to TRUE only if we successfully locked and loaded
755 * the settings file */
756 mData->mAccessible = TRUE;
757
758 /* commit all changes made during loading the settings file */
759 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
760 /// @todo r=klaus for some reason the settings loading logic backs up
761 // the settings, and therefore a commit is needed. Should probably be changed.
762 }
763 else
764 {
765 /* If the machine is registered, then, instead of returning a
766 * failure, we mark it as inaccessible and set the result to
767 * success to give it a try later */
768
769 /* fetch the current error info */
770 mData->mAccessError = com::ErrorInfo();
771 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
772
773 /* rollback all changes */
774 i_rollback(false /* aNotify */);
775
776 // uninit media from this machine's media registry, or else
777 // reloading the settings will fail
778 mParent->i_unregisterMachineMedia(i_getId());
779
780 /* uninitialize the common part to make sure all data is reset to
781 * default (null) values */
782 uninitDataAndChildObjects();
783
784 rc = S_OK;
785 }
786
787 return rc;
788}
789
790/**
791 * Uninitializes the instance.
792 * Called either from FinalRelease() or by the parent when it gets destroyed.
793 *
794 * @note The caller of this method must make sure that this object
795 * a) doesn't have active callers on the current thread and b) is not locked
796 * by the current thread; otherwise uninit() will hang either a) due to
797 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
798 * a dead-lock caused by this thread waiting for all callers on the other
799 * threads are done but preventing them from doing so by holding a lock.
800 */
801void Machine::uninit()
802{
803 LogFlowThisFuncEnter();
804
805 Assert(!isWriteLockOnCurrentThread());
806
807 Assert(!uRegistryNeedsSaving);
808 if (uRegistryNeedsSaving)
809 {
810 AutoCaller autoCaller(this);
811 if (SUCCEEDED(autoCaller.rc()))
812 {
813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
814 i_saveSettings(NULL, Machine::SaveS_Force);
815 }
816 }
817
818 /* Enclose the state transition Ready->InUninit->NotReady */
819 AutoUninitSpan autoUninitSpan(this);
820 if (autoUninitSpan.uninitDone())
821 return;
822
823 Assert(!i_isSnapshotMachine());
824 Assert(!i_isSessionMachine());
825 Assert(!!mData);
826
827 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
828 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
829
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831
832 if (!mData->mSession.mMachine.isNull())
833 {
834 /* Theoretically, this can only happen if the VirtualBox server has been
835 * terminated while there were clients running that owned open direct
836 * sessions. Since in this case we are definitely called by
837 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
838 * won't happen on the client watcher thread (because it has a
839 * VirtualBox caller for the duration of the
840 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
841 * cannot happen until the VirtualBox caller is released). This is
842 * important, because SessionMachine::uninit() cannot correctly operate
843 * after we return from this method (it expects the Machine instance is
844 * still valid). We'll call it ourselves below.
845 */
846 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
847 (SessionMachine*)mData->mSession.mMachine));
848
849 if (Global::IsOnlineOrTransient(mData->mMachineState))
850 {
851 Log1WarningThisFunc(("Setting state to Aborted!\n"));
852 /* set machine state using SessionMachine reimplementation */
853 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
854 }
855
856 /*
857 * Uninitialize SessionMachine using public uninit() to indicate
858 * an unexpected uninitialization.
859 */
860 mData->mSession.mMachine->uninit();
861 /* SessionMachine::uninit() must set mSession.mMachine to null */
862 Assert(mData->mSession.mMachine.isNull());
863 }
864
865 // uninit media from this machine's media registry, if they're still there
866 Guid uuidMachine(i_getId());
867
868 /* the lock is no more necessary (SessionMachine is uninitialized) */
869 alock.release();
870
871 /* XXX This will fail with
872 * "cannot be closed because it is still attached to 1 virtual machines"
873 * because at this point we did not call uninitDataAndChildObjects() yet
874 * and therefore also removeBackReference() for all these mediums was not called! */
875
876 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
877 mParent->i_unregisterMachineMedia(uuidMachine);
878
879 // has machine been modified?
880 if (mData->flModifications)
881 {
882 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
883 i_rollback(false /* aNotify */);
884 }
885
886 if (mData->mAccessible)
887 uninitDataAndChildObjects();
888
889 /* free the essential data structure last */
890 mData.free();
891
892 LogFlowThisFuncLeave();
893}
894
895// Wrapped IMachine properties
896/////////////////////////////////////////////////////////////////////////////
897HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
898{
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 aParent = pVirtualBox;
902
903 return S_OK;
904}
905
906
907HRESULT Machine::getAccessible(BOOL *aAccessible)
908{
909 /* In some cases (medium registry related), it is necessary to be able to
910 * go through the list of all machines. Happens when an inaccessible VM
911 * has a sensible medium registry. */
912 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
914
915 HRESULT rc = S_OK;
916
917 if (!mData->mAccessible)
918 {
919 /* try to initialize the VM once more if not accessible */
920
921 AutoReinitSpan autoReinitSpan(this);
922 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
923
924#ifdef DEBUG
925 LogFlowThisFunc(("Dumping media backreferences\n"));
926 mParent->i_dumpAllBackRefs();
927#endif
928
929 if (mData->pMachineConfigFile)
930 {
931 // reset the XML file to force loadSettings() (called from i_registeredInit())
932 // to parse it again; the file might have changed
933 delete mData->pMachineConfigFile;
934 mData->pMachineConfigFile = NULL;
935 }
936
937 rc = i_registeredInit();
938
939 if (SUCCEEDED(rc) && mData->mAccessible)
940 {
941 autoReinitSpan.setSucceeded();
942
943 /* make sure interesting parties will notice the accessibility
944 * state change */
945 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
946 mParent->i_onMachineDataChange(mData->mUuid);
947 }
948 }
949
950 if (SUCCEEDED(rc))
951 *aAccessible = mData->mAccessible;
952
953 LogFlowThisFuncLeave();
954
955 return rc;
956}
957
958HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
959{
960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
961
962 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
963 {
964 /* return shortly */
965 aAccessError = NULL;
966 return S_OK;
967 }
968
969 HRESULT rc = S_OK;
970
971 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
972 rc = errorInfo.createObject();
973 if (SUCCEEDED(rc))
974 {
975 errorInfo->init(mData->mAccessError.getResultCode(),
976 mData->mAccessError.getInterfaceID().ref(),
977 Utf8Str(mData->mAccessError.getComponent()).c_str(),
978 Utf8Str(mData->mAccessError.getText()));
979 aAccessError = errorInfo;
980 }
981
982 return rc;
983}
984
985HRESULT Machine::getName(com::Utf8Str &aName)
986{
987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
989 aName = mUserData->s.strName;
990
991 return S_OK;
992}
993
994HRESULT Machine::setName(const com::Utf8Str &aName)
995{
996 // prohibit setting a UUID only as the machine name, or else it can
997 // never be found by findMachine()
998 Guid test(aName);
999
1000 if (test.isValid())
1001 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1002
1003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 HRESULT rc = i_checkStateDependency(MutableStateDep);
1006 if (FAILED(rc)) return rc;
1007
1008 i_setModified(IsModified_MachineData);
1009 mUserData.backup();
1010 mUserData->s.strName = aName;
1011
1012 return S_OK;
1013}
1014
1015HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1016{
1017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 aDescription = mUserData->s.strDescription;
1020
1021 return S_OK;
1022}
1023
1024HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1025{
1026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 // this can be done in principle in any state as it doesn't affect the VM
1029 // significantly, but play safe by not messing around while complex
1030 // activities are going on
1031 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1032 if (FAILED(rc)) return rc;
1033
1034 i_setModified(IsModified_MachineData);
1035 mUserData.backup();
1036 mUserData->s.strDescription = aDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::getId(com::Guid &aId)
1042{
1043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 aId = mData->mUuid;
1046
1047 return S_OK;
1048}
1049
1050HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1051{
1052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1053 aGroups.resize(mUserData->s.llGroups.size());
1054 size_t i = 0;
1055 for (StringsList::const_iterator
1056 it = mUserData->s.llGroups.begin();
1057 it != mUserData->s.llGroups.end();
1058 ++it, ++i)
1059 aGroups[i] = (*it);
1060
1061 return S_OK;
1062}
1063
1064HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1065{
1066 StringsList llGroups;
1067 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1068 if (FAILED(rc))
1069 return rc;
1070
1071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1072
1073 rc = i_checkStateDependency(MutableOrSavedStateDep);
1074 if (FAILED(rc)) return rc;
1075
1076 i_setModified(IsModified_MachineData);
1077 mUserData.backup();
1078 mUserData->s.llGroups = llGroups;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1084{
1085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1086
1087 aOSTypeId = mUserData->s.strOsType;
1088
1089 return S_OK;
1090}
1091
1092HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1093{
1094 /* look up the object by Id to check it is valid */
1095 ComObjPtr<GuestOSType> pGuestOSType;
1096 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1097
1098 /* when setting, always use the "etalon" value for consistency -- lookup
1099 * by ID is case-insensitive and the input value may have different case */
1100 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1101
1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 HRESULT rc = i_checkStateDependency(MutableStateDep);
1105 if (FAILED(rc)) return rc;
1106
1107 i_setModified(IsModified_MachineData);
1108 mUserData.backup();
1109 mUserData->s.strOsType = osTypeId;
1110
1111 return S_OK;
1112}
1113
1114HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1115{
1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 *aFirmwareType = mHWData->mFirmwareType;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1124{
1125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 HRESULT rc = i_checkStateDependency(MutableStateDep);
1128 if (FAILED(rc)) return rc;
1129
1130 i_setModified(IsModified_MachineData);
1131 mHWData.backup();
1132 mHWData->mFirmwareType = aFirmwareType;
1133
1134 return S_OK;
1135}
1136
1137HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1138{
1139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1140
1141 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1142
1143 return S_OK;
1144}
1145
1146HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1147{
1148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1149
1150 HRESULT rc = i_checkStateDependency(MutableStateDep);
1151 if (FAILED(rc)) return rc;
1152
1153 i_setModified(IsModified_MachineData);
1154 mHWData.backup();
1155 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1161{
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHIDType = mHWData->mPointingHIDType;
1165
1166 return S_OK;
1167}
1168
1169HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1170{
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = i_checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 i_setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mPointingHIDType = aPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1184{
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aChipsetType = mHWData->mChipsetType;
1188
1189 return S_OK;
1190}
1191
1192HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1193{
1194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 HRESULT rc = i_checkStateDependency(MutableStateDep);
1197 if (FAILED(rc)) return rc;
1198
1199 if (aChipsetType != mHWData->mChipsetType)
1200 {
1201 i_setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mChipsetType = aChipsetType;
1204
1205 // Resize network adapter array, to be finalized on commit/rollback.
1206 // We must not throw away entries yet, otherwise settings are lost
1207 // without a way to roll back.
1208 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1209 size_t oldCount = mNetworkAdapters.size();
1210 if (newCount > oldCount)
1211 {
1212 mNetworkAdapters.resize(newCount);
1213 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1214 {
1215 unconst(mNetworkAdapters[slot]).createObject();
1216 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1217 }
1218 }
1219 }
1220
1221 return S_OK;
1222}
1223
1224HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1225{
1226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 aParavirtDebug = mHWData->mParavirtDebug;
1229 return S_OK;
1230}
1231
1232HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1233{
1234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 HRESULT rc = i_checkStateDependency(MutableStateDep);
1237 if (FAILED(rc)) return rc;
1238
1239 /** @todo Parse/validate options? */
1240 if (aParavirtDebug != mHWData->mParavirtDebug)
1241 {
1242 i_setModified(IsModified_MachineData);
1243 mHWData.backup();
1244 mHWData->mParavirtDebug = aParavirtDebug;
1245 }
1246
1247 return S_OK;
1248}
1249
1250HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1251{
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aParavirtProvider = mHWData->mParavirtProvider;
1255
1256 return S_OK;
1257}
1258
1259HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1260{
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = i_checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aParavirtProvider != mHWData->mParavirtProvider)
1267 {
1268 i_setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mParavirtProvider = aParavirtProvider;
1271 }
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1277{
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 *aParavirtProvider = mHWData->mParavirtProvider;
1281 switch (mHWData->mParavirtProvider)
1282 {
1283 case ParavirtProvider_None:
1284 case ParavirtProvider_HyperV:
1285 case ParavirtProvider_KVM:
1286 case ParavirtProvider_Minimal:
1287 break;
1288
1289 /* Resolve dynamic provider types to the effective types. */
1290 default:
1291 {
1292 ComObjPtr<GuestOSType> pGuestOSType;
1293 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1294 pGuestOSType);
1295 if (FAILED(hrc2) || pGuestOSType.isNull())
1296 {
1297 *aParavirtProvider = ParavirtProvider_None;
1298 break;
1299 }
1300
1301 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1302 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1303
1304 switch (mHWData->mParavirtProvider)
1305 {
1306 case ParavirtProvider_Legacy:
1307 {
1308 if (fOsXGuest)
1309 *aParavirtProvider = ParavirtProvider_Minimal;
1310 else
1311 *aParavirtProvider = ParavirtProvider_None;
1312 break;
1313 }
1314
1315 case ParavirtProvider_Default:
1316 {
1317 if (fOsXGuest)
1318 *aParavirtProvider = ParavirtProvider_Minimal;
1319 else if ( mUserData->s.strOsType == "Windows10"
1320 || mUserData->s.strOsType == "Windows10_64"
1321 || mUserData->s.strOsType == "Windows81"
1322 || mUserData->s.strOsType == "Windows81_64"
1323 || mUserData->s.strOsType == "Windows8"
1324 || mUserData->s.strOsType == "Windows8_64"
1325 || mUserData->s.strOsType == "Windows7"
1326 || mUserData->s.strOsType == "Windows7_64"
1327 || mUserData->s.strOsType == "WindowsVista"
1328 || mUserData->s.strOsType == "WindowsVista_64"
1329 || mUserData->s.strOsType == "Windows2012"
1330 || mUserData->s.strOsType == "Windows2012_64"
1331 || mUserData->s.strOsType == "Windows2008"
1332 || mUserData->s.strOsType == "Windows2008_64")
1333 {
1334 *aParavirtProvider = ParavirtProvider_HyperV;
1335 }
1336 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1337 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1338 || mUserData->s.strOsType == "Linux"
1339 || mUserData->s.strOsType == "Linux_64"
1340 || mUserData->s.strOsType == "ArchLinux"
1341 || mUserData->s.strOsType == "ArchLinux_64"
1342 || mUserData->s.strOsType == "Debian"
1343 || mUserData->s.strOsType == "Debian_64"
1344 || mUserData->s.strOsType == "Fedora"
1345 || mUserData->s.strOsType == "Fedora_64"
1346 || mUserData->s.strOsType == "Gentoo"
1347 || mUserData->s.strOsType == "Gentoo_64"
1348 || mUserData->s.strOsType == "Mandriva"
1349 || mUserData->s.strOsType == "Mandriva_64"
1350 || mUserData->s.strOsType == "OpenSUSE"
1351 || mUserData->s.strOsType == "OpenSUSE_64"
1352 || mUserData->s.strOsType == "Oracle"
1353 || mUserData->s.strOsType == "Oracle_64"
1354 || mUserData->s.strOsType == "RedHat"
1355 || mUserData->s.strOsType == "RedHat_64"
1356 || mUserData->s.strOsType == "Turbolinux"
1357 || mUserData->s.strOsType == "Turbolinux_64"
1358 || mUserData->s.strOsType == "Ubuntu"
1359 || mUserData->s.strOsType == "Ubuntu_64"
1360 || mUserData->s.strOsType == "Xandros"
1361 || mUserData->s.strOsType == "Xandros_64")
1362 {
1363 *aParavirtProvider = ParavirtProvider_KVM;
1364 }
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 default: AssertFailedBreak(); /* Shut up MSC. */
1371 }
1372 break;
1373 }
1374 }
1375
1376 Assert( *aParavirtProvider == ParavirtProvider_None
1377 || *aParavirtProvider == ParavirtProvider_Minimal
1378 || *aParavirtProvider == ParavirtProvider_HyperV
1379 || *aParavirtProvider == ParavirtProvider_KVM);
1380 return S_OK;
1381}
1382
1383HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1384{
1385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1386
1387 aHardwareVersion = mHWData->mHWVersion;
1388
1389 return S_OK;
1390}
1391
1392HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1393{
1394 /* check known version */
1395 Utf8Str hwVersion = aHardwareVersion;
1396 if ( hwVersion.compare("1") != 0
1397 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1398 return setError(E_INVALIDARG,
1399 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1400
1401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1402
1403 HRESULT rc = i_checkStateDependency(MutableStateDep);
1404 if (FAILED(rc)) return rc;
1405
1406 i_setModified(IsModified_MachineData);
1407 mHWData.backup();
1408 mHWData->mHWVersion = aHardwareVersion;
1409
1410 return S_OK;
1411}
1412
1413HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1414{
1415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 if (!mHWData->mHardwareUUID.isZero())
1418 aHardwareUUID = mHWData->mHardwareUUID;
1419 else
1420 aHardwareUUID = mData->mUuid;
1421
1422 return S_OK;
1423}
1424
1425HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1426{
1427 if (!aHardwareUUID.isValid())
1428 return E_INVALIDARG;
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 if (aHardwareUUID == mData->mUuid)
1438 mHWData->mHardwareUUID.clear();
1439 else
1440 mHWData->mHardwareUUID = aHardwareUUID;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1446{
1447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 *aMemorySize = mHWData->mMemorySize;
1450
1451 return S_OK;
1452}
1453
1454HRESULT Machine::setMemorySize(ULONG aMemorySize)
1455{
1456 /* check RAM limits */
1457 if ( aMemorySize < MM_RAM_MIN_IN_MB
1458 || aMemorySize > MM_RAM_MAX_IN_MB
1459 )
1460 return setError(E_INVALIDARG,
1461 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1462 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1463
1464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 HRESULT rc = i_checkStateDependency(MutableStateDep);
1467 if (FAILED(rc)) return rc;
1468
1469 i_setModified(IsModified_MachineData);
1470 mHWData.backup();
1471 mHWData->mMemorySize = aMemorySize;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1477{
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 *aCPUCount = mHWData->mCPUCount;
1481
1482 return S_OK;
1483}
1484
1485HRESULT Machine::setCPUCount(ULONG aCPUCount)
1486{
1487 /* check CPU limits */
1488 if ( aCPUCount < SchemaDefs::MinCPUCount
1489 || aCPUCount > SchemaDefs::MaxCPUCount
1490 )
1491 return setError(E_INVALIDARG,
1492 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1493 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1498 if (mHWData->mCPUHotPlugEnabled)
1499 {
1500 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1501 {
1502 if (mHWData->mCPUAttached[idx])
1503 return setError(E_INVALIDARG,
1504 tr("There is still a CPU attached to socket %lu."
1505 "Detach the CPU before removing the socket"),
1506 aCPUCount, idx+1);
1507 }
1508 }
1509
1510 HRESULT rc = i_checkStateDependency(MutableStateDep);
1511 if (FAILED(rc)) return rc;
1512
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCPUCount = aCPUCount;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1521{
1522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1525
1526 return S_OK;
1527}
1528
1529HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1530{
1531 HRESULT rc = S_OK;
1532
1533 /* check throttle limits */
1534 if ( aCPUExecutionCap < 1
1535 || aCPUExecutionCap > 100
1536 )
1537 return setError(E_INVALIDARG,
1538 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1539 aCPUExecutionCap, 1, 100);
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 alock.release();
1544 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1545 alock.acquire();
1546 if (FAILED(rc)) return rc;
1547
1548 i_setModified(IsModified_MachineData);
1549 mHWData.backup();
1550 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1551
1552 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1553 if (Global::IsOnline(mData->mMachineState))
1554 i_saveSettings(NULL);
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1569{
1570 HRESULT rc = S_OK;
1571
1572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1573
1574 rc = i_checkStateDependency(MutableStateDep);
1575 if (FAILED(rc)) return rc;
1576
1577 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1578 {
1579 if (aCPUHotPlugEnabled)
1580 {
1581 i_setModified(IsModified_MachineData);
1582 mHWData.backup();
1583
1584 /* Add the amount of CPUs currently attached */
1585 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1586 mHWData->mCPUAttached[i] = true;
1587 }
1588 else
1589 {
1590 /*
1591 * We can disable hotplug only if the amount of maximum CPUs is equal
1592 * to the amount of attached CPUs
1593 */
1594 unsigned cCpusAttached = 0;
1595 unsigned iHighestId = 0;
1596
1597 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1598 {
1599 if (mHWData->mCPUAttached[i])
1600 {
1601 cCpusAttached++;
1602 iHighestId = i;
1603 }
1604 }
1605
1606 if ( (cCpusAttached != mHWData->mCPUCount)
1607 || (iHighestId >= mHWData->mCPUCount))
1608 return setError(E_INVALIDARG,
1609 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1610
1611 i_setModified(IsModified_MachineData);
1612 mHWData.backup();
1613 }
1614 }
1615
1616 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1617
1618 return rc;
1619}
1620
1621HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1631{
1632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1633
1634 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1635 if (SUCCEEDED(hrc))
1636 {
1637 i_setModified(IsModified_MachineData);
1638 mHWData.backup();
1639 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1640 }
1641 return hrc;
1642}
1643
1644HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1645{
1646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 aCPUProfile = mHWData->mCpuProfile;
1648 return S_OK;
1649}
1650
1651HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1652{
1653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1654 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1655 if (SUCCEEDED(hrc))
1656 {
1657 i_setModified(IsModified_MachineData);
1658 mHWData.backup();
1659 /* Empty equals 'host'. */
1660 if (aCPUProfile.isNotEmpty())
1661 mHWData->mCpuProfile = aCPUProfile;
1662 else
1663 mHWData->mCpuProfile = "host";
1664 }
1665 return hrc;
1666}
1667
1668HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1669{
1670#ifdef VBOX_WITH_USB_CARDREADER
1671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1672
1673 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1674
1675 return S_OK;
1676#else
1677 NOREF(aEmulatedUSBCardReaderEnabled);
1678 return E_NOTIMPL;
1679#endif
1680}
1681
1682HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1683{
1684#ifdef VBOX_WITH_USB_CARDREADER
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1688 if (FAILED(rc)) return rc;
1689
1690 i_setModified(IsModified_MachineData);
1691 mHWData.backup();
1692 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1693
1694 return S_OK;
1695#else
1696 NOREF(aEmulatedUSBCardReaderEnabled);
1697 return E_NOTIMPL;
1698#endif
1699}
1700
1701HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1702{
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *aHPETEnabled = mHWData->mHPETEnabled;
1706
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1711{
1712 HRESULT rc = S_OK;
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 rc = i_checkStateDependency(MutableStateDep);
1717 if (FAILED(rc)) return rc;
1718
1719 i_setModified(IsModified_MachineData);
1720 mHWData.backup();
1721
1722 mHWData->mHPETEnabled = aHPETEnabled;
1723
1724 return rc;
1725}
1726
1727HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1732
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1737{
1738 switch (aGraphicsControllerType)
1739 {
1740 case GraphicsControllerType_Null:
1741 case GraphicsControllerType_VBoxVGA:
1742#ifdef VBOX_WITH_VMSVGA
1743 case GraphicsControllerType_VMSVGA:
1744 case GraphicsControllerType_VBoxSVGA:
1745#endif
1746 break;
1747 default:
1748 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1749 }
1750
1751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1752
1753 HRESULT rc = i_checkStateDependency(MutableStateDep);
1754 if (FAILED(rc)) return rc;
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766
1767 *aVRAMSize = mHWData->mVRAMSize;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1773{
1774 /* check VRAM limits */
1775 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1776 return setError(E_INVALIDARG,
1777 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1778 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 HRESULT rc = i_checkStateDependency(MutableStateDep);
1783 if (FAILED(rc)) return rc;
1784
1785 i_setModified(IsModified_MachineData);
1786 mHWData.backup();
1787 mHWData->mVRAMSize = aVRAMSize;
1788
1789 return S_OK;
1790}
1791
1792/** @todo this method should not be public */
1793HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1794{
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1798
1799 return S_OK;
1800}
1801
1802/**
1803 * Set the memory balloon size.
1804 *
1805 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1806 * we have to make sure that we never call IGuest from here.
1807 */
1808HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1809{
1810 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1811#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1812 /* check limits */
1813 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1814 return setError(E_INVALIDARG,
1815 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1816 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1817
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 i_setModified(IsModified_MachineData);
1821 mHWData.backup();
1822 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1823
1824 return S_OK;
1825#else
1826 NOREF(aMemoryBalloonSize);
1827 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1828#endif
1829}
1830
1831HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1832{
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1836 return S_OK;
1837}
1838
1839HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1840{
1841#ifdef VBOX_WITH_PAGE_SHARING
1842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1845 i_setModified(IsModified_MachineData);
1846 mHWData.backup();
1847 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1848 return S_OK;
1849#else
1850 NOREF(aPageFusionEnabled);
1851 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1852#endif
1853}
1854
1855HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1856{
1857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1858
1859 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1860
1861 return S_OK;
1862}
1863
1864HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1865{
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 HRESULT rc = i_checkStateDependency(MutableStateDep);
1869 if (FAILED(rc)) return rc;
1870
1871 /** @todo check validity! */
1872
1873 i_setModified(IsModified_MachineData);
1874 mHWData.backup();
1875 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1876
1877 return S_OK;
1878}
1879
1880
1881HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1886
1887 return S_OK;
1888}
1889
1890HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1891{
1892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 HRESULT rc = i_checkStateDependency(MutableStateDep);
1895 if (FAILED(rc)) return rc;
1896
1897 /** @todo check validity! */
1898 i_setModified(IsModified_MachineData);
1899 mHWData.backup();
1900 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1901
1902 return S_OK;
1903}
1904
1905HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1906{
1907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 *aMonitorCount = mHWData->mMonitorCount;
1910
1911 return S_OK;
1912}
1913
1914HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1915{
1916 /* make sure monitor count is a sensible number */
1917 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1918 return setError(E_INVALIDARG,
1919 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1920 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1921
1922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 HRESULT rc = i_checkStateDependency(MutableStateDep);
1925 if (FAILED(rc)) return rc;
1926
1927 i_setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mMonitorCount = aMonitorCount;
1930
1931 return S_OK;
1932}
1933
1934HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1935{
1936 /* mBIOSSettings is constant during life time, no need to lock */
1937 aBIOSSettings = mBIOSSettings;
1938
1939 return S_OK;
1940}
1941
1942HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1943{
1944 /* mRecordingSettings is constant during life time, no need to lock */
1945 aRecordingSettings = mRecordingSettings;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 switch (aProperty)
1955 {
1956 case CPUPropertyType_PAE:
1957 *aValue = mHWData->mPAEEnabled;
1958 break;
1959
1960 case CPUPropertyType_LongMode:
1961 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1962 *aValue = TRUE;
1963 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1964 *aValue = FALSE;
1965#if HC_ARCH_BITS == 64
1966 else
1967 *aValue = TRUE;
1968#else
1969 else
1970 {
1971 *aValue = FALSE;
1972
1973 ComObjPtr<GuestOSType> pGuestOSType;
1974 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1975 pGuestOSType);
1976 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1977 {
1978 if (pGuestOSType->i_is64Bit())
1979 {
1980 ComObjPtr<Host> pHost = mParent->i_host();
1981 alock.release();
1982
1983 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1984 if (FAILED(hrc2))
1985 *aValue = FALSE;
1986 }
1987 }
1988 }
1989#endif
1990 break;
1991
1992 case CPUPropertyType_TripleFaultReset:
1993 *aValue = mHWData->mTripleFaultReset;
1994 break;
1995
1996 case CPUPropertyType_APIC:
1997 *aValue = mHWData->mAPIC;
1998 break;
1999
2000 case CPUPropertyType_X2APIC:
2001 *aValue = mHWData->mX2APIC;
2002 break;
2003
2004 case CPUPropertyType_IBPBOnVMExit:
2005 *aValue = mHWData->mIBPBOnVMExit;
2006 break;
2007
2008 case CPUPropertyType_IBPBOnVMEntry:
2009 *aValue = mHWData->mIBPBOnVMEntry;
2010 break;
2011
2012 case CPUPropertyType_SpecCtrl:
2013 *aValue = mHWData->mSpecCtrl;
2014 break;
2015
2016 case CPUPropertyType_SpecCtrlByHost:
2017 *aValue = mHWData->mSpecCtrlByHost;
2018 break;
2019
2020 case CPUPropertyType_HWVirt:
2021 *aValue = mHWData->mNestedHWVirt;
2022 break;
2023
2024 default:
2025 return E_INVALIDARG;
2026 }
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2031{
2032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2033
2034 HRESULT rc = i_checkStateDependency(MutableStateDep);
2035 if (FAILED(rc)) return rc;
2036
2037 switch (aProperty)
2038 {
2039 case CPUPropertyType_PAE:
2040 i_setModified(IsModified_MachineData);
2041 mHWData.backup();
2042 mHWData->mPAEEnabled = !!aValue;
2043 break;
2044
2045 case CPUPropertyType_LongMode:
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2049 break;
2050
2051 case CPUPropertyType_TripleFaultReset:
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mTripleFaultReset = !!aValue;
2055 break;
2056
2057 case CPUPropertyType_APIC:
2058 if (mHWData->mX2APIC)
2059 aValue = TRUE;
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mAPIC = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_X2APIC:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mX2APIC = !!aValue;
2069 if (aValue)
2070 mHWData->mAPIC = !!aValue;
2071 break;
2072
2073 case CPUPropertyType_IBPBOnVMExit:
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mIBPBOnVMExit = !!aValue;
2077 break;
2078
2079 case CPUPropertyType_IBPBOnVMEntry:
2080 i_setModified(IsModified_MachineData);
2081 mHWData.backup();
2082 mHWData->mIBPBOnVMEntry = !!aValue;
2083 break;
2084
2085 case CPUPropertyType_SpecCtrl:
2086 i_setModified(IsModified_MachineData);
2087 mHWData.backup();
2088 mHWData->mSpecCtrl = !!aValue;
2089 break;
2090
2091 case CPUPropertyType_SpecCtrlByHost:
2092 i_setModified(IsModified_MachineData);
2093 mHWData.backup();
2094 mHWData->mSpecCtrlByHost = !!aValue;
2095 break;
2096
2097 case CPUPropertyType_HWVirt:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mNestedHWVirt = !!aValue;
2101 break;
2102
2103 default:
2104 return E_INVALIDARG;
2105 }
2106 return S_OK;
2107}
2108
2109HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2110 ULONG *aValEcx, ULONG *aValEdx)
2111{
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2114 {
2115 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2116 it != mHWData->mCpuIdLeafList.end();
2117 ++it)
2118 {
2119 if (aOrdinal == 0)
2120 {
2121 const settings::CpuIdLeaf &rLeaf= *it;
2122 *aIdx = rLeaf.idx;
2123 *aSubIdx = rLeaf.idxSub;
2124 *aValEax = rLeaf.uEax;
2125 *aValEbx = rLeaf.uEbx;
2126 *aValEcx = rLeaf.uEcx;
2127 *aValEdx = rLeaf.uEdx;
2128 return S_OK;
2129 }
2130 aOrdinal--;
2131 }
2132 }
2133 return E_INVALIDARG;
2134}
2135
2136HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 /*
2141 * Search the list.
2142 */
2143 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2144 {
2145 const settings::CpuIdLeaf &rLeaf= *it;
2146 if ( rLeaf.idx == aIdx
2147 && ( aSubIdx == UINT32_MAX
2148 || rLeaf.idxSub == aSubIdx) )
2149 {
2150 *aValEax = rLeaf.uEax;
2151 *aValEbx = rLeaf.uEbx;
2152 *aValEcx = rLeaf.uEcx;
2153 *aValEdx = rLeaf.uEdx;
2154 return S_OK;
2155 }
2156 }
2157
2158 return E_INVALIDARG;
2159}
2160
2161
2162HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2163{
2164 /*
2165 * Validate input before taking locks and checking state.
2166 */
2167 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2168 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2169 if ( aIdx >= UINT32_C(0x20)
2170 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2171 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2172 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2173
2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2175 HRESULT rc = i_checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 /*
2179 * Impose a maximum number of leaves.
2180 */
2181 if (mHWData->mCpuIdLeafList.size() > 256)
2182 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2183
2184 /*
2185 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2186 */
2187 i_setModified(IsModified_MachineData);
2188 mHWData.backup();
2189
2190 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2191 {
2192 settings::CpuIdLeaf &rLeaf= *it;
2193 if ( rLeaf.idx == aIdx
2194 && ( aSubIdx == UINT32_MAX
2195 || rLeaf.idxSub == aSubIdx) )
2196 it = mHWData->mCpuIdLeafList.erase(it);
2197 else
2198 ++it;
2199 }
2200
2201 settings::CpuIdLeaf NewLeaf;
2202 NewLeaf.idx = aIdx;
2203 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2204 NewLeaf.uEax = aValEax;
2205 NewLeaf.uEbx = aValEbx;
2206 NewLeaf.uEcx = aValEcx;
2207 NewLeaf.uEdx = aValEdx;
2208 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2209 return S_OK;
2210}
2211
2212HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2213{
2214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2215
2216 HRESULT rc = i_checkStateDependency(MutableStateDep);
2217 if (FAILED(rc)) return rc;
2218
2219 /*
2220 * Do the removal.
2221 */
2222 bool fModified = false;
2223 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2224 {
2225 settings::CpuIdLeaf &rLeaf= *it;
2226 if ( rLeaf.idx == aIdx
2227 && ( aSubIdx == UINT32_MAX
2228 || rLeaf.idxSub == aSubIdx) )
2229 {
2230 if (!fModified)
2231 {
2232 fModified = true;
2233 i_setModified(IsModified_MachineData);
2234 mHWData.backup();
2235 }
2236 it = mHWData->mCpuIdLeafList.erase(it);
2237 }
2238 else
2239 ++it;
2240 }
2241
2242 return S_OK;
2243}
2244
2245HRESULT Machine::removeAllCPUIDLeaves()
2246{
2247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 HRESULT rc = i_checkStateDependency(MutableStateDep);
2250 if (FAILED(rc)) return rc;
2251
2252 if (mHWData->mCpuIdLeafList.size() > 0)
2253 {
2254 i_setModified(IsModified_MachineData);
2255 mHWData.backup();
2256
2257 mHWData->mCpuIdLeafList.clear();
2258 }
2259
2260 return S_OK;
2261}
2262HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2263{
2264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2265
2266 switch(aProperty)
2267 {
2268 case HWVirtExPropertyType_Enabled:
2269 *aValue = mHWData->mHWVirtExEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_VPID:
2273 *aValue = mHWData->mHWVirtExVPIDEnabled;
2274 break;
2275
2276 case HWVirtExPropertyType_NestedPaging:
2277 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2278 break;
2279
2280 case HWVirtExPropertyType_UnrestrictedExecution:
2281 *aValue = mHWData->mHWVirtExUXEnabled;
2282 break;
2283
2284 case HWVirtExPropertyType_LargePages:
2285 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2286#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2287 *aValue = FALSE;
2288#endif
2289 break;
2290
2291 case HWVirtExPropertyType_Force:
2292 *aValue = mHWData->mHWVirtExForceEnabled;
2293 break;
2294
2295 case HWVirtExPropertyType_UseNativeApi:
2296 *aValue = mHWData->mHWVirtExUseNativeApi;
2297 break;
2298
2299 default:
2300 return E_INVALIDARG;
2301 }
2302 return S_OK;
2303}
2304
2305HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 switch (aProperty)
2313 {
2314 case HWVirtExPropertyType_Enabled:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_VPID:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_NestedPaging:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_UnrestrictedExecution:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExUXEnabled = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_LargePages:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2342 break;
2343
2344 case HWVirtExPropertyType_Force:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mHWVirtExForceEnabled = !!aValue;
2348 break;
2349
2350 case HWVirtExPropertyType_UseNativeApi:
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 mHWData->mHWVirtExUseNativeApi = !!aValue;
2354 break;
2355
2356 default:
2357 return E_INVALIDARG;
2358 }
2359
2360 return S_OK;
2361}
2362
2363HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2364{
2365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2368
2369 return S_OK;
2370}
2371
2372HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2373{
2374 /** @todo (r=dmik):
2375 * 1. Allow to change the name of the snapshot folder containing snapshots
2376 * 2. Rename the folder on disk instead of just changing the property
2377 * value (to be smart and not to leave garbage). Note that it cannot be
2378 * done here because the change may be rolled back. Thus, the right
2379 * place is #saveSettings().
2380 */
2381
2382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 HRESULT rc = i_checkStateDependency(MutableStateDep);
2385 if (FAILED(rc)) return rc;
2386
2387 if (!mData->mCurrentSnapshot.isNull())
2388 return setError(E_FAIL,
2389 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2390
2391 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2392
2393 if (strSnapshotFolder.isEmpty())
2394 strSnapshotFolder = "Snapshots";
2395 int vrc = i_calculateFullPath(strSnapshotFolder,
2396 strSnapshotFolder);
2397 if (RT_FAILURE(vrc))
2398 return setErrorBoth(E_FAIL, vrc,
2399 tr("Invalid snapshot folder '%s' (%Rrc)"),
2400 strSnapshotFolder.c_str(), vrc);
2401
2402 i_setModified(IsModified_MachineData);
2403 mUserData.backup();
2404
2405 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2406
2407 return S_OK;
2408}
2409
2410HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2411{
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 aMediumAttachments.resize(mMediumAttachments->size());
2415 size_t i = 0;
2416 for (MediumAttachmentList::const_iterator
2417 it = mMediumAttachments->begin();
2418 it != mMediumAttachments->end();
2419 ++it, ++i)
2420 aMediumAttachments[i] = *it;
2421
2422 return S_OK;
2423}
2424
2425HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 Assert(!!mVRDEServer);
2430
2431 aVRDEServer = mVRDEServer;
2432
2433 return S_OK;
2434}
2435
2436HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2437{
2438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2439
2440 aAudioAdapter = mAudioAdapter;
2441
2442 return S_OK;
2443}
2444
2445HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2446{
2447#ifdef VBOX_WITH_VUSB
2448 clearError();
2449 MultiResult rc(S_OK);
2450
2451# ifdef VBOX_WITH_USB
2452 rc = mParent->i_host()->i_checkUSBProxyService();
2453 if (FAILED(rc)) return rc;
2454# endif
2455
2456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 aUSBControllers.resize(mUSBControllers->size());
2459 size_t i = 0;
2460 for (USBControllerList::const_iterator
2461 it = mUSBControllers->begin();
2462 it != mUSBControllers->end();
2463 ++it, ++i)
2464 aUSBControllers[i] = *it;
2465
2466 return S_OK;
2467#else
2468 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2469 * extended error info to indicate that USB is simply not available
2470 * (w/o treating it as a failure), for example, as in OSE */
2471 NOREF(aUSBControllers);
2472 ReturnComNotImplemented();
2473#endif /* VBOX_WITH_VUSB */
2474}
2475
2476HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2477{
2478#ifdef VBOX_WITH_VUSB
2479 clearError();
2480 MultiResult rc(S_OK);
2481
2482# ifdef VBOX_WITH_USB
2483 rc = mParent->i_host()->i_checkUSBProxyService();
2484 if (FAILED(rc)) return rc;
2485# endif
2486
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 aUSBDeviceFilters = mUSBDeviceFilters;
2490 return rc;
2491#else
2492 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2493 * extended error info to indicate that USB is simply not available
2494 * (w/o treating it as a failure), for example, as in OSE */
2495 NOREF(aUSBDeviceFilters);
2496 ReturnComNotImplemented();
2497#endif /* VBOX_WITH_VUSB */
2498}
2499
2500HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2501{
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 aSettingsFilePath = mData->m_strConfigFileFull;
2505
2506 return S_OK;
2507}
2508
2509HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2510{
2511 RT_NOREF(aSettingsFilePath);
2512 ReturnComNotImplemented();
2513}
2514
2515HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2516{
2517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2520 if (FAILED(rc)) return rc;
2521
2522 if (!mData->pMachineConfigFile->fileExists())
2523 // this is a new machine, and no config file exists yet:
2524 *aSettingsModified = TRUE;
2525 else
2526 *aSettingsModified = (mData->flModifications != 0);
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 *aSessionState = mData->mSession.mState;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 aSessionName = mData->mSession.mName;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2550{
2551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 *aSessionPID = mData->mSession.mPID;
2554
2555 return S_OK;
2556}
2557
2558HRESULT Machine::getState(MachineState_T *aState)
2559{
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 *aState = mData->mMachineState;
2563 Assert(mData->mMachineState != MachineState_Null);
2564
2565 return S_OK;
2566}
2567
2568HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2569{
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 aStateFilePath = mSSData->strStateFilePath;
2582
2583 return S_OK;
2584}
2585
2586HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2587{
2588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2589
2590 i_getLogFolder(aLogFolder);
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 aCurrentSnapshot = mData->mCurrentSnapshot;
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2609 ? 0
2610 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2611
2612 return S_OK;
2613}
2614
2615HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2616{
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 /* Note: for machines with no snapshots, we always return FALSE
2620 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2621 * reasons :) */
2622
2623 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2624 ? FALSE
2625 : mData->mCurrentStateModified;
2626
2627 return S_OK;
2628}
2629
2630HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2631{
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 aSharedFolders.resize(mHWData->mSharedFolders.size());
2635 size_t i = 0;
2636 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2637 it = mHWData->mSharedFolders.begin();
2638 it != mHWData->mSharedFolders.end();
2639 ++it, ++i)
2640 aSharedFolders[i] = *it;
2641
2642 return S_OK;
2643}
2644
2645HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2646{
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 *aClipboardMode = mHWData->mClipboardMode;
2650
2651 return S_OK;
2652}
2653
2654HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2655{
2656 HRESULT rc = S_OK;
2657
2658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 alock.release();
2661 rc = i_onClipboardModeChange(aClipboardMode);
2662 alock.acquire();
2663 if (FAILED(rc)) return rc;
2664
2665 i_setModified(IsModified_MachineData);
2666 mHWData.backup();
2667 mHWData->mClipboardMode = aClipboardMode;
2668
2669 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2670 if (Global::IsOnline(mData->mMachineState))
2671 i_saveSettings(NULL);
2672
2673 return S_OK;
2674}
2675
2676HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2677{
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 *aDnDMode = mHWData->mDnDMode;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2686{
2687 HRESULT rc = S_OK;
2688
2689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 alock.release();
2692 rc = i_onDnDModeChange(aDnDMode);
2693
2694 alock.acquire();
2695 if (FAILED(rc)) return rc;
2696
2697 i_setModified(IsModified_MachineData);
2698 mHWData.backup();
2699 mHWData->mDnDMode = aDnDMode;
2700
2701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2702 if (Global::IsOnline(mData->mMachineState))
2703 i_saveSettings(NULL);
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 aStorageControllers.resize(mStorageControllers->size());
2713 size_t i = 0;
2714 for (StorageControllerList::const_iterator
2715 it = mStorageControllers->begin();
2716 it != mStorageControllers->end();
2717 ++it, ++i)
2718 aStorageControllers[i] = *it;
2719
2720 return S_OK;
2721}
2722
2723HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2724{
2725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2726
2727 *aEnabled = mUserData->s.fTeleporterEnabled;
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2733{
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 /* Only allow it to be set to true when PoweredOff or Aborted.
2737 (Clearing it is always permitted.) */
2738 if ( aTeleporterEnabled
2739 && mData->mRegistered
2740 && ( !i_isSessionMachine()
2741 || ( mData->mMachineState != MachineState_PoweredOff
2742 && mData->mMachineState != MachineState_Teleported
2743 && mData->mMachineState != MachineState_Aborted
2744 )
2745 )
2746 )
2747 return setError(VBOX_E_INVALID_VM_STATE,
2748 tr("The machine is not powered off (state is %s)"),
2749 Global::stringifyMachineState(mData->mMachineState));
2750
2751 i_setModified(IsModified_MachineData);
2752 mUserData.backup();
2753 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2763
2764 return S_OK;
2765}
2766
2767HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2768{
2769 if (aTeleporterPort >= _64K)
2770 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2771
2772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2775 if (FAILED(rc)) return rc;
2776
2777 i_setModified(IsModified_MachineData);
2778 mUserData.backup();
2779 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2794{
2795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2798 if (FAILED(rc)) return rc;
2799
2800 i_setModified(IsModified_MachineData);
2801 mUserData.backup();
2802 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2816{
2817 /*
2818 * Hash the password first.
2819 */
2820 com::Utf8Str aT = aTeleporterPassword;
2821
2822 if (!aT.isEmpty())
2823 {
2824 if (VBoxIsPasswordHashed(&aT))
2825 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2826 VBoxHashPassword(&aT);
2827 }
2828
2829 /*
2830 * Do the update.
2831 */
2832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2833 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2834 if (SUCCEEDED(hrc))
2835 {
2836 i_setModified(IsModified_MachineData);
2837 mUserData.backup();
2838 mUserData->s.strTeleporterPassword = aT;
2839 }
2840
2841 return hrc;
2842}
2843
2844HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2849 return S_OK;
2850}
2851
2852HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2853{
2854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 /** @todo deal with running state change. */
2857 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2858 if (FAILED(rc)) return rc;
2859
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2863 return S_OK;
2864}
2865
2866HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2867{
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2871 return S_OK;
2872}
2873
2874HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2875{
2876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2877
2878 /** @todo deal with running state change. */
2879 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2880 if (FAILED(rc)) return rc;
2881
2882 i_setModified(IsModified_MachineData);
2883 mUserData.backup();
2884 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2885 return S_OK;
2886}
2887
2888HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2889{
2890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2891
2892 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2893 return S_OK;
2894}
2895
2896HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2897{
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 /** @todo deal with running state change. */
2901 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2902 if (FAILED(rc)) return rc;
2903
2904 i_setModified(IsModified_MachineData);
2905 mUserData.backup();
2906 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2907 return S_OK;
2908}
2909
2910HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2911{
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2915
2916 return S_OK;
2917}
2918
2919HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2920{
2921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 /** @todo deal with running state change. */
2924 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2925 if (FAILED(rc)) return rc;
2926
2927 i_setModified(IsModified_MachineData);
2928 mUserData.backup();
2929 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2935{
2936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2943{
2944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 /** @todo deal with running state change. */
2947 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2948 if (FAILED(rc)) return rc;
2949
2950 i_setModified(IsModified_MachineData);
2951 mUserData.backup();
2952 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2953 return S_OK;
2954}
2955
2956HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2957{
2958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2961
2962 return S_OK;
2963}
2964
2965HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2966{
2967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 /* Only allow it to be set to true when PoweredOff or Aborted.
2970 (Clearing it is always permitted.) */
2971 if ( aRTCUseUTC
2972 && mData->mRegistered
2973 && ( !i_isSessionMachine()
2974 || ( mData->mMachineState != MachineState_PoweredOff
2975 && mData->mMachineState != MachineState_Teleported
2976 && mData->mMachineState != MachineState_Aborted
2977 )
2978 )
2979 )
2980 return setError(VBOX_E_INVALID_VM_STATE,
2981 tr("The machine is not powered off (state is %s)"),
2982 Global::stringifyMachineState(mData->mMachineState));
2983
2984 i_setModified(IsModified_MachineData);
2985 mUserData.backup();
2986 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3001{
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 HRESULT rc = i_checkStateDependency(MutableStateDep);
3005 if (FAILED(rc)) return rc;
3006
3007 i_setModified(IsModified_MachineData);
3008 mHWData.backup();
3009 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3010
3011 return S_OK;
3012}
3013
3014HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3015{
3016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 *aIOCacheSize = mHWData->mIOCacheSize;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3024{
3025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 HRESULT rc = i_checkStateDependency(MutableStateDep);
3028 if (FAILED(rc)) return rc;
3029
3030 i_setModified(IsModified_MachineData);
3031 mHWData.backup();
3032 mHWData->mIOCacheSize = aIOCacheSize;
3033
3034 return S_OK;
3035}
3036
3037
3038/**
3039 * @note Locks objects!
3040 */
3041HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3042 LockType_T aLockType)
3043{
3044 /* check the session state */
3045 SessionState_T state;
3046 HRESULT rc = aSession->COMGETTER(State)(&state);
3047 if (FAILED(rc)) return rc;
3048
3049 if (state != SessionState_Unlocked)
3050 return setError(VBOX_E_INVALID_OBJECT_STATE,
3051 tr("The given session is busy"));
3052
3053 // get the client's IInternalSessionControl interface
3054 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3055 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3056 E_INVALIDARG);
3057
3058 // session name (only used in some code paths)
3059 Utf8Str strSessionName;
3060
3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 if (!mData->mRegistered)
3064 return setError(E_UNEXPECTED,
3065 tr("The machine '%s' is not registered"),
3066 mUserData->s.strName.c_str());
3067
3068 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3069
3070 SessionState_T oldState = mData->mSession.mState;
3071 /* Hack: in case the session is closing and there is a progress object
3072 * which allows waiting for the session to be closed, take the opportunity
3073 * and do a limited wait (max. 1 second). This helps a lot when the system
3074 * is busy and thus session closing can take a little while. */
3075 if ( mData->mSession.mState == SessionState_Unlocking
3076 && mData->mSession.mProgress)
3077 {
3078 alock.release();
3079 mData->mSession.mProgress->WaitForCompletion(1000);
3080 alock.acquire();
3081 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3082 }
3083
3084 // try again now
3085 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3086 // (i.e. session machine exists)
3087 && (aLockType == LockType_Shared) // caller wants a shared link to the
3088 // existing session that holds the write lock:
3089 )
3090 {
3091 // OK, share the session... we are now dealing with three processes:
3092 // 1) VBoxSVC (where this code runs);
3093 // 2) process C: the caller's client process (who wants a shared session);
3094 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3095
3096 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3097 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3098 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3099 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3100 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3101
3102 /*
3103 * Release the lock before calling the client process. It's safe here
3104 * since the only thing to do after we get the lock again is to add
3105 * the remote control to the list (which doesn't directly influence
3106 * anything).
3107 */
3108 alock.release();
3109
3110 // get the console of the session holding the write lock (this is a remote call)
3111 ComPtr<IConsole> pConsoleW;
3112 if (mData->mSession.mLockType == LockType_VM)
3113 {
3114 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3115 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3116 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3117 if (FAILED(rc))
3118 // the failure may occur w/o any error info (from RPC), so provide one
3119 return setError(VBOX_E_VM_ERROR,
3120 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3121 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3122 }
3123
3124 // share the session machine and W's console with the caller's session
3125 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3126 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3127 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3128
3129 if (FAILED(rc))
3130 // the failure may occur w/o any error info (from RPC), so provide one
3131 return setError(VBOX_E_VM_ERROR,
3132 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3133 alock.acquire();
3134
3135 // need to revalidate the state after acquiring the lock again
3136 if (mData->mSession.mState != SessionState_Locked)
3137 {
3138 pSessionControl->Uninitialize();
3139 return setError(VBOX_E_INVALID_SESSION_STATE,
3140 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3141 mUserData->s.strName.c_str());
3142 }
3143
3144 // add the caller's session to the list
3145 mData->mSession.mRemoteControls.push_back(pSessionControl);
3146 }
3147 else if ( mData->mSession.mState == SessionState_Locked
3148 || mData->mSession.mState == SessionState_Unlocking
3149 )
3150 {
3151 // sharing not permitted, or machine still unlocking:
3152 return setError(VBOX_E_INVALID_OBJECT_STATE,
3153 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3154 mUserData->s.strName.c_str());
3155 }
3156 else
3157 {
3158 // machine is not locked: then write-lock the machine (create the session machine)
3159
3160 // must not be busy
3161 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3162
3163 // get the caller's session PID
3164 RTPROCESS pid = NIL_RTPROCESS;
3165 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3166 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3167 Assert(pid != NIL_RTPROCESS);
3168
3169 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3170
3171 if (fLaunchingVMProcess)
3172 {
3173 if (mData->mSession.mPID == NIL_RTPROCESS)
3174 {
3175 // two or more clients racing for a lock, the one which set the
3176 // session state to Spawning will win, the others will get an
3177 // error as we can't decide here if waiting a little would help
3178 // (only for shared locks this would avoid an error)
3179 return setError(VBOX_E_INVALID_OBJECT_STATE,
3180 tr("The machine '%s' already has a lock request pending"),
3181 mUserData->s.strName.c_str());
3182 }
3183
3184 // this machine is awaiting for a spawning session to be opened:
3185 // then the calling process must be the one that got started by
3186 // LaunchVMProcess()
3187
3188 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3189 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3190
3191#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3192 /* Hardened windows builds spawns three processes when a VM is
3193 launched, the 3rd one is the one that will end up here. */
3194 RTPROCESS ppid;
3195 int rc = RTProcQueryParent(pid, &ppid);
3196 if (RT_SUCCESS(rc))
3197 rc = RTProcQueryParent(ppid, &ppid);
3198 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3199 || rc == VERR_ACCESS_DENIED)
3200 {
3201 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3202 mData->mSession.mPID = pid;
3203 }
3204#endif
3205
3206 if (mData->mSession.mPID != pid)
3207 return setError(E_ACCESSDENIED,
3208 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3209 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3210 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3211 }
3212
3213 // create the mutable SessionMachine from the current machine
3214 ComObjPtr<SessionMachine> sessionMachine;
3215 sessionMachine.createObject();
3216 rc = sessionMachine->init(this);
3217 AssertComRC(rc);
3218
3219 /* NOTE: doing return from this function after this point but
3220 * before the end is forbidden since it may call SessionMachine::uninit()
3221 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3222 * lock while still holding the Machine lock in alock so that a deadlock
3223 * is possible due to the wrong lock order. */
3224
3225 if (SUCCEEDED(rc))
3226 {
3227 /*
3228 * Set the session state to Spawning to protect against subsequent
3229 * attempts to open a session and to unregister the machine after
3230 * we release the lock.
3231 */
3232 SessionState_T origState = mData->mSession.mState;
3233 mData->mSession.mState = SessionState_Spawning;
3234
3235#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3236 /* Get the client token ID to be passed to the client process */
3237 Utf8Str strTokenId;
3238 sessionMachine->i_getTokenId(strTokenId);
3239 Assert(!strTokenId.isEmpty());
3240#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3241 /* Get the client token to be passed to the client process */
3242 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3243 /* The token is now "owned" by pToken, fix refcount */
3244 if (!pToken.isNull())
3245 pToken->Release();
3246#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3247
3248 /*
3249 * Release the lock before calling the client process -- it will call
3250 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3251 * because the state is Spawning, so that LaunchVMProcess() and
3252 * LockMachine() calls will fail. This method, called before we
3253 * acquire the lock again, will fail because of the wrong PID.
3254 *
3255 * Note that mData->mSession.mRemoteControls accessed outside
3256 * the lock may not be modified when state is Spawning, so it's safe.
3257 */
3258 alock.release();
3259
3260 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3261#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3262 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3263#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3264 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3265 /* Now the token is owned by the client process. */
3266 pToken.setNull();
3267#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3268 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3269
3270 /* The failure may occur w/o any error info (from RPC), so provide one */
3271 if (FAILED(rc))
3272 setError(VBOX_E_VM_ERROR,
3273 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3274
3275 // get session name, either to remember or to compare against
3276 // the already known session name.
3277 {
3278 Bstr bstrSessionName;
3279 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3280 if (SUCCEEDED(rc2))
3281 strSessionName = bstrSessionName;
3282 }
3283
3284 if ( SUCCEEDED(rc)
3285 && fLaunchingVMProcess
3286 )
3287 {
3288 /* complete the remote session initialization */
3289
3290 /* get the console from the direct session */
3291 ComPtr<IConsole> console;
3292 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3293 ComAssertComRC(rc);
3294
3295 if (SUCCEEDED(rc) && !console)
3296 {
3297 ComAssert(!!console);
3298 rc = E_FAIL;
3299 }
3300
3301 /* assign machine & console to the remote session */
3302 if (SUCCEEDED(rc))
3303 {
3304 /*
3305 * after LaunchVMProcess(), the first and the only
3306 * entry in remoteControls is that remote session
3307 */
3308 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3309 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3310 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3311
3312 /* The failure may occur w/o any error info (from RPC), so provide one */
3313 if (FAILED(rc))
3314 setError(VBOX_E_VM_ERROR,
3315 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3316 }
3317
3318 if (FAILED(rc))
3319 pSessionControl->Uninitialize();
3320 }
3321
3322 /* acquire the lock again */
3323 alock.acquire();
3324
3325 /* Restore the session state */
3326 mData->mSession.mState = origState;
3327 }
3328
3329 // finalize spawning anyway (this is why we don't return on errors above)
3330 if (fLaunchingVMProcess)
3331 {
3332 Assert(mData->mSession.mName == strSessionName);
3333 /* Note that the progress object is finalized later */
3334 /** @todo Consider checking mData->mSession.mProgress for cancellation
3335 * around here. */
3336
3337 /* We don't reset mSession.mPID here because it is necessary for
3338 * SessionMachine::uninit() to reap the child process later. */
3339
3340 if (FAILED(rc))
3341 {
3342 /* Close the remote session, remove the remote control from the list
3343 * and reset session state to Closed (@note keep the code in sync
3344 * with the relevant part in checkForSpawnFailure()). */
3345
3346 Assert(mData->mSession.mRemoteControls.size() == 1);
3347 if (mData->mSession.mRemoteControls.size() == 1)
3348 {
3349 ErrorInfoKeeper eik;
3350 mData->mSession.mRemoteControls.front()->Uninitialize();
3351 }
3352
3353 mData->mSession.mRemoteControls.clear();
3354 mData->mSession.mState = SessionState_Unlocked;
3355 }
3356 }
3357 else
3358 {
3359 /* memorize PID of the directly opened session */
3360 if (SUCCEEDED(rc))
3361 mData->mSession.mPID = pid;
3362 }
3363
3364 if (SUCCEEDED(rc))
3365 {
3366 mData->mSession.mLockType = aLockType;
3367 /* memorize the direct session control and cache IUnknown for it */
3368 mData->mSession.mDirectControl = pSessionControl;
3369 mData->mSession.mState = SessionState_Locked;
3370 if (!fLaunchingVMProcess)
3371 mData->mSession.mName = strSessionName;
3372 /* associate the SessionMachine with this Machine */
3373 mData->mSession.mMachine = sessionMachine;
3374
3375 /* request an IUnknown pointer early from the remote party for later
3376 * identity checks (it will be internally cached within mDirectControl
3377 * at least on XPCOM) */
3378 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3379 NOREF(unk);
3380 }
3381
3382 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3383 * would break the lock order */
3384 alock.release();
3385
3386 /* uninitialize the created session machine on failure */
3387 if (FAILED(rc))
3388 sessionMachine->uninit();
3389 }
3390
3391 if (SUCCEEDED(rc))
3392 {
3393 /*
3394 * tell the client watcher thread to update the set of
3395 * machines that have open sessions
3396 */
3397 mParent->i_updateClientWatcher();
3398
3399 if (oldState != SessionState_Locked)
3400 /* fire an event */
3401 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3402 }
3403
3404 return rc;
3405}
3406
3407/**
3408 * @note Locks objects!
3409 */
3410HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3411 const com::Utf8Str &aName,
3412 const com::Utf8Str &aEnvironment,
3413 ComPtr<IProgress> &aProgress)
3414{
3415 Utf8Str strFrontend(aName);
3416 /* "emergencystop" doesn't need the session, so skip the checks/interface
3417 * retrieval. This code doesn't quite fit in here, but introducing a
3418 * special API method would be even more effort, and would require explicit
3419 * support by every API client. It's better to hide the feature a bit. */
3420 if (strFrontend != "emergencystop")
3421 CheckComArgNotNull(aSession);
3422
3423 HRESULT rc = S_OK;
3424 if (strFrontend.isEmpty())
3425 {
3426 Bstr bstrFrontend;
3427 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3428 if (FAILED(rc))
3429 return rc;
3430 strFrontend = bstrFrontend;
3431 if (strFrontend.isEmpty())
3432 {
3433 ComPtr<ISystemProperties> systemProperties;
3434 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3435 if (FAILED(rc))
3436 return rc;
3437 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3438 if (FAILED(rc))
3439 return rc;
3440 strFrontend = bstrFrontend;
3441 }
3442 /* paranoia - emergencystop is not a valid default */
3443 if (strFrontend == "emergencystop")
3444 strFrontend = Utf8Str::Empty;
3445 }
3446 /* default frontend: Qt GUI */
3447 if (strFrontend.isEmpty())
3448 strFrontend = "GUI/Qt";
3449
3450 if (strFrontend != "emergencystop")
3451 {
3452 /* check the session state */
3453 SessionState_T state;
3454 rc = aSession->COMGETTER(State)(&state);
3455 if (FAILED(rc))
3456 return rc;
3457
3458 if (state != SessionState_Unlocked)
3459 return setError(VBOX_E_INVALID_OBJECT_STATE,
3460 tr("The given session is busy"));
3461
3462 /* get the IInternalSessionControl interface */
3463 ComPtr<IInternalSessionControl> control(aSession);
3464 ComAssertMsgRet(!control.isNull(),
3465 ("No IInternalSessionControl interface"),
3466 E_INVALIDARG);
3467
3468 /* get the teleporter enable state for the progress object init. */
3469 BOOL fTeleporterEnabled;
3470 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3471 if (FAILED(rc))
3472 return rc;
3473
3474 /* create a progress object */
3475 ComObjPtr<ProgressProxy> progress;
3476 progress.createObject();
3477 rc = progress->init(mParent,
3478 static_cast<IMachine*>(this),
3479 Bstr(tr("Starting VM")).raw(),
3480 TRUE /* aCancelable */,
3481 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3482 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3483 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3484 2 /* uFirstOperationWeight */,
3485 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3486
3487 if (SUCCEEDED(rc))
3488 {
3489 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3490 if (SUCCEEDED(rc))
3491 {
3492 aProgress = progress;
3493
3494 /* signal the client watcher thread */
3495 mParent->i_updateClientWatcher();
3496
3497 /* fire an event */
3498 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3499 }
3500 }
3501 }
3502 else
3503 {
3504 /* no progress object - either instant success or failure */
3505 aProgress = NULL;
3506
3507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3508
3509 if (mData->mSession.mState != SessionState_Locked)
3510 return setError(VBOX_E_INVALID_OBJECT_STATE,
3511 tr("The machine '%s' is not locked by a session"),
3512 mUserData->s.strName.c_str());
3513
3514 /* must have a VM process associated - do not kill normal API clients
3515 * with an open session */
3516 if (!Global::IsOnline(mData->mMachineState))
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The machine '%s' does not have a VM process"),
3519 mUserData->s.strName.c_str());
3520
3521 /* forcibly terminate the VM process */
3522 if (mData->mSession.mPID != NIL_RTPROCESS)
3523 RTProcTerminate(mData->mSession.mPID);
3524
3525 /* signal the client watcher thread, as most likely the client has
3526 * been terminated */
3527 mParent->i_updateClientWatcher();
3528 }
3529
3530 return rc;
3531}
3532
3533HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3534{
3535 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3536 return setError(E_INVALIDARG,
3537 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3538 aPosition, SchemaDefs::MaxBootPosition);
3539
3540 if (aDevice == DeviceType_USB)
3541 return setError(E_NOTIMPL,
3542 tr("Booting from USB device is currently not supported"));
3543
3544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3545
3546 HRESULT rc = i_checkStateDependency(MutableStateDep);
3547 if (FAILED(rc)) return rc;
3548
3549 i_setModified(IsModified_MachineData);
3550 mHWData.backup();
3551 mHWData->mBootOrder[aPosition - 1] = aDevice;
3552
3553 return S_OK;
3554}
3555
3556HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3557{
3558 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3559 return setError(E_INVALIDARG,
3560 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3561 aPosition, SchemaDefs::MaxBootPosition);
3562
3563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3564
3565 *aDevice = mHWData->mBootOrder[aPosition - 1];
3566
3567 return S_OK;
3568}
3569
3570HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3571 LONG aControllerPort,
3572 LONG aDevice,
3573 DeviceType_T aType,
3574 const ComPtr<IMedium> &aMedium)
3575{
3576 IMedium *aM = aMedium;
3577 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3578 aName.c_str(), aControllerPort, aDevice, aType, aM));
3579
3580 // request the host lock first, since might be calling Host methods for getting host drives;
3581 // next, protect the media tree all the while we're in here, as well as our member variables
3582 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3583 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3584
3585 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3586 if (FAILED(rc)) return rc;
3587
3588 /// @todo NEWMEDIA implicit machine registration
3589 if (!mData->mRegistered)
3590 return setError(VBOX_E_INVALID_OBJECT_STATE,
3591 tr("Cannot attach storage devices to an unregistered machine"));
3592
3593 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3594
3595 /* Check for an existing controller. */
3596 ComObjPtr<StorageController> ctl;
3597 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3598 if (FAILED(rc)) return rc;
3599
3600 StorageControllerType_T ctrlType;
3601 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3602 if (FAILED(rc))
3603 return setError(E_FAIL,
3604 tr("Could not get type of controller '%s'"),
3605 aName.c_str());
3606
3607 bool fSilent = false;
3608 Utf8Str strReconfig;
3609
3610 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3611 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3612 if ( mData->mMachineState == MachineState_Paused
3613 && strReconfig == "1")
3614 fSilent = true;
3615
3616 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3617 bool fHotplug = false;
3618 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3619 fHotplug = true;
3620
3621 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3622 return setError(VBOX_E_INVALID_VM_STATE,
3623 tr("Controller '%s' does not support hotplugging"),
3624 aName.c_str());
3625
3626 // check that the port and device are not out of range
3627 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3628 if (FAILED(rc)) return rc;
3629
3630 /* check if the device slot is already busy */
3631 MediumAttachment *pAttachTemp;
3632 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3633 aName,
3634 aControllerPort,
3635 aDevice)))
3636 {
3637 Medium *pMedium = pAttachTemp->i_getMedium();
3638 if (pMedium)
3639 {
3640 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3641 return setError(VBOX_E_OBJECT_IN_USE,
3642 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3643 pMedium->i_getLocationFull().c_str(),
3644 aControllerPort,
3645 aDevice,
3646 aName.c_str());
3647 }
3648 else
3649 return setError(VBOX_E_OBJECT_IN_USE,
3650 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3651 aControllerPort, aDevice, aName.c_str());
3652 }
3653
3654 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3655 if (aMedium && medium.isNull())
3656 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3657
3658 AutoCaller mediumCaller(medium);
3659 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3660
3661 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3662
3663 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3664 && !medium.isNull()
3665 )
3666 return setError(VBOX_E_OBJECT_IN_USE,
3667 tr("Medium '%s' is already attached to this virtual machine"),
3668 medium->i_getLocationFull().c_str());
3669
3670 if (!medium.isNull())
3671 {
3672 MediumType_T mtype = medium->i_getType();
3673 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3674 // For DVDs it's not written to the config file, so needs no global config
3675 // version bump. For floppies it's a new attribute "type", which is ignored
3676 // by older VirtualBox version, so needs no global config version bump either.
3677 // For hard disks this type is not accepted.
3678 if (mtype == MediumType_MultiAttach)
3679 {
3680 // This type is new with VirtualBox 4.0 and therefore requires settings
3681 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3682 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3683 // two reasons: The medium type is a property of the media registry tree, which
3684 // can reside in the global config file (for pre-4.0 media); we would therefore
3685 // possibly need to bump the global config version. We don't want to do that though
3686 // because that might make downgrading to pre-4.0 impossible.
3687 // As a result, we can only use these two new types if the medium is NOT in the
3688 // global registry:
3689 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3690 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3691 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3692 )
3693 return setError(VBOX_E_INVALID_OBJECT_STATE,
3694 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3695 "to machines that were created with VirtualBox 4.0 or later"),
3696 medium->i_getLocationFull().c_str());
3697 }
3698 }
3699
3700 bool fIndirect = false;
3701 if (!medium.isNull())
3702 fIndirect = medium->i_isReadOnly();
3703 bool associate = true;
3704
3705 do
3706 {
3707 if ( aType == DeviceType_HardDisk
3708 && mMediumAttachments.isBackedUp())
3709 {
3710 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3711
3712 /* check if the medium was attached to the VM before we started
3713 * changing attachments in which case the attachment just needs to
3714 * be restored */
3715 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3716 {
3717 AssertReturn(!fIndirect, E_FAIL);
3718
3719 /* see if it's the same bus/channel/device */
3720 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3721 {
3722 /* the simplest case: restore the whole attachment
3723 * and return, nothing else to do */
3724 mMediumAttachments->push_back(pAttachTemp);
3725
3726 /* Reattach the medium to the VM. */
3727 if (fHotplug || fSilent)
3728 {
3729 mediumLock.release();
3730 treeLock.release();
3731 alock.release();
3732
3733 MediumLockList *pMediumLockList(new MediumLockList());
3734
3735 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3736 medium /* pToLockWrite */,
3737 false /* fMediumLockWriteAll */,
3738 NULL,
3739 *pMediumLockList);
3740 alock.acquire();
3741 if (FAILED(rc))
3742 delete pMediumLockList;
3743 else
3744 {
3745 mData->mSession.mLockedMedia.Unlock();
3746 alock.release();
3747 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3748 mData->mSession.mLockedMedia.Lock();
3749 alock.acquire();
3750 }
3751 alock.release();
3752
3753 if (SUCCEEDED(rc))
3754 {
3755 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3756 /* Remove lock list in case of error. */
3757 if (FAILED(rc))
3758 {
3759 mData->mSession.mLockedMedia.Unlock();
3760 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3761 mData->mSession.mLockedMedia.Lock();
3762 }
3763 }
3764 }
3765
3766 return S_OK;
3767 }
3768
3769 /* bus/channel/device differ; we need a new attachment object,
3770 * but don't try to associate it again */
3771 associate = false;
3772 break;
3773 }
3774 }
3775
3776 /* go further only if the attachment is to be indirect */
3777 if (!fIndirect)
3778 break;
3779
3780 /* perform the so called smart attachment logic for indirect
3781 * attachments. Note that smart attachment is only applicable to base
3782 * hard disks. */
3783
3784 if (medium->i_getParent().isNull())
3785 {
3786 /* first, investigate the backup copy of the current hard disk
3787 * attachments to make it possible to re-attach existing diffs to
3788 * another device slot w/o losing their contents */
3789 if (mMediumAttachments.isBackedUp())
3790 {
3791 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3792
3793 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3794 uint32_t foundLevel = 0;
3795
3796 for (MediumAttachmentList::const_iterator
3797 it = oldAtts.begin();
3798 it != oldAtts.end();
3799 ++it)
3800 {
3801 uint32_t level = 0;
3802 MediumAttachment *pAttach = *it;
3803 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3804 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3805 if (pMedium.isNull())
3806 continue;
3807
3808 if (pMedium->i_getBase(&level) == medium)
3809 {
3810 /* skip the hard disk if its currently attached (we
3811 * cannot attach the same hard disk twice) */
3812 if (i_findAttachment(*mMediumAttachments.data(),
3813 pMedium))
3814 continue;
3815
3816 /* matched device, channel and bus (i.e. attached to the
3817 * same place) will win and immediately stop the search;
3818 * otherwise the attachment that has the youngest
3819 * descendant of medium will be used
3820 */
3821 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3822 {
3823 /* the simplest case: restore the whole attachment
3824 * and return, nothing else to do */
3825 mMediumAttachments->push_back(*it);
3826
3827 /* Reattach the medium to the VM. */
3828 if (fHotplug || fSilent)
3829 {
3830 mediumLock.release();
3831 treeLock.release();
3832 alock.release();
3833
3834 MediumLockList *pMediumLockList(new MediumLockList());
3835
3836 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3837 medium /* pToLockWrite */,
3838 false /* fMediumLockWriteAll */,
3839 NULL,
3840 *pMediumLockList);
3841 alock.acquire();
3842 if (FAILED(rc))
3843 delete pMediumLockList;
3844 else
3845 {
3846 mData->mSession.mLockedMedia.Unlock();
3847 alock.release();
3848 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3849 mData->mSession.mLockedMedia.Lock();
3850 alock.acquire();
3851 }
3852 alock.release();
3853
3854 if (SUCCEEDED(rc))
3855 {
3856 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3857 /* Remove lock list in case of error. */
3858 if (FAILED(rc))
3859 {
3860 mData->mSession.mLockedMedia.Unlock();
3861 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3862 mData->mSession.mLockedMedia.Lock();
3863 }
3864 }
3865 }
3866
3867 return S_OK;
3868 }
3869 else if ( foundIt == oldAtts.end()
3870 || level > foundLevel /* prefer younger */
3871 )
3872 {
3873 foundIt = it;
3874 foundLevel = level;
3875 }
3876 }
3877 }
3878
3879 if (foundIt != oldAtts.end())
3880 {
3881 /* use the previously attached hard disk */
3882 medium = (*foundIt)->i_getMedium();
3883 mediumCaller.attach(medium);
3884 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3885 mediumLock.attach(medium);
3886 /* not implicit, doesn't require association with this VM */
3887 fIndirect = false;
3888 associate = false;
3889 /* go right to the MediumAttachment creation */
3890 break;
3891 }
3892 }
3893
3894 /* must give up the medium lock and medium tree lock as below we
3895 * go over snapshots, which needs a lock with higher lock order. */
3896 mediumLock.release();
3897 treeLock.release();
3898
3899 /* then, search through snapshots for the best diff in the given
3900 * hard disk's chain to base the new diff on */
3901
3902 ComObjPtr<Medium> base;
3903 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3904 while (snap)
3905 {
3906 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3907
3908 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3909
3910 MediumAttachment *pAttachFound = NULL;
3911 uint32_t foundLevel = 0;
3912
3913 for (MediumAttachmentList::const_iterator
3914 it = snapAtts.begin();
3915 it != snapAtts.end();
3916 ++it)
3917 {
3918 MediumAttachment *pAttach = *it;
3919 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3920 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3921 if (pMedium.isNull())
3922 continue;
3923
3924 uint32_t level = 0;
3925 if (pMedium->i_getBase(&level) == medium)
3926 {
3927 /* matched device, channel and bus (i.e. attached to the
3928 * same place) will win and immediately stop the search;
3929 * otherwise the attachment that has the youngest
3930 * descendant of medium will be used
3931 */
3932 if ( pAttach->i_getDevice() == aDevice
3933 && pAttach->i_getPort() == aControllerPort
3934 && pAttach->i_getControllerName() == aName
3935 )
3936 {
3937 pAttachFound = pAttach;
3938 break;
3939 }
3940 else if ( !pAttachFound
3941 || level > foundLevel /* prefer younger */
3942 )
3943 {
3944 pAttachFound = pAttach;
3945 foundLevel = level;
3946 }
3947 }
3948 }
3949
3950 if (pAttachFound)
3951 {
3952 base = pAttachFound->i_getMedium();
3953 break;
3954 }
3955
3956 snap = snap->i_getParent();
3957 }
3958
3959 /* re-lock medium tree and the medium, as we need it below */
3960 treeLock.acquire();
3961 mediumLock.acquire();
3962
3963 /* found a suitable diff, use it as a base */
3964 if (!base.isNull())
3965 {
3966 medium = base;
3967 mediumCaller.attach(medium);
3968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3969 mediumLock.attach(medium);
3970 }
3971 }
3972
3973 Utf8Str strFullSnapshotFolder;
3974 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3975
3976 ComObjPtr<Medium> diff;
3977 diff.createObject();
3978 // store this diff in the same registry as the parent
3979 Guid uuidRegistryParent;
3980 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3981 {
3982 // parent image has no registry: this can happen if we're attaching a new immutable
3983 // image that has not yet been attached (medium then points to the base and we're
3984 // creating the diff image for the immutable, and the parent is not yet registered);
3985 // put the parent in the machine registry then
3986 mediumLock.release();
3987 treeLock.release();
3988 alock.release();
3989 i_addMediumToRegistry(medium);
3990 alock.acquire();
3991 treeLock.acquire();
3992 mediumLock.acquire();
3993 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3994 }
3995 rc = diff->init(mParent,
3996 medium->i_getPreferredDiffFormat(),
3997 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3998 uuidRegistryParent,
3999 DeviceType_HardDisk);
4000 if (FAILED(rc)) return rc;
4001
4002 /* Apply the normal locking logic to the entire chain. */
4003 MediumLockList *pMediumLockList(new MediumLockList());
4004 mediumLock.release();
4005 treeLock.release();
4006 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4007 diff /* pToLockWrite */,
4008 false /* fMediumLockWriteAll */,
4009 medium,
4010 *pMediumLockList);
4011 treeLock.acquire();
4012 mediumLock.acquire();
4013 if (SUCCEEDED(rc))
4014 {
4015 mediumLock.release();
4016 treeLock.release();
4017 rc = pMediumLockList->Lock();
4018 treeLock.acquire();
4019 mediumLock.acquire();
4020 if (FAILED(rc))
4021 setError(rc,
4022 tr("Could not lock medium when creating diff '%s'"),
4023 diff->i_getLocationFull().c_str());
4024 else
4025 {
4026 /* will release the lock before the potentially lengthy
4027 * operation, so protect with the special state */
4028 MachineState_T oldState = mData->mMachineState;
4029 i_setMachineState(MachineState_SettingUp);
4030
4031 mediumLock.release();
4032 treeLock.release();
4033 alock.release();
4034
4035 rc = medium->i_createDiffStorage(diff,
4036 medium->i_getPreferredDiffVariant(),
4037 pMediumLockList,
4038 NULL /* aProgress */,
4039 true /* aWait */,
4040 false /* aNotify */);
4041
4042 alock.acquire();
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045
4046 i_setMachineState(oldState);
4047 }
4048 }
4049
4050 /* Unlock the media and free the associated memory. */
4051 delete pMediumLockList;
4052
4053 if (FAILED(rc)) return rc;
4054
4055 /* use the created diff for the actual attachment */
4056 medium = diff;
4057 mediumCaller.attach(medium);
4058 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4059 mediumLock.attach(medium);
4060 }
4061 while (0);
4062
4063 ComObjPtr<MediumAttachment> attachment;
4064 attachment.createObject();
4065 rc = attachment->init(this,
4066 medium,
4067 aName,
4068 aControllerPort,
4069 aDevice,
4070 aType,
4071 fIndirect,
4072 false /* fPassthrough */,
4073 false /* fTempEject */,
4074 false /* fNonRotational */,
4075 false /* fDiscard */,
4076 fHotplug /* fHotPluggable */,
4077 Utf8Str::Empty);
4078 if (FAILED(rc)) return rc;
4079
4080 if (associate && !medium.isNull())
4081 {
4082 // as the last step, associate the medium to the VM
4083 rc = medium->i_addBackReference(mData->mUuid);
4084 // here we can fail because of Deleting, or being in process of creating a Diff
4085 if (FAILED(rc)) return rc;
4086
4087 mediumLock.release();
4088 treeLock.release();
4089 alock.release();
4090 i_addMediumToRegistry(medium);
4091 alock.acquire();
4092 treeLock.acquire();
4093 mediumLock.acquire();
4094 }
4095
4096 /* success: finally remember the attachment */
4097 i_setModified(IsModified_Storage);
4098 mMediumAttachments.backup();
4099 mMediumAttachments->push_back(attachment);
4100
4101 mediumLock.release();
4102 treeLock.release();
4103 alock.release();
4104
4105 if (fHotplug || fSilent)
4106 {
4107 if (!medium.isNull())
4108 {
4109 MediumLockList *pMediumLockList(new MediumLockList());
4110
4111 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4112 medium /* pToLockWrite */,
4113 false /* fMediumLockWriteAll */,
4114 NULL,
4115 *pMediumLockList);
4116 alock.acquire();
4117 if (FAILED(rc))
4118 delete pMediumLockList;
4119 else
4120 {
4121 mData->mSession.mLockedMedia.Unlock();
4122 alock.release();
4123 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4124 mData->mSession.mLockedMedia.Lock();
4125 alock.acquire();
4126 }
4127 alock.release();
4128 }
4129
4130 if (SUCCEEDED(rc))
4131 {
4132 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4133 /* Remove lock list in case of error. */
4134 if (FAILED(rc))
4135 {
4136 mData->mSession.mLockedMedia.Unlock();
4137 mData->mSession.mLockedMedia.Remove(attachment);
4138 mData->mSession.mLockedMedia.Lock();
4139 }
4140 }
4141 }
4142
4143 /* Save modified registries, but skip this machine as it's the caller's
4144 * job to save its settings like all other settings changes. */
4145 mParent->i_unmarkRegistryModified(i_getId());
4146 mParent->i_saveModifiedRegistries();
4147
4148 if (aM)
4149 mParent->i_onMediumConfigChanged(aM);
4150 return rc;
4151}
4152
4153HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4154 LONG aDevice)
4155{
4156 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4157 aName.c_str(), aControllerPort, aDevice));
4158
4159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4160
4161 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4162 if (FAILED(rc)) return rc;
4163
4164 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4165
4166 /* Check for an existing controller. */
4167 ComObjPtr<StorageController> ctl;
4168 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4169 if (FAILED(rc)) return rc;
4170
4171 StorageControllerType_T ctrlType;
4172 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4173 if (FAILED(rc))
4174 return setError(E_FAIL,
4175 tr("Could not get type of controller '%s'"),
4176 aName.c_str());
4177
4178 bool fSilent = false;
4179 Utf8Str strReconfig;
4180
4181 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4182 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4183 if ( mData->mMachineState == MachineState_Paused
4184 && strReconfig == "1")
4185 fSilent = true;
4186
4187 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4188 bool fHotplug = false;
4189 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4190 fHotplug = true;
4191
4192 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4193 return setError(VBOX_E_INVALID_VM_STATE,
4194 tr("Controller '%s' does not support hotplugging"),
4195 aName.c_str());
4196
4197 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4198 aName,
4199 aControllerPort,
4200 aDevice);
4201 if (!pAttach)
4202 return setError(VBOX_E_OBJECT_NOT_FOUND,
4203 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4204 aDevice, aControllerPort, aName.c_str());
4205
4206 if (fHotplug && !pAttach->i_getHotPluggable())
4207 return setError(VBOX_E_NOT_SUPPORTED,
4208 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4209 aDevice, aControllerPort, aName.c_str());
4210
4211 /*
4212 * The VM has to detach the device before we delete any implicit diffs.
4213 * If this fails we can roll back without loosing data.
4214 */
4215 if (fHotplug || fSilent)
4216 {
4217 alock.release();
4218 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4219 alock.acquire();
4220 }
4221 if (FAILED(rc)) return rc;
4222
4223 /* If we are here everything went well and we can delete the implicit now. */
4224 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4225
4226 alock.release();
4227
4228 /* Save modified registries, but skip this machine as it's the caller's
4229 * job to save its settings like all other settings changes. */
4230 mParent->i_unmarkRegistryModified(i_getId());
4231 mParent->i_saveModifiedRegistries();
4232
4233 return rc;
4234}
4235
4236HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4237 LONG aDevice, BOOL aPassthrough)
4238{
4239 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4240 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4245 if (FAILED(rc)) return rc;
4246
4247 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4248
4249 /* Check for an existing controller. */
4250 ComObjPtr<StorageController> ctl;
4251 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4252 if (FAILED(rc)) return rc;
4253
4254 StorageControllerType_T ctrlType;
4255 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4256 if (FAILED(rc))
4257 return setError(E_FAIL,
4258 tr("Could not get type of controller '%s'"),
4259 aName.c_str());
4260
4261 bool fSilent = false;
4262 Utf8Str strReconfig;
4263
4264 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4265 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4266 if ( mData->mMachineState == MachineState_Paused
4267 && strReconfig == "1")
4268 fSilent = true;
4269
4270 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4271 bool fHotplug = false;
4272 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4273 fHotplug = true;
4274
4275 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4276 return setError(VBOX_E_INVALID_VM_STATE,
4277 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4278 aName.c_str());
4279
4280 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4281 aName,
4282 aControllerPort,
4283 aDevice);
4284 if (!pAttach)
4285 return setError(VBOX_E_OBJECT_NOT_FOUND,
4286 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4287 aDevice, aControllerPort, aName.c_str());
4288
4289
4290 i_setModified(IsModified_Storage);
4291 mMediumAttachments.backup();
4292
4293 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4294
4295 if (pAttach->i_getType() != DeviceType_DVD)
4296 return setError(E_INVALIDARG,
4297 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4298 aDevice, aControllerPort, aName.c_str());
4299 pAttach->i_updatePassthrough(!!aPassthrough);
4300
4301 attLock.release();
4302 alock.release();
4303 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4304
4305 return rc;
4306}
4307
4308HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4309 LONG aDevice, BOOL aTemporaryEject)
4310{
4311
4312 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4313 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4314
4315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4316
4317 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4318 if (FAILED(rc)) return rc;
4319
4320 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4321 aName,
4322 aControllerPort,
4323 aDevice);
4324 if (!pAttach)
4325 return setError(VBOX_E_OBJECT_NOT_FOUND,
4326 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4327 aDevice, aControllerPort, aName.c_str());
4328
4329
4330 i_setModified(IsModified_Storage);
4331 mMediumAttachments.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->i_getType() != DeviceType_DVD)
4336 return setError(E_INVALIDARG,
4337 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4338 aDevice, aControllerPort, aName.c_str());
4339 pAttach->i_updateTempEject(!!aTemporaryEject);
4340
4341 return S_OK;
4342}
4343
4344HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4345 LONG aDevice, BOOL aNonRotational)
4346{
4347
4348 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4349 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4350
4351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4352
4353 HRESULT rc = i_checkStateDependency(MutableStateDep);
4354 if (FAILED(rc)) return rc;
4355
4356 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4357
4358 if (Global::IsOnlineOrTransient(mData->mMachineState))
4359 return setError(VBOX_E_INVALID_VM_STATE,
4360 tr("Invalid machine state: %s"),
4361 Global::stringifyMachineState(mData->mMachineState));
4362
4363 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4364 aName,
4365 aControllerPort,
4366 aDevice);
4367 if (!pAttach)
4368 return setError(VBOX_E_OBJECT_NOT_FOUND,
4369 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4370 aDevice, aControllerPort, aName.c_str());
4371
4372
4373 i_setModified(IsModified_Storage);
4374 mMediumAttachments.backup();
4375
4376 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4377
4378 if (pAttach->i_getType() != DeviceType_HardDisk)
4379 return setError(E_INVALIDARG,
4380 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"),
4381 aDevice, aControllerPort, aName.c_str());
4382 pAttach->i_updateNonRotational(!!aNonRotational);
4383
4384 return S_OK;
4385}
4386
4387HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4388 LONG aDevice, BOOL aDiscard)
4389{
4390
4391 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4392 aName.c_str(), aControllerPort, aDevice, aDiscard));
4393
4394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4395
4396 HRESULT rc = i_checkStateDependency(MutableStateDep);
4397 if (FAILED(rc)) return rc;
4398
4399 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4400
4401 if (Global::IsOnlineOrTransient(mData->mMachineState))
4402 return setError(VBOX_E_INVALID_VM_STATE,
4403 tr("Invalid machine state: %s"),
4404 Global::stringifyMachineState(mData->mMachineState));
4405
4406 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4407 aName,
4408 aControllerPort,
4409 aDevice);
4410 if (!pAttach)
4411 return setError(VBOX_E_OBJECT_NOT_FOUND,
4412 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4413 aDevice, aControllerPort, aName.c_str());
4414
4415
4416 i_setModified(IsModified_Storage);
4417 mMediumAttachments.backup();
4418
4419 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4420
4421 if (pAttach->i_getType() != DeviceType_HardDisk)
4422 return setError(E_INVALIDARG,
4423 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"),
4424 aDevice, aControllerPort, aName.c_str());
4425 pAttach->i_updateDiscard(!!aDiscard);
4426
4427 return S_OK;
4428}
4429
4430HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4431 LONG aDevice, BOOL aHotPluggable)
4432{
4433 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4434 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4435
4436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4437
4438 HRESULT rc = i_checkStateDependency(MutableStateDep);
4439 if (FAILED(rc)) return rc;
4440
4441 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4442
4443 if (Global::IsOnlineOrTransient(mData->mMachineState))
4444 return setError(VBOX_E_INVALID_VM_STATE,
4445 tr("Invalid machine state: %s"),
4446 Global::stringifyMachineState(mData->mMachineState));
4447
4448 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4449 aName,
4450 aControllerPort,
4451 aDevice);
4452 if (!pAttach)
4453 return setError(VBOX_E_OBJECT_NOT_FOUND,
4454 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 /* Check for an existing controller. */
4458 ComObjPtr<StorageController> ctl;
4459 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4460 if (FAILED(rc)) return rc;
4461
4462 StorageControllerType_T ctrlType;
4463 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4464 if (FAILED(rc))
4465 return setError(E_FAIL,
4466 tr("Could not get type of controller '%s'"),
4467 aName.c_str());
4468
4469 if (!i_isControllerHotplugCapable(ctrlType))
4470 return setError(VBOX_E_NOT_SUPPORTED,
4471 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4472 aName.c_str());
4473
4474 i_setModified(IsModified_Storage);
4475 mMediumAttachments.backup();
4476
4477 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4478
4479 if (pAttach->i_getType() == DeviceType_Floppy)
4480 return setError(E_INVALIDARG,
4481 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"),
4482 aDevice, aControllerPort, aName.c_str());
4483 pAttach->i_updateHotPluggable(!!aHotPluggable);
4484
4485 return S_OK;
4486}
4487
4488HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4489 LONG aDevice)
4490{
4491 int rc = S_OK;
4492 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4493 aName.c_str(), aControllerPort, aDevice));
4494
4495 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4496
4497 return rc;
4498}
4499
4500HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4501 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4502{
4503 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4504 aName.c_str(), aControllerPort, aDevice));
4505
4506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4507
4508 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4509 if (FAILED(rc)) return rc;
4510
4511 if (Global::IsOnlineOrTransient(mData->mMachineState))
4512 return setError(VBOX_E_INVALID_VM_STATE,
4513 tr("Invalid machine state: %s"),
4514 Global::stringifyMachineState(mData->mMachineState));
4515
4516 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4517 aName,
4518 aControllerPort,
4519 aDevice);
4520 if (!pAttach)
4521 return setError(VBOX_E_OBJECT_NOT_FOUND,
4522 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4523 aDevice, aControllerPort, aName.c_str());
4524
4525
4526 i_setModified(IsModified_Storage);
4527 mMediumAttachments.backup();
4528
4529 IBandwidthGroup *iB = aBandwidthGroup;
4530 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4531 if (aBandwidthGroup && group.isNull())
4532 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4533
4534 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4535
4536 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4537 if (strBandwidthGroupOld.isNotEmpty())
4538 {
4539 /* Get the bandwidth group object and release it - this must not fail. */
4540 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4541 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4542 Assert(SUCCEEDED(rc));
4543
4544 pBandwidthGroupOld->i_release();
4545 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4546 }
4547
4548 if (!group.isNull())
4549 {
4550 group->i_reference();
4551 pAttach->i_updateBandwidthGroup(group->i_getName());
4552 }
4553
4554 return S_OK;
4555}
4556
4557HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4558 LONG aControllerPort,
4559 LONG aDevice,
4560 DeviceType_T aType)
4561{
4562 HRESULT rc = S_OK;
4563
4564 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4565 aName.c_str(), aControllerPort, aDevice, aType));
4566
4567 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4568
4569 return rc;
4570}
4571
4572
4573HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4574 LONG aControllerPort,
4575 LONG aDevice,
4576 BOOL aForce)
4577{
4578 int rc = S_OK;
4579 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4580 aName.c_str(), aControllerPort, aForce));
4581
4582 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4583
4584 return rc;
4585}
4586
4587HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4588 LONG aControllerPort,
4589 LONG aDevice,
4590 const ComPtr<IMedium> &aMedium,
4591 BOOL aForce)
4592{
4593 int rc = S_OK;
4594 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4595 aName.c_str(), aControllerPort, aDevice, aForce));
4596
4597 // request the host lock first, since might be calling Host methods for getting host drives;
4598 // next, protect the media tree all the while we're in here, as well as our member variables
4599 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4600 this->lockHandle(),
4601 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4602
4603 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4604 aName,
4605 aControllerPort,
4606 aDevice);
4607 if (pAttach.isNull())
4608 return setError(VBOX_E_OBJECT_NOT_FOUND,
4609 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4610 aDevice, aControllerPort, aName.c_str());
4611
4612 /* Remember previously mounted medium. The medium before taking the
4613 * backup is not necessarily the same thing. */
4614 ComObjPtr<Medium> oldmedium;
4615 oldmedium = pAttach->i_getMedium();
4616
4617 IMedium *iM = aMedium;
4618 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4619 if (aMedium && pMedium.isNull())
4620 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4621
4622 AutoCaller mediumCaller(pMedium);
4623 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4624
4625 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4626 if (pMedium)
4627 {
4628 DeviceType_T mediumType = pAttach->i_getType();
4629 switch (mediumType)
4630 {
4631 case DeviceType_DVD:
4632 case DeviceType_Floppy:
4633 break;
4634
4635 default:
4636 return setError(VBOX_E_INVALID_OBJECT_STATE,
4637 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4638 aControllerPort,
4639 aDevice,
4640 aName.c_str());
4641 }
4642 }
4643
4644 i_setModified(IsModified_Storage);
4645 mMediumAttachments.backup();
4646
4647 {
4648 // The backup operation makes the pAttach reference point to the
4649 // old settings. Re-get the correct reference.
4650 pAttach = i_findAttachment(*mMediumAttachments.data(),
4651 aName,
4652 aControllerPort,
4653 aDevice);
4654 if (!oldmedium.isNull())
4655 oldmedium->i_removeBackReference(mData->mUuid);
4656 if (!pMedium.isNull())
4657 {
4658 pMedium->i_addBackReference(mData->mUuid);
4659
4660 mediumLock.release();
4661 multiLock.release();
4662 i_addMediumToRegistry(pMedium);
4663 multiLock.acquire();
4664 mediumLock.acquire();
4665 }
4666
4667 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4668 pAttach->i_updateMedium(pMedium);
4669 }
4670
4671 i_setModified(IsModified_Storage);
4672
4673 mediumLock.release();
4674 multiLock.release();
4675 rc = i_onMediumChange(pAttach, aForce);
4676 multiLock.acquire();
4677 mediumLock.acquire();
4678
4679 /* On error roll back this change only. */
4680 if (FAILED(rc))
4681 {
4682 if (!pMedium.isNull())
4683 pMedium->i_removeBackReference(mData->mUuid);
4684 pAttach = i_findAttachment(*mMediumAttachments.data(),
4685 aName,
4686 aControllerPort,
4687 aDevice);
4688 /* If the attachment is gone in the meantime, bail out. */
4689 if (pAttach.isNull())
4690 return rc;
4691 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4692 if (!oldmedium.isNull())
4693 oldmedium->i_addBackReference(mData->mUuid);
4694 pAttach->i_updateMedium(oldmedium);
4695 }
4696
4697 mediumLock.release();
4698 multiLock.release();
4699
4700 /* Save modified registries, but skip this machine as it's the caller's
4701 * job to save its settings like all other settings changes. */
4702 mParent->i_unmarkRegistryModified(i_getId());
4703 mParent->i_saveModifiedRegistries();
4704
4705 return rc;
4706}
4707HRESULT Machine::getMedium(const com::Utf8Str &aName,
4708 LONG aControllerPort,
4709 LONG aDevice,
4710 ComPtr<IMedium> &aMedium)
4711{
4712 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4713 aName.c_str(), aControllerPort, aDevice));
4714
4715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4716
4717 aMedium = NULL;
4718
4719 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4720 aName,
4721 aControllerPort,
4722 aDevice);
4723 if (pAttach.isNull())
4724 return setError(VBOX_E_OBJECT_NOT_FOUND,
4725 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4726 aDevice, aControllerPort, aName.c_str());
4727
4728 aMedium = pAttach->i_getMedium();
4729
4730 return S_OK;
4731}
4732
4733HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4734{
4735
4736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4737
4738 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4739
4740 return S_OK;
4741}
4742
4743HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4744{
4745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4746
4747 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4748
4749 return S_OK;
4750}
4751
4752HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4753{
4754 /* Do not assert if slot is out of range, just return the advertised
4755 status. testdriver/vbox.py triggers this in logVmInfo. */
4756 if (aSlot >= mNetworkAdapters.size())
4757 return setError(E_INVALIDARG,
4758 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4759 aSlot, mNetworkAdapters.size());
4760
4761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4762
4763 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4764
4765 return S_OK;
4766}
4767
4768HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4769{
4770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4773 size_t i = 0;
4774 for (settings::StringsMap::const_iterator
4775 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4776 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4777 ++it, ++i)
4778 aKeys[i] = it->first;
4779
4780 return S_OK;
4781}
4782
4783 /**
4784 * @note Locks this object for reading.
4785 */
4786HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4787 com::Utf8Str &aValue)
4788{
4789 /* start with nothing found */
4790 aValue = "";
4791
4792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4793
4794 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4795 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4796 // found:
4797 aValue = it->second; // source is a Utf8Str
4798
4799 /* return the result to caller (may be empty) */
4800 return S_OK;
4801}
4802
4803 /**
4804 * @note Locks mParent for writing + this object for writing.
4805 */
4806HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4807{
4808 /* Because non-ASCII characters in aKey have caused problems in the settings
4809 * they are rejected unless the key should be deleted. */
4810 if (!aValue.isEmpty())
4811 {
4812 for (size_t i = 0; i < aKey.length(); ++i)
4813 {
4814 char ch = aKey[i];
4815 if (!RTLocCIsPrint(ch))
4816 return E_INVALIDARG;
4817 }
4818 }
4819
4820 Utf8Str strOldValue; // empty
4821
4822 // locking note: we only hold the read lock briefly to look up the old value,
4823 // then release it and call the onExtraCanChange callbacks. There is a small
4824 // chance of a race insofar as the callback might be called twice if two callers
4825 // change the same key at the same time, but that's a much better solution
4826 // than the deadlock we had here before. The actual changing of the extradata
4827 // is then performed under the write lock and race-free.
4828
4829 // look up the old value first; if nothing has changed then we need not do anything
4830 {
4831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4832
4833 // For snapshots don't even think about allowing changes, extradata
4834 // is global for a machine, so there is nothing snapshot specific.
4835 if (i_isSnapshotMachine())
4836 return setError(VBOX_E_INVALID_VM_STATE,
4837 tr("Cannot set extradata for a snapshot"));
4838
4839 // check if the right IMachine instance is used
4840 if (mData->mRegistered && !i_isSessionMachine())
4841 return setError(VBOX_E_INVALID_VM_STATE,
4842 tr("Cannot set extradata for an immutable machine"));
4843
4844 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4845 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4846 strOldValue = it->second;
4847 }
4848
4849 bool fChanged;
4850 if ((fChanged = (strOldValue != aValue)))
4851 {
4852 // ask for permission from all listeners outside the locks;
4853 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4854 // lock to copy the list of callbacks to invoke
4855 Bstr error;
4856 Bstr bstrValue(aValue);
4857
4858 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4859 {
4860 const char *sep = error.isEmpty() ? "" : ": ";
4861 CBSTR err = error.raw();
4862 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4863 return setError(E_ACCESSDENIED,
4864 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4865 aKey.c_str(),
4866 aValue.c_str(),
4867 sep,
4868 err);
4869 }
4870
4871 // data is changing and change not vetoed: then write it out under the lock
4872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4873
4874 if (aValue.isEmpty())
4875 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4876 else
4877 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4878 // creates a new key if needed
4879
4880 bool fNeedsGlobalSaveSettings = false;
4881 // This saving of settings is tricky: there is no "old state" for the
4882 // extradata items at all (unlike all other settings), so the old/new
4883 // settings comparison would give a wrong result!
4884 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4885
4886 if (fNeedsGlobalSaveSettings)
4887 {
4888 // save the global settings; for that we should hold only the VirtualBox lock
4889 alock.release();
4890 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4891 mParent->i_saveSettings();
4892 }
4893 }
4894
4895 // fire notification outside the lock
4896 if (fChanged)
4897 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4898
4899 return S_OK;
4900}
4901
4902HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4903{
4904 aProgress = NULL;
4905 NOREF(aSettingsFilePath);
4906 ReturnComNotImplemented();
4907}
4908
4909HRESULT Machine::saveSettings()
4910{
4911 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4912
4913 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4914 if (FAILED(rc)) return rc;
4915
4916 /* the settings file path may never be null */
4917 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4918
4919 /* save all VM data excluding snapshots */
4920 bool fNeedsGlobalSaveSettings = false;
4921 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4922 mlock.release();
4923
4924 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4925 {
4926 // save the global settings; for that we should hold only the VirtualBox lock
4927 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4928 rc = mParent->i_saveSettings();
4929 }
4930
4931 return rc;
4932}
4933
4934
4935HRESULT Machine::discardSettings()
4936{
4937 /*
4938 * We need to take the machine list lock here as well as the machine one
4939 * or we'll get into trouble should any media stuff require rolling back.
4940 *
4941 * Details:
4942 *
4943 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4944 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4945 * 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]
4946 * 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
4947 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4948 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4949 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4950 * 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
4951 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4952 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4953 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4954 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4955 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4956 * 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]
4957 * 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] (*)
4958 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4959 * 0:005> k
4960 * # Child-SP RetAddr Call Site
4961 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4962 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4963 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4964 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4965 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4966 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4967 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4968 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4969 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4970 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4971 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4972 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4973 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4974 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4975 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4976 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4977 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4978 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4979 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4980 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4981 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4982 *
4983 */
4984 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4986
4987 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4988 if (FAILED(rc)) return rc;
4989
4990 /*
4991 * during this rollback, the session will be notified if data has
4992 * been actually changed
4993 */
4994 i_rollback(true /* aNotify */);
4995
4996 return S_OK;
4997}
4998
4999/** @note Locks objects! */
5000HRESULT Machine::unregister(AutoCaller &autoCaller,
5001 CleanupMode_T aCleanupMode,
5002 std::vector<ComPtr<IMedium> > &aMedia)
5003{
5004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5005
5006 Guid id(i_getId());
5007
5008 if (mData->mSession.mState != SessionState_Unlocked)
5009 return setError(VBOX_E_INVALID_OBJECT_STATE,
5010 tr("Cannot unregister the machine '%s' while it is locked"),
5011 mUserData->s.strName.c_str());
5012
5013 // wait for state dependents to drop to zero
5014 i_ensureNoStateDependencies();
5015
5016 if (!mData->mAccessible)
5017 {
5018 // inaccessible maschines can only be unregistered; uninitialize ourselves
5019 // here because currently there may be no unregistered that are inaccessible
5020 // (this state combination is not supported). Note releasing the caller and
5021 // leaving the lock before calling uninit()
5022 alock.release();
5023 autoCaller.release();
5024
5025 uninit();
5026
5027 mParent->i_unregisterMachine(this, id);
5028 // calls VirtualBox::i_saveSettings()
5029
5030 return S_OK;
5031 }
5032
5033 HRESULT rc = S_OK;
5034
5035 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5036 // discard saved state
5037 if (mData->mMachineState == MachineState_Saved)
5038 {
5039 // add the saved state file to the list of files the caller should delete
5040 Assert(!mSSData->strStateFilePath.isEmpty());
5041 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5042
5043 mSSData->strStateFilePath.setNull();
5044
5045 // unconditionally set the machine state to powered off, we now
5046 // know no session has locked the machine
5047 mData->mMachineState = MachineState_PoweredOff;
5048 }
5049
5050 size_t cSnapshots = 0;
5051 if (mData->mFirstSnapshot)
5052 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5053 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5054 // fail now before we start detaching media
5055 return setError(VBOX_E_INVALID_OBJECT_STATE,
5056 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5057 mUserData->s.strName.c_str(), cSnapshots);
5058
5059 // This list collects the medium objects from all medium attachments
5060 // which we will detach from the machine and its snapshots, in a specific
5061 // order which allows for closing all media without getting "media in use"
5062 // errors, simply by going through the list from the front to the back:
5063 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5064 // and must be closed before the parent media from the snapshots, or closing the parents
5065 // will fail because they still have children);
5066 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5067 // the root ("first") snapshot of the machine.
5068 MediaList llMedia;
5069
5070 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5071 && mMediumAttachments->size()
5072 )
5073 {
5074 // we have media attachments: detach them all and add the Medium objects to our list
5075 if (aCleanupMode != CleanupMode_UnregisterOnly)
5076 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5077 else
5078 return setError(VBOX_E_INVALID_OBJECT_STATE,
5079 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5080 mUserData->s.strName.c_str(), mMediumAttachments->size());
5081 }
5082
5083 if (cSnapshots)
5084 {
5085 // add the media from the medium attachments of the snapshots to llMedia
5086 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5087 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5088 // into the children first
5089
5090 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5091 MachineState_T oldState = mData->mMachineState;
5092 mData->mMachineState = MachineState_DeletingSnapshot;
5093
5094 // make a copy of the first snapshot so the refcount does not drop to 0
5095 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5096 // because of the AutoCaller voodoo)
5097 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5098
5099 // GO!
5100 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5101
5102 mData->mMachineState = oldState;
5103 }
5104
5105 if (FAILED(rc))
5106 {
5107 i_rollbackMedia();
5108 return rc;
5109 }
5110
5111 // commit all the media changes made above
5112 i_commitMedia();
5113
5114 mData->mRegistered = false;
5115
5116 // machine lock no longer needed
5117 alock.release();
5118
5119 // return media to caller
5120 aMedia.resize(llMedia.size());
5121 size_t i = 0;
5122 for (MediaList::const_iterator
5123 it = llMedia.begin();
5124 it != llMedia.end();
5125 ++it, ++i)
5126 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5127
5128 mParent->i_unregisterMachine(this, id);
5129 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5130
5131 return S_OK;
5132}
5133
5134/**
5135 * Task record for deleting a machine config.
5136 */
5137class Machine::DeleteConfigTask
5138 : public Machine::Task
5139{
5140public:
5141 DeleteConfigTask(Machine *m,
5142 Progress *p,
5143 const Utf8Str &t,
5144 const RTCList<ComPtr<IMedium> > &llMediums,
5145 const StringsList &llFilesToDelete)
5146 : Task(m, p, t),
5147 m_llMediums(llMediums),
5148 m_llFilesToDelete(llFilesToDelete)
5149 {}
5150
5151private:
5152 void handler()
5153 {
5154 try
5155 {
5156 m_pMachine->i_deleteConfigHandler(*this);
5157 }
5158 catch (...)
5159 {
5160 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5161 }
5162 }
5163
5164 RTCList<ComPtr<IMedium> > m_llMediums;
5165 StringsList m_llFilesToDelete;
5166
5167 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5168};
5169
5170/**
5171 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5172 * SessionMachine::taskHandler().
5173 *
5174 * @note Locks this object for writing.
5175 *
5176 * @param task
5177 * @return
5178 */
5179void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5180{
5181 LogFlowThisFuncEnter();
5182
5183 AutoCaller autoCaller(this);
5184 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5185 if (FAILED(autoCaller.rc()))
5186 {
5187 /* we might have been uninitialized because the session was accidentally
5188 * closed by the client, so don't assert */
5189 HRESULT rc = setError(E_FAIL,
5190 tr("The session has been accidentally closed"));
5191 task.m_pProgress->i_notifyComplete(rc);
5192 LogFlowThisFuncLeave();
5193 return;
5194 }
5195
5196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5197
5198 HRESULT rc = S_OK;
5199
5200 try
5201 {
5202 ULONG uLogHistoryCount = 3;
5203 ComPtr<ISystemProperties> systemProperties;
5204 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5205 if (FAILED(rc)) throw rc;
5206
5207 if (!systemProperties.isNull())
5208 {
5209 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5210 if (FAILED(rc)) throw rc;
5211 }
5212
5213 MachineState_T oldState = mData->mMachineState;
5214 i_setMachineState(MachineState_SettingUp);
5215 alock.release();
5216 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5217 {
5218 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5219 {
5220 AutoCaller mac(pMedium);
5221 if (FAILED(mac.rc())) throw mac.rc();
5222 Utf8Str strLocation = pMedium->i_getLocationFull();
5223 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5224 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5225 if (FAILED(rc)) throw rc;
5226 }
5227 if (pMedium->i_isMediumFormatFile())
5228 {
5229 ComPtr<IProgress> pProgress2;
5230 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5231 if (FAILED(rc)) throw rc;
5232 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5233 if (FAILED(rc)) throw rc;
5234 }
5235
5236 /* Close the medium, deliberately without checking the return
5237 * code, and without leaving any trace in the error info, as
5238 * a failure here is a very minor issue, which shouldn't happen
5239 * as above we even managed to delete the medium. */
5240 {
5241 ErrorInfoKeeper eik;
5242 pMedium->Close();
5243 }
5244 }
5245 i_setMachineState(oldState);
5246 alock.acquire();
5247
5248 // delete the files pushed on the task list by Machine::Delete()
5249 // (this includes saved states of the machine and snapshots and
5250 // medium storage files from the IMedium list passed in, and the
5251 // machine XML file)
5252 for (StringsList::const_iterator
5253 it = task.m_llFilesToDelete.begin();
5254 it != task.m_llFilesToDelete.end();
5255 ++it)
5256 {
5257 const Utf8Str &strFile = *it;
5258 LogFunc(("Deleting file %s\n", strFile.c_str()));
5259 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5260 if (FAILED(rc)) throw rc;
5261
5262 int vrc = RTFileDelete(strFile.c_str());
5263 if (RT_FAILURE(vrc))
5264 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5265 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5266 }
5267
5268 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5269 if (FAILED(rc)) throw rc;
5270
5271 /* delete the settings only when the file actually exists */
5272 if (mData->pMachineConfigFile->fileExists())
5273 {
5274 /* Delete any backup or uncommitted XML files. Ignore failures.
5275 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5276 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5277 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5278 RTFileDelete(otherXml.c_str());
5279 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5280 RTFileDelete(otherXml.c_str());
5281
5282 /* delete the Logs folder, nothing important should be left
5283 * there (we don't check for errors because the user might have
5284 * some private files there that we don't want to delete) */
5285 Utf8Str logFolder;
5286 getLogFolder(logFolder);
5287 Assert(logFolder.length());
5288 if (RTDirExists(logFolder.c_str()))
5289 {
5290 /* Delete all VBox.log[.N] files from the Logs folder
5291 * (this must be in sync with the rotation logic in
5292 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5293 * files that may have been created by the GUI. */
5294 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5295 logFolder.c_str(), RTPATH_DELIMITER);
5296 RTFileDelete(log.c_str());
5297 log = Utf8StrFmt("%s%cVBox.png",
5298 logFolder.c_str(), RTPATH_DELIMITER);
5299 RTFileDelete(log.c_str());
5300 for (int i = uLogHistoryCount; i > 0; i--)
5301 {
5302 log = Utf8StrFmt("%s%cVBox.log.%d",
5303 logFolder.c_str(), RTPATH_DELIMITER, i);
5304 RTFileDelete(log.c_str());
5305 log = Utf8StrFmt("%s%cVBox.png.%d",
5306 logFolder.c_str(), RTPATH_DELIMITER, i);
5307 RTFileDelete(log.c_str());
5308 }
5309#if defined(RT_OS_WINDOWS)
5310 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5311 RTFileDelete(log.c_str());
5312 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5313 RTFileDelete(log.c_str());
5314#endif
5315
5316 RTDirRemove(logFolder.c_str());
5317 }
5318
5319 /* delete the Snapshots folder, nothing important should be left
5320 * there (we don't check for errors because the user might have
5321 * some private files there that we don't want to delete) */
5322 Utf8Str strFullSnapshotFolder;
5323 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5324 Assert(!strFullSnapshotFolder.isEmpty());
5325 if (RTDirExists(strFullSnapshotFolder.c_str()))
5326 RTDirRemove(strFullSnapshotFolder.c_str());
5327
5328 // delete the directory that contains the settings file, but only
5329 // if it matches the VM name
5330 Utf8Str settingsDir;
5331 if (i_isInOwnDir(&settingsDir))
5332 RTDirRemove(settingsDir.c_str());
5333 }
5334
5335 alock.release();
5336
5337 mParent->i_saveModifiedRegistries();
5338 }
5339 catch (HRESULT aRC) { rc = aRC; }
5340
5341 task.m_pProgress->i_notifyComplete(rc);
5342
5343 LogFlowThisFuncLeave();
5344}
5345
5346HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5347{
5348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5349
5350 HRESULT rc = i_checkStateDependency(MutableStateDep);
5351 if (FAILED(rc)) return rc;
5352
5353 if (mData->mRegistered)
5354 return setError(VBOX_E_INVALID_VM_STATE,
5355 tr("Cannot delete settings of a registered machine"));
5356
5357 // collect files to delete
5358 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5359 if (mData->pMachineConfigFile->fileExists())
5360 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5361
5362 RTCList<ComPtr<IMedium> > llMediums;
5363 for (size_t i = 0; i < aMedia.size(); ++i)
5364 {
5365 IMedium *pIMedium(aMedia[i]);
5366 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5367 if (pMedium.isNull())
5368 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5369 SafeArray<BSTR> ids;
5370 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5371 if (FAILED(rc)) return rc;
5372 /* At this point the medium should not have any back references
5373 * anymore. If it has it is attached to another VM and *must* not
5374 * deleted. */
5375 if (ids.size() < 1)
5376 llMediums.append(pMedium);
5377 }
5378
5379 ComObjPtr<Progress> pProgress;
5380 pProgress.createObject();
5381 rc = pProgress->init(i_getVirtualBox(),
5382 static_cast<IMachine*>(this) /* aInitiator */,
5383 tr("Deleting files"),
5384 true /* fCancellable */,
5385 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5386 tr("Collecting file inventory"));
5387 if (FAILED(rc))
5388 return rc;
5389
5390 /* create and start the task on a separate thread (note that it will not
5391 * start working until we release alock) */
5392 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5393 rc = pTask->createThread();
5394 if (FAILED(rc))
5395 return rc;
5396
5397 pProgress.queryInterfaceTo(aProgress.asOutParam());
5398
5399 LogFlowFuncLeave();
5400
5401 return S_OK;
5402}
5403
5404HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5405{
5406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5407
5408 ComObjPtr<Snapshot> pSnapshot;
5409 HRESULT rc;
5410
5411 if (aNameOrId.isEmpty())
5412 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5413 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5414 else
5415 {
5416 Guid uuid(aNameOrId);
5417 if (uuid.isValid())
5418 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5419 else
5420 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5421 }
5422 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5423
5424 return rc;
5425}
5426
5427HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5428 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5429{
5430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5433 if (FAILED(rc)) return rc;
5434
5435 ComObjPtr<SharedFolder> sharedFolder;
5436 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5437 if (SUCCEEDED(rc))
5438 return setError(VBOX_E_OBJECT_IN_USE,
5439 tr("Shared folder named '%s' already exists"),
5440 aName.c_str());
5441
5442 sharedFolder.createObject();
5443 rc = sharedFolder->init(i_getMachine(),
5444 aName,
5445 aHostPath,
5446 !!aWritable,
5447 !!aAutomount,
5448 aAutoMountPoint,
5449 true /* fFailOnError */);
5450 if (FAILED(rc)) return rc;
5451
5452 i_setModified(IsModified_SharedFolders);
5453 mHWData.backup();
5454 mHWData->mSharedFolders.push_back(sharedFolder);
5455
5456 /* inform the direct session if any */
5457 alock.release();
5458 i_onSharedFolderChange();
5459
5460 return S_OK;
5461}
5462
5463HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5464{
5465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5466
5467 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5468 if (FAILED(rc)) return rc;
5469
5470 ComObjPtr<SharedFolder> sharedFolder;
5471 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5472 if (FAILED(rc)) return rc;
5473
5474 i_setModified(IsModified_SharedFolders);
5475 mHWData.backup();
5476 mHWData->mSharedFolders.remove(sharedFolder);
5477
5478 /* inform the direct session if any */
5479 alock.release();
5480 i_onSharedFolderChange();
5481
5482 return S_OK;
5483}
5484
5485HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5486{
5487 /* start with No */
5488 *aCanShow = FALSE;
5489
5490 ComPtr<IInternalSessionControl> directControl;
5491 {
5492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5493
5494 if (mData->mSession.mState != SessionState_Locked)
5495 return setError(VBOX_E_INVALID_VM_STATE,
5496 tr("Machine is not locked for session (session state: %s)"),
5497 Global::stringifySessionState(mData->mSession.mState));
5498
5499 if (mData->mSession.mLockType == LockType_VM)
5500 directControl = mData->mSession.mDirectControl;
5501 }
5502
5503 /* ignore calls made after #OnSessionEnd() is called */
5504 if (!directControl)
5505 return S_OK;
5506
5507 LONG64 dummy;
5508 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5509}
5510
5511HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5512{
5513 ComPtr<IInternalSessionControl> directControl;
5514 {
5515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5516
5517 if (mData->mSession.mState != SessionState_Locked)
5518 return setError(E_FAIL,
5519 tr("Machine is not locked for session (session state: %s)"),
5520 Global::stringifySessionState(mData->mSession.mState));
5521
5522 if (mData->mSession.mLockType == LockType_VM)
5523 directControl = mData->mSession.mDirectControl;
5524 }
5525
5526 /* ignore calls made after #OnSessionEnd() is called */
5527 if (!directControl)
5528 return S_OK;
5529
5530 BOOL dummy;
5531 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5532}
5533
5534#ifdef VBOX_WITH_GUEST_PROPS
5535/**
5536 * Look up a guest property in VBoxSVC's internal structures.
5537 */
5538HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5539 com::Utf8Str &aValue,
5540 LONG64 *aTimestamp,
5541 com::Utf8Str &aFlags) const
5542{
5543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5544
5545 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5546 if (it != mHWData->mGuestProperties.end())
5547 {
5548 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5549 aValue = it->second.strValue;
5550 *aTimestamp = it->second.mTimestamp;
5551 GuestPropWriteFlags(it->second.mFlags, szFlags);
5552 aFlags = Utf8Str(szFlags);
5553 }
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Query the VM that a guest property belongs to for the property.
5560 * @returns E_ACCESSDENIED if the VM process is not available or not
5561 * currently handling queries and the lookup should then be done in
5562 * VBoxSVC.
5563 */
5564HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5565 com::Utf8Str &aValue,
5566 LONG64 *aTimestamp,
5567 com::Utf8Str &aFlags) const
5568{
5569 HRESULT rc = S_OK;
5570 BSTR bValue = NULL;
5571 BSTR bFlags = NULL;
5572
5573 ComPtr<IInternalSessionControl> directControl;
5574 {
5575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5576 if (mData->mSession.mLockType == LockType_VM)
5577 directControl = mData->mSession.mDirectControl;
5578 }
5579
5580 /* ignore calls made after #OnSessionEnd() is called */
5581 if (!directControl)
5582 rc = E_ACCESSDENIED;
5583 else
5584 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5585 0 /* accessMode */,
5586 &bValue, aTimestamp, &bFlags);
5587
5588 aValue = bValue;
5589 aFlags = bFlags;
5590
5591 return rc;
5592}
5593#endif // VBOX_WITH_GUEST_PROPS
5594
5595HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags)
5599{
5600#ifndef VBOX_WITH_GUEST_PROPS
5601 ReturnComNotImplemented();
5602#else // VBOX_WITH_GUEST_PROPS
5603
5604 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5605
5606 if (rc == E_ACCESSDENIED)
5607 /* The VM is not running or the service is not (yet) accessible */
5608 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5609 return rc;
5610#endif // VBOX_WITH_GUEST_PROPS
5611}
5612
5613HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5614{
5615 LONG64 dummyTimestamp;
5616 com::Utf8Str dummyFlags;
5617 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5618 return rc;
5619
5620}
5621HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5622{
5623 com::Utf8Str dummyFlags;
5624 com::Utf8Str dummyValue;
5625 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5626 return rc;
5627}
5628
5629#ifdef VBOX_WITH_GUEST_PROPS
5630/**
5631 * Set a guest property in VBoxSVC's internal structures.
5632 */
5633HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5634 const com::Utf8Str &aFlags, bool fDelete)
5635{
5636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5637 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5638 if (FAILED(rc)) return rc;
5639
5640 try
5641 {
5642 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5643 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5644 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5645
5646 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5647 if (it == mHWData->mGuestProperties.end())
5648 {
5649 if (!fDelete)
5650 {
5651 i_setModified(IsModified_MachineData);
5652 mHWData.backupEx();
5653
5654 RTTIMESPEC time;
5655 HWData::GuestProperty prop;
5656 prop.strValue = Bstr(aValue).raw();
5657 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5658 prop.mFlags = fFlags;
5659 mHWData->mGuestProperties[aName] = prop;
5660 }
5661 }
5662 else
5663 {
5664 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5665 {
5666 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5667 }
5668 else
5669 {
5670 i_setModified(IsModified_MachineData);
5671 mHWData.backupEx();
5672
5673 /* The backupEx() operation invalidates our iterator,
5674 * so get a new one. */
5675 it = mHWData->mGuestProperties.find(aName);
5676 Assert(it != mHWData->mGuestProperties.end());
5677
5678 if (!fDelete)
5679 {
5680 RTTIMESPEC time;
5681 it->second.strValue = aValue;
5682 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5683 it->second.mFlags = fFlags;
5684 }
5685 else
5686 mHWData->mGuestProperties.erase(it);
5687 }
5688 }
5689
5690 if (SUCCEEDED(rc))
5691 {
5692 alock.release();
5693
5694 mParent->i_onGuestPropertyChange(mData->mUuid,
5695 Bstr(aName).raw(),
5696 Bstr(aValue).raw(),
5697 Bstr(aFlags).raw());
5698 }
5699 }
5700 catch (std::bad_alloc &)
5701 {
5702 rc = E_OUTOFMEMORY;
5703 }
5704
5705 return rc;
5706}
5707
5708/**
5709 * Set a property on the VM that that property belongs to.
5710 * @returns E_ACCESSDENIED if the VM process is not available or not
5711 * currently handling queries and the setting should then be done in
5712 * VBoxSVC.
5713 */
5714HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5715 const com::Utf8Str &aFlags, bool fDelete)
5716{
5717 HRESULT rc;
5718
5719 try
5720 {
5721 ComPtr<IInternalSessionControl> directControl;
5722 {
5723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5724 if (mData->mSession.mLockType == LockType_VM)
5725 directControl = mData->mSession.mDirectControl;
5726 }
5727
5728 BSTR dummy = NULL; /* will not be changed (setter) */
5729 LONG64 dummy64;
5730 if (!directControl)
5731 rc = E_ACCESSDENIED;
5732 else
5733 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5734 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5735 fDelete? 2: 1 /* accessMode */,
5736 &dummy, &dummy64, &dummy);
5737 }
5738 catch (std::bad_alloc &)
5739 {
5740 rc = E_OUTOFMEMORY;
5741 }
5742
5743 return rc;
5744}
5745#endif // VBOX_WITH_GUEST_PROPS
5746
5747HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5748 const com::Utf8Str &aFlags)
5749{
5750#ifndef VBOX_WITH_GUEST_PROPS
5751 ReturnComNotImplemented();
5752#else // VBOX_WITH_GUEST_PROPS
5753 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5754 if (rc == E_ACCESSDENIED)
5755 /* The VM is not running or the service is not (yet) accessible */
5756 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5757 return rc;
5758#endif // VBOX_WITH_GUEST_PROPS
5759}
5760
5761HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5762{
5763 return setGuestProperty(aProperty, aValue, "");
5764}
5765
5766HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5767{
5768#ifndef VBOX_WITH_GUEST_PROPS
5769 ReturnComNotImplemented();
5770#else // VBOX_WITH_GUEST_PROPS
5771 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5772 if (rc == E_ACCESSDENIED)
5773 /* The VM is not running or the service is not (yet) accessible */
5774 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5775 return rc;
5776#endif // VBOX_WITH_GUEST_PROPS
5777}
5778
5779#ifdef VBOX_WITH_GUEST_PROPS
5780/**
5781 * Enumerate the guest properties in VBoxSVC's internal structures.
5782 */
5783HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5784 std::vector<com::Utf8Str> &aNames,
5785 std::vector<com::Utf8Str> &aValues,
5786 std::vector<LONG64> &aTimestamps,
5787 std::vector<com::Utf8Str> &aFlags)
5788{
5789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5790 Utf8Str strPatterns(aPatterns);
5791
5792 /*
5793 * Look for matching patterns and build up a list.
5794 */
5795 HWData::GuestPropertyMap propMap;
5796 for (HWData::GuestPropertyMap::const_iterator
5797 it = mHWData->mGuestProperties.begin();
5798 it != mHWData->mGuestProperties.end();
5799 ++it)
5800 {
5801 if ( strPatterns.isEmpty()
5802 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5803 RTSTR_MAX,
5804 it->first.c_str(),
5805 RTSTR_MAX,
5806 NULL)
5807 )
5808 propMap.insert(*it);
5809 }
5810
5811 alock.release();
5812
5813 /*
5814 * And build up the arrays for returning the property information.
5815 */
5816 size_t cEntries = propMap.size();
5817
5818 aNames.resize(cEntries);
5819 aValues.resize(cEntries);
5820 aTimestamps.resize(cEntries);
5821 aFlags.resize(cEntries);
5822
5823 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5824 size_t i = 0;
5825 for (HWData::GuestPropertyMap::const_iterator
5826 it = propMap.begin();
5827 it != propMap.end();
5828 ++it, ++i)
5829 {
5830 aNames[i] = it->first;
5831 aValues[i] = it->second.strValue;
5832 aTimestamps[i] = it->second.mTimestamp;
5833 GuestPropWriteFlags(it->second.mFlags, szFlags);
5834 aFlags[i] = Utf8Str(szFlags);
5835 }
5836
5837 return S_OK;
5838}
5839
5840/**
5841 * Enumerate the properties managed by a VM.
5842 * @returns E_ACCESSDENIED if the VM process is not available or not
5843 * currently handling queries and the setting should then be done in
5844 * VBoxSVC.
5845 */
5846HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5847 std::vector<com::Utf8Str> &aNames,
5848 std::vector<com::Utf8Str> &aValues,
5849 std::vector<LONG64> &aTimestamps,
5850 std::vector<com::Utf8Str> &aFlags)
5851{
5852 HRESULT rc;
5853 ComPtr<IInternalSessionControl> directControl;
5854 {
5855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5856 if (mData->mSession.mLockType == LockType_VM)
5857 directControl = mData->mSession.mDirectControl;
5858 }
5859
5860 com::SafeArray<BSTR> bNames;
5861 com::SafeArray<BSTR> bValues;
5862 com::SafeArray<LONG64> bTimestamps;
5863 com::SafeArray<BSTR> bFlags;
5864
5865 if (!directControl)
5866 rc = E_ACCESSDENIED;
5867 else
5868 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5869 ComSafeArrayAsOutParam(bNames),
5870 ComSafeArrayAsOutParam(bValues),
5871 ComSafeArrayAsOutParam(bTimestamps),
5872 ComSafeArrayAsOutParam(bFlags));
5873 size_t i;
5874 aNames.resize(bNames.size());
5875 for (i = 0; i < bNames.size(); ++i)
5876 aNames[i] = Utf8Str(bNames[i]);
5877 aValues.resize(bValues.size());
5878 for (i = 0; i < bValues.size(); ++i)
5879 aValues[i] = Utf8Str(bValues[i]);
5880 aTimestamps.resize(bTimestamps.size());
5881 for (i = 0; i < bTimestamps.size(); ++i)
5882 aTimestamps[i] = bTimestamps[i];
5883 aFlags.resize(bFlags.size());
5884 for (i = 0; i < bFlags.size(); ++i)
5885 aFlags[i] = Utf8Str(bFlags[i]);
5886
5887 return rc;
5888}
5889#endif // VBOX_WITH_GUEST_PROPS
5890HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5891 std::vector<com::Utf8Str> &aNames,
5892 std::vector<com::Utf8Str> &aValues,
5893 std::vector<LONG64> &aTimestamps,
5894 std::vector<com::Utf8Str> &aFlags)
5895{
5896#ifndef VBOX_WITH_GUEST_PROPS
5897 ReturnComNotImplemented();
5898#else // VBOX_WITH_GUEST_PROPS
5899
5900 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5901
5902 if (rc == E_ACCESSDENIED)
5903 /* The VM is not running or the service is not (yet) accessible */
5904 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5905 return rc;
5906#endif // VBOX_WITH_GUEST_PROPS
5907}
5908
5909HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5910 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5911{
5912 MediumAttachmentList atts;
5913
5914 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5915 if (FAILED(rc)) return rc;
5916
5917 aMediumAttachments.resize(atts.size());
5918 size_t i = 0;
5919 for (MediumAttachmentList::const_iterator
5920 it = atts.begin();
5921 it != atts.end();
5922 ++it, ++i)
5923 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5924
5925 return S_OK;
5926}
5927
5928HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5929 LONG aControllerPort,
5930 LONG aDevice,
5931 ComPtr<IMediumAttachment> &aAttachment)
5932{
5933 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5934 aName.c_str(), aControllerPort, aDevice));
5935
5936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5937
5938 aAttachment = NULL;
5939
5940 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5941 aName,
5942 aControllerPort,
5943 aDevice);
5944 if (pAttach.isNull())
5945 return setError(VBOX_E_OBJECT_NOT_FOUND,
5946 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5947 aDevice, aControllerPort, aName.c_str());
5948
5949 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5950
5951 return S_OK;
5952}
5953
5954
5955HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5956 StorageBus_T aConnectionType,
5957 ComPtr<IStorageController> &aController)
5958{
5959 if ( (aConnectionType <= StorageBus_Null)
5960 || (aConnectionType > StorageBus_PCIe))
5961 return setError(E_INVALIDARG,
5962 tr("Invalid connection type: %d"),
5963 aConnectionType);
5964
5965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5966
5967 HRESULT rc = i_checkStateDependency(MutableStateDep);
5968 if (FAILED(rc)) return rc;
5969
5970 /* try to find one with the name first. */
5971 ComObjPtr<StorageController> ctrl;
5972
5973 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5974 if (SUCCEEDED(rc))
5975 return setError(VBOX_E_OBJECT_IN_USE,
5976 tr("Storage controller named '%s' already exists"),
5977 aName.c_str());
5978
5979 ctrl.createObject();
5980
5981 /* get a new instance number for the storage controller */
5982 ULONG ulInstance = 0;
5983 bool fBootable = true;
5984 for (StorageControllerList::const_iterator
5985 it = mStorageControllers->begin();
5986 it != mStorageControllers->end();
5987 ++it)
5988 {
5989 if ((*it)->i_getStorageBus() == aConnectionType)
5990 {
5991 ULONG ulCurInst = (*it)->i_getInstance();
5992
5993 if (ulCurInst >= ulInstance)
5994 ulInstance = ulCurInst + 1;
5995
5996 /* Only one controller of each type can be marked as bootable. */
5997 if ((*it)->i_getBootable())
5998 fBootable = false;
5999 }
6000 }
6001
6002 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6003 if (FAILED(rc)) return rc;
6004
6005 i_setModified(IsModified_Storage);
6006 mStorageControllers.backup();
6007 mStorageControllers->push_back(ctrl);
6008
6009 ctrl.queryInterfaceTo(aController.asOutParam());
6010
6011 /* inform the direct session if any */
6012 alock.release();
6013 i_onStorageControllerChange();
6014
6015 return S_OK;
6016}
6017
6018HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6019 ComPtr<IStorageController> &aStorageController)
6020{
6021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6022
6023 ComObjPtr<StorageController> ctrl;
6024
6025 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6026 if (SUCCEEDED(rc))
6027 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6028
6029 return rc;
6030}
6031
6032HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6033 ULONG aInstance,
6034 ComPtr<IStorageController> &aStorageController)
6035{
6036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6037
6038 for (StorageControllerList::const_iterator
6039 it = mStorageControllers->begin();
6040 it != mStorageControllers->end();
6041 ++it)
6042 {
6043 if ( (*it)->i_getStorageBus() == aConnectionType
6044 && (*it)->i_getInstance() == aInstance)
6045 {
6046 (*it).queryInterfaceTo(aStorageController.asOutParam());
6047 return S_OK;
6048 }
6049 }
6050
6051 return setError(VBOX_E_OBJECT_NOT_FOUND,
6052 tr("Could not find a storage controller with instance number '%lu'"),
6053 aInstance);
6054}
6055
6056HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6057{
6058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6059
6060 HRESULT rc = i_checkStateDependency(MutableStateDep);
6061 if (FAILED(rc)) return rc;
6062
6063 ComObjPtr<StorageController> ctrl;
6064
6065 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6066 if (SUCCEEDED(rc))
6067 {
6068 /* Ensure that only one controller of each type is marked as bootable. */
6069 if (aBootable == TRUE)
6070 {
6071 for (StorageControllerList::const_iterator
6072 it = mStorageControllers->begin();
6073 it != mStorageControllers->end();
6074 ++it)
6075 {
6076 ComObjPtr<StorageController> aCtrl = (*it);
6077
6078 if ( (aCtrl->i_getName() != aName)
6079 && aCtrl->i_getBootable() == TRUE
6080 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6081 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6082 {
6083 aCtrl->i_setBootable(FALSE);
6084 break;
6085 }
6086 }
6087 }
6088
6089 if (SUCCEEDED(rc))
6090 {
6091 ctrl->i_setBootable(aBootable);
6092 i_setModified(IsModified_Storage);
6093 }
6094 }
6095
6096 if (SUCCEEDED(rc))
6097 {
6098 /* inform the direct session if any */
6099 alock.release();
6100 i_onStorageControllerChange();
6101 }
6102
6103 return rc;
6104}
6105
6106HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6107{
6108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6109
6110 HRESULT rc = i_checkStateDependency(MutableStateDep);
6111 if (FAILED(rc)) return rc;
6112
6113 ComObjPtr<StorageController> ctrl;
6114 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6115 if (FAILED(rc)) return rc;
6116
6117 {
6118 /* find all attached devices to the appropriate storage controller and detach them all */
6119 // make a temporary list because detachDevice invalidates iterators into
6120 // mMediumAttachments
6121 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6122
6123 for (MediumAttachmentList::const_iterator
6124 it = llAttachments2.begin();
6125 it != llAttachments2.end();
6126 ++it)
6127 {
6128 MediumAttachment *pAttachTemp = *it;
6129
6130 AutoCaller localAutoCaller(pAttachTemp);
6131 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6132
6133 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6134
6135 if (pAttachTemp->i_getControllerName() == aName)
6136 {
6137 rc = i_detachDevice(pAttachTemp, alock, NULL);
6138 if (FAILED(rc)) return rc;
6139 }
6140 }
6141 }
6142
6143 /* We can remove it now. */
6144 i_setModified(IsModified_Storage);
6145 mStorageControllers.backup();
6146
6147 ctrl->i_unshare();
6148
6149 mStorageControllers->remove(ctrl);
6150
6151 /* inform the direct session if any */
6152 alock.release();
6153 i_onStorageControllerChange();
6154
6155 return S_OK;
6156}
6157
6158HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6159 ComPtr<IUSBController> &aController)
6160{
6161 if ( (aType <= USBControllerType_Null)
6162 || (aType >= USBControllerType_Last))
6163 return setError(E_INVALIDARG,
6164 tr("Invalid USB controller type: %d"),
6165 aType);
6166
6167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6168
6169 HRESULT rc = i_checkStateDependency(MutableStateDep);
6170 if (FAILED(rc)) return rc;
6171
6172 /* try to find one with the same type first. */
6173 ComObjPtr<USBController> ctrl;
6174
6175 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6176 if (SUCCEEDED(rc))
6177 return setError(VBOX_E_OBJECT_IN_USE,
6178 tr("USB controller named '%s' already exists"),
6179 aName.c_str());
6180
6181 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6182 ULONG maxInstances;
6183 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6184 if (FAILED(rc))
6185 return rc;
6186
6187 ULONG cInstances = i_getUSBControllerCountByType(aType);
6188 if (cInstances >= maxInstances)
6189 return setError(E_INVALIDARG,
6190 tr("Too many USB controllers of this type"));
6191
6192 ctrl.createObject();
6193
6194 rc = ctrl->init(this, aName, aType);
6195 if (FAILED(rc)) return rc;
6196
6197 i_setModified(IsModified_USB);
6198 mUSBControllers.backup();
6199 mUSBControllers->push_back(ctrl);
6200
6201 ctrl.queryInterfaceTo(aController.asOutParam());
6202
6203 /* inform the direct session if any */
6204 alock.release();
6205 i_onUSBControllerChange();
6206
6207 return S_OK;
6208}
6209
6210HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6211{
6212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 ComObjPtr<USBController> ctrl;
6215
6216 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6217 if (SUCCEEDED(rc))
6218 ctrl.queryInterfaceTo(aController.asOutParam());
6219
6220 return rc;
6221}
6222
6223HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6224 ULONG *aControllers)
6225{
6226 if ( (aType <= USBControllerType_Null)
6227 || (aType >= USBControllerType_Last))
6228 return setError(E_INVALIDARG,
6229 tr("Invalid USB controller type: %d"),
6230 aType);
6231
6232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6233
6234 ComObjPtr<USBController> ctrl;
6235
6236 *aControllers = i_getUSBControllerCountByType(aType);
6237
6238 return S_OK;
6239}
6240
6241HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6242{
6243
6244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6245
6246 HRESULT rc = i_checkStateDependency(MutableStateDep);
6247 if (FAILED(rc)) return rc;
6248
6249 ComObjPtr<USBController> ctrl;
6250 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6251 if (FAILED(rc)) return rc;
6252
6253 i_setModified(IsModified_USB);
6254 mUSBControllers.backup();
6255
6256 ctrl->i_unshare();
6257
6258 mUSBControllers->remove(ctrl);
6259
6260 /* inform the direct session if any */
6261 alock.release();
6262 i_onUSBControllerChange();
6263
6264 return S_OK;
6265}
6266
6267HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6268 ULONG *aOriginX,
6269 ULONG *aOriginY,
6270 ULONG *aWidth,
6271 ULONG *aHeight,
6272 BOOL *aEnabled)
6273{
6274 uint32_t u32OriginX= 0;
6275 uint32_t u32OriginY= 0;
6276 uint32_t u32Width = 0;
6277 uint32_t u32Height = 0;
6278 uint16_t u16Flags = 0;
6279
6280 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6281 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6282 if (RT_FAILURE(vrc))
6283 {
6284#ifdef RT_OS_WINDOWS
6285 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6286 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6287 * So just assign fEnable to TRUE again.
6288 * The right fix would be to change GUI API wrappers to make sure that parameters
6289 * are changed only if API succeeds.
6290 */
6291 *aEnabled = TRUE;
6292#endif
6293 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6294 tr("Saved guest size is not available (%Rrc)"),
6295 vrc);
6296 }
6297
6298 *aOriginX = u32OriginX;
6299 *aOriginY = u32OriginY;
6300 *aWidth = u32Width;
6301 *aHeight = u32Height;
6302 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6303
6304 return S_OK;
6305}
6306
6307HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6308 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6309{
6310 if (aScreenId != 0)
6311 return E_NOTIMPL;
6312
6313 if ( aBitmapFormat != BitmapFormat_BGR0
6314 && aBitmapFormat != BitmapFormat_BGRA
6315 && aBitmapFormat != BitmapFormat_RGBA
6316 && aBitmapFormat != BitmapFormat_PNG)
6317 return setError(E_NOTIMPL,
6318 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6319
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 uint8_t *pu8Data = NULL;
6323 uint32_t cbData = 0;
6324 uint32_t u32Width = 0;
6325 uint32_t u32Height = 0;
6326
6327 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6328
6329 if (RT_FAILURE(vrc))
6330 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6331 tr("Saved thumbnail data is not available (%Rrc)"),
6332 vrc);
6333
6334 HRESULT hr = S_OK;
6335
6336 *aWidth = u32Width;
6337 *aHeight = u32Height;
6338
6339 if (cbData > 0)
6340 {
6341 /* Convert pixels to the format expected by the API caller. */
6342 if (aBitmapFormat == BitmapFormat_BGR0)
6343 {
6344 /* [0] B, [1] G, [2] R, [3] 0. */
6345 aData.resize(cbData);
6346 memcpy(&aData.front(), pu8Data, cbData);
6347 }
6348 else if (aBitmapFormat == BitmapFormat_BGRA)
6349 {
6350 /* [0] B, [1] G, [2] R, [3] A. */
6351 aData.resize(cbData);
6352 for (uint32_t i = 0; i < cbData; i += 4)
6353 {
6354 aData[i] = pu8Data[i];
6355 aData[i + 1] = pu8Data[i + 1];
6356 aData[i + 2] = pu8Data[i + 2];
6357 aData[i + 3] = 0xff;
6358 }
6359 }
6360 else if (aBitmapFormat == BitmapFormat_RGBA)
6361 {
6362 /* [0] R, [1] G, [2] B, [3] A. */
6363 aData.resize(cbData);
6364 for (uint32_t i = 0; i < cbData; i += 4)
6365 {
6366 aData[i] = pu8Data[i + 2];
6367 aData[i + 1] = pu8Data[i + 1];
6368 aData[i + 2] = pu8Data[i];
6369 aData[i + 3] = 0xff;
6370 }
6371 }
6372 else if (aBitmapFormat == BitmapFormat_PNG)
6373 {
6374 uint8_t *pu8PNG = NULL;
6375 uint32_t cbPNG = 0;
6376 uint32_t cxPNG = 0;
6377 uint32_t cyPNG = 0;
6378
6379 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6380
6381 if (RT_SUCCESS(vrc))
6382 {
6383 aData.resize(cbPNG);
6384 if (cbPNG)
6385 memcpy(&aData.front(), pu8PNG, cbPNG);
6386 }
6387 else
6388 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6389 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6390 vrc);
6391
6392 RTMemFree(pu8PNG);
6393 }
6394 }
6395
6396 freeSavedDisplayScreenshot(pu8Data);
6397
6398 return hr;
6399}
6400
6401HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6402 ULONG *aWidth,
6403 ULONG *aHeight,
6404 std::vector<BitmapFormat_T> &aBitmapFormats)
6405{
6406 if (aScreenId != 0)
6407 return E_NOTIMPL;
6408
6409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6410
6411 uint8_t *pu8Data = NULL;
6412 uint32_t cbData = 0;
6413 uint32_t u32Width = 0;
6414 uint32_t u32Height = 0;
6415
6416 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6417
6418 if (RT_FAILURE(vrc))
6419 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6420 tr("Saved screenshot data is not available (%Rrc)"),
6421 vrc);
6422
6423 *aWidth = u32Width;
6424 *aHeight = u32Height;
6425 aBitmapFormats.resize(1);
6426 aBitmapFormats[0] = BitmapFormat_PNG;
6427
6428 freeSavedDisplayScreenshot(pu8Data);
6429
6430 return S_OK;
6431}
6432
6433HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6434 BitmapFormat_T aBitmapFormat,
6435 ULONG *aWidth,
6436 ULONG *aHeight,
6437 std::vector<BYTE> &aData)
6438{
6439 if (aScreenId != 0)
6440 return E_NOTIMPL;
6441
6442 if (aBitmapFormat != BitmapFormat_PNG)
6443 return E_NOTIMPL;
6444
6445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6446
6447 uint8_t *pu8Data = NULL;
6448 uint32_t cbData = 0;
6449 uint32_t u32Width = 0;
6450 uint32_t u32Height = 0;
6451
6452 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6453
6454 if (RT_FAILURE(vrc))
6455 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6456 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6457 vrc);
6458
6459 *aWidth = u32Width;
6460 *aHeight = u32Height;
6461
6462 aData.resize(cbData);
6463 if (cbData)
6464 memcpy(&aData.front(), pu8Data, cbData);
6465
6466 freeSavedDisplayScreenshot(pu8Data);
6467
6468 return S_OK;
6469}
6470
6471HRESULT Machine::hotPlugCPU(ULONG aCpu)
6472{
6473 HRESULT rc = S_OK;
6474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6475
6476 if (!mHWData->mCPUHotPlugEnabled)
6477 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6478
6479 if (aCpu >= mHWData->mCPUCount)
6480 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6481
6482 if (mHWData->mCPUAttached[aCpu])
6483 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6484
6485 alock.release();
6486 rc = i_onCPUChange(aCpu, false);
6487 alock.acquire();
6488 if (FAILED(rc)) return rc;
6489
6490 i_setModified(IsModified_MachineData);
6491 mHWData.backup();
6492 mHWData->mCPUAttached[aCpu] = true;
6493
6494 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6495 if (Global::IsOnline(mData->mMachineState))
6496 i_saveSettings(NULL);
6497
6498 return S_OK;
6499}
6500
6501HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6502{
6503 HRESULT rc = S_OK;
6504
6505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6506
6507 if (!mHWData->mCPUHotPlugEnabled)
6508 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6509
6510 if (aCpu >= SchemaDefs::MaxCPUCount)
6511 return setError(E_INVALIDARG,
6512 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6513 SchemaDefs::MaxCPUCount);
6514
6515 if (!mHWData->mCPUAttached[aCpu])
6516 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6517
6518 /* CPU 0 can't be detached */
6519 if (aCpu == 0)
6520 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6521
6522 alock.release();
6523 rc = i_onCPUChange(aCpu, true);
6524 alock.acquire();
6525 if (FAILED(rc)) return rc;
6526
6527 i_setModified(IsModified_MachineData);
6528 mHWData.backup();
6529 mHWData->mCPUAttached[aCpu] = false;
6530
6531 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6532 if (Global::IsOnline(mData->mMachineState))
6533 i_saveSettings(NULL);
6534
6535 return S_OK;
6536}
6537
6538HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6539{
6540 *aAttached = false;
6541
6542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6543
6544 /* If hotplug is enabled the CPU is always enabled. */
6545 if (!mHWData->mCPUHotPlugEnabled)
6546 {
6547 if (aCpu < mHWData->mCPUCount)
6548 *aAttached = true;
6549 }
6550 else
6551 {
6552 if (aCpu < SchemaDefs::MaxCPUCount)
6553 *aAttached = mHWData->mCPUAttached[aCpu];
6554 }
6555
6556 return S_OK;
6557}
6558
6559HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6560{
6561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6562
6563 Utf8Str log = i_getLogFilename(aIdx);
6564 if (!RTFileExists(log.c_str()))
6565 log.setNull();
6566 aFilename = log;
6567
6568 return S_OK;
6569}
6570
6571HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6572{
6573 if (aSize < 0)
6574 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6575
6576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6577
6578 HRESULT rc = S_OK;
6579 Utf8Str log = i_getLogFilename(aIdx);
6580
6581 /* do not unnecessarily hold the lock while doing something which does
6582 * not need the lock and potentially takes a long time. */
6583 alock.release();
6584
6585 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6586 * keeps the SOAP reply size under 1M for the webservice (we're using
6587 * base64 encoded strings for binary data for years now, avoiding the
6588 * expansion of each byte array element to approx. 25 bytes of XML. */
6589 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6590 aData.resize(cbData);
6591
6592 RTFILE LogFile;
6593 int vrc = RTFileOpen(&LogFile, log.c_str(),
6594 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6595 if (RT_SUCCESS(vrc))
6596 {
6597 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6598 if (RT_SUCCESS(vrc))
6599 aData.resize(cbData);
6600 else
6601 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6602 tr("Could not read log file '%s' (%Rrc)"),
6603 log.c_str(), vrc);
6604 RTFileClose(LogFile);
6605 }
6606 else
6607 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6608 tr("Could not open log file '%s' (%Rrc)"),
6609 log.c_str(), vrc);
6610
6611 if (FAILED(rc))
6612 aData.resize(0);
6613
6614 return rc;
6615}
6616
6617
6618/**
6619 * Currently this method doesn't attach device to the running VM,
6620 * just makes sure it's plugged on next VM start.
6621 */
6622HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6623{
6624 // lock scope
6625 {
6626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6627
6628 HRESULT rc = i_checkStateDependency(MutableStateDep);
6629 if (FAILED(rc)) return rc;
6630
6631 ChipsetType_T aChipset = ChipsetType_PIIX3;
6632 COMGETTER(ChipsetType)(&aChipset);
6633
6634 if (aChipset != ChipsetType_ICH9)
6635 {
6636 return setError(E_INVALIDARG,
6637 tr("Host PCI attachment only supported with ICH9 chipset"));
6638 }
6639
6640 // check if device with this host PCI address already attached
6641 for (HWData::PCIDeviceAssignmentList::const_iterator
6642 it = mHWData->mPCIDeviceAssignments.begin();
6643 it != mHWData->mPCIDeviceAssignments.end();
6644 ++it)
6645 {
6646 LONG iHostAddress = -1;
6647 ComPtr<PCIDeviceAttachment> pAttach;
6648 pAttach = *it;
6649 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6650 if (iHostAddress == aHostAddress)
6651 return setError(E_INVALIDARG,
6652 tr("Device with host PCI address already attached to this VM"));
6653 }
6654
6655 ComObjPtr<PCIDeviceAttachment> pda;
6656 char name[32];
6657
6658 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6659 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6660 pda.createObject();
6661 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6662 i_setModified(IsModified_MachineData);
6663 mHWData.backup();
6664 mHWData->mPCIDeviceAssignments.push_back(pda);
6665 }
6666
6667 return S_OK;
6668}
6669
6670/**
6671 * Currently this method doesn't detach device from the running VM,
6672 * just makes sure it's not plugged on next VM start.
6673 */
6674HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6675{
6676 ComObjPtr<PCIDeviceAttachment> pAttach;
6677 bool fRemoved = false;
6678 HRESULT rc;
6679
6680 // lock scope
6681 {
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 rc = i_checkStateDependency(MutableStateDep);
6685 if (FAILED(rc)) return rc;
6686
6687 for (HWData::PCIDeviceAssignmentList::const_iterator
6688 it = mHWData->mPCIDeviceAssignments.begin();
6689 it != mHWData->mPCIDeviceAssignments.end();
6690 ++it)
6691 {
6692 LONG iHostAddress = -1;
6693 pAttach = *it;
6694 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6695 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6696 {
6697 i_setModified(IsModified_MachineData);
6698 mHWData.backup();
6699 mHWData->mPCIDeviceAssignments.remove(pAttach);
6700 fRemoved = true;
6701 break;
6702 }
6703 }
6704 }
6705
6706
6707 /* Fire event outside of the lock */
6708 if (fRemoved)
6709 {
6710 Assert(!pAttach.isNull());
6711 ComPtr<IEventSource> es;
6712 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6713 Assert(SUCCEEDED(rc));
6714 Bstr mid;
6715 rc = this->COMGETTER(Id)(mid.asOutParam());
6716 Assert(SUCCEEDED(rc));
6717 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6718 }
6719
6720 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6721 tr("No host PCI device %08x attached"),
6722 aHostAddress
6723 );
6724}
6725
6726HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6727{
6728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6729
6730 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6731 size_t i = 0;
6732 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6733 it = mHWData->mPCIDeviceAssignments.begin();
6734 it != mHWData->mPCIDeviceAssignments.end();
6735 ++it, ++i)
6736 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6737
6738 return S_OK;
6739}
6740
6741HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6742{
6743 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6749{
6750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6751
6752 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6753
6754 return S_OK;
6755}
6756
6757HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6758{
6759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6760 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6761 if (SUCCEEDED(hrc))
6762 {
6763 hrc = mHWData.backupEx();
6764 if (SUCCEEDED(hrc))
6765 {
6766 i_setModified(IsModified_MachineData);
6767 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6768 }
6769 }
6770 return hrc;
6771}
6772
6773HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6777 return S_OK;
6778}
6779
6780HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6781{
6782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6783 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6784 if (SUCCEEDED(hrc))
6785 {
6786 hrc = mHWData.backupEx();
6787 if (SUCCEEDED(hrc))
6788 {
6789 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6790 if (SUCCEEDED(hrc))
6791 i_setModified(IsModified_MachineData);
6792 }
6793 }
6794 return hrc;
6795}
6796
6797HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6798{
6799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6800
6801 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6802
6803 return S_OK;
6804}
6805
6806HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6807{
6808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6809 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6810 if (SUCCEEDED(hrc))
6811 {
6812 hrc = mHWData.backupEx();
6813 if (SUCCEEDED(hrc))
6814 {
6815 i_setModified(IsModified_MachineData);
6816 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6817 }
6818 }
6819 return hrc;
6820}
6821
6822HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6823{
6824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6825
6826 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6832{
6833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6836 if ( SUCCEEDED(hrc)
6837 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6838 {
6839 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6840 int vrc;
6841
6842 if (aAutostartEnabled)
6843 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6844 else
6845 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6846
6847 if (RT_SUCCESS(vrc))
6848 {
6849 hrc = mHWData.backupEx();
6850 if (SUCCEEDED(hrc))
6851 {
6852 i_setModified(IsModified_MachineData);
6853 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6854 }
6855 }
6856 else if (vrc == VERR_NOT_SUPPORTED)
6857 hrc = setError(VBOX_E_NOT_SUPPORTED,
6858 tr("The VM autostart feature is not supported on this platform"));
6859 else if (vrc == VERR_PATH_NOT_FOUND)
6860 hrc = setError(E_FAIL,
6861 tr("The path to the autostart database is not set"));
6862 else
6863 hrc = setError(E_UNEXPECTED,
6864 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6865 aAutostartEnabled ? "Adding" : "Removing",
6866 mUserData->s.strName.c_str(), vrc);
6867 }
6868 return hrc;
6869}
6870
6871HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6872{
6873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6874
6875 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6876
6877 return S_OK;
6878}
6879
6880HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6881{
6882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6883 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6884 if (SUCCEEDED(hrc))
6885 {
6886 hrc = mHWData.backupEx();
6887 if (SUCCEEDED(hrc))
6888 {
6889 i_setModified(IsModified_MachineData);
6890 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6891 }
6892 }
6893 return hrc;
6894}
6895
6896HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6897{
6898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6899
6900 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6901
6902 return S_OK;
6903}
6904
6905HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6906{
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6909 if ( SUCCEEDED(hrc)
6910 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6911 {
6912 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6913 int vrc;
6914
6915 if (aAutostopType != AutostopType_Disabled)
6916 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6917 else
6918 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6919
6920 if (RT_SUCCESS(vrc))
6921 {
6922 hrc = mHWData.backupEx();
6923 if (SUCCEEDED(hrc))
6924 {
6925 i_setModified(IsModified_MachineData);
6926 mHWData->mAutostart.enmAutostopType = aAutostopType;
6927 }
6928 }
6929 else if (vrc == VERR_NOT_SUPPORTED)
6930 hrc = setError(VBOX_E_NOT_SUPPORTED,
6931 tr("The VM autostop feature is not supported on this platform"));
6932 else if (vrc == VERR_PATH_NOT_FOUND)
6933 hrc = setError(E_FAIL,
6934 tr("The path to the autostart database is not set"));
6935 else
6936 hrc = setError(E_UNEXPECTED,
6937 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6938 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6939 mUserData->s.strName.c_str(), vrc);
6940 }
6941 return hrc;
6942}
6943
6944HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6945{
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947
6948 aDefaultFrontend = mHWData->mDefaultFrontend;
6949
6950 return S_OK;
6951}
6952
6953HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6954{
6955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6956 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6957 if (SUCCEEDED(hrc))
6958 {
6959 hrc = mHWData.backupEx();
6960 if (SUCCEEDED(hrc))
6961 {
6962 i_setModified(IsModified_MachineData);
6963 mHWData->mDefaultFrontend = aDefaultFrontend;
6964 }
6965 }
6966 return hrc;
6967}
6968
6969HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6970{
6971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6972 size_t cbIcon = mUserData->s.ovIcon.size();
6973 aIcon.resize(cbIcon);
6974 if (cbIcon)
6975 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6976 return S_OK;
6977}
6978
6979HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6980{
6981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6982 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6983 if (SUCCEEDED(hrc))
6984 {
6985 i_setModified(IsModified_MachineData);
6986 mUserData.backup();
6987 size_t cbIcon = aIcon.size();
6988 mUserData->s.ovIcon.resize(cbIcon);
6989 if (cbIcon)
6990 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6991 }
6992 return hrc;
6993}
6994
6995HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6996{
6997#ifdef VBOX_WITH_USB
6998 *aUSBProxyAvailable = true;
6999#else
7000 *aUSBProxyAvailable = false;
7001#endif
7002 return S_OK;
7003}
7004
7005HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7006{
7007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7008
7009 aVMProcessPriority = mUserData->s.strVMPriority;
7010
7011 return S_OK;
7012}
7013
7014HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7015{
7016 RT_NOREF(aVMProcessPriority);
7017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7018 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7019 if (SUCCEEDED(hrc))
7020 {
7021 /** @todo r=klaus: currently this is marked as not implemented, as
7022 * the code for setting the priority of the process is not there
7023 * (neither when starting the VM nor at runtime). */
7024 ReturnComNotImplemented();
7025#if 0
7026 hrc = mUserData.backupEx();
7027 if (SUCCEEDED(hrc))
7028 {
7029 i_setModified(IsModified_MachineData);
7030 mUserData->s.strVMPriority = aVMProcessPriority;
7031 }
7032#endif
7033 }
7034 return hrc;
7035}
7036
7037HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7038 ComPtr<IProgress> &aProgress)
7039{
7040 ComObjPtr<Progress> pP;
7041 Progress *ppP = pP;
7042 IProgress *iP = static_cast<IProgress *>(ppP);
7043 IProgress **pProgress = &iP;
7044
7045 IMachine *pTarget = aTarget;
7046
7047 /* Convert the options. */
7048 RTCList<CloneOptions_T> optList;
7049 if (aOptions.size())
7050 for (size_t i = 0; i < aOptions.size(); ++i)
7051 optList.append(aOptions[i]);
7052
7053 if (optList.contains(CloneOptions_Link))
7054 {
7055 if (!i_isSnapshotMachine())
7056 return setError(E_INVALIDARG,
7057 tr("Linked clone can only be created from a snapshot"));
7058 if (aMode != CloneMode_MachineState)
7059 return setError(E_INVALIDARG,
7060 tr("Linked clone can only be created for a single machine state"));
7061 }
7062 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7063
7064 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7065
7066 HRESULT rc = pWorker->start(pProgress);
7067
7068 pP = static_cast<Progress *>(*pProgress);
7069 pP.queryInterfaceTo(aProgress.asOutParam());
7070
7071 return rc;
7072
7073}
7074
7075HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7076 const com::Utf8Str &aType,
7077 ComPtr<IProgress> &aProgress)
7078{
7079 LogFlowThisFuncEnter();
7080
7081 ComObjPtr<Progress> progress;
7082
7083 progress.createObject();
7084
7085 HRESULT rc = S_OK;
7086 Utf8Str targetPath = aTargetPath;
7087 Utf8Str type = aType;
7088
7089 /* Initialize our worker task */
7090 MachineMoveVM* task = NULL;
7091 try
7092 {
7093 task = new MachineMoveVM(this, targetPath, type, progress);
7094 }
7095 catch(...)
7096 {
7097 delete task;
7098 return rc;
7099 }
7100
7101 /*
7102 * task pointer will be owned by the ThreadTask class.
7103 * There is no need to call operator "delete" in the end.
7104 */
7105 rc = task->init();
7106 if (SUCCEEDED(rc))
7107 {
7108 rc = task->createThread();
7109 if (FAILED(rc))
7110 {
7111 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7112 }
7113
7114 /* Return progress to the caller */
7115 progress.queryInterfaceTo(aProgress.asOutParam());
7116 }
7117
7118 LogFlowThisFuncLeave();
7119 return rc;
7120
7121}
7122
7123HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7124{
7125 NOREF(aProgress);
7126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7127
7128 // This check should always fail.
7129 HRESULT rc = i_checkStateDependency(MutableStateDep);
7130 if (FAILED(rc)) return rc;
7131
7132 AssertFailedReturn(E_NOTIMPL);
7133}
7134
7135HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7136{
7137 NOREF(aSavedStateFile);
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 // This check should always fail.
7141 HRESULT rc = i_checkStateDependency(MutableStateDep);
7142 if (FAILED(rc)) return rc;
7143
7144 AssertFailedReturn(E_NOTIMPL);
7145}
7146
7147HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7148{
7149 NOREF(aFRemoveFile);
7150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 // This check should always fail.
7153 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7154 if (FAILED(rc)) return rc;
7155
7156 AssertFailedReturn(E_NOTIMPL);
7157}
7158
7159// public methods for internal purposes
7160/////////////////////////////////////////////////////////////////////////////
7161
7162/**
7163 * Adds the given IsModified_* flag to the dirty flags of the machine.
7164 * This must be called either during i_loadSettings or under the machine write lock.
7165 * @param fl Flag
7166 * @param fAllowStateModification If state modifications are allowed.
7167 */
7168void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7169{
7170 mData->flModifications |= fl;
7171 if (fAllowStateModification && i_isStateModificationAllowed())
7172 mData->mCurrentStateModified = true;
7173}
7174
7175/**
7176 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7177 * care of the write locking.
7178 *
7179 * @param fModification The flag to add.
7180 * @param fAllowStateModification If state modifications are allowed.
7181 */
7182void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7183{
7184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7185 i_setModified(fModification, fAllowStateModification);
7186}
7187
7188/**
7189 * Saves the registry entry of this machine to the given configuration node.
7190 *
7191 * @param data Machine registry data.
7192 *
7193 * @note locks this object for reading.
7194 */
7195HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7196{
7197 AutoLimitedCaller autoCaller(this);
7198 AssertComRCReturnRC(autoCaller.rc());
7199
7200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7201
7202 data.uuid = mData->mUuid;
7203 data.strSettingsFile = mData->m_strConfigFile;
7204
7205 return S_OK;
7206}
7207
7208/**
7209 * Calculates the absolute path of the given path taking the directory of the
7210 * machine settings file as the current directory.
7211 *
7212 * @param strPath Path to calculate the absolute path for.
7213 * @param aResult Where to put the result (used only on success, can be the
7214 * same Utf8Str instance as passed in @a aPath).
7215 * @return IPRT result.
7216 *
7217 * @note Locks this object for reading.
7218 */
7219int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7220{
7221 AutoCaller autoCaller(this);
7222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7223
7224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7225
7226 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7227
7228 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7229
7230 strSettingsDir.stripFilename();
7231 char folder[RTPATH_MAX];
7232 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7233 if (RT_SUCCESS(vrc))
7234 aResult = folder;
7235
7236 return vrc;
7237}
7238
7239/**
7240 * Copies strSource to strTarget, making it relative to the machine folder
7241 * if it is a subdirectory thereof, or simply copying it otherwise.
7242 *
7243 * @param strSource Path to evaluate and copy.
7244 * @param strTarget Buffer to receive target path.
7245 *
7246 * @note Locks this object for reading.
7247 */
7248void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7249 Utf8Str &strTarget)
7250{
7251 AutoCaller autoCaller(this);
7252 AssertComRCReturn(autoCaller.rc(), (void)0);
7253
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255
7256 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7257 // use strTarget as a temporary buffer to hold the machine settings dir
7258 strTarget = mData->m_strConfigFileFull;
7259 strTarget.stripFilename();
7260 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7261 {
7262 // is relative: then append what's left
7263 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7264 // for empty paths (only possible for subdirs) use "." to avoid
7265 // triggering default settings for not present config attributes.
7266 if (strTarget.isEmpty())
7267 strTarget = ".";
7268 }
7269 else
7270 // is not relative: then overwrite
7271 strTarget = strSource;
7272}
7273
7274/**
7275 * Returns the full path to the machine's log folder in the
7276 * \a aLogFolder argument.
7277 */
7278void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7279{
7280 AutoCaller autoCaller(this);
7281 AssertComRCReturnVoid(autoCaller.rc());
7282
7283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7284
7285 char szTmp[RTPATH_MAX];
7286 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7287 if (RT_SUCCESS(vrc))
7288 {
7289 if (szTmp[0] && !mUserData.isNull())
7290 {
7291 char szTmp2[RTPATH_MAX];
7292 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7293 if (RT_SUCCESS(vrc))
7294 aLogFolder = Utf8StrFmt("%s%c%s",
7295 szTmp2,
7296 RTPATH_DELIMITER,
7297 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7298 }
7299 else
7300 vrc = VERR_PATH_IS_RELATIVE;
7301 }
7302
7303 if (RT_FAILURE(vrc))
7304 {
7305 // fallback if VBOX_USER_LOGHOME is not set or invalid
7306 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7307 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7308 aLogFolder.append(RTPATH_DELIMITER);
7309 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7310 }
7311}
7312
7313/**
7314 * Returns the full path to the machine's log file for an given index.
7315 */
7316Utf8Str Machine::i_getLogFilename(ULONG idx)
7317{
7318 Utf8Str logFolder;
7319 getLogFolder(logFolder);
7320 Assert(logFolder.length());
7321
7322 Utf8Str log;
7323 if (idx == 0)
7324 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7325#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7326 else if (idx == 1)
7327 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7328 else
7329 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7330#else
7331 else
7332 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7333#endif
7334 return log;
7335}
7336
7337/**
7338 * Returns the full path to the machine's hardened log file.
7339 */
7340Utf8Str Machine::i_getHardeningLogFilename(void)
7341{
7342 Utf8Str strFilename;
7343 getLogFolder(strFilename);
7344 Assert(strFilename.length());
7345 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7346 return strFilename;
7347}
7348
7349
7350/**
7351 * Composes a unique saved state filename based on the current system time. The filename is
7352 * granular to the second so this will work so long as no more than one snapshot is taken on
7353 * a machine per second.
7354 *
7355 * Before version 4.1, we used this formula for saved state files:
7356 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7357 * which no longer works because saved state files can now be shared between the saved state of the
7358 * "saved" machine and an online snapshot, and the following would cause problems:
7359 * 1) save machine
7360 * 2) create online snapshot from that machine state --> reusing saved state file
7361 * 3) save machine again --> filename would be reused, breaking the online snapshot
7362 *
7363 * So instead we now use a timestamp.
7364 *
7365 * @param strStateFilePath
7366 */
7367
7368void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturnVoid(autoCaller.rc());
7372
7373 {
7374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7375 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7376 }
7377
7378 RTTIMESPEC ts;
7379 RTTimeNow(&ts);
7380 RTTIME time;
7381 RTTimeExplode(&time, &ts);
7382
7383 strStateFilePath += RTPATH_DELIMITER;
7384 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7385 time.i32Year, time.u8Month, time.u8MonthDay,
7386 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7387}
7388
7389/**
7390 * Returns whether at least one USB controller is present for the VM.
7391 */
7392bool Machine::i_isUSBControllerPresent()
7393{
7394 AutoCaller autoCaller(this);
7395 AssertComRCReturn(autoCaller.rc(), false);
7396
7397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7398
7399 return (mUSBControllers->size() > 0);
7400}
7401
7402/**
7403 * @note Locks this object for writing, calls the client process
7404 * (inside the lock).
7405 */
7406HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7407 const Utf8Str &strFrontend,
7408 const Utf8Str &strEnvironment,
7409 ProgressProxy *aProgress)
7410{
7411 LogFlowThisFuncEnter();
7412
7413 AssertReturn(aControl, E_FAIL);
7414 AssertReturn(aProgress, E_FAIL);
7415 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7416
7417 AutoCaller autoCaller(this);
7418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7419
7420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7421
7422 if (!mData->mRegistered)
7423 return setError(E_UNEXPECTED,
7424 tr("The machine '%s' is not registered"),
7425 mUserData->s.strName.c_str());
7426
7427 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7428
7429 /* The process started when launching a VM with separate UI/VM processes is always
7430 * the UI process, i.e. needs special handling as it won't claim the session. */
7431 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7432
7433 if (fSeparate)
7434 {
7435 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7436 return setError(VBOX_E_INVALID_OBJECT_STATE,
7437 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7438 mUserData->s.strName.c_str());
7439 }
7440 else
7441 {
7442 if ( mData->mSession.mState == SessionState_Locked
7443 || mData->mSession.mState == SessionState_Spawning
7444 || mData->mSession.mState == SessionState_Unlocking)
7445 return setError(VBOX_E_INVALID_OBJECT_STATE,
7446 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7447 mUserData->s.strName.c_str());
7448
7449 /* may not be busy */
7450 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7451 }
7452
7453 /* get the path to the executable */
7454 char szPath[RTPATH_MAX];
7455 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7456 size_t cchBufLeft = strlen(szPath);
7457 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7458 szPath[cchBufLeft] = 0;
7459 char *pszNamePart = szPath + cchBufLeft;
7460 cchBufLeft = sizeof(szPath) - cchBufLeft;
7461
7462 int vrc = VINF_SUCCESS;
7463 RTPROCESS pid = NIL_RTPROCESS;
7464
7465 RTENV env = RTENV_DEFAULT;
7466
7467 if (!strEnvironment.isEmpty())
7468 {
7469 char *newEnvStr = NULL;
7470
7471 do
7472 {
7473 /* clone the current environment */
7474 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7475 AssertRCBreakStmt(vrc2, vrc = vrc2);
7476
7477 newEnvStr = RTStrDup(strEnvironment.c_str());
7478 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7479
7480 /* put new variables to the environment
7481 * (ignore empty variable names here since RTEnv API
7482 * intentionally doesn't do that) */
7483 char *var = newEnvStr;
7484 for (char *p = newEnvStr; *p; ++p)
7485 {
7486 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7487 {
7488 *p = '\0';
7489 if (*var)
7490 {
7491 char *val = strchr(var, '=');
7492 if (val)
7493 {
7494 *val++ = '\0';
7495 vrc2 = RTEnvSetEx(env, var, val);
7496 }
7497 else
7498 vrc2 = RTEnvUnsetEx(env, var);
7499 if (RT_FAILURE(vrc2))
7500 break;
7501 }
7502 var = p + 1;
7503 }
7504 }
7505 if (RT_SUCCESS(vrc2) && *var)
7506 vrc2 = RTEnvPutEx(env, var);
7507
7508 AssertRCBreakStmt(vrc2, vrc = vrc2);
7509 }
7510 while (0);
7511
7512 if (newEnvStr != NULL)
7513 RTStrFree(newEnvStr);
7514 }
7515
7516 /* Hardening logging */
7517#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7518 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7519 {
7520 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7521 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7522 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7523 {
7524 Utf8Str strStartupLogDir = strHardeningLogFile;
7525 strStartupLogDir.stripFilename();
7526 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7527 file without stripping the file. */
7528 }
7529 strSupHardeningLogArg.append(strHardeningLogFile);
7530
7531 /* Remove legacy log filename to avoid confusion. */
7532 Utf8Str strOldStartupLogFile;
7533 getLogFolder(strOldStartupLogFile);
7534 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7535 RTFileDelete(strOldStartupLogFile.c_str());
7536 }
7537 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7538#else
7539 const char *pszSupHardeningLogArg = NULL;
7540#endif
7541
7542 Utf8Str strCanonicalName;
7543
7544#ifdef VBOX_WITH_QTGUI
7545 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7546 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7547 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7548 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7550 {
7551 strCanonicalName = "GUI/Qt";
7552# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7553 /* Modify the base path so that we don't need to use ".." below. */
7554 RTPathStripTrailingSlash(szPath);
7555 RTPathStripFilename(szPath);
7556 cchBufLeft = strlen(szPath);
7557 pszNamePart = szPath + cchBufLeft;
7558 cchBufLeft = sizeof(szPath) - cchBufLeft;
7559
7560# define OSX_APP_NAME "VirtualBoxVM"
7561# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7562
7563 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7564 if ( strAppOverride.contains(".")
7565 || strAppOverride.contains("/")
7566 || strAppOverride.contains("\\")
7567 || strAppOverride.contains(":"))
7568 strAppOverride.setNull();
7569 Utf8Str strAppPath;
7570 if (!strAppOverride.isEmpty())
7571 {
7572 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7573 Utf8Str strFullPath(szPath);
7574 strFullPath.append(strAppPath);
7575 /* there is a race, but people using this deserve the failure */
7576 if (!RTFileExists(strFullPath.c_str()))
7577 strAppOverride.setNull();
7578 }
7579 if (strAppOverride.isEmpty())
7580 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7581 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7582 strcpy(pszNamePart, strAppPath.c_str());
7583# else
7584# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7585 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7586# else
7587 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7588# endif
7589 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7590 strcpy(pszNamePart, s_szVirtualBox_exe);
7591# endif
7592
7593 Utf8Str idStr = mData->mUuid.toString();
7594 const char *apszArgs[] =
7595 {
7596 szPath,
7597 "--comment", mUserData->s.strName.c_str(),
7598 "--startvm", idStr.c_str(),
7599 "--no-startvm-errormsgbox",
7600 NULL, /* For "--separate". */
7601 NULL, /* For "--sup-startup-log". */
7602 NULL
7603 };
7604 unsigned iArg = 6;
7605 if (fSeparate)
7606 apszArgs[iArg++] = "--separate";
7607 apszArgs[iArg++] = pszSupHardeningLogArg;
7608
7609 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7610 }
7611#else /* !VBOX_WITH_QTGUI */
7612 if (0)
7613 ;
7614#endif /* VBOX_WITH_QTGUI */
7615
7616 else
7617
7618#ifdef VBOX_WITH_VBOXSDL
7619 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7620 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7621 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7622 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7623 {
7624 strCanonicalName = "GUI/SDL";
7625 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7626 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7627 strcpy(pszNamePart, s_szVBoxSDL_exe);
7628
7629 Utf8Str idStr = mData->mUuid.toString();
7630 const char *apszArgs[] =
7631 {
7632 szPath,
7633 "--comment", mUserData->s.strName.c_str(),
7634 "--startvm", idStr.c_str(),
7635 NULL, /* For "--separate". */
7636 NULL, /* For "--sup-startup-log". */
7637 NULL
7638 };
7639 unsigned iArg = 5;
7640 if (fSeparate)
7641 apszArgs[iArg++] = "--separate";
7642 apszArgs[iArg++] = pszSupHardeningLogArg;
7643
7644 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7645 }
7646#else /* !VBOX_WITH_VBOXSDL */
7647 if (0)
7648 ;
7649#endif /* !VBOX_WITH_VBOXSDL */
7650
7651 else
7652
7653#ifdef VBOX_WITH_HEADLESS
7654 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7655 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7656 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7657 )
7658 {
7659 strCanonicalName = "headless";
7660 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7661 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7662 * and a VM works even if the server has not been installed.
7663 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7664 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7665 * differently in 4.0 and 3.x.
7666 */
7667 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7668 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7669 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7670
7671 Utf8Str idStr = mData->mUuid.toString();
7672 const char *apszArgs[] =
7673 {
7674 szPath,
7675 "--comment", mUserData->s.strName.c_str(),
7676 "--startvm", idStr.c_str(),
7677 "--vrde", "config",
7678 NULL, /* For "--capture". */
7679 NULL, /* For "--sup-startup-log". */
7680 NULL
7681 };
7682 unsigned iArg = 7;
7683 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7684 apszArgs[iArg++] = "--capture";
7685 apszArgs[iArg++] = pszSupHardeningLogArg;
7686
7687# ifdef RT_OS_WINDOWS
7688 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7689# else
7690 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7691# endif
7692 }
7693#else /* !VBOX_WITH_HEADLESS */
7694 if (0)
7695 ;
7696#endif /* !VBOX_WITH_HEADLESS */
7697 else
7698 {
7699 RTEnvDestroy(env);
7700 return setError(E_INVALIDARG,
7701 tr("Invalid frontend name: '%s'"),
7702 strFrontend.c_str());
7703 }
7704
7705 RTEnvDestroy(env);
7706
7707 if (RT_FAILURE(vrc))
7708 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7709 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7710 mUserData->s.strName.c_str(), vrc);
7711
7712 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7713
7714 if (!fSeparate)
7715 {
7716 /*
7717 * Note that we don't release the lock here before calling the client,
7718 * because it doesn't need to call us back if called with a NULL argument.
7719 * Releasing the lock here is dangerous because we didn't prepare the
7720 * launch data yet, but the client we've just started may happen to be
7721 * too fast and call LockMachine() that will fail (because of PID, etc.),
7722 * so that the Machine will never get out of the Spawning session state.
7723 */
7724
7725 /* inform the session that it will be a remote one */
7726 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7727#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7728 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7729#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7730 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7731#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7732 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7733
7734 if (FAILED(rc))
7735 {
7736 /* restore the session state */
7737 mData->mSession.mState = SessionState_Unlocked;
7738 alock.release();
7739 mParent->i_addProcessToReap(pid);
7740 /* The failure may occur w/o any error info (from RPC), so provide one */
7741 return setError(VBOX_E_VM_ERROR,
7742 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7743 }
7744
7745 /* attach launch data to the machine */
7746 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7747 mData->mSession.mRemoteControls.push_back(aControl);
7748 mData->mSession.mProgress = aProgress;
7749 mData->mSession.mPID = pid;
7750 mData->mSession.mState = SessionState_Spawning;
7751 Assert(strCanonicalName.isNotEmpty());
7752 mData->mSession.mName = strCanonicalName;
7753 }
7754 else
7755 {
7756 /* For separate UI process we declare the launch as completed instantly, as the
7757 * actual headless VM start may or may not come. No point in remembering anything
7758 * yet, as what matters for us is when the headless VM gets started. */
7759 aProgress->i_notifyComplete(S_OK);
7760 }
7761
7762 alock.release();
7763 mParent->i_addProcessToReap(pid);
7764
7765 LogFlowThisFuncLeave();
7766 return S_OK;
7767}
7768
7769/**
7770 * Returns @c true if the given session machine instance has an open direct
7771 * session (and optionally also for direct sessions which are closing) and
7772 * returns the session control machine instance if so.
7773 *
7774 * Note that when the method returns @c false, the arguments remain unchanged.
7775 *
7776 * @param aMachine Session machine object.
7777 * @param aControl Direct session control object (optional).
7778 * @param aRequireVM If true then only allow VM sessions.
7779 * @param aAllowClosing If true then additionally a session which is currently
7780 * being closed will also be allowed.
7781 *
7782 * @note locks this object for reading.
7783 */
7784bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7785 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7786 bool aRequireVM /*= false*/,
7787 bool aAllowClosing /*= false*/)
7788{
7789 AutoLimitedCaller autoCaller(this);
7790 AssertComRCReturn(autoCaller.rc(), false);
7791
7792 /* just return false for inaccessible machines */
7793 if (getObjectState().getState() != ObjectState::Ready)
7794 return false;
7795
7796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 if ( ( mData->mSession.mState == SessionState_Locked
7799 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7800 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7801 )
7802 {
7803 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7804
7805 aMachine = mData->mSession.mMachine;
7806
7807 if (aControl != NULL)
7808 *aControl = mData->mSession.mDirectControl;
7809
7810 return true;
7811 }
7812
7813 return false;
7814}
7815
7816/**
7817 * Returns @c true if the given machine has an spawning direct session.
7818 *
7819 * @note locks this object for reading.
7820 */
7821bool Machine::i_isSessionSpawning()
7822{
7823 AutoLimitedCaller autoCaller(this);
7824 AssertComRCReturn(autoCaller.rc(), false);
7825
7826 /* just return false for inaccessible machines */
7827 if (getObjectState().getState() != ObjectState::Ready)
7828 return false;
7829
7830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7831
7832 if (mData->mSession.mState == SessionState_Spawning)
7833 return true;
7834
7835 return false;
7836}
7837
7838/**
7839 * Called from the client watcher thread to check for unexpected client process
7840 * death during Session_Spawning state (e.g. before it successfully opened a
7841 * direct session).
7842 *
7843 * On Win32 and on OS/2, this method is called only when we've got the
7844 * direct client's process termination notification, so it always returns @c
7845 * true.
7846 *
7847 * On other platforms, this method returns @c true if the client process is
7848 * terminated and @c false if it's still alive.
7849 *
7850 * @note Locks this object for writing.
7851 */
7852bool Machine::i_checkForSpawnFailure()
7853{
7854 AutoCaller autoCaller(this);
7855 if (!autoCaller.isOk())
7856 {
7857 /* nothing to do */
7858 LogFlowThisFunc(("Already uninitialized!\n"));
7859 return true;
7860 }
7861
7862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7863
7864 if (mData->mSession.mState != SessionState_Spawning)
7865 {
7866 /* nothing to do */
7867 LogFlowThisFunc(("Not spawning any more!\n"));
7868 return true;
7869 }
7870
7871 HRESULT rc = S_OK;
7872
7873 /* PID not yet initialized, skip check. */
7874 if (mData->mSession.mPID == NIL_RTPROCESS)
7875 return false;
7876
7877 RTPROCSTATUS status;
7878 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7879
7880 if (vrc != VERR_PROCESS_RUNNING)
7881 {
7882 Utf8Str strExtraInfo;
7883
7884#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7885 /* If the startup logfile exists and is of non-zero length, tell the
7886 user to look there for more details to encourage them to attach it
7887 when reporting startup issues. */
7888 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7889 uint64_t cbStartupLogFile = 0;
7890 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7891 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7892 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7893#endif
7894
7895 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7896 rc = setError(E_FAIL,
7897 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7898 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7899 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7900 rc = setError(E_FAIL,
7901 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7902 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7903 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7904 rc = setError(E_FAIL,
7905 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7906 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7907 else
7908 rc = setErrorBoth(E_FAIL, vrc,
7909 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7910 i_getName().c_str(), vrc, strExtraInfo.c_str());
7911 }
7912
7913 if (FAILED(rc))
7914 {
7915 /* Close the remote session, remove the remote control from the list
7916 * and reset session state to Closed (@note keep the code in sync with
7917 * the relevant part in LockMachine()). */
7918
7919 Assert(mData->mSession.mRemoteControls.size() == 1);
7920 if (mData->mSession.mRemoteControls.size() == 1)
7921 {
7922 ErrorInfoKeeper eik;
7923 mData->mSession.mRemoteControls.front()->Uninitialize();
7924 }
7925
7926 mData->mSession.mRemoteControls.clear();
7927 mData->mSession.mState = SessionState_Unlocked;
7928
7929 /* finalize the progress after setting the state */
7930 if (!mData->mSession.mProgress.isNull())
7931 {
7932 mData->mSession.mProgress->notifyComplete(rc);
7933 mData->mSession.mProgress.setNull();
7934 }
7935
7936 mData->mSession.mPID = NIL_RTPROCESS;
7937
7938 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7939 return true;
7940 }
7941
7942 return false;
7943}
7944
7945/**
7946 * Checks whether the machine can be registered. If so, commits and saves
7947 * all settings.
7948 *
7949 * @note Must be called from mParent's write lock. Locks this object and
7950 * children for writing.
7951 */
7952HRESULT Machine::i_prepareRegister()
7953{
7954 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7955
7956 AutoLimitedCaller autoCaller(this);
7957 AssertComRCReturnRC(autoCaller.rc());
7958
7959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7960
7961 /* wait for state dependents to drop to zero */
7962 i_ensureNoStateDependencies();
7963
7964 if (!mData->mAccessible)
7965 return setError(VBOX_E_INVALID_OBJECT_STATE,
7966 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7967 mUserData->s.strName.c_str(),
7968 mData->mUuid.toString().c_str());
7969
7970 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7971
7972 if (mData->mRegistered)
7973 return setError(VBOX_E_INVALID_OBJECT_STATE,
7974 tr("The machine '%s' with UUID {%s} is already registered"),
7975 mUserData->s.strName.c_str(),
7976 mData->mUuid.toString().c_str());
7977
7978 HRESULT rc = S_OK;
7979
7980 // Ensure the settings are saved. If we are going to be registered and
7981 // no config file exists yet, create it by calling i_saveSettings() too.
7982 if ( (mData->flModifications)
7983 || (!mData->pMachineConfigFile->fileExists())
7984 )
7985 {
7986 rc = i_saveSettings(NULL);
7987 // no need to check whether VirtualBox.xml needs saving too since
7988 // we can't have a machine XML file rename pending
7989 if (FAILED(rc)) return rc;
7990 }
7991
7992 /* more config checking goes here */
7993
7994 if (SUCCEEDED(rc))
7995 {
7996 /* we may have had implicit modifications we want to fix on success */
7997 i_commit();
7998
7999 mData->mRegistered = true;
8000 }
8001 else
8002 {
8003 /* we may have had implicit modifications we want to cancel on failure*/
8004 i_rollback(false /* aNotify */);
8005 }
8006
8007 return rc;
8008}
8009
8010/**
8011 * Increases the number of objects dependent on the machine state or on the
8012 * registered state. Guarantees that these two states will not change at least
8013 * until #i_releaseStateDependency() is called.
8014 *
8015 * Depending on the @a aDepType value, additional state checks may be made.
8016 * These checks will set extended error info on failure. See
8017 * #i_checkStateDependency() for more info.
8018 *
8019 * If this method returns a failure, the dependency is not added and the caller
8020 * is not allowed to rely on any particular machine state or registration state
8021 * value and may return the failed result code to the upper level.
8022 *
8023 * @param aDepType Dependency type to add.
8024 * @param aState Current machine state (NULL if not interested).
8025 * @param aRegistered Current registered state (NULL if not interested).
8026 *
8027 * @note Locks this object for writing.
8028 */
8029HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8030 MachineState_T *aState /* = NULL */,
8031 BOOL *aRegistered /* = NULL */)
8032{
8033 AutoCaller autoCaller(this);
8034 AssertComRCReturnRC(autoCaller.rc());
8035
8036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8037
8038 HRESULT rc = i_checkStateDependency(aDepType);
8039 if (FAILED(rc)) return rc;
8040
8041 {
8042 if (mData->mMachineStateChangePending != 0)
8043 {
8044 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8045 * drop to zero so don't add more. It may make sense to wait a bit
8046 * and retry before reporting an error (since the pending state
8047 * transition should be really quick) but let's just assert for
8048 * now to see if it ever happens on practice. */
8049
8050 AssertFailed();
8051
8052 return setError(E_ACCESSDENIED,
8053 tr("Machine state change is in progress. Please retry the operation later."));
8054 }
8055
8056 ++mData->mMachineStateDeps;
8057 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8058 }
8059
8060 if (aState)
8061 *aState = mData->mMachineState;
8062 if (aRegistered)
8063 *aRegistered = mData->mRegistered;
8064
8065 return S_OK;
8066}
8067
8068/**
8069 * Decreases the number of objects dependent on the machine state.
8070 * Must always complete the #i_addStateDependency() call after the state
8071 * dependency is no more necessary.
8072 */
8073void Machine::i_releaseStateDependency()
8074{
8075 AutoCaller autoCaller(this);
8076 AssertComRCReturnVoid(autoCaller.rc());
8077
8078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8079
8080 /* releaseStateDependency() w/o addStateDependency()? */
8081 AssertReturnVoid(mData->mMachineStateDeps != 0);
8082 -- mData->mMachineStateDeps;
8083
8084 if (mData->mMachineStateDeps == 0)
8085 {
8086 /* inform i_ensureNoStateDependencies() that there are no more deps */
8087 if (mData->mMachineStateChangePending != 0)
8088 {
8089 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8090 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8091 }
8092 }
8093}
8094
8095Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8096{
8097 /* start with nothing found */
8098 Utf8Str strResult("");
8099
8100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8101
8102 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8103 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8104 // found:
8105 strResult = it->second; // source is a Utf8Str
8106
8107 return strResult;
8108}
8109
8110// protected methods
8111/////////////////////////////////////////////////////////////////////////////
8112
8113/**
8114 * Performs machine state checks based on the @a aDepType value. If a check
8115 * fails, this method will set extended error info, otherwise it will return
8116 * S_OK. It is supposed, that on failure, the caller will immediately return
8117 * the return value of this method to the upper level.
8118 *
8119 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8120 *
8121 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8122 * current state of this machine object allows to change settings of the
8123 * machine (i.e. the machine is not registered, or registered but not running
8124 * and not saved). It is useful to call this method from Machine setters
8125 * before performing any change.
8126 *
8127 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8128 * as for MutableStateDep except that if the machine is saved, S_OK is also
8129 * returned. This is useful in setters which allow changing machine
8130 * properties when it is in the saved state.
8131 *
8132 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8133 * if the current state of this machine object allows to change runtime
8134 * changeable settings of the machine (i.e. the machine is not registered, or
8135 * registered but either running or not running and not saved). It is useful
8136 * to call this method from Machine setters before performing any changes to
8137 * runtime changeable settings.
8138 *
8139 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8140 * the same as for MutableOrRunningStateDep except that if the machine is
8141 * saved, S_OK is also returned. This is useful in setters which allow
8142 * changing runtime and saved state changeable machine properties.
8143 *
8144 * @param aDepType Dependency type to check.
8145 *
8146 * @note Non Machine based classes should use #i_addStateDependency() and
8147 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8148 * template.
8149 *
8150 * @note This method must be called from under this object's read or write
8151 * lock.
8152 */
8153HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8154{
8155 switch (aDepType)
8156 {
8157 case AnyStateDep:
8158 {
8159 break;
8160 }
8161 case MutableStateDep:
8162 {
8163 if ( mData->mRegistered
8164 && ( !i_isSessionMachine()
8165 || ( mData->mMachineState != MachineState_Aborted
8166 && mData->mMachineState != MachineState_Teleported
8167 && mData->mMachineState != MachineState_PoweredOff
8168 )
8169 )
8170 )
8171 return setError(VBOX_E_INVALID_VM_STATE,
8172 tr("The machine is not mutable (state is %s)"),
8173 Global::stringifyMachineState(mData->mMachineState));
8174 break;
8175 }
8176 case MutableOrSavedStateDep:
8177 {
8178 if ( mData->mRegistered
8179 && ( !i_isSessionMachine()
8180 || ( mData->mMachineState != MachineState_Aborted
8181 && mData->mMachineState != MachineState_Teleported
8182 && mData->mMachineState != MachineState_Saved
8183 && mData->mMachineState != MachineState_PoweredOff
8184 )
8185 )
8186 )
8187 return setError(VBOX_E_INVALID_VM_STATE,
8188 tr("The machine is not mutable or saved (state is %s)"),
8189 Global::stringifyMachineState(mData->mMachineState));
8190 break;
8191 }
8192 case MutableOrRunningStateDep:
8193 {
8194 if ( mData->mRegistered
8195 && ( !i_isSessionMachine()
8196 || ( mData->mMachineState != MachineState_Aborted
8197 && mData->mMachineState != MachineState_Teleported
8198 && mData->mMachineState != MachineState_PoweredOff
8199 && !Global::IsOnline(mData->mMachineState)
8200 )
8201 )
8202 )
8203 return setError(VBOX_E_INVALID_VM_STATE,
8204 tr("The machine is not mutable or running (state is %s)"),
8205 Global::stringifyMachineState(mData->mMachineState));
8206 break;
8207 }
8208 case MutableOrSavedOrRunningStateDep:
8209 {
8210 if ( mData->mRegistered
8211 && ( !i_isSessionMachine()
8212 || ( mData->mMachineState != MachineState_Aborted
8213 && mData->mMachineState != MachineState_Teleported
8214 && mData->mMachineState != MachineState_Saved
8215 && mData->mMachineState != MachineState_PoweredOff
8216 && !Global::IsOnline(mData->mMachineState)
8217 )
8218 )
8219 )
8220 return setError(VBOX_E_INVALID_VM_STATE,
8221 tr("The machine is not mutable, saved or running (state is %s)"),
8222 Global::stringifyMachineState(mData->mMachineState));
8223 break;
8224 }
8225 }
8226
8227 return S_OK;
8228}
8229
8230/**
8231 * Helper to initialize all associated child objects and allocate data
8232 * structures.
8233 *
8234 * This method must be called as a part of the object's initialization procedure
8235 * (usually done in the #init() method).
8236 *
8237 * @note Must be called only from #init() or from #i_registeredInit().
8238 */
8239HRESULT Machine::initDataAndChildObjects()
8240{
8241 AutoCaller autoCaller(this);
8242 AssertComRCReturnRC(autoCaller.rc());
8243 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8244 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8245
8246 AssertReturn(!mData->mAccessible, E_FAIL);
8247
8248 /* allocate data structures */
8249 mSSData.allocate();
8250 mUserData.allocate();
8251 mHWData.allocate();
8252 mMediumAttachments.allocate();
8253 mStorageControllers.allocate();
8254 mUSBControllers.allocate();
8255
8256 /* initialize mOSTypeId */
8257 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8258
8259/** @todo r=bird: init() methods never fails, right? Why don't we make them
8260 * return void then! */
8261
8262 /* create associated BIOS settings object */
8263 unconst(mBIOSSettings).createObject();
8264 mBIOSSettings->init(this);
8265
8266 /* create associated record settings object */
8267 unconst(mRecordingSettings).createObject();
8268 mRecordingSettings->init(this);
8269
8270 /* create an associated VRDE object (default is disabled) */
8271 unconst(mVRDEServer).createObject();
8272 mVRDEServer->init(this);
8273
8274 /* create associated serial port objects */
8275 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8276 {
8277 unconst(mSerialPorts[slot]).createObject();
8278 mSerialPorts[slot]->init(this, slot);
8279 }
8280
8281 /* create associated parallel port objects */
8282 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8283 {
8284 unconst(mParallelPorts[slot]).createObject();
8285 mParallelPorts[slot]->init(this, slot);
8286 }
8287
8288 /* create the audio adapter object (always present, default is disabled) */
8289 unconst(mAudioAdapter).createObject();
8290 mAudioAdapter->init(this);
8291
8292 /* create the USB device filters object (always present) */
8293 unconst(mUSBDeviceFilters).createObject();
8294 mUSBDeviceFilters->init(this);
8295
8296 /* create associated network adapter objects */
8297 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8298 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8299 {
8300 unconst(mNetworkAdapters[slot]).createObject();
8301 mNetworkAdapters[slot]->init(this, slot);
8302 }
8303
8304 /* create the bandwidth control */
8305 unconst(mBandwidthControl).createObject();
8306 mBandwidthControl->init(this);
8307
8308 return S_OK;
8309}
8310
8311/**
8312 * Helper to uninitialize all associated child objects and to free all data
8313 * structures.
8314 *
8315 * This method must be called as a part of the object's uninitialization
8316 * procedure (usually done in the #uninit() method).
8317 *
8318 * @note Must be called only from #uninit() or from #i_registeredInit().
8319 */
8320void Machine::uninitDataAndChildObjects()
8321{
8322 AutoCaller autoCaller(this);
8323 AssertComRCReturnVoid(autoCaller.rc());
8324 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8325 || getObjectState().getState() == ObjectState::Limited);
8326
8327 /* tell all our other child objects we've been uninitialized */
8328 if (mBandwidthControl)
8329 {
8330 mBandwidthControl->uninit();
8331 unconst(mBandwidthControl).setNull();
8332 }
8333
8334 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8335 {
8336 if (mNetworkAdapters[slot])
8337 {
8338 mNetworkAdapters[slot]->uninit();
8339 unconst(mNetworkAdapters[slot]).setNull();
8340 }
8341 }
8342
8343 if (mUSBDeviceFilters)
8344 {
8345 mUSBDeviceFilters->uninit();
8346 unconst(mUSBDeviceFilters).setNull();
8347 }
8348
8349 if (mAudioAdapter)
8350 {
8351 mAudioAdapter->uninit();
8352 unconst(mAudioAdapter).setNull();
8353 }
8354
8355 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8356 {
8357 if (mParallelPorts[slot])
8358 {
8359 mParallelPorts[slot]->uninit();
8360 unconst(mParallelPorts[slot]).setNull();
8361 }
8362 }
8363
8364 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8365 {
8366 if (mSerialPorts[slot])
8367 {
8368 mSerialPorts[slot]->uninit();
8369 unconst(mSerialPorts[slot]).setNull();
8370 }
8371 }
8372
8373 if (mVRDEServer)
8374 {
8375 mVRDEServer->uninit();
8376 unconst(mVRDEServer).setNull();
8377 }
8378
8379 if (mBIOSSettings)
8380 {
8381 mBIOSSettings->uninit();
8382 unconst(mBIOSSettings).setNull();
8383 }
8384
8385 if (mRecordingSettings)
8386 {
8387 mRecordingSettings->uninit();
8388 unconst(mRecordingSettings).setNull();
8389 }
8390
8391 /* Deassociate media (only when a real Machine or a SnapshotMachine
8392 * instance is uninitialized; SessionMachine instances refer to real
8393 * Machine media). This is necessary for a clean re-initialization of
8394 * the VM after successfully re-checking the accessibility state. Note
8395 * that in case of normal Machine or SnapshotMachine uninitialization (as
8396 * a result of unregistering or deleting the snapshot), outdated media
8397 * attachments will already be uninitialized and deleted, so this
8398 * code will not affect them. */
8399 if ( !mMediumAttachments.isNull()
8400 && !i_isSessionMachine()
8401 )
8402 {
8403 for (MediumAttachmentList::const_iterator
8404 it = mMediumAttachments->begin();
8405 it != mMediumAttachments->end();
8406 ++it)
8407 {
8408 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8409 if (pMedium.isNull())
8410 continue;
8411 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8412 AssertComRC(rc);
8413 }
8414 }
8415
8416 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8417 {
8418 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8419 if (mData->mFirstSnapshot)
8420 {
8421 // snapshots tree is protected by machine write lock; strictly
8422 // this isn't necessary here since we're deleting the entire
8423 // machine, but otherwise we assert in Snapshot::uninit()
8424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8425 mData->mFirstSnapshot->uninit();
8426 mData->mFirstSnapshot.setNull();
8427 }
8428
8429 mData->mCurrentSnapshot.setNull();
8430 }
8431
8432 /* free data structures (the essential mData structure is not freed here
8433 * since it may be still in use) */
8434 mMediumAttachments.free();
8435 mStorageControllers.free();
8436 mUSBControllers.free();
8437 mHWData.free();
8438 mUserData.free();
8439 mSSData.free();
8440}
8441
8442/**
8443 * Returns a pointer to the Machine object for this machine that acts like a
8444 * parent for complex machine data objects such as shared folders, etc.
8445 *
8446 * For primary Machine objects and for SnapshotMachine objects, returns this
8447 * object's pointer itself. For SessionMachine objects, returns the peer
8448 * (primary) machine pointer.
8449 */
8450Machine *Machine::i_getMachine()
8451{
8452 if (i_isSessionMachine())
8453 return (Machine*)mPeer;
8454 return this;
8455}
8456
8457/**
8458 * Makes sure that there are no machine state dependents. If necessary, waits
8459 * for the number of dependents to drop to zero.
8460 *
8461 * Make sure this method is called from under this object's write lock to
8462 * guarantee that no new dependents may be added when this method returns
8463 * control to the caller.
8464 *
8465 * @note Locks this object for writing. The lock will be released while waiting
8466 * (if necessary).
8467 *
8468 * @warning To be used only in methods that change the machine state!
8469 */
8470void Machine::i_ensureNoStateDependencies()
8471{
8472 AssertReturnVoid(isWriteLockOnCurrentThread());
8473
8474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8475
8476 /* Wait for all state dependents if necessary */
8477 if (mData->mMachineStateDeps != 0)
8478 {
8479 /* lazy semaphore creation */
8480 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8481 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8482
8483 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8484 mData->mMachineStateDeps));
8485
8486 ++mData->mMachineStateChangePending;
8487
8488 /* reset the semaphore before waiting, the last dependent will signal
8489 * it */
8490 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8491
8492 alock.release();
8493
8494 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8495
8496 alock.acquire();
8497
8498 -- mData->mMachineStateChangePending;
8499 }
8500}
8501
8502/**
8503 * Changes the machine state and informs callbacks.
8504 *
8505 * This method is not intended to fail so it either returns S_OK or asserts (and
8506 * returns a failure).
8507 *
8508 * @note Locks this object for writing.
8509 */
8510HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8511{
8512 LogFlowThisFuncEnter();
8513 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8514 Assert(aMachineState != MachineState_Null);
8515
8516 AutoCaller autoCaller(this);
8517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8518
8519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8520
8521 /* wait for state dependents to drop to zero */
8522 i_ensureNoStateDependencies();
8523
8524 MachineState_T const enmOldState = mData->mMachineState;
8525 if (enmOldState != aMachineState)
8526 {
8527 mData->mMachineState = aMachineState;
8528 RTTimeNow(&mData->mLastStateChange);
8529
8530#ifdef VBOX_WITH_DTRACE_R3_MAIN
8531 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8532#endif
8533 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8534 }
8535
8536 LogFlowThisFuncLeave();
8537 return S_OK;
8538}
8539
8540/**
8541 * Searches for a shared folder with the given logical name
8542 * in the collection of shared folders.
8543 *
8544 * @param aName logical name of the shared folder
8545 * @param aSharedFolder where to return the found object
8546 * @param aSetError whether to set the error info if the folder is
8547 * not found
8548 * @return
8549 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8550 *
8551 * @note
8552 * must be called from under the object's lock!
8553 */
8554HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8555 ComObjPtr<SharedFolder> &aSharedFolder,
8556 bool aSetError /* = false */)
8557{
8558 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8559 for (HWData::SharedFolderList::const_iterator
8560 it = mHWData->mSharedFolders.begin();
8561 it != mHWData->mSharedFolders.end();
8562 ++it)
8563 {
8564 SharedFolder *pSF = *it;
8565 AutoCaller autoCaller(pSF);
8566 if (pSF->i_getName() == aName)
8567 {
8568 aSharedFolder = pSF;
8569 rc = S_OK;
8570 break;
8571 }
8572 }
8573
8574 if (aSetError && FAILED(rc))
8575 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8576
8577 return rc;
8578}
8579
8580/**
8581 * Initializes all machine instance data from the given settings structures
8582 * from XML. The exception is the machine UUID which needs special handling
8583 * depending on the caller's use case, so the caller needs to set that herself.
8584 *
8585 * This gets called in several contexts during machine initialization:
8586 *
8587 * -- When machine XML exists on disk already and needs to be loaded into memory,
8588 * for example, from #i_registeredInit() to load all registered machines on
8589 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8590 * attached to the machine should be part of some media registry already.
8591 *
8592 * -- During OVF import, when a machine config has been constructed from an
8593 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8594 * ensure that the media listed as attachments in the config (which have
8595 * been imported from the OVF) receive the correct registry ID.
8596 *
8597 * -- During VM cloning.
8598 *
8599 * @param config Machine settings from XML.
8600 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8601 * for each attached medium in the config.
8602 * @return
8603 */
8604HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8605 const Guid *puuidRegistry)
8606{
8607 // copy name, description, OS type, teleporter, UTC etc.
8608 mUserData->s = config.machineUserData;
8609
8610 // look up the object by Id to check it is valid
8611 ComObjPtr<GuestOSType> pGuestOSType;
8612 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8613 if (!pGuestOSType.isNull())
8614 mUserData->s.strOsType = pGuestOSType->i_id();
8615
8616 // stateFile (optional)
8617 if (config.strStateFile.isEmpty())
8618 mSSData->strStateFilePath.setNull();
8619 else
8620 {
8621 Utf8Str stateFilePathFull(config.strStateFile);
8622 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8623 if (RT_FAILURE(vrc))
8624 return setErrorBoth(E_FAIL, vrc,
8625 tr("Invalid saved state file path '%s' (%Rrc)"),
8626 config.strStateFile.c_str(),
8627 vrc);
8628 mSSData->strStateFilePath = stateFilePathFull;
8629 }
8630
8631 // snapshot folder needs special processing so set it again
8632 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8633 if (FAILED(rc)) return rc;
8634
8635 /* Copy the extra data items (config may or may not be the same as
8636 * mData->pMachineConfigFile) if necessary. When loading the XML files
8637 * from disk they are the same, but not for OVF import. */
8638 if (mData->pMachineConfigFile != &config)
8639 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8640
8641 /* currentStateModified (optional, default is true) */
8642 mData->mCurrentStateModified = config.fCurrentStateModified;
8643
8644 mData->mLastStateChange = config.timeLastStateChange;
8645
8646 /*
8647 * note: all mUserData members must be assigned prior this point because
8648 * we need to commit changes in order to let mUserData be shared by all
8649 * snapshot machine instances.
8650 */
8651 mUserData.commitCopy();
8652
8653 // machine registry, if present (must be loaded before snapshots)
8654 if (config.canHaveOwnMediaRegistry())
8655 {
8656 // determine machine folder
8657 Utf8Str strMachineFolder = i_getSettingsFileFull();
8658 strMachineFolder.stripFilename();
8659 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8660 config.mediaRegistry,
8661 strMachineFolder);
8662 if (FAILED(rc)) return rc;
8663 }
8664
8665 /* Snapshot node (optional) */
8666 size_t cRootSnapshots;
8667 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8668 {
8669 // there must be only one root snapshot
8670 Assert(cRootSnapshots == 1);
8671
8672 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8673
8674 rc = i_loadSnapshot(snap,
8675 config.uuidCurrentSnapshot,
8676 NULL); // no parent == first snapshot
8677 if (FAILED(rc)) return rc;
8678 }
8679
8680 // hardware data
8681 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8682 if (FAILED(rc)) return rc;
8683
8684 /*
8685 * NOTE: the assignment below must be the last thing to do,
8686 * otherwise it will be not possible to change the settings
8687 * somewhere in the code above because all setters will be
8688 * blocked by i_checkStateDependency(MutableStateDep).
8689 */
8690
8691 /* set the machine state to Aborted or Saved when appropriate */
8692 if (config.fAborted)
8693 {
8694 mSSData->strStateFilePath.setNull();
8695
8696 /* no need to use i_setMachineState() during init() */
8697 mData->mMachineState = MachineState_Aborted;
8698 }
8699 else if (!mSSData->strStateFilePath.isEmpty())
8700 {
8701 /* no need to use i_setMachineState() during init() */
8702 mData->mMachineState = MachineState_Saved;
8703 }
8704
8705 // after loading settings, we are no longer different from the XML on disk
8706 mData->flModifications = 0;
8707
8708 return S_OK;
8709}
8710
8711/**
8712 * Recursively loads all snapshots starting from the given.
8713 *
8714 * @param data snapshot settings.
8715 * @param aCurSnapshotId Current snapshot ID from the settings file.
8716 * @param aParentSnapshot Parent snapshot.
8717 */
8718HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8719 const Guid &aCurSnapshotId,
8720 Snapshot *aParentSnapshot)
8721{
8722 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8723 AssertReturn(!i_isSessionMachine(), E_FAIL);
8724
8725 HRESULT rc = S_OK;
8726
8727 Utf8Str strStateFile;
8728 if (!data.strStateFile.isEmpty())
8729 {
8730 /* optional */
8731 strStateFile = data.strStateFile;
8732 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8733 if (RT_FAILURE(vrc))
8734 return setErrorBoth(E_FAIL, vrc,
8735 tr("Invalid saved state file path '%s' (%Rrc)"),
8736 strStateFile.c_str(),
8737 vrc);
8738 }
8739
8740 /* create a snapshot machine object */
8741 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8742 pSnapshotMachine.createObject();
8743 rc = pSnapshotMachine->initFromSettings(this,
8744 data.hardware,
8745 &data.debugging,
8746 &data.autostart,
8747 data.uuid.ref(),
8748 strStateFile);
8749 if (FAILED(rc)) return rc;
8750
8751 /* create a snapshot object */
8752 ComObjPtr<Snapshot> pSnapshot;
8753 pSnapshot.createObject();
8754 /* initialize the snapshot */
8755 rc = pSnapshot->init(mParent, // VirtualBox object
8756 data.uuid,
8757 data.strName,
8758 data.strDescription,
8759 data.timestamp,
8760 pSnapshotMachine,
8761 aParentSnapshot);
8762 if (FAILED(rc)) return rc;
8763
8764 /* memorize the first snapshot if necessary */
8765 if (!mData->mFirstSnapshot)
8766 mData->mFirstSnapshot = pSnapshot;
8767
8768 /* memorize the current snapshot when appropriate */
8769 if ( !mData->mCurrentSnapshot
8770 && pSnapshot->i_getId() == aCurSnapshotId
8771 )
8772 mData->mCurrentSnapshot = pSnapshot;
8773
8774 // now create the children
8775 for (settings::SnapshotsList::const_iterator
8776 it = data.llChildSnapshots.begin();
8777 it != data.llChildSnapshots.end();
8778 ++it)
8779 {
8780 const settings::Snapshot &childData = *it;
8781 // recurse
8782 rc = i_loadSnapshot(childData,
8783 aCurSnapshotId,
8784 pSnapshot); // parent = the one we created above
8785 if (FAILED(rc)) return rc;
8786 }
8787
8788 return rc;
8789}
8790
8791/**
8792 * Loads settings into mHWData.
8793 *
8794 * @param puuidRegistry Registry ID.
8795 * @param puuidSnapshot Snapshot ID
8796 * @param data Reference to the hardware settings.
8797 * @param pDbg Pointer to the debugging settings.
8798 * @param pAutostart Pointer to the autostart settings.
8799 */
8800HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8801 const Guid *puuidSnapshot,
8802 const settings::Hardware &data,
8803 const settings::Debugging *pDbg,
8804 const settings::Autostart *pAutostart)
8805{
8806 AssertReturn(!i_isSessionMachine(), E_FAIL);
8807
8808 HRESULT rc = S_OK;
8809
8810 try
8811 {
8812 ComObjPtr<GuestOSType> pGuestOSType;
8813 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8814
8815 /* The hardware version attribute (optional). */
8816 mHWData->mHWVersion = data.strVersion;
8817 mHWData->mHardwareUUID = data.uuid;
8818
8819 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8820 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8821 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8822 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8823 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8824 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8825 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8826 mHWData->mPAEEnabled = data.fPAE;
8827 mHWData->mLongMode = data.enmLongMode;
8828 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8829 mHWData->mAPIC = data.fAPIC;
8830 mHWData->mX2APIC = data.fX2APIC;
8831 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8832 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8833 mHWData->mSpecCtrl = data.fSpecCtrl;
8834 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8835 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8836 mHWData->mCPUCount = data.cCPUs;
8837 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8838 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8839 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8840 mHWData->mCpuProfile = data.strCpuProfile;
8841
8842 // cpu
8843 if (mHWData->mCPUHotPlugEnabled)
8844 {
8845 for (settings::CpuList::const_iterator
8846 it = data.llCpus.begin();
8847 it != data.llCpus.end();
8848 ++it)
8849 {
8850 const settings::Cpu &cpu = *it;
8851
8852 mHWData->mCPUAttached[cpu.ulId] = true;
8853 }
8854 }
8855
8856 // cpuid leafs
8857 for (settings::CpuIdLeafsList::const_iterator
8858 it = data.llCpuIdLeafs.begin();
8859 it != data.llCpuIdLeafs.end();
8860 ++it)
8861 {
8862 const settings::CpuIdLeaf &rLeaf= *it;
8863 if ( rLeaf.idx < UINT32_C(0x20)
8864 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8865 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8866 mHWData->mCpuIdLeafList.push_back(rLeaf);
8867 /* else: just ignore */
8868 }
8869
8870 mHWData->mMemorySize = data.ulMemorySizeMB;
8871 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8872
8873 // boot order
8874 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8875 {
8876 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8877 if (it == data.mapBootOrder.end())
8878 mHWData->mBootOrder[i] = DeviceType_Null;
8879 else
8880 mHWData->mBootOrder[i] = it->second;
8881 }
8882
8883 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8884 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8885 mHWData->mMonitorCount = data.cMonitors;
8886 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8887 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8888 mHWData->mFirmwareType = data.firmwareType;
8889 mHWData->mPointingHIDType = data.pointingHIDType;
8890 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8891 mHWData->mChipsetType = data.chipsetType;
8892 mHWData->mParavirtProvider = data.paravirtProvider;
8893 mHWData->mParavirtDebug = data.strParavirtDebug;
8894 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8895 mHWData->mHPETEnabled = data.fHPETEnabled;
8896
8897 /* VRDEServer */
8898 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8899 if (FAILED(rc)) return rc;
8900
8901 /* BIOS */
8902 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8903 if (FAILED(rc)) return rc;
8904
8905 /* Recording settings */
8906 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8907 if (FAILED(rc)) return rc;
8908
8909 // Bandwidth control (must come before network adapters)
8910 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8911 if (FAILED(rc)) return rc;
8912
8913 /* USB controllers */
8914 for (settings::USBControllerList::const_iterator
8915 it = data.usbSettings.llUSBControllers.begin();
8916 it != data.usbSettings.llUSBControllers.end();
8917 ++it)
8918 {
8919 const settings::USBController &settingsCtrl = *it;
8920 ComObjPtr<USBController> newCtrl;
8921
8922 newCtrl.createObject();
8923 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8924 mUSBControllers->push_back(newCtrl);
8925 }
8926
8927 /* USB device filters */
8928 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8929 if (FAILED(rc)) return rc;
8930
8931 // network adapters (establish array size first and apply defaults, to
8932 // ensure reading the same settings as we saved, since the list skips
8933 // adapters having defaults)
8934 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8935 size_t oldCount = mNetworkAdapters.size();
8936 if (newCount > oldCount)
8937 {
8938 mNetworkAdapters.resize(newCount);
8939 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8940 {
8941 unconst(mNetworkAdapters[slot]).createObject();
8942 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8943 }
8944 }
8945 else if (newCount < oldCount)
8946 mNetworkAdapters.resize(newCount);
8947 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8948 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8949 for (settings::NetworkAdaptersList::const_iterator
8950 it = data.llNetworkAdapters.begin();
8951 it != data.llNetworkAdapters.end();
8952 ++it)
8953 {
8954 const settings::NetworkAdapter &nic = *it;
8955
8956 /* slot uniqueness is guaranteed by XML Schema */
8957 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8958 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8959 if (FAILED(rc)) return rc;
8960 }
8961
8962 // serial ports (establish defaults first, to ensure reading the same
8963 // settings as we saved, since the list skips ports having defaults)
8964 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8965 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8966 for (settings::SerialPortsList::const_iterator
8967 it = data.llSerialPorts.begin();
8968 it != data.llSerialPorts.end();
8969 ++it)
8970 {
8971 const settings::SerialPort &s = *it;
8972
8973 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8974 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8975 if (FAILED(rc)) return rc;
8976 }
8977
8978 // parallel ports (establish defaults first, to ensure reading the same
8979 // settings as we saved, since the list skips ports having defaults)
8980 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8981 mParallelPorts[i]->i_applyDefaults();
8982 for (settings::ParallelPortsList::const_iterator
8983 it = data.llParallelPorts.begin();
8984 it != data.llParallelPorts.end();
8985 ++it)
8986 {
8987 const settings::ParallelPort &p = *it;
8988
8989 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8990 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8991 if (FAILED(rc)) return rc;
8992 }
8993
8994 /* AudioAdapter */
8995 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8996 if (FAILED(rc)) return rc;
8997
8998 /* storage controllers */
8999 rc = i_loadStorageControllers(data.storage,
9000 puuidRegistry,
9001 puuidSnapshot);
9002 if (FAILED(rc)) return rc;
9003
9004 /* Shared folders */
9005 for (settings::SharedFoldersList::const_iterator
9006 it = data.llSharedFolders.begin();
9007 it != data.llSharedFolders.end();
9008 ++it)
9009 {
9010 const settings::SharedFolder &sf = *it;
9011
9012 ComObjPtr<SharedFolder> sharedFolder;
9013 /* Check for double entries. Not allowed! */
9014 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9015 if (SUCCEEDED(rc))
9016 return setError(VBOX_E_OBJECT_IN_USE,
9017 tr("Shared folder named '%s' already exists"),
9018 sf.strName.c_str());
9019
9020 /* Create the new shared folder. Don't break on error. This will be
9021 * reported when the machine starts. */
9022 sharedFolder.createObject();
9023 rc = sharedFolder->init(i_getMachine(),
9024 sf.strName,
9025 sf.strHostPath,
9026 RT_BOOL(sf.fWritable),
9027 RT_BOOL(sf.fAutoMount),
9028 sf.strAutoMountPoint,
9029 false /* fFailOnError */);
9030 if (FAILED(rc)) return rc;
9031 mHWData->mSharedFolders.push_back(sharedFolder);
9032 }
9033
9034 // Clipboard
9035 mHWData->mClipboardMode = data.clipboardMode;
9036
9037 // drag'n'drop
9038 mHWData->mDnDMode = data.dndMode;
9039
9040 // guest settings
9041 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9042
9043 // IO settings
9044 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9045 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9046
9047 // Host PCI devices
9048 for (settings::HostPCIDeviceAttachmentList::const_iterator
9049 it = data.pciAttachments.begin();
9050 it != data.pciAttachments.end();
9051 ++it)
9052 {
9053 const settings::HostPCIDeviceAttachment &hpda = *it;
9054 ComObjPtr<PCIDeviceAttachment> pda;
9055
9056 pda.createObject();
9057 pda->i_loadSettings(this, hpda);
9058 mHWData->mPCIDeviceAssignments.push_back(pda);
9059 }
9060
9061 /*
9062 * (The following isn't really real hardware, but it lives in HWData
9063 * for reasons of convenience.)
9064 */
9065
9066#ifdef VBOX_WITH_GUEST_PROPS
9067 /* Guest properties (optional) */
9068
9069 /* Only load transient guest properties for configs which have saved
9070 * state, because there shouldn't be any for powered off VMs. The same
9071 * logic applies for snapshots, as offline snapshots shouldn't have
9072 * any such properties. They confuse the code in various places.
9073 * Note: can't rely on the machine state, as it isn't set yet. */
9074 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9075 /* apologies for the hacky unconst() usage, but this needs hacking
9076 * actually inconsistent settings into consistency, otherwise there
9077 * will be some corner cases where the inconsistency survives
9078 * surprisingly long without getting fixed, especially for snapshots
9079 * as there are no config changes. */
9080 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9081 for (settings::GuestPropertiesList::iterator
9082 it = llGuestProperties.begin();
9083 it != llGuestProperties.end();
9084 /*nothing*/)
9085 {
9086 const settings::GuestProperty &prop = *it;
9087 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9088 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9089 if ( fSkipTransientGuestProperties
9090 && ( fFlags & GUEST_PROP_F_TRANSIENT
9091 || fFlags & GUEST_PROP_F_TRANSRESET))
9092 {
9093 it = llGuestProperties.erase(it);
9094 continue;
9095 }
9096 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9097 mHWData->mGuestProperties[prop.strName] = property;
9098 ++it;
9099 }
9100#endif /* VBOX_WITH_GUEST_PROPS defined */
9101
9102 rc = i_loadDebugging(pDbg);
9103 if (FAILED(rc))
9104 return rc;
9105
9106 mHWData->mAutostart = *pAutostart;
9107
9108 /* default frontend */
9109 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9110 }
9111 catch (std::bad_alloc &)
9112 {
9113 return E_OUTOFMEMORY;
9114 }
9115
9116 AssertComRC(rc);
9117 return rc;
9118}
9119
9120/**
9121 * Called from i_loadHardware() to load the debugging settings of the
9122 * machine.
9123 *
9124 * @param pDbg Pointer to the settings.
9125 */
9126HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9127{
9128 mHWData->mDebugging = *pDbg;
9129 /* no more processing currently required, this will probably change. */
9130 return S_OK;
9131}
9132
9133/**
9134 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9135 *
9136 * @param data storage settings.
9137 * @param puuidRegistry media registry ID to set media to or NULL;
9138 * see Machine::i_loadMachineDataFromSettings()
9139 * @param puuidSnapshot snapshot ID
9140 * @return
9141 */
9142HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9143 const Guid *puuidRegistry,
9144 const Guid *puuidSnapshot)
9145{
9146 AssertReturn(!i_isSessionMachine(), E_FAIL);
9147
9148 HRESULT rc = S_OK;
9149
9150 for (settings::StorageControllersList::const_iterator
9151 it = data.llStorageControllers.begin();
9152 it != data.llStorageControllers.end();
9153 ++it)
9154 {
9155 const settings::StorageController &ctlData = *it;
9156
9157 ComObjPtr<StorageController> pCtl;
9158 /* Try to find one with the name first. */
9159 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9160 if (SUCCEEDED(rc))
9161 return setError(VBOX_E_OBJECT_IN_USE,
9162 tr("Storage controller named '%s' already exists"),
9163 ctlData.strName.c_str());
9164
9165 pCtl.createObject();
9166 rc = pCtl->init(this,
9167 ctlData.strName,
9168 ctlData.storageBus,
9169 ctlData.ulInstance,
9170 ctlData.fBootable);
9171 if (FAILED(rc)) return rc;
9172
9173 mStorageControllers->push_back(pCtl);
9174
9175 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9176 if (FAILED(rc)) return rc;
9177
9178 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9179 if (FAILED(rc)) return rc;
9180
9181 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9182 if (FAILED(rc)) return rc;
9183
9184 /* Load the attached devices now. */
9185 rc = i_loadStorageDevices(pCtl,
9186 ctlData,
9187 puuidRegistry,
9188 puuidSnapshot);
9189 if (FAILED(rc)) return rc;
9190 }
9191
9192 return S_OK;
9193}
9194
9195/**
9196 * Called from i_loadStorageControllers for a controller's devices.
9197 *
9198 * @param aStorageController
9199 * @param data
9200 * @param puuidRegistry media registry ID to set media to or NULL; see
9201 * Machine::i_loadMachineDataFromSettings()
9202 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9203 * @return
9204 */
9205HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9206 const settings::StorageController &data,
9207 const Guid *puuidRegistry,
9208 const Guid *puuidSnapshot)
9209{
9210 HRESULT rc = S_OK;
9211
9212 /* paranoia: detect duplicate attachments */
9213 for (settings::AttachedDevicesList::const_iterator
9214 it = data.llAttachedDevices.begin();
9215 it != data.llAttachedDevices.end();
9216 ++it)
9217 {
9218 const settings::AttachedDevice &ad = *it;
9219
9220 for (settings::AttachedDevicesList::const_iterator it2 = it;
9221 it2 != data.llAttachedDevices.end();
9222 ++it2)
9223 {
9224 if (it == it2)
9225 continue;
9226
9227 const settings::AttachedDevice &ad2 = *it2;
9228
9229 if ( ad.lPort == ad2.lPort
9230 && ad.lDevice == ad2.lDevice)
9231 {
9232 return setError(E_FAIL,
9233 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9234 aStorageController->i_getName().c_str(),
9235 ad.lPort,
9236 ad.lDevice,
9237 mUserData->s.strName.c_str());
9238 }
9239 }
9240 }
9241
9242 for (settings::AttachedDevicesList::const_iterator
9243 it = data.llAttachedDevices.begin();
9244 it != data.llAttachedDevices.end();
9245 ++it)
9246 {
9247 const settings::AttachedDevice &dev = *it;
9248 ComObjPtr<Medium> medium;
9249
9250 switch (dev.deviceType)
9251 {
9252 case DeviceType_Floppy:
9253 case DeviceType_DVD:
9254 if (dev.strHostDriveSrc.isNotEmpty())
9255 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9256 false /* fRefresh */, medium);
9257 else
9258 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9259 dev.uuid,
9260 false /* fRefresh */,
9261 false /* aSetError */,
9262 medium);
9263 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9264 // This is not an error. The host drive or UUID might have vanished, so just go
9265 // ahead without this removeable medium attachment
9266 rc = S_OK;
9267 break;
9268
9269 case DeviceType_HardDisk:
9270 {
9271 /* find a hard disk by UUID */
9272 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9273 if (FAILED(rc))
9274 {
9275 if (i_isSnapshotMachine())
9276 {
9277 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9278 // so the user knows that the bad disk is in a snapshot somewhere
9279 com::ErrorInfo info;
9280 return setError(E_FAIL,
9281 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9282 puuidSnapshot->raw(),
9283 info.getText().raw());
9284 }
9285 else
9286 return rc;
9287 }
9288
9289 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9290
9291 if (medium->i_getType() == MediumType_Immutable)
9292 {
9293 if (i_isSnapshotMachine())
9294 return setError(E_FAIL,
9295 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9296 "of the virtual machine '%s' ('%s')"),
9297 medium->i_getLocationFull().c_str(),
9298 dev.uuid.raw(),
9299 puuidSnapshot->raw(),
9300 mUserData->s.strName.c_str(),
9301 mData->m_strConfigFileFull.c_str());
9302
9303 return setError(E_FAIL,
9304 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9305 medium->i_getLocationFull().c_str(),
9306 dev.uuid.raw(),
9307 mUserData->s.strName.c_str(),
9308 mData->m_strConfigFileFull.c_str());
9309 }
9310
9311 if (medium->i_getType() == MediumType_MultiAttach)
9312 {
9313 if (i_isSnapshotMachine())
9314 return setError(E_FAIL,
9315 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9316 "of the virtual machine '%s' ('%s')"),
9317 medium->i_getLocationFull().c_str(),
9318 dev.uuid.raw(),
9319 puuidSnapshot->raw(),
9320 mUserData->s.strName.c_str(),
9321 mData->m_strConfigFileFull.c_str());
9322
9323 return setError(E_FAIL,
9324 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9325 medium->i_getLocationFull().c_str(),
9326 dev.uuid.raw(),
9327 mUserData->s.strName.c_str(),
9328 mData->m_strConfigFileFull.c_str());
9329 }
9330
9331 if ( !i_isSnapshotMachine()
9332 && medium->i_getChildren().size() != 0
9333 )
9334 return setError(E_FAIL,
9335 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9336 "because it has %d differencing child hard disks"),
9337 medium->i_getLocationFull().c_str(),
9338 dev.uuid.raw(),
9339 mUserData->s.strName.c_str(),
9340 mData->m_strConfigFileFull.c_str(),
9341 medium->i_getChildren().size());
9342
9343 if (i_findAttachment(*mMediumAttachments.data(),
9344 medium))
9345 return setError(E_FAIL,
9346 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9347 medium->i_getLocationFull().c_str(),
9348 dev.uuid.raw(),
9349 mUserData->s.strName.c_str(),
9350 mData->m_strConfigFileFull.c_str());
9351
9352 break;
9353 }
9354
9355 default:
9356 return setError(E_FAIL,
9357 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9358 medium->i_getLocationFull().c_str(),
9359 mUserData->s.strName.c_str(),
9360 mData->m_strConfigFileFull.c_str());
9361 }
9362
9363 if (FAILED(rc))
9364 break;
9365
9366 /* Bandwidth groups are loaded at this point. */
9367 ComObjPtr<BandwidthGroup> pBwGroup;
9368
9369 if (!dev.strBwGroup.isEmpty())
9370 {
9371 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9372 if (FAILED(rc))
9373 return setError(E_FAIL,
9374 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9375 medium->i_getLocationFull().c_str(),
9376 dev.strBwGroup.c_str(),
9377 mUserData->s.strName.c_str(),
9378 mData->m_strConfigFileFull.c_str());
9379 pBwGroup->i_reference();
9380 }
9381
9382 const Utf8Str controllerName = aStorageController->i_getName();
9383 ComObjPtr<MediumAttachment> pAttachment;
9384 pAttachment.createObject();
9385 rc = pAttachment->init(this,
9386 medium,
9387 controllerName,
9388 dev.lPort,
9389 dev.lDevice,
9390 dev.deviceType,
9391 false,
9392 dev.fPassThrough,
9393 dev.fTempEject,
9394 dev.fNonRotational,
9395 dev.fDiscard,
9396 dev.fHotPluggable,
9397 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9398 if (FAILED(rc)) break;
9399
9400 /* associate the medium with this machine and snapshot */
9401 if (!medium.isNull())
9402 {
9403 AutoCaller medCaller(medium);
9404 if (FAILED(medCaller.rc())) return medCaller.rc();
9405 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9406
9407 if (i_isSnapshotMachine())
9408 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9409 else
9410 rc = medium->i_addBackReference(mData->mUuid);
9411 /* If the medium->addBackReference fails it sets an appropriate
9412 * error message, so no need to do any guesswork here. */
9413
9414 if (puuidRegistry)
9415 // caller wants registry ID to be set on all attached media (OVF import case)
9416 medium->i_addRegistry(*puuidRegistry);
9417 }
9418
9419 if (FAILED(rc))
9420 break;
9421
9422 /* back up mMediumAttachments to let registeredInit() properly rollback
9423 * on failure (= limited accessibility) */
9424 i_setModified(IsModified_Storage);
9425 mMediumAttachments.backup();
9426 mMediumAttachments->push_back(pAttachment);
9427 }
9428
9429 return rc;
9430}
9431
9432/**
9433 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9434 *
9435 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9436 * @param aSnapshot where to return the found snapshot
9437 * @param aSetError true to set extended error info on failure
9438 */
9439HRESULT Machine::i_findSnapshotById(const Guid &aId,
9440 ComObjPtr<Snapshot> &aSnapshot,
9441 bool aSetError /* = false */)
9442{
9443 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9444
9445 if (!mData->mFirstSnapshot)
9446 {
9447 if (aSetError)
9448 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9449 return E_FAIL;
9450 }
9451
9452 if (aId.isZero())
9453 aSnapshot = mData->mFirstSnapshot;
9454 else
9455 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9456
9457 if (!aSnapshot)
9458 {
9459 if (aSetError)
9460 return setError(E_FAIL,
9461 tr("Could not find a snapshot with UUID {%s}"),
9462 aId.toString().c_str());
9463 return E_FAIL;
9464 }
9465
9466 return S_OK;
9467}
9468
9469/**
9470 * Returns the snapshot with the given name or fails of no such snapshot.
9471 *
9472 * @param strName snapshot name to find
9473 * @param aSnapshot where to return the found snapshot
9474 * @param aSetError true to set extended error info on failure
9475 */
9476HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9477 ComObjPtr<Snapshot> &aSnapshot,
9478 bool aSetError /* = false */)
9479{
9480 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9481
9482 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9483
9484 if (!mData->mFirstSnapshot)
9485 {
9486 if (aSetError)
9487 return setError(VBOX_E_OBJECT_NOT_FOUND,
9488 tr("This machine does not have any snapshots"));
9489 return VBOX_E_OBJECT_NOT_FOUND;
9490 }
9491
9492 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9493
9494 if (!aSnapshot)
9495 {
9496 if (aSetError)
9497 return setError(VBOX_E_OBJECT_NOT_FOUND,
9498 tr("Could not find a snapshot named '%s'"), strName.c_str());
9499 return VBOX_E_OBJECT_NOT_FOUND;
9500 }
9501
9502 return S_OK;
9503}
9504
9505/**
9506 * Returns a storage controller object with the given name.
9507 *
9508 * @param aName storage controller name to find
9509 * @param aStorageController where to return the found storage controller
9510 * @param aSetError true to set extended error info on failure
9511 */
9512HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9513 ComObjPtr<StorageController> &aStorageController,
9514 bool aSetError /* = false */)
9515{
9516 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9517
9518 for (StorageControllerList::const_iterator
9519 it = mStorageControllers->begin();
9520 it != mStorageControllers->end();
9521 ++it)
9522 {
9523 if ((*it)->i_getName() == aName)
9524 {
9525 aStorageController = (*it);
9526 return S_OK;
9527 }
9528 }
9529
9530 if (aSetError)
9531 return setError(VBOX_E_OBJECT_NOT_FOUND,
9532 tr("Could not find a storage controller named '%s'"),
9533 aName.c_str());
9534 return VBOX_E_OBJECT_NOT_FOUND;
9535}
9536
9537/**
9538 * Returns a USB controller object with the given name.
9539 *
9540 * @param aName USB controller name to find
9541 * @param aUSBController where to return the found USB controller
9542 * @param aSetError true to set extended error info on failure
9543 */
9544HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9545 ComObjPtr<USBController> &aUSBController,
9546 bool aSetError /* = false */)
9547{
9548 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9549
9550 for (USBControllerList::const_iterator
9551 it = mUSBControllers->begin();
9552 it != mUSBControllers->end();
9553 ++it)
9554 {
9555 if ((*it)->i_getName() == aName)
9556 {
9557 aUSBController = (*it);
9558 return S_OK;
9559 }
9560 }
9561
9562 if (aSetError)
9563 return setError(VBOX_E_OBJECT_NOT_FOUND,
9564 tr("Could not find a storage controller named '%s'"),
9565 aName.c_str());
9566 return VBOX_E_OBJECT_NOT_FOUND;
9567}
9568
9569/**
9570 * Returns the number of USB controller instance of the given type.
9571 *
9572 * @param enmType USB controller type.
9573 */
9574ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9575{
9576 ULONG cCtrls = 0;
9577
9578 for (USBControllerList::const_iterator
9579 it = mUSBControllers->begin();
9580 it != mUSBControllers->end();
9581 ++it)
9582 {
9583 if ((*it)->i_getControllerType() == enmType)
9584 cCtrls++;
9585 }
9586
9587 return cCtrls;
9588}
9589
9590HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9591 MediumAttachmentList &atts)
9592{
9593 AutoCaller autoCaller(this);
9594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9595
9596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9597
9598 for (MediumAttachmentList::const_iterator
9599 it = mMediumAttachments->begin();
9600 it != mMediumAttachments->end();
9601 ++it)
9602 {
9603 const ComObjPtr<MediumAttachment> &pAtt = *it;
9604 // should never happen, but deal with NULL pointers in the list.
9605 AssertContinue(!pAtt.isNull());
9606
9607 // getControllerName() needs caller+read lock
9608 AutoCaller autoAttCaller(pAtt);
9609 if (FAILED(autoAttCaller.rc()))
9610 {
9611 atts.clear();
9612 return autoAttCaller.rc();
9613 }
9614 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9615
9616 if (pAtt->i_getControllerName() == aName)
9617 atts.push_back(pAtt);
9618 }
9619
9620 return S_OK;
9621}
9622
9623
9624/**
9625 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9626 * file if the machine name was changed and about creating a new settings file
9627 * if this is a new machine.
9628 *
9629 * @note Must be never called directly but only from #saveSettings().
9630 */
9631HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9632{
9633 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9634
9635 HRESULT rc = S_OK;
9636
9637 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9638
9639 /// @todo need to handle primary group change, too
9640
9641 /* attempt to rename the settings file if machine name is changed */
9642 if ( mUserData->s.fNameSync
9643 && mUserData.isBackedUp()
9644 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9645 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9646 )
9647 {
9648 bool dirRenamed = false;
9649 bool fileRenamed = false;
9650
9651 Utf8Str configFile, newConfigFile;
9652 Utf8Str configFilePrev, newConfigFilePrev;
9653 Utf8Str configDir, newConfigDir;
9654
9655 do
9656 {
9657 int vrc = VINF_SUCCESS;
9658
9659 Utf8Str name = mUserData.backedUpData()->s.strName;
9660 Utf8Str newName = mUserData->s.strName;
9661 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9662 if (group == "/")
9663 group.setNull();
9664 Utf8Str newGroup = mUserData->s.llGroups.front();
9665 if (newGroup == "/")
9666 newGroup.setNull();
9667
9668 configFile = mData->m_strConfigFileFull;
9669
9670 /* first, rename the directory if it matches the group and machine name */
9671 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9672 group.c_str(), RTPATH_DELIMITER, name.c_str());
9673 /** @todo hack, make somehow use of ComposeMachineFilename */
9674 if (mUserData->s.fDirectoryIncludesUUID)
9675 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9676 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9677 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9678 /** @todo hack, make somehow use of ComposeMachineFilename */
9679 if (mUserData->s.fDirectoryIncludesUUID)
9680 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9681 configDir = configFile;
9682 configDir.stripFilename();
9683 newConfigDir = configDir;
9684 if ( configDir.length() >= groupPlusName.length()
9685 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9686 groupPlusName.c_str()))
9687 {
9688 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9689 Utf8Str newConfigBaseDir(newConfigDir);
9690 newConfigDir.append(newGroupPlusName);
9691 /* consistency: use \ if appropriate on the platform */
9692 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9693 /* new dir and old dir cannot be equal here because of 'if'
9694 * above and because name != newName */
9695 Assert(configDir != newConfigDir);
9696 if (!fSettingsFileIsNew)
9697 {
9698 /* perform real rename only if the machine is not new */
9699 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9700 if ( vrc == VERR_FILE_NOT_FOUND
9701 || vrc == VERR_PATH_NOT_FOUND)
9702 {
9703 /* create the parent directory, then retry renaming */
9704 Utf8Str parent(newConfigDir);
9705 parent.stripFilename();
9706 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9707 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9708 }
9709 if (RT_FAILURE(vrc))
9710 {
9711 rc = setErrorBoth(E_FAIL, vrc,
9712 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9713 configDir.c_str(),
9714 newConfigDir.c_str(),
9715 vrc);
9716 break;
9717 }
9718 /* delete subdirectories which are no longer needed */
9719 Utf8Str dir(configDir);
9720 dir.stripFilename();
9721 while (dir != newConfigBaseDir && dir != ".")
9722 {
9723 vrc = RTDirRemove(dir.c_str());
9724 if (RT_FAILURE(vrc))
9725 break;
9726 dir.stripFilename();
9727 }
9728 dirRenamed = true;
9729 }
9730 }
9731
9732 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9733 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9734
9735 /* then try to rename the settings file itself */
9736 if (newConfigFile != configFile)
9737 {
9738 /* get the path to old settings file in renamed directory */
9739 configFile = Utf8StrFmt("%s%c%s",
9740 newConfigDir.c_str(),
9741 RTPATH_DELIMITER,
9742 RTPathFilename(configFile.c_str()));
9743 if (!fSettingsFileIsNew)
9744 {
9745 /* perform real rename only if the machine is not new */
9746 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9747 if (RT_FAILURE(vrc))
9748 {
9749 rc = setErrorBoth(E_FAIL, vrc,
9750 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9751 configFile.c_str(),
9752 newConfigFile.c_str(),
9753 vrc);
9754 break;
9755 }
9756 fileRenamed = true;
9757 configFilePrev = configFile;
9758 configFilePrev += "-prev";
9759 newConfigFilePrev = newConfigFile;
9760 newConfigFilePrev += "-prev";
9761 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9762 }
9763 }
9764
9765 // update m_strConfigFileFull amd mConfigFile
9766 mData->m_strConfigFileFull = newConfigFile;
9767 // compute the relative path too
9768 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9769
9770 // store the old and new so that VirtualBox::i_saveSettings() can update
9771 // the media registry
9772 if ( mData->mRegistered
9773 && (configDir != newConfigDir || configFile != newConfigFile))
9774 {
9775 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9776
9777 if (pfNeedsGlobalSaveSettings)
9778 *pfNeedsGlobalSaveSettings = true;
9779 }
9780
9781 // in the saved state file path, replace the old directory with the new directory
9782 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9783 {
9784 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9785 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9786 }
9787
9788 // and do the same thing for the saved state file paths of all the online snapshots
9789 if (mData->mFirstSnapshot)
9790 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9791 newConfigDir.c_str());
9792 }
9793 while (0);
9794
9795 if (FAILED(rc))
9796 {
9797 /* silently try to rename everything back */
9798 if (fileRenamed)
9799 {
9800 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9801 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9802 }
9803 if (dirRenamed)
9804 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9805 }
9806
9807 if (FAILED(rc)) return rc;
9808 }
9809
9810 if (fSettingsFileIsNew)
9811 {
9812 /* create a virgin config file */
9813 int vrc = VINF_SUCCESS;
9814
9815 /* ensure the settings directory exists */
9816 Utf8Str path(mData->m_strConfigFileFull);
9817 path.stripFilename();
9818 if (!RTDirExists(path.c_str()))
9819 {
9820 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9821 if (RT_FAILURE(vrc))
9822 {
9823 return setErrorBoth(E_FAIL, vrc,
9824 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9825 path.c_str(),
9826 vrc);
9827 }
9828 }
9829
9830 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9831 path = Utf8Str(mData->m_strConfigFileFull);
9832 RTFILE f = NIL_RTFILE;
9833 vrc = RTFileOpen(&f, path.c_str(),
9834 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9835 if (RT_FAILURE(vrc))
9836 return setErrorBoth(E_FAIL, vrc,
9837 tr("Could not create the settings file '%s' (%Rrc)"),
9838 path.c_str(),
9839 vrc);
9840 RTFileClose(f);
9841 }
9842
9843 return rc;
9844}
9845
9846/**
9847 * Saves and commits machine data, user data and hardware data.
9848 *
9849 * Note that on failure, the data remains uncommitted.
9850 *
9851 * @a aFlags may combine the following flags:
9852 *
9853 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9854 * Used when saving settings after an operation that makes them 100%
9855 * correspond to the settings from the current snapshot.
9856 * - SaveS_Force: settings will be saved without doing a deep compare of the
9857 * settings structures. This is used when this is called because snapshots
9858 * have changed to avoid the overhead of the deep compare.
9859 *
9860 * @note Must be called from under this object's write lock. Locks children for
9861 * writing.
9862 *
9863 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9864 * initialized to false and that will be set to true by this function if
9865 * the caller must invoke VirtualBox::i_saveSettings() because the global
9866 * settings have changed. This will happen if a machine rename has been
9867 * saved and the global machine and media registries will therefore need
9868 * updating.
9869 * @param aFlags Flags.
9870 */
9871HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9872 int aFlags /*= 0*/)
9873{
9874 LogFlowThisFuncEnter();
9875
9876 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9877
9878 /* make sure child objects are unable to modify the settings while we are
9879 * saving them */
9880 i_ensureNoStateDependencies();
9881
9882 AssertReturn(!i_isSnapshotMachine(),
9883 E_FAIL);
9884
9885 HRESULT rc = S_OK;
9886 bool fNeedsWrite = false;
9887
9888 /* First, prepare to save settings. It will care about renaming the
9889 * settings directory and file if the machine name was changed and about
9890 * creating a new settings file if this is a new machine. */
9891 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9892 if (FAILED(rc)) return rc;
9893
9894 // keep a pointer to the current settings structures
9895 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9896 settings::MachineConfigFile *pNewConfig = NULL;
9897
9898 try
9899 {
9900 // make a fresh one to have everyone write stuff into
9901 pNewConfig = new settings::MachineConfigFile(NULL);
9902 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9903
9904 // now go and copy all the settings data from COM to the settings structures
9905 // (this calls i_saveSettings() on all the COM objects in the machine)
9906 i_copyMachineDataToSettings(*pNewConfig);
9907
9908 if (aFlags & SaveS_ResetCurStateModified)
9909 {
9910 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9911 mData->mCurrentStateModified = FALSE;
9912 fNeedsWrite = true; // always, no need to compare
9913 }
9914 else if (aFlags & SaveS_Force)
9915 {
9916 fNeedsWrite = true; // always, no need to compare
9917 }
9918 else
9919 {
9920 if (!mData->mCurrentStateModified)
9921 {
9922 // do a deep compare of the settings that we just saved with the settings
9923 // previously stored in the config file; this invokes MachineConfigFile::operator==
9924 // which does a deep compare of all the settings, which is expensive but less expensive
9925 // than writing out XML in vain
9926 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9927
9928 // could still be modified if any settings changed
9929 mData->mCurrentStateModified = fAnySettingsChanged;
9930
9931 fNeedsWrite = fAnySettingsChanged;
9932 }
9933 else
9934 fNeedsWrite = true;
9935 }
9936
9937 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9938
9939 if (fNeedsWrite)
9940 // now spit it all out!
9941 pNewConfig->write(mData->m_strConfigFileFull);
9942
9943 mData->pMachineConfigFile = pNewConfig;
9944 delete pOldConfig;
9945 i_commit();
9946
9947 // after saving settings, we are no longer different from the XML on disk
9948 mData->flModifications = 0;
9949 }
9950 catch (HRESULT err)
9951 {
9952 // we assume that error info is set by the thrower
9953 rc = err;
9954
9955 // restore old config
9956 delete pNewConfig;
9957 mData->pMachineConfigFile = pOldConfig;
9958 }
9959 catch (...)
9960 {
9961 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9962 }
9963
9964 if (fNeedsWrite)
9965 {
9966 /* Fire the data change event, even on failure (since we've already
9967 * committed all data). This is done only for SessionMachines because
9968 * mutable Machine instances are always not registered (i.e. private
9969 * to the client process that creates them) and thus don't need to
9970 * inform callbacks. */
9971 if (i_isSessionMachine())
9972 mParent->i_onMachineDataChange(mData->mUuid);
9973 }
9974
9975 LogFlowThisFunc(("rc=%08X\n", rc));
9976 LogFlowThisFuncLeave();
9977 return rc;
9978}
9979
9980/**
9981 * Implementation for saving the machine settings into the given
9982 * settings::MachineConfigFile instance. This copies machine extradata
9983 * from the previous machine config file in the instance data, if any.
9984 *
9985 * This gets called from two locations:
9986 *
9987 * -- Machine::i_saveSettings(), during the regular XML writing;
9988 *
9989 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9990 * exported to OVF and we write the VirtualBox proprietary XML
9991 * into a <vbox:Machine> tag.
9992 *
9993 * This routine fills all the fields in there, including snapshots, *except*
9994 * for the following:
9995 *
9996 * -- fCurrentStateModified. There is some special logic associated with that.
9997 *
9998 * The caller can then call MachineConfigFile::write() or do something else
9999 * with it.
10000 *
10001 * Caller must hold the machine lock!
10002 *
10003 * This throws XML errors and HRESULT, so the caller must have a catch block!
10004 */
10005void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10006{
10007 // deep copy extradata, being extra careful with self assignment (the STL
10008 // map assignment on Mac OS X clang based Xcode isn't checking)
10009 if (&config != mData->pMachineConfigFile)
10010 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10011
10012 config.uuid = mData->mUuid;
10013
10014 // copy name, description, OS type, teleport, UTC etc.
10015 config.machineUserData = mUserData->s;
10016
10017 if ( mData->mMachineState == MachineState_Saved
10018 || mData->mMachineState == MachineState_Restoring
10019 // when doing certain snapshot operations we may or may not have
10020 // a saved state in the current state, so keep everything as is
10021 || ( ( mData->mMachineState == MachineState_Snapshotting
10022 || mData->mMachineState == MachineState_DeletingSnapshot
10023 || mData->mMachineState == MachineState_RestoringSnapshot)
10024 && (!mSSData->strStateFilePath.isEmpty())
10025 )
10026 )
10027 {
10028 Assert(!mSSData->strStateFilePath.isEmpty());
10029 /* try to make the file name relative to the settings file dir */
10030 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10031 }
10032 else
10033 {
10034 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10035 config.strStateFile.setNull();
10036 }
10037
10038 if (mData->mCurrentSnapshot)
10039 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10040 else
10041 config.uuidCurrentSnapshot.clear();
10042
10043 config.timeLastStateChange = mData->mLastStateChange;
10044 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10045 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10046
10047 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10048 if (FAILED(rc)) throw rc;
10049
10050 // save machine's media registry if this is VirtualBox 4.0 or later
10051 if (config.canHaveOwnMediaRegistry())
10052 {
10053 // determine machine folder
10054 Utf8Str strMachineFolder = i_getSettingsFileFull();
10055 strMachineFolder.stripFilename();
10056 mParent->i_saveMediaRegistry(config.mediaRegistry,
10057 i_getId(), // only media with registry ID == machine UUID
10058 strMachineFolder);
10059 // this throws HRESULT
10060 }
10061
10062 // save snapshots
10063 rc = i_saveAllSnapshots(config);
10064 if (FAILED(rc)) throw rc;
10065}
10066
10067/**
10068 * Saves all snapshots of the machine into the given machine config file. Called
10069 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10070 * @param config
10071 * @return
10072 */
10073HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10074{
10075 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10076
10077 HRESULT rc = S_OK;
10078
10079 try
10080 {
10081 config.llFirstSnapshot.clear();
10082
10083 if (mData->mFirstSnapshot)
10084 {
10085 // the settings use a list for "the first snapshot"
10086 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10087
10088 // get reference to the snapshot on the list and work on that
10089 // element straight in the list to avoid excessive copying later
10090 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10091 if (FAILED(rc)) throw rc;
10092 }
10093
10094// if (mType == IsSessionMachine)
10095// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10096
10097 }
10098 catch (HRESULT err)
10099 {
10100 /* we assume that error info is set by the thrower */
10101 rc = err;
10102 }
10103 catch (...)
10104 {
10105 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10106 }
10107
10108 return rc;
10109}
10110
10111/**
10112 * Saves the VM hardware configuration. It is assumed that the
10113 * given node is empty.
10114 *
10115 * @param data Reference to the settings object for the hardware config.
10116 * @param pDbg Pointer to the settings object for the debugging config
10117 * which happens to live in mHWData.
10118 * @param pAutostart Pointer to the settings object for the autostart config
10119 * which happens to live in mHWData.
10120 */
10121HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10122 settings::Autostart *pAutostart)
10123{
10124 HRESULT rc = S_OK;
10125
10126 try
10127 {
10128 /* The hardware version attribute (optional).
10129 Automatically upgrade from 1 to current default hardware version
10130 when there is no saved state. (ugly!) */
10131 if ( mHWData->mHWVersion == "1"
10132 && mSSData->strStateFilePath.isEmpty()
10133 )
10134 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10135
10136 data.strVersion = mHWData->mHWVersion;
10137 data.uuid = mHWData->mHardwareUUID;
10138
10139 // CPU
10140 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10141 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10142 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10143 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10144 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10145 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10146 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10147 data.fPAE = !!mHWData->mPAEEnabled;
10148 data.enmLongMode = mHWData->mLongMode;
10149 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10150 data.fAPIC = !!mHWData->mAPIC;
10151 data.fX2APIC = !!mHWData->mX2APIC;
10152 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10153 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10154 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10155 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10156 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10157 data.cCPUs = mHWData->mCPUCount;
10158 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10159 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10160 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10161 data.strCpuProfile = mHWData->mCpuProfile;
10162
10163 data.llCpus.clear();
10164 if (data.fCpuHotPlug)
10165 {
10166 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10167 {
10168 if (mHWData->mCPUAttached[idx])
10169 {
10170 settings::Cpu cpu;
10171 cpu.ulId = idx;
10172 data.llCpus.push_back(cpu);
10173 }
10174 }
10175 }
10176
10177 /* Standard and Extended CPUID leafs. */
10178 data.llCpuIdLeafs.clear();
10179 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10180
10181 // memory
10182 data.ulMemorySizeMB = mHWData->mMemorySize;
10183 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10184
10185 // firmware
10186 data.firmwareType = mHWData->mFirmwareType;
10187
10188 // HID
10189 data.pointingHIDType = mHWData->mPointingHIDType;
10190 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10191
10192 // chipset
10193 data.chipsetType = mHWData->mChipsetType;
10194
10195 // paravirt
10196 data.paravirtProvider = mHWData->mParavirtProvider;
10197 data.strParavirtDebug = mHWData->mParavirtDebug;
10198
10199 // emulated USB card reader
10200 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10201
10202 // HPET
10203 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10204
10205 // boot order
10206 data.mapBootOrder.clear();
10207 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10208 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10209
10210 // display
10211 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10212 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10213 data.cMonitors = mHWData->mMonitorCount;
10214 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10215 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10216
10217 /* VRDEServer settings (optional) */
10218 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10219 if (FAILED(rc)) throw rc;
10220
10221 /* BIOS settings (required) */
10222 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10223 if (FAILED(rc)) throw rc;
10224
10225 /* Recording settings (required) */
10226 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10227 if (FAILED(rc)) throw rc;
10228
10229 /* USB Controller (required) */
10230 data.usbSettings.llUSBControllers.clear();
10231 for (USBControllerList::const_iterator
10232 it = mUSBControllers->begin();
10233 it != mUSBControllers->end();
10234 ++it)
10235 {
10236 ComObjPtr<USBController> ctrl = *it;
10237 settings::USBController settingsCtrl;
10238
10239 settingsCtrl.strName = ctrl->i_getName();
10240 settingsCtrl.enmType = ctrl->i_getControllerType();
10241
10242 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10243 }
10244
10245 /* USB device filters (required) */
10246 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10247 if (FAILED(rc)) throw rc;
10248
10249 /* Network adapters (required) */
10250 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10251 data.llNetworkAdapters.clear();
10252 /* Write out only the nominal number of network adapters for this
10253 * chipset type. Since Machine::commit() hasn't been called there
10254 * may be extra NIC settings in the vector. */
10255 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10256 {
10257 settings::NetworkAdapter nic;
10258 nic.ulSlot = (uint32_t)slot;
10259 /* paranoia check... must not be NULL, but must not crash either. */
10260 if (mNetworkAdapters[slot])
10261 {
10262 if (mNetworkAdapters[slot]->i_hasDefaults())
10263 continue;
10264
10265 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10266 if (FAILED(rc)) throw rc;
10267
10268 data.llNetworkAdapters.push_back(nic);
10269 }
10270 }
10271
10272 /* Serial ports */
10273 data.llSerialPorts.clear();
10274 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10275 {
10276 if (mSerialPorts[slot]->i_hasDefaults())
10277 continue;
10278
10279 settings::SerialPort s;
10280 s.ulSlot = slot;
10281 rc = mSerialPorts[slot]->i_saveSettings(s);
10282 if (FAILED(rc)) return rc;
10283
10284 data.llSerialPorts.push_back(s);
10285 }
10286
10287 /* Parallel ports */
10288 data.llParallelPorts.clear();
10289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10290 {
10291 if (mParallelPorts[slot]->i_hasDefaults())
10292 continue;
10293
10294 settings::ParallelPort p;
10295 p.ulSlot = slot;
10296 rc = mParallelPorts[slot]->i_saveSettings(p);
10297 if (FAILED(rc)) return rc;
10298
10299 data.llParallelPorts.push_back(p);
10300 }
10301
10302 /* Audio adapter */
10303 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10304 if (FAILED(rc)) return rc;
10305
10306 rc = i_saveStorageControllers(data.storage);
10307 if (FAILED(rc)) return rc;
10308
10309 /* Shared folders */
10310 data.llSharedFolders.clear();
10311 for (HWData::SharedFolderList::const_iterator
10312 it = mHWData->mSharedFolders.begin();
10313 it != mHWData->mSharedFolders.end();
10314 ++it)
10315 {
10316 SharedFolder *pSF = *it;
10317 AutoCaller sfCaller(pSF);
10318 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10319 settings::SharedFolder sf;
10320 sf.strName = pSF->i_getName();
10321 sf.strHostPath = pSF->i_getHostPath();
10322 sf.fWritable = !!pSF->i_isWritable();
10323 sf.fAutoMount = !!pSF->i_isAutoMounted();
10324 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10325
10326 data.llSharedFolders.push_back(sf);
10327 }
10328
10329 // clipboard
10330 data.clipboardMode = mHWData->mClipboardMode;
10331
10332 // drag'n'drop
10333 data.dndMode = mHWData->mDnDMode;
10334
10335 /* Guest */
10336 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10337
10338 // IO settings
10339 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10340 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10341
10342 /* BandwidthControl (required) */
10343 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10344 if (FAILED(rc)) throw rc;
10345
10346 /* Host PCI devices */
10347 data.pciAttachments.clear();
10348 for (HWData::PCIDeviceAssignmentList::const_iterator
10349 it = mHWData->mPCIDeviceAssignments.begin();
10350 it != mHWData->mPCIDeviceAssignments.end();
10351 ++it)
10352 {
10353 ComObjPtr<PCIDeviceAttachment> pda = *it;
10354 settings::HostPCIDeviceAttachment hpda;
10355
10356 rc = pda->i_saveSettings(hpda);
10357 if (FAILED(rc)) throw rc;
10358
10359 data.pciAttachments.push_back(hpda);
10360 }
10361
10362 // guest properties
10363 data.llGuestProperties.clear();
10364#ifdef VBOX_WITH_GUEST_PROPS
10365 for (HWData::GuestPropertyMap::const_iterator
10366 it = mHWData->mGuestProperties.begin();
10367 it != mHWData->mGuestProperties.end();
10368 ++it)
10369 {
10370 HWData::GuestProperty property = it->second;
10371
10372 /* Remove transient guest properties at shutdown unless we
10373 * are saving state. Note that restoring snapshot intentionally
10374 * keeps them, they will be removed if appropriate once the final
10375 * machine state is set (as crashes etc. need to work). */
10376 if ( ( mData->mMachineState == MachineState_PoweredOff
10377 || mData->mMachineState == MachineState_Aborted
10378 || mData->mMachineState == MachineState_Teleported)
10379 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10380 continue;
10381 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10382 prop.strName = it->first;
10383 prop.strValue = property.strValue;
10384 prop.timestamp = property.mTimestamp;
10385 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10386 GuestPropWriteFlags(property.mFlags, szFlags);
10387 prop.strFlags = szFlags;
10388
10389 data.llGuestProperties.push_back(prop);
10390 }
10391
10392 /* I presume this doesn't require a backup(). */
10393 mData->mGuestPropertiesModified = FALSE;
10394#endif /* VBOX_WITH_GUEST_PROPS defined */
10395
10396 *pDbg = mHWData->mDebugging;
10397 *pAutostart = mHWData->mAutostart;
10398
10399 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10400 }
10401 catch (std::bad_alloc &)
10402 {
10403 return E_OUTOFMEMORY;
10404 }
10405
10406 AssertComRC(rc);
10407 return rc;
10408}
10409
10410/**
10411 * Saves the storage controller configuration.
10412 *
10413 * @param data storage settings.
10414 */
10415HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10416{
10417 data.llStorageControllers.clear();
10418
10419 for (StorageControllerList::const_iterator
10420 it = mStorageControllers->begin();
10421 it != mStorageControllers->end();
10422 ++it)
10423 {
10424 HRESULT rc;
10425 ComObjPtr<StorageController> pCtl = *it;
10426
10427 settings::StorageController ctl;
10428 ctl.strName = pCtl->i_getName();
10429 ctl.controllerType = pCtl->i_getControllerType();
10430 ctl.storageBus = pCtl->i_getStorageBus();
10431 ctl.ulInstance = pCtl->i_getInstance();
10432 ctl.fBootable = pCtl->i_getBootable();
10433
10434 /* Save the port count. */
10435 ULONG portCount;
10436 rc = pCtl->COMGETTER(PortCount)(&portCount);
10437 ComAssertComRCRet(rc, rc);
10438 ctl.ulPortCount = portCount;
10439
10440 /* Save fUseHostIOCache */
10441 BOOL fUseHostIOCache;
10442 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10443 ComAssertComRCRet(rc, rc);
10444 ctl.fUseHostIOCache = !!fUseHostIOCache;
10445
10446 /* save the devices now. */
10447 rc = i_saveStorageDevices(pCtl, ctl);
10448 ComAssertComRCRet(rc, rc);
10449
10450 data.llStorageControllers.push_back(ctl);
10451 }
10452
10453 return S_OK;
10454}
10455
10456/**
10457 * Saves the hard disk configuration.
10458 */
10459HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10460 settings::StorageController &data)
10461{
10462 MediumAttachmentList atts;
10463
10464 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10465 if (FAILED(rc)) return rc;
10466
10467 data.llAttachedDevices.clear();
10468 for (MediumAttachmentList::const_iterator
10469 it = atts.begin();
10470 it != atts.end();
10471 ++it)
10472 {
10473 settings::AttachedDevice dev;
10474 IMediumAttachment *iA = *it;
10475 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10476 Medium *pMedium = pAttach->i_getMedium();
10477
10478 dev.deviceType = pAttach->i_getType();
10479 dev.lPort = pAttach->i_getPort();
10480 dev.lDevice = pAttach->i_getDevice();
10481 dev.fPassThrough = pAttach->i_getPassthrough();
10482 dev.fHotPluggable = pAttach->i_getHotPluggable();
10483 if (pMedium)
10484 {
10485 if (pMedium->i_isHostDrive())
10486 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10487 else
10488 dev.uuid = pMedium->i_getId();
10489 dev.fTempEject = pAttach->i_getTempEject();
10490 dev.fNonRotational = pAttach->i_getNonRotational();
10491 dev.fDiscard = pAttach->i_getDiscard();
10492 }
10493
10494 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10495
10496 data.llAttachedDevices.push_back(dev);
10497 }
10498
10499 return S_OK;
10500}
10501
10502/**
10503 * Saves machine state settings as defined by aFlags
10504 * (SaveSTS_* values).
10505 *
10506 * @param aFlags Combination of SaveSTS_* flags.
10507 *
10508 * @note Locks objects for writing.
10509 */
10510HRESULT Machine::i_saveStateSettings(int aFlags)
10511{
10512 if (aFlags == 0)
10513 return S_OK;
10514
10515 AutoCaller autoCaller(this);
10516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10517
10518 /* This object's write lock is also necessary to serialize file access
10519 * (prevent concurrent reads and writes) */
10520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10521
10522 HRESULT rc = S_OK;
10523
10524 Assert(mData->pMachineConfigFile);
10525
10526 try
10527 {
10528 if (aFlags & SaveSTS_CurStateModified)
10529 mData->pMachineConfigFile->fCurrentStateModified = true;
10530
10531 if (aFlags & SaveSTS_StateFilePath)
10532 {
10533 if (!mSSData->strStateFilePath.isEmpty())
10534 /* try to make the file name relative to the settings file dir */
10535 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10536 else
10537 mData->pMachineConfigFile->strStateFile.setNull();
10538 }
10539
10540 if (aFlags & SaveSTS_StateTimeStamp)
10541 {
10542 Assert( mData->mMachineState != MachineState_Aborted
10543 || mSSData->strStateFilePath.isEmpty());
10544
10545 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10546
10547 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10548/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10549 }
10550
10551 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10552 }
10553 catch (...)
10554 {
10555 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10556 }
10557
10558 return rc;
10559}
10560
10561/**
10562 * Ensures that the given medium is added to a media registry. If this machine
10563 * was created with 4.0 or later, then the machine registry is used. Otherwise
10564 * the global VirtualBox media registry is used.
10565 *
10566 * Caller must NOT hold machine lock, media tree or any medium locks!
10567 *
10568 * @param pMedium
10569 */
10570void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10571{
10572 /* Paranoia checks: do not hold machine or media tree locks. */
10573 AssertReturnVoid(!isWriteLockOnCurrentThread());
10574 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10575
10576 ComObjPtr<Medium> pBase;
10577 {
10578 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10579 pBase = pMedium->i_getBase();
10580 }
10581
10582 /* Paranoia checks: do not hold medium locks. */
10583 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10584 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10585
10586 // decide which medium registry to use now that the medium is attached:
10587 Guid uuid;
10588 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10589 if (fCanHaveOwnMediaRegistry)
10590 // machine XML is VirtualBox 4.0 or higher:
10591 uuid = i_getId(); // machine UUID
10592 else
10593 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10594
10595 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10596 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10597 if (pMedium->i_addRegistry(uuid))
10598 mParent->i_markRegistryModified(uuid);
10599
10600 /* For more complex hard disk structures it can happen that the base
10601 * medium isn't yet associated with any medium registry. Do that now. */
10602 if (pMedium != pBase)
10603 {
10604 /* Tree lock needed by Medium::addRegistry when recursing. */
10605 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10606 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10607 {
10608 treeLock.release();
10609 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10610 treeLock.acquire();
10611 }
10612 if (pBase->i_addRegistryRecursive(uuid))
10613 {
10614 treeLock.release();
10615 mParent->i_markRegistryModified(uuid);
10616 }
10617 }
10618}
10619
10620/**
10621 * Creates differencing hard disks for all normal hard disks attached to this
10622 * machine and a new set of attachments to refer to created disks.
10623 *
10624 * Used when taking a snapshot or when deleting the current state. Gets called
10625 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10626 *
10627 * This method assumes that mMediumAttachments contains the original hard disk
10628 * attachments it needs to create diffs for. On success, these attachments will
10629 * be replaced with the created diffs.
10630 *
10631 * Attachments with non-normal hard disks are left as is.
10632 *
10633 * If @a aOnline is @c false then the original hard disks that require implicit
10634 * diffs will be locked for reading. Otherwise it is assumed that they are
10635 * already locked for writing (when the VM was started). Note that in the latter
10636 * case it is responsibility of the caller to lock the newly created diffs for
10637 * writing if this method succeeds.
10638 *
10639 * @param aProgress Progress object to run (must contain at least as
10640 * many operations left as the number of hard disks
10641 * attached).
10642 * @param aWeight Weight of this operation.
10643 * @param aOnline Whether the VM was online prior to this operation.
10644 *
10645 * @note The progress object is not marked as completed, neither on success nor
10646 * on failure. This is a responsibility of the caller.
10647 *
10648 * @note Locks this object and the media tree for writing.
10649 */
10650HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10651 ULONG aWeight,
10652 bool aOnline)
10653{
10654 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10655
10656 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10657 AssertReturn(!!pProgressControl, E_INVALIDARG);
10658
10659 AutoCaller autoCaller(this);
10660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10661
10662 AutoMultiWriteLock2 alock(this->lockHandle(),
10663 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10664
10665 /* must be in a protective state because we release the lock below */
10666 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10667 || mData->mMachineState == MachineState_OnlineSnapshotting
10668 || mData->mMachineState == MachineState_LiveSnapshotting
10669 || mData->mMachineState == MachineState_RestoringSnapshot
10670 || mData->mMachineState == MachineState_DeletingSnapshot
10671 , E_FAIL);
10672
10673 HRESULT rc = S_OK;
10674
10675 // use appropriate locked media map (online or offline)
10676 MediumLockListMap lockedMediaOffline;
10677 MediumLockListMap *lockedMediaMap;
10678 if (aOnline)
10679 lockedMediaMap = &mData->mSession.mLockedMedia;
10680 else
10681 lockedMediaMap = &lockedMediaOffline;
10682
10683 try
10684 {
10685 if (!aOnline)
10686 {
10687 /* lock all attached hard disks early to detect "in use"
10688 * situations before creating actual diffs */
10689 for (MediumAttachmentList::const_iterator
10690 it = mMediumAttachments->begin();
10691 it != mMediumAttachments->end();
10692 ++it)
10693 {
10694 MediumAttachment *pAtt = *it;
10695 if (pAtt->i_getType() == DeviceType_HardDisk)
10696 {
10697 Medium *pMedium = pAtt->i_getMedium();
10698 Assert(pMedium);
10699
10700 MediumLockList *pMediumLockList(new MediumLockList());
10701 alock.release();
10702 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10703 NULL /* pToLockWrite */,
10704 false /* fMediumLockWriteAll */,
10705 NULL,
10706 *pMediumLockList);
10707 alock.acquire();
10708 if (FAILED(rc))
10709 {
10710 delete pMediumLockList;
10711 throw rc;
10712 }
10713 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10714 if (FAILED(rc))
10715 {
10716 throw setError(rc,
10717 tr("Collecting locking information for all attached media failed"));
10718 }
10719 }
10720 }
10721
10722 /* Now lock all media. If this fails, nothing is locked. */
10723 alock.release();
10724 rc = lockedMediaMap->Lock();
10725 alock.acquire();
10726 if (FAILED(rc))
10727 {
10728 throw setError(rc,
10729 tr("Locking of attached media failed"));
10730 }
10731 }
10732
10733 /* remember the current list (note that we don't use backup() since
10734 * mMediumAttachments may be already backed up) */
10735 MediumAttachmentList atts = *mMediumAttachments.data();
10736
10737 /* start from scratch */
10738 mMediumAttachments->clear();
10739
10740 /* go through remembered attachments and create diffs for normal hard
10741 * disks and attach them */
10742 for (MediumAttachmentList::const_iterator
10743 it = atts.begin();
10744 it != atts.end();
10745 ++it)
10746 {
10747 MediumAttachment *pAtt = *it;
10748
10749 DeviceType_T devType = pAtt->i_getType();
10750 Medium *pMedium = pAtt->i_getMedium();
10751
10752 if ( devType != DeviceType_HardDisk
10753 || pMedium == NULL
10754 || pMedium->i_getType() != MediumType_Normal)
10755 {
10756 /* copy the attachment as is */
10757
10758 /** @todo the progress object created in SessionMachine::TakeSnaphot
10759 * only expects operations for hard disks. Later other
10760 * device types need to show up in the progress as well. */
10761 if (devType == DeviceType_HardDisk)
10762 {
10763 if (pMedium == NULL)
10764 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10765 aWeight); // weight
10766 else
10767 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10768 pMedium->i_getBase()->i_getName().c_str()).raw(),
10769 aWeight); // weight
10770 }
10771
10772 mMediumAttachments->push_back(pAtt);
10773 continue;
10774 }
10775
10776 /* need a diff */
10777 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10778 pMedium->i_getBase()->i_getName().c_str()).raw(),
10779 aWeight); // weight
10780
10781 Utf8Str strFullSnapshotFolder;
10782 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10783
10784 ComObjPtr<Medium> diff;
10785 diff.createObject();
10786 // store the diff in the same registry as the parent
10787 // (this cannot fail here because we can't create implicit diffs for
10788 // unregistered images)
10789 Guid uuidRegistryParent;
10790 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10791 Assert(fInRegistry); NOREF(fInRegistry);
10792 rc = diff->init(mParent,
10793 pMedium->i_getPreferredDiffFormat(),
10794 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10795 uuidRegistryParent,
10796 DeviceType_HardDisk);
10797 if (FAILED(rc)) throw rc;
10798
10799 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10800 * the push_back? Looks like we're going to release medium with the
10801 * wrong kind of lock (general issue with if we fail anywhere at all)
10802 * and an orphaned VDI in the snapshots folder. */
10803
10804 /* update the appropriate lock list */
10805 MediumLockList *pMediumLockList;
10806 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10807 AssertComRCThrowRC(rc);
10808 if (aOnline)
10809 {
10810 alock.release();
10811 /* The currently attached medium will be read-only, change
10812 * the lock type to read. */
10813 rc = pMediumLockList->Update(pMedium, false);
10814 alock.acquire();
10815 AssertComRCThrowRC(rc);
10816 }
10817
10818 /* release the locks before the potentially lengthy operation */
10819 alock.release();
10820 rc = pMedium->i_createDiffStorage(diff,
10821 pMedium->i_getPreferredDiffVariant(),
10822 pMediumLockList,
10823 NULL /* aProgress */,
10824 true /* aWait */,
10825 false /* aNotify */);
10826 alock.acquire();
10827 if (FAILED(rc)) throw rc;
10828
10829 /* actual lock list update is done in Machine::i_commitMedia */
10830
10831 rc = diff->i_addBackReference(mData->mUuid);
10832 AssertComRCThrowRC(rc);
10833
10834 /* add a new attachment */
10835 ComObjPtr<MediumAttachment> attachment;
10836 attachment.createObject();
10837 rc = attachment->init(this,
10838 diff,
10839 pAtt->i_getControllerName(),
10840 pAtt->i_getPort(),
10841 pAtt->i_getDevice(),
10842 DeviceType_HardDisk,
10843 true /* aImplicit */,
10844 false /* aPassthrough */,
10845 false /* aTempEject */,
10846 pAtt->i_getNonRotational(),
10847 pAtt->i_getDiscard(),
10848 pAtt->i_getHotPluggable(),
10849 pAtt->i_getBandwidthGroup());
10850 if (FAILED(rc)) throw rc;
10851
10852 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10853 AssertComRCThrowRC(rc);
10854 mMediumAttachments->push_back(attachment);
10855 }
10856 }
10857 catch (HRESULT aRC) { rc = aRC; }
10858
10859 /* unlock all hard disks we locked when there is no VM */
10860 if (!aOnline)
10861 {
10862 ErrorInfoKeeper eik;
10863
10864 HRESULT rc1 = lockedMediaMap->Clear();
10865 AssertComRC(rc1);
10866 }
10867
10868 return rc;
10869}
10870
10871/**
10872 * Deletes implicit differencing hard disks created either by
10873 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10874 * mMediumAttachments.
10875 *
10876 * Note that to delete hard disks created by #attachDevice() this method is
10877 * called from #i_rollbackMedia() when the changes are rolled back.
10878 *
10879 * @note Locks this object and the media tree for writing.
10880 */
10881HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10882{
10883 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10884
10885 AutoCaller autoCaller(this);
10886 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10887
10888 AutoMultiWriteLock2 alock(this->lockHandle(),
10889 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10890
10891 /* We absolutely must have backed up state. */
10892 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10893
10894 /* Check if there are any implicitly created diff images. */
10895 bool fImplicitDiffs = false;
10896 for (MediumAttachmentList::const_iterator
10897 it = mMediumAttachments->begin();
10898 it != mMediumAttachments->end();
10899 ++it)
10900 {
10901 const ComObjPtr<MediumAttachment> &pAtt = *it;
10902 if (pAtt->i_isImplicit())
10903 {
10904 fImplicitDiffs = true;
10905 break;
10906 }
10907 }
10908 /* If there is nothing to do, leave early. This saves lots of image locking
10909 * effort. It also avoids a MachineStateChanged event without real reason.
10910 * This is important e.g. when loading a VM config, because there should be
10911 * no events. Otherwise API clients can become thoroughly confused for
10912 * inaccessible VMs (the code for loading VM configs uses this method for
10913 * cleanup if the config makes no sense), as they take such events as an
10914 * indication that the VM is alive, and they would force the VM config to
10915 * be reread, leading to an endless loop. */
10916 if (!fImplicitDiffs)
10917 return S_OK;
10918
10919 HRESULT rc = S_OK;
10920 MachineState_T oldState = mData->mMachineState;
10921
10922 /* will release the lock before the potentially lengthy operation,
10923 * so protect with the special state (unless already protected) */
10924 if ( oldState != MachineState_Snapshotting
10925 && oldState != MachineState_OnlineSnapshotting
10926 && oldState != MachineState_LiveSnapshotting
10927 && oldState != MachineState_RestoringSnapshot
10928 && oldState != MachineState_DeletingSnapshot
10929 && oldState != MachineState_DeletingSnapshotOnline
10930 && oldState != MachineState_DeletingSnapshotPaused
10931 )
10932 i_setMachineState(MachineState_SettingUp);
10933
10934 // use appropriate locked media map (online or offline)
10935 MediumLockListMap lockedMediaOffline;
10936 MediumLockListMap *lockedMediaMap;
10937 if (aOnline)
10938 lockedMediaMap = &mData->mSession.mLockedMedia;
10939 else
10940 lockedMediaMap = &lockedMediaOffline;
10941
10942 try
10943 {
10944 if (!aOnline)
10945 {
10946 /* lock all attached hard disks early to detect "in use"
10947 * situations before deleting actual diffs */
10948 for (MediumAttachmentList::const_iterator
10949 it = mMediumAttachments->begin();
10950 it != mMediumAttachments->end();
10951 ++it)
10952 {
10953 MediumAttachment *pAtt = *it;
10954 if (pAtt->i_getType() == DeviceType_HardDisk)
10955 {
10956 Medium *pMedium = pAtt->i_getMedium();
10957 Assert(pMedium);
10958
10959 MediumLockList *pMediumLockList(new MediumLockList());
10960 alock.release();
10961 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10962 NULL /* pToLockWrite */,
10963 false /* fMediumLockWriteAll */,
10964 NULL,
10965 *pMediumLockList);
10966 alock.acquire();
10967
10968 if (FAILED(rc))
10969 {
10970 delete pMediumLockList;
10971 throw rc;
10972 }
10973
10974 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10975 if (FAILED(rc))
10976 throw rc;
10977 }
10978 }
10979
10980 if (FAILED(rc))
10981 throw rc;
10982 } // end of offline
10983
10984 /* Lock lists are now up to date and include implicitly created media */
10985
10986 /* Go through remembered attachments and delete all implicitly created
10987 * diffs and fix up the attachment information */
10988 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10989 MediumAttachmentList implicitAtts;
10990 for (MediumAttachmentList::const_iterator
10991 it = mMediumAttachments->begin();
10992 it != mMediumAttachments->end();
10993 ++it)
10994 {
10995 ComObjPtr<MediumAttachment> pAtt = *it;
10996 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10997 if (pMedium.isNull())
10998 continue;
10999
11000 // Implicit attachments go on the list for deletion and back references are removed.
11001 if (pAtt->i_isImplicit())
11002 {
11003 /* Deassociate and mark for deletion */
11004 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11005 rc = pMedium->i_removeBackReference(mData->mUuid);
11006 if (FAILED(rc))
11007 throw rc;
11008 implicitAtts.push_back(pAtt);
11009 continue;
11010 }
11011
11012 /* Was this medium attached before? */
11013 if (!i_findAttachment(oldAtts, pMedium))
11014 {
11015 /* no: de-associate */
11016 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11017 rc = pMedium->i_removeBackReference(mData->mUuid);
11018 if (FAILED(rc))
11019 throw rc;
11020 continue;
11021 }
11022 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11023 }
11024
11025 /* If there are implicit attachments to delete, throw away the lock
11026 * map contents (which will unlock all media) since the medium
11027 * attachments will be rolled back. Below we need to completely
11028 * recreate the lock map anyway since it is infinitely complex to
11029 * do this incrementally (would need reconstructing each attachment
11030 * change, which would be extremely hairy). */
11031 if (implicitAtts.size() != 0)
11032 {
11033 ErrorInfoKeeper eik;
11034
11035 HRESULT rc1 = lockedMediaMap->Clear();
11036 AssertComRC(rc1);
11037 }
11038
11039 /* rollback hard disk changes */
11040 mMediumAttachments.rollback();
11041
11042 MultiResult mrc(S_OK);
11043
11044 // Delete unused implicit diffs.
11045 if (implicitAtts.size() != 0)
11046 {
11047 alock.release();
11048
11049 for (MediumAttachmentList::const_iterator
11050 it = implicitAtts.begin();
11051 it != implicitAtts.end();
11052 ++it)
11053 {
11054 // Remove medium associated with this attachment.
11055 ComObjPtr<MediumAttachment> pAtt = *it;
11056 Assert(pAtt);
11057 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11058 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11059 Assert(pMedium);
11060
11061 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11062 // continue on delete failure, just collect error messages
11063 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11064 pMedium->i_getLocationFull().c_str() ));
11065 mrc = rc;
11066 }
11067 // Clear the list of deleted implicit attachments now, while not
11068 // holding the lock, as it will ultimately trigger Medium::uninit()
11069 // calls which assume that the media tree lock isn't held.
11070 implicitAtts.clear();
11071
11072 alock.acquire();
11073
11074 /* if there is a VM recreate media lock map as mentioned above,
11075 * otherwise it is a waste of time and we leave things unlocked */
11076 if (aOnline)
11077 {
11078 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11079 /* must never be NULL, but better safe than sorry */
11080 if (!pMachine.isNull())
11081 {
11082 alock.release();
11083 rc = mData->mSession.mMachine->i_lockMedia();
11084 alock.acquire();
11085 if (FAILED(rc))
11086 throw rc;
11087 }
11088 }
11089 }
11090 }
11091 catch (HRESULT aRC) {rc = aRC;}
11092
11093 if (mData->mMachineState == MachineState_SettingUp)
11094 i_setMachineState(oldState);
11095
11096 /* unlock all hard disks we locked when there is no VM */
11097 if (!aOnline)
11098 {
11099 ErrorInfoKeeper eik;
11100
11101 HRESULT rc1 = lockedMediaMap->Clear();
11102 AssertComRC(rc1);
11103 }
11104
11105 return rc;
11106}
11107
11108
11109/**
11110 * Looks through the given list of media attachments for one with the given parameters
11111 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11112 * can be searched as well if needed.
11113 *
11114 * @param ll
11115 * @param aControllerName
11116 * @param aControllerPort
11117 * @param aDevice
11118 * @return
11119 */
11120MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11121 const Utf8Str &aControllerName,
11122 LONG aControllerPort,
11123 LONG aDevice)
11124{
11125 for (MediumAttachmentList::const_iterator
11126 it = ll.begin();
11127 it != ll.end();
11128 ++it)
11129 {
11130 MediumAttachment *pAttach = *it;
11131 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11132 return pAttach;
11133 }
11134
11135 return NULL;
11136}
11137
11138/**
11139 * Looks through the given list of media attachments for one with the given parameters
11140 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11141 * can be searched as well if needed.
11142 *
11143 * @param ll
11144 * @param pMedium
11145 * @return
11146 */
11147MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11148 ComObjPtr<Medium> pMedium)
11149{
11150 for (MediumAttachmentList::const_iterator
11151 it = ll.begin();
11152 it != ll.end();
11153 ++it)
11154 {
11155 MediumAttachment *pAttach = *it;
11156 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11157 if (pMediumThis == pMedium)
11158 return pAttach;
11159 }
11160
11161 return NULL;
11162}
11163
11164/**
11165 * Looks through the given list of media attachments for one with the given parameters
11166 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11167 * can be searched as well if needed.
11168 *
11169 * @param ll
11170 * @param id
11171 * @return
11172 */
11173MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11174 Guid &id)
11175{
11176 for (MediumAttachmentList::const_iterator
11177 it = ll.begin();
11178 it != ll.end();
11179 ++it)
11180 {
11181 MediumAttachment *pAttach = *it;
11182 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11183 if (pMediumThis->i_getId() == id)
11184 return pAttach;
11185 }
11186
11187 return NULL;
11188}
11189
11190/**
11191 * Main implementation for Machine::DetachDevice. This also gets called
11192 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11193 *
11194 * @param pAttach Medium attachment to detach.
11195 * @param writeLock Machine write lock which the caller must have locked once.
11196 * This may be released temporarily in here.
11197 * @param pSnapshot If NULL, then the detachment is for the current machine.
11198 * Otherwise this is for a SnapshotMachine, and this must be
11199 * its snapshot.
11200 * @return
11201 */
11202HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11203 AutoWriteLock &writeLock,
11204 Snapshot *pSnapshot)
11205{
11206 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11207 DeviceType_T mediumType = pAttach->i_getType();
11208
11209 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11210
11211 if (pAttach->i_isImplicit())
11212 {
11213 /* attempt to implicitly delete the implicitly created diff */
11214
11215 /// @todo move the implicit flag from MediumAttachment to Medium
11216 /// and forbid any hard disk operation when it is implicit. Or maybe
11217 /// a special media state for it to make it even more simple.
11218
11219 Assert(mMediumAttachments.isBackedUp());
11220
11221 /* will release the lock before the potentially lengthy operation, so
11222 * protect with the special state */
11223 MachineState_T oldState = mData->mMachineState;
11224 i_setMachineState(MachineState_SettingUp);
11225
11226 writeLock.release();
11227
11228 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11229 true /*aWait*/,
11230 false /*aNotify*/);
11231
11232 writeLock.acquire();
11233
11234 i_setMachineState(oldState);
11235
11236 if (FAILED(rc)) return rc;
11237 }
11238
11239 i_setModified(IsModified_Storage);
11240 mMediumAttachments.backup();
11241 mMediumAttachments->remove(pAttach);
11242
11243 if (!oldmedium.isNull())
11244 {
11245 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11246 if (pSnapshot)
11247 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11248 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11249 else if (mediumType != DeviceType_HardDisk)
11250 oldmedium->i_removeBackReference(mData->mUuid);
11251 }
11252
11253 return S_OK;
11254}
11255
11256/**
11257 * Goes thru all media of the given list and
11258 *
11259 * 1) calls i_detachDevice() on each of them for this machine and
11260 * 2) adds all Medium objects found in the process to the given list,
11261 * depending on cleanupMode.
11262 *
11263 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11264 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11265 * media to the list.
11266 *
11267 * This gets called from Machine::Unregister, both for the actual Machine and
11268 * the SnapshotMachine objects that might be found in the snapshots.
11269 *
11270 * Requires caller and locking. The machine lock must be passed in because it
11271 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11272 *
11273 * @param writeLock Machine lock from top-level caller; this gets passed to
11274 * i_detachDevice.
11275 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11276 * object if called for a SnapshotMachine.
11277 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11278 * added to llMedia; if Full, then all media get added;
11279 * otherwise no media get added.
11280 * @param llMedia Caller's list to receive Medium objects which got detached so
11281 * caller can close() them, depending on cleanupMode.
11282 * @return
11283 */
11284HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11285 Snapshot *pSnapshot,
11286 CleanupMode_T cleanupMode,
11287 MediaList &llMedia)
11288{
11289 Assert(isWriteLockOnCurrentThread());
11290
11291 HRESULT rc;
11292
11293 // make a temporary list because i_detachDevice invalidates iterators into
11294 // mMediumAttachments
11295 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11296
11297 for (MediumAttachmentList::iterator
11298 it = llAttachments2.begin();
11299 it != llAttachments2.end();
11300 ++it)
11301 {
11302 ComObjPtr<MediumAttachment> &pAttach = *it;
11303 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11304
11305 if (!pMedium.isNull())
11306 {
11307 AutoCaller mac(pMedium);
11308 if (FAILED(mac.rc())) return mac.rc();
11309 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11310 DeviceType_T devType = pMedium->i_getDeviceType();
11311 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11312 && devType == DeviceType_HardDisk)
11313 || (cleanupMode == CleanupMode_Full)
11314 )
11315 {
11316 llMedia.push_back(pMedium);
11317 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11318 /* Not allowed to keep this lock as below we need the parent
11319 * medium lock, and the lock order is parent to child. */
11320 lock.release();
11321 /*
11322 * Search for medias which are not attached to any machine, but
11323 * in the chain to an attached disk. Mediums are only consided
11324 * if they are:
11325 * - have only one child
11326 * - no references to any machines
11327 * - are of normal medium type
11328 */
11329 while (!pParent.isNull())
11330 {
11331 AutoCaller mac1(pParent);
11332 if (FAILED(mac1.rc())) return mac1.rc();
11333 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11334 if (pParent->i_getChildren().size() == 1)
11335 {
11336 if ( pParent->i_getMachineBackRefCount() == 0
11337 && pParent->i_getType() == MediumType_Normal
11338 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11339 llMedia.push_back(pParent);
11340 }
11341 else
11342 break;
11343 pParent = pParent->i_getParent();
11344 }
11345 }
11346 }
11347
11348 // real machine: then we need to use the proper method
11349 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11350
11351 if (FAILED(rc))
11352 return rc;
11353 }
11354
11355 return S_OK;
11356}
11357
11358/**
11359 * Perform deferred hard disk detachments.
11360 *
11361 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11362 * changed (not backed up).
11363 *
11364 * If @a aOnline is @c true then this method will also unlock the old hard
11365 * disks for which the new implicit diffs were created and will lock these new
11366 * diffs for writing.
11367 *
11368 * @param aOnline Whether the VM was online prior to this operation.
11369 *
11370 * @note Locks this object for writing!
11371 */
11372void Machine::i_commitMedia(bool aOnline /*= false*/)
11373{
11374 AutoCaller autoCaller(this);
11375 AssertComRCReturnVoid(autoCaller.rc());
11376
11377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11378
11379 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11380
11381 HRESULT rc = S_OK;
11382
11383 /* no attach/detach operations -- nothing to do */
11384 if (!mMediumAttachments.isBackedUp())
11385 return;
11386
11387 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11388 bool fMediaNeedsLocking = false;
11389
11390 /* enumerate new attachments */
11391 for (MediumAttachmentList::const_iterator
11392 it = mMediumAttachments->begin();
11393 it != mMediumAttachments->end();
11394 ++it)
11395 {
11396 MediumAttachment *pAttach = *it;
11397
11398 pAttach->i_commit();
11399
11400 Medium *pMedium = pAttach->i_getMedium();
11401 bool fImplicit = pAttach->i_isImplicit();
11402
11403 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11404 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11405 fImplicit));
11406
11407 /** @todo convert all this Machine-based voodoo to MediumAttachment
11408 * based commit logic. */
11409 if (fImplicit)
11410 {
11411 /* convert implicit attachment to normal */
11412 pAttach->i_setImplicit(false);
11413
11414 if ( aOnline
11415 && pMedium
11416 && pAttach->i_getType() == DeviceType_HardDisk
11417 )
11418 {
11419 /* update the appropriate lock list */
11420 MediumLockList *pMediumLockList;
11421 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11422 AssertComRC(rc);
11423 if (pMediumLockList)
11424 {
11425 /* unlock if there's a need to change the locking */
11426 if (!fMediaNeedsLocking)
11427 {
11428 rc = mData->mSession.mLockedMedia.Unlock();
11429 AssertComRC(rc);
11430 fMediaNeedsLocking = true;
11431 }
11432 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11433 AssertComRC(rc);
11434 rc = pMediumLockList->Append(pMedium, true);
11435 AssertComRC(rc);
11436 }
11437 }
11438
11439 continue;
11440 }
11441
11442 if (pMedium)
11443 {
11444 /* was this medium attached before? */
11445 for (MediumAttachmentList::iterator
11446 oldIt = oldAtts.begin();
11447 oldIt != oldAtts.end();
11448 ++oldIt)
11449 {
11450 MediumAttachment *pOldAttach = *oldIt;
11451 if (pOldAttach->i_getMedium() == pMedium)
11452 {
11453 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11454
11455 /* yes: remove from old to avoid de-association */
11456 oldAtts.erase(oldIt);
11457 break;
11458 }
11459 }
11460 }
11461 }
11462
11463 /* enumerate remaining old attachments and de-associate from the
11464 * current machine state */
11465 for (MediumAttachmentList::const_iterator
11466 it = oldAtts.begin();
11467 it != oldAtts.end();
11468 ++it)
11469 {
11470 MediumAttachment *pAttach = *it;
11471 Medium *pMedium = pAttach->i_getMedium();
11472
11473 /* Detach only hard disks, since DVD/floppy media is detached
11474 * instantly in MountMedium. */
11475 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11476 {
11477 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11478
11479 /* now de-associate from the current machine state */
11480 rc = pMedium->i_removeBackReference(mData->mUuid);
11481 AssertComRC(rc);
11482
11483 if (aOnline)
11484 {
11485 /* unlock since medium is not used anymore */
11486 MediumLockList *pMediumLockList;
11487 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11488 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11489 {
11490 /* this happens for online snapshots, there the attachment
11491 * is changing, but only to a diff image created under
11492 * the old one, so there is no separate lock list */
11493 Assert(!pMediumLockList);
11494 }
11495 else
11496 {
11497 AssertComRC(rc);
11498 if (pMediumLockList)
11499 {
11500 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11501 AssertComRC(rc);
11502 }
11503 }
11504 }
11505 }
11506 }
11507
11508 /* take media locks again so that the locking state is consistent */
11509 if (fMediaNeedsLocking)
11510 {
11511 Assert(aOnline);
11512 rc = mData->mSession.mLockedMedia.Lock();
11513 AssertComRC(rc);
11514 }
11515
11516 /* commit the hard disk changes */
11517 mMediumAttachments.commit();
11518
11519 if (i_isSessionMachine())
11520 {
11521 /*
11522 * Update the parent machine to point to the new owner.
11523 * This is necessary because the stored parent will point to the
11524 * session machine otherwise and cause crashes or errors later
11525 * when the session machine gets invalid.
11526 */
11527 /** @todo Change the MediumAttachment class to behave like any other
11528 * class in this regard by creating peer MediumAttachment
11529 * objects for session machines and share the data with the peer
11530 * machine.
11531 */
11532 for (MediumAttachmentList::const_iterator
11533 it = mMediumAttachments->begin();
11534 it != mMediumAttachments->end();
11535 ++it)
11536 (*it)->i_updateParentMachine(mPeer);
11537
11538 /* attach new data to the primary machine and reshare it */
11539 mPeer->mMediumAttachments.attach(mMediumAttachments);
11540 }
11541
11542 return;
11543}
11544
11545/**
11546 * Perform deferred deletion of implicitly created diffs.
11547 *
11548 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11549 * changed (not backed up).
11550 *
11551 * @note Locks this object for writing!
11552 */
11553void Machine::i_rollbackMedia()
11554{
11555 AutoCaller autoCaller(this);
11556 AssertComRCReturnVoid(autoCaller.rc());
11557
11558 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11559 LogFlowThisFunc(("Entering rollbackMedia\n"));
11560
11561 HRESULT rc = S_OK;
11562
11563 /* no attach/detach operations -- nothing to do */
11564 if (!mMediumAttachments.isBackedUp())
11565 return;
11566
11567 /* enumerate new attachments */
11568 for (MediumAttachmentList::const_iterator
11569 it = mMediumAttachments->begin();
11570 it != mMediumAttachments->end();
11571 ++it)
11572 {
11573 MediumAttachment *pAttach = *it;
11574 /* Fix up the backrefs for DVD/floppy media. */
11575 if (pAttach->i_getType() != DeviceType_HardDisk)
11576 {
11577 Medium *pMedium = pAttach->i_getMedium();
11578 if (pMedium)
11579 {
11580 rc = pMedium->i_removeBackReference(mData->mUuid);
11581 AssertComRC(rc);
11582 }
11583 }
11584
11585 (*it)->i_rollback();
11586
11587 pAttach = *it;
11588 /* Fix up the backrefs for DVD/floppy media. */
11589 if (pAttach->i_getType() != DeviceType_HardDisk)
11590 {
11591 Medium *pMedium = pAttach->i_getMedium();
11592 if (pMedium)
11593 {
11594 rc = pMedium->i_addBackReference(mData->mUuid);
11595 AssertComRC(rc);
11596 }
11597 }
11598 }
11599
11600 /** @todo convert all this Machine-based voodoo to MediumAttachment
11601 * based rollback logic. */
11602 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11603
11604 return;
11605}
11606
11607/**
11608 * Returns true if the settings file is located in the directory named exactly
11609 * as the machine; this means, among other things, that the machine directory
11610 * should be auto-renamed.
11611 *
11612 * @param aSettingsDir if not NULL, the full machine settings file directory
11613 * name will be assigned there.
11614 *
11615 * @note Doesn't lock anything.
11616 * @note Not thread safe (must be called from this object's lock).
11617 */
11618bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11619{
11620 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11621 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11622 if (aSettingsDir)
11623 *aSettingsDir = strMachineDirName;
11624 strMachineDirName.stripPath(); // vmname
11625 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11626 strConfigFileOnly.stripPath() // vmname.vbox
11627 .stripSuffix(); // vmname
11628 /** @todo hack, make somehow use of ComposeMachineFilename */
11629 if (mUserData->s.fDirectoryIncludesUUID)
11630 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11631
11632 AssertReturn(!strMachineDirName.isEmpty(), false);
11633 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11634
11635 return strMachineDirName == strConfigFileOnly;
11636}
11637
11638/**
11639 * Discards all changes to machine settings.
11640 *
11641 * @param aNotify Whether to notify the direct session about changes or not.
11642 *
11643 * @note Locks objects for writing!
11644 */
11645void Machine::i_rollback(bool aNotify)
11646{
11647 AutoCaller autoCaller(this);
11648 AssertComRCReturn(autoCaller.rc(), (void)0);
11649
11650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11651
11652 if (!mStorageControllers.isNull())
11653 {
11654 if (mStorageControllers.isBackedUp())
11655 {
11656 /* unitialize all new devices (absent in the backed up list). */
11657 StorageControllerList *backedList = mStorageControllers.backedUpData();
11658 for (StorageControllerList::const_iterator
11659 it = mStorageControllers->begin();
11660 it != mStorageControllers->end();
11661 ++it)
11662 {
11663 if ( std::find(backedList->begin(), backedList->end(), *it)
11664 == backedList->end()
11665 )
11666 {
11667 (*it)->uninit();
11668 }
11669 }
11670
11671 /* restore the list */
11672 mStorageControllers.rollback();
11673 }
11674
11675 /* rollback any changes to devices after restoring the list */
11676 if (mData->flModifications & IsModified_Storage)
11677 {
11678 for (StorageControllerList::const_iterator
11679 it = mStorageControllers->begin();
11680 it != mStorageControllers->end();
11681 ++it)
11682 {
11683 (*it)->i_rollback();
11684 }
11685 }
11686 }
11687
11688 if (!mUSBControllers.isNull())
11689 {
11690 if (mUSBControllers.isBackedUp())
11691 {
11692 /* unitialize all new devices (absent in the backed up list). */
11693 USBControllerList *backedList = mUSBControllers.backedUpData();
11694 for (USBControllerList::const_iterator
11695 it = mUSBControllers->begin();
11696 it != mUSBControllers->end();
11697 ++it)
11698 {
11699 if ( std::find(backedList->begin(), backedList->end(), *it)
11700 == backedList->end()
11701 )
11702 {
11703 (*it)->uninit();
11704 }
11705 }
11706
11707 /* restore the list */
11708 mUSBControllers.rollback();
11709 }
11710
11711 /* rollback any changes to devices after restoring the list */
11712 if (mData->flModifications & IsModified_USB)
11713 {
11714 for (USBControllerList::const_iterator
11715 it = mUSBControllers->begin();
11716 it != mUSBControllers->end();
11717 ++it)
11718 {
11719 (*it)->i_rollback();
11720 }
11721 }
11722 }
11723
11724 mUserData.rollback();
11725
11726 mHWData.rollback();
11727
11728 if (mData->flModifications & IsModified_Storage)
11729 i_rollbackMedia();
11730
11731 if (mBIOSSettings)
11732 mBIOSSettings->i_rollback();
11733
11734 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11735 mRecordingSettings->i_rollback();
11736
11737 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11738 mVRDEServer->i_rollback();
11739
11740 if (mAudioAdapter)
11741 mAudioAdapter->i_rollback();
11742
11743 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11744 mUSBDeviceFilters->i_rollback();
11745
11746 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11747 mBandwidthControl->i_rollback();
11748
11749 if (!mHWData.isNull())
11750 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11751 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11752 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11753 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11754
11755 if (mData->flModifications & IsModified_NetworkAdapters)
11756 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11757 if ( mNetworkAdapters[slot]
11758 && mNetworkAdapters[slot]->i_isModified())
11759 {
11760 mNetworkAdapters[slot]->i_rollback();
11761 networkAdapters[slot] = mNetworkAdapters[slot];
11762 }
11763
11764 if (mData->flModifications & IsModified_SerialPorts)
11765 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11766 if ( mSerialPorts[slot]
11767 && mSerialPorts[slot]->i_isModified())
11768 {
11769 mSerialPorts[slot]->i_rollback();
11770 serialPorts[slot] = mSerialPorts[slot];
11771 }
11772
11773 if (mData->flModifications & IsModified_ParallelPorts)
11774 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11775 if ( mParallelPorts[slot]
11776 && mParallelPorts[slot]->i_isModified())
11777 {
11778 mParallelPorts[slot]->i_rollback();
11779 parallelPorts[slot] = mParallelPorts[slot];
11780 }
11781
11782 if (aNotify)
11783 {
11784 /* inform the direct session about changes */
11785
11786 ComObjPtr<Machine> that = this;
11787 uint32_t flModifications = mData->flModifications;
11788 alock.release();
11789
11790 if (flModifications & IsModified_SharedFolders)
11791 that->i_onSharedFolderChange();
11792
11793 if (flModifications & IsModified_VRDEServer)
11794 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11795 if (flModifications & IsModified_USB)
11796 that->i_onUSBControllerChange();
11797
11798 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11799 if (networkAdapters[slot])
11800 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11801 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11802 if (serialPorts[slot])
11803 that->i_onSerialPortChange(serialPorts[slot]);
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11805 if (parallelPorts[slot])
11806 that->i_onParallelPortChange(parallelPorts[slot]);
11807
11808 if (flModifications & IsModified_Storage)
11809 that->i_onStorageControllerChange();
11810
11811#if 0
11812 if (flModifications & IsModified_BandwidthControl)
11813 that->onBandwidthControlChange();
11814#endif
11815 }
11816}
11817
11818/**
11819 * Commits all the changes to machine settings.
11820 *
11821 * Note that this operation is supposed to never fail.
11822 *
11823 * @note Locks this object and children for writing.
11824 */
11825void Machine::i_commit()
11826{
11827 AutoCaller autoCaller(this);
11828 AssertComRCReturnVoid(autoCaller.rc());
11829
11830 AutoCaller peerCaller(mPeer);
11831 AssertComRCReturnVoid(peerCaller.rc());
11832
11833 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11834
11835 /*
11836 * use safe commit to ensure Snapshot machines (that share mUserData)
11837 * will still refer to a valid memory location
11838 */
11839 mUserData.commitCopy();
11840
11841 mHWData.commit();
11842
11843 if (mMediumAttachments.isBackedUp())
11844 i_commitMedia(Global::IsOnline(mData->mMachineState));
11845
11846 mBIOSSettings->i_commit();
11847 mRecordingSettings->i_commit();
11848 mVRDEServer->i_commit();
11849 mAudioAdapter->i_commit();
11850 mUSBDeviceFilters->i_commit();
11851 mBandwidthControl->i_commit();
11852
11853 /* Since mNetworkAdapters is a list which might have been changed (resized)
11854 * without using the Backupable<> template we need to handle the copying
11855 * of the list entries manually, including the creation of peers for the
11856 * new objects. */
11857 bool commitNetworkAdapters = false;
11858 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11859 if (mPeer)
11860 {
11861 /* commit everything, even the ones which will go away */
11862 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11863 mNetworkAdapters[slot]->i_commit();
11864 /* copy over the new entries, creating a peer and uninit the original */
11865 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11866 for (size_t slot = 0; slot < newSize; slot++)
11867 {
11868 /* look if this adapter has a peer device */
11869 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11870 if (!peer)
11871 {
11872 /* no peer means the adapter is a newly created one;
11873 * create a peer owning data this data share it with */
11874 peer.createObject();
11875 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11876 }
11877 mPeer->mNetworkAdapters[slot] = peer;
11878 }
11879 /* uninit any no longer needed network adapters */
11880 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11881 mNetworkAdapters[slot]->uninit();
11882 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11883 {
11884 if (mPeer->mNetworkAdapters[slot])
11885 mPeer->mNetworkAdapters[slot]->uninit();
11886 }
11887 /* Keep the original network adapter count until this point, so that
11888 * discarding a chipset type change will not lose settings. */
11889 mNetworkAdapters.resize(newSize);
11890 mPeer->mNetworkAdapters.resize(newSize);
11891 }
11892 else
11893 {
11894 /* we have no peer (our parent is the newly created machine);
11895 * just commit changes to the network adapters */
11896 commitNetworkAdapters = true;
11897 }
11898 if (commitNetworkAdapters)
11899 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11900 mNetworkAdapters[slot]->i_commit();
11901
11902 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11903 mSerialPorts[slot]->i_commit();
11904 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11905 mParallelPorts[slot]->i_commit();
11906
11907 bool commitStorageControllers = false;
11908
11909 if (mStorageControllers.isBackedUp())
11910 {
11911 mStorageControllers.commit();
11912
11913 if (mPeer)
11914 {
11915 /* Commit all changes to new controllers (this will reshare data with
11916 * peers for those who have peers) */
11917 StorageControllerList *newList = new StorageControllerList();
11918 for (StorageControllerList::const_iterator
11919 it = mStorageControllers->begin();
11920 it != mStorageControllers->end();
11921 ++it)
11922 {
11923 (*it)->i_commit();
11924
11925 /* look if this controller has a peer device */
11926 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11927 if (!peer)
11928 {
11929 /* no peer means the device is a newly created one;
11930 * create a peer owning data this device share it with */
11931 peer.createObject();
11932 peer->init(mPeer, *it, true /* aReshare */);
11933 }
11934 else
11935 {
11936 /* remove peer from the old list */
11937 mPeer->mStorageControllers->remove(peer);
11938 }
11939 /* and add it to the new list */
11940 newList->push_back(peer);
11941 }
11942
11943 /* uninit old peer's controllers that are left */
11944 for (StorageControllerList::const_iterator
11945 it = mPeer->mStorageControllers->begin();
11946 it != mPeer->mStorageControllers->end();
11947 ++it)
11948 {
11949 (*it)->uninit();
11950 }
11951
11952 /* attach new list of controllers to our peer */
11953 mPeer->mStorageControllers.attach(newList);
11954 }
11955 else
11956 {
11957 /* we have no peer (our parent is the newly created machine);
11958 * just commit changes to devices */
11959 commitStorageControllers = true;
11960 }
11961 }
11962 else
11963 {
11964 /* the list of controllers itself is not changed,
11965 * just commit changes to controllers themselves */
11966 commitStorageControllers = true;
11967 }
11968
11969 if (commitStorageControllers)
11970 {
11971 for (StorageControllerList::const_iterator
11972 it = mStorageControllers->begin();
11973 it != mStorageControllers->end();
11974 ++it)
11975 {
11976 (*it)->i_commit();
11977 }
11978 }
11979
11980 bool commitUSBControllers = false;
11981
11982 if (mUSBControllers.isBackedUp())
11983 {
11984 mUSBControllers.commit();
11985
11986 if (mPeer)
11987 {
11988 /* Commit all changes to new controllers (this will reshare data with
11989 * peers for those who have peers) */
11990 USBControllerList *newList = new USBControllerList();
11991 for (USBControllerList::const_iterator
11992 it = mUSBControllers->begin();
11993 it != mUSBControllers->end();
11994 ++it)
11995 {
11996 (*it)->i_commit();
11997
11998 /* look if this controller has a peer device */
11999 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12000 if (!peer)
12001 {
12002 /* no peer means the device is a newly created one;
12003 * create a peer owning data this device share it with */
12004 peer.createObject();
12005 peer->init(mPeer, *it, true /* aReshare */);
12006 }
12007 else
12008 {
12009 /* remove peer from the old list */
12010 mPeer->mUSBControllers->remove(peer);
12011 }
12012 /* and add it to the new list */
12013 newList->push_back(peer);
12014 }
12015
12016 /* uninit old peer's controllers that are left */
12017 for (USBControllerList::const_iterator
12018 it = mPeer->mUSBControllers->begin();
12019 it != mPeer->mUSBControllers->end();
12020 ++it)
12021 {
12022 (*it)->uninit();
12023 }
12024
12025 /* attach new list of controllers to our peer */
12026 mPeer->mUSBControllers.attach(newList);
12027 }
12028 else
12029 {
12030 /* we have no peer (our parent is the newly created machine);
12031 * just commit changes to devices */
12032 commitUSBControllers = true;
12033 }
12034 }
12035 else
12036 {
12037 /* the list of controllers itself is not changed,
12038 * just commit changes to controllers themselves */
12039 commitUSBControllers = true;
12040 }
12041
12042 if (commitUSBControllers)
12043 {
12044 for (USBControllerList::const_iterator
12045 it = mUSBControllers->begin();
12046 it != mUSBControllers->end();
12047 ++it)
12048 {
12049 (*it)->i_commit();
12050 }
12051 }
12052
12053 if (i_isSessionMachine())
12054 {
12055 /* attach new data to the primary machine and reshare it */
12056 mPeer->mUserData.attach(mUserData);
12057 mPeer->mHWData.attach(mHWData);
12058 /* mmMediumAttachments is reshared by fixupMedia */
12059 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12060 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12061 }
12062}
12063
12064/**
12065 * Copies all the hardware data from the given machine.
12066 *
12067 * Currently, only called when the VM is being restored from a snapshot. In
12068 * particular, this implies that the VM is not running during this method's
12069 * call.
12070 *
12071 * @note This method must be called from under this object's lock.
12072 *
12073 * @note This method doesn't call #i_commit(), so all data remains backed up and
12074 * unsaved.
12075 */
12076void Machine::i_copyFrom(Machine *aThat)
12077{
12078 AssertReturnVoid(!i_isSnapshotMachine());
12079 AssertReturnVoid(aThat->i_isSnapshotMachine());
12080
12081 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12082
12083 mHWData.assignCopy(aThat->mHWData);
12084
12085 // create copies of all shared folders (mHWData after attaching a copy
12086 // contains just references to original objects)
12087 for (HWData::SharedFolderList::iterator
12088 it = mHWData->mSharedFolders.begin();
12089 it != mHWData->mSharedFolders.end();
12090 ++it)
12091 {
12092 ComObjPtr<SharedFolder> folder;
12093 folder.createObject();
12094 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12095 AssertComRC(rc);
12096 *it = folder;
12097 }
12098
12099 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12100 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12101 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12102 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12103 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12104 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12105
12106 /* create private copies of all controllers */
12107 mStorageControllers.backup();
12108 mStorageControllers->clear();
12109 for (StorageControllerList::const_iterator
12110 it = aThat->mStorageControllers->begin();
12111 it != aThat->mStorageControllers->end();
12112 ++it)
12113 {
12114 ComObjPtr<StorageController> ctrl;
12115 ctrl.createObject();
12116 ctrl->initCopy(this, *it);
12117 mStorageControllers->push_back(ctrl);
12118 }
12119
12120 /* create private copies of all USB controllers */
12121 mUSBControllers.backup();
12122 mUSBControllers->clear();
12123 for (USBControllerList::const_iterator
12124 it = aThat->mUSBControllers->begin();
12125 it != aThat->mUSBControllers->end();
12126 ++it)
12127 {
12128 ComObjPtr<USBController> ctrl;
12129 ctrl.createObject();
12130 ctrl->initCopy(this, *it);
12131 mUSBControllers->push_back(ctrl);
12132 }
12133
12134 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12135 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12136 {
12137 if (mNetworkAdapters[slot].isNotNull())
12138 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12139 else
12140 {
12141 unconst(mNetworkAdapters[slot]).createObject();
12142 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12143 }
12144 }
12145 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12146 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12147 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12148 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12149}
12150
12151/**
12152 * Returns whether the given storage controller is hotplug capable.
12153 *
12154 * @returns true if the controller supports hotplugging
12155 * false otherwise.
12156 * @param enmCtrlType The controller type to check for.
12157 */
12158bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12159{
12160 ComPtr<ISystemProperties> systemProperties;
12161 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12162 if (FAILED(rc))
12163 return false;
12164
12165 BOOL aHotplugCapable = FALSE;
12166 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12167
12168 return RT_BOOL(aHotplugCapable);
12169}
12170
12171#ifdef VBOX_WITH_RESOURCE_USAGE_API
12172
12173void Machine::i_getDiskList(MediaList &list)
12174{
12175 for (MediumAttachmentList::const_iterator
12176 it = mMediumAttachments->begin();
12177 it != mMediumAttachments->end();
12178 ++it)
12179 {
12180 MediumAttachment *pAttach = *it;
12181 /* just in case */
12182 AssertContinue(pAttach);
12183
12184 AutoCaller localAutoCallerA(pAttach);
12185 if (FAILED(localAutoCallerA.rc())) continue;
12186
12187 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12188
12189 if (pAttach->i_getType() == DeviceType_HardDisk)
12190 list.push_back(pAttach->i_getMedium());
12191 }
12192}
12193
12194void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12195{
12196 AssertReturnVoid(isWriteLockOnCurrentThread());
12197 AssertPtrReturnVoid(aCollector);
12198
12199 pm::CollectorHAL *hal = aCollector->getHAL();
12200 /* Create sub metrics */
12201 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12202 "Percentage of processor time spent in user mode by the VM process.");
12203 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12204 "Percentage of processor time spent in kernel mode by the VM process.");
12205 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12206 "Size of resident portion of VM process in memory.");
12207 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12208 "Actual size of all VM disks combined.");
12209 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12210 "Network receive rate.");
12211 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12212 "Network transmit rate.");
12213 /* Create and register base metrics */
12214 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12215 cpuLoadUser, cpuLoadKernel);
12216 aCollector->registerBaseMetric(cpuLoad);
12217 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12218 ramUsageUsed);
12219 aCollector->registerBaseMetric(ramUsage);
12220 MediaList disks;
12221 i_getDiskList(disks);
12222 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12223 diskUsageUsed);
12224 aCollector->registerBaseMetric(diskUsage);
12225
12226 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12228 new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12230 new pm::AggregateMin()));
12231 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12232 new pm::AggregateMax()));
12233 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12234 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12235 new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12237 new pm::AggregateMin()));
12238 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12239 new pm::AggregateMax()));
12240
12241 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12242 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12243 new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12245 new pm::AggregateMin()));
12246 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12247 new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12250 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12251 new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12253 new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12255 new pm::AggregateMax()));
12256
12257
12258 /* Guest metrics collector */
12259 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12260 aCollector->registerGuest(mCollectorGuest);
12261 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12262
12263 /* Create sub metrics */
12264 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12265 "Percentage of processor time spent in user mode as seen by the guest.");
12266 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12267 "Percentage of processor time spent in kernel mode as seen by the guest.");
12268 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12269 "Percentage of processor time spent idling as seen by the guest.");
12270
12271 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12272 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12273 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12274 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12275 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12276 pm::SubMetric *guestMemCache = new pm::SubMetric(
12277 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12278
12279 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12280 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12281
12282 /* Create and register base metrics */
12283 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12284 machineNetRx, machineNetTx);
12285 aCollector->registerBaseMetric(machineNetRate);
12286
12287 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12288 guestLoadUser, guestLoadKernel, guestLoadIdle);
12289 aCollector->registerBaseMetric(guestCpuLoad);
12290
12291 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12292 guestMemTotal, guestMemFree,
12293 guestMemBalloon, guestMemShared,
12294 guestMemCache, guestPagedTotal);
12295 aCollector->registerBaseMetric(guestCpuMem);
12296
12297 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12303 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12304 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12306
12307 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12309 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12311
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12314 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12316
12317 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12318 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12319 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12321
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12326
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12331
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12336
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12339 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12341
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12351}
12352
12353void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12354{
12355 AssertReturnVoid(isWriteLockOnCurrentThread());
12356
12357 if (aCollector)
12358 {
12359 aCollector->unregisterMetricsFor(aMachine);
12360 aCollector->unregisterBaseMetricsFor(aMachine);
12361 }
12362}
12363
12364#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12365
12366
12367////////////////////////////////////////////////////////////////////////////////
12368
12369DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12370
12371HRESULT SessionMachine::FinalConstruct()
12372{
12373 LogFlowThisFunc(("\n"));
12374
12375 mClientToken = NULL;
12376
12377 return BaseFinalConstruct();
12378}
12379
12380void SessionMachine::FinalRelease()
12381{
12382 LogFlowThisFunc(("\n"));
12383
12384 Assert(!mClientToken);
12385 /* paranoia, should not hang around any more */
12386 if (mClientToken)
12387 {
12388 delete mClientToken;
12389 mClientToken = NULL;
12390 }
12391
12392 uninit(Uninit::Unexpected);
12393
12394 BaseFinalRelease();
12395}
12396
12397/**
12398 * @note Must be called only by Machine::LockMachine() from its own write lock.
12399 */
12400HRESULT SessionMachine::init(Machine *aMachine)
12401{
12402 LogFlowThisFuncEnter();
12403 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12404
12405 AssertReturn(aMachine, E_INVALIDARG);
12406
12407 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12408
12409 /* Enclose the state transition NotReady->InInit->Ready */
12410 AutoInitSpan autoInitSpan(this);
12411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12412
12413 HRESULT rc = S_OK;
12414
12415 RT_ZERO(mAuthLibCtx);
12416
12417 /* create the machine client token */
12418 try
12419 {
12420 mClientToken = new ClientToken(aMachine, this);
12421 if (!mClientToken->isReady())
12422 {
12423 delete mClientToken;
12424 mClientToken = NULL;
12425 rc = E_FAIL;
12426 }
12427 }
12428 catch (std::bad_alloc &)
12429 {
12430 rc = E_OUTOFMEMORY;
12431 }
12432 if (FAILED(rc))
12433 return rc;
12434
12435 /* memorize the peer Machine */
12436 unconst(mPeer) = aMachine;
12437 /* share the parent pointer */
12438 unconst(mParent) = aMachine->mParent;
12439
12440 /* take the pointers to data to share */
12441 mData.share(aMachine->mData);
12442 mSSData.share(aMachine->mSSData);
12443
12444 mUserData.share(aMachine->mUserData);
12445 mHWData.share(aMachine->mHWData);
12446 mMediumAttachments.share(aMachine->mMediumAttachments);
12447
12448 mStorageControllers.allocate();
12449 for (StorageControllerList::const_iterator
12450 it = aMachine->mStorageControllers->begin();
12451 it != aMachine->mStorageControllers->end();
12452 ++it)
12453 {
12454 ComObjPtr<StorageController> ctl;
12455 ctl.createObject();
12456 ctl->init(this, *it);
12457 mStorageControllers->push_back(ctl);
12458 }
12459
12460 mUSBControllers.allocate();
12461 for (USBControllerList::const_iterator
12462 it = aMachine->mUSBControllers->begin();
12463 it != aMachine->mUSBControllers->end();
12464 ++it)
12465 {
12466 ComObjPtr<USBController> ctl;
12467 ctl.createObject();
12468 ctl->init(this, *it);
12469 mUSBControllers->push_back(ctl);
12470 }
12471
12472 unconst(mBIOSSettings).createObject();
12473 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12474 unconst(mRecordingSettings).createObject();
12475 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12476 /* create another VRDEServer object that will be mutable */
12477 unconst(mVRDEServer).createObject();
12478 mVRDEServer->init(this, aMachine->mVRDEServer);
12479 /* create another audio adapter object that will be mutable */
12480 unconst(mAudioAdapter).createObject();
12481 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12482 /* create a list of serial ports that will be mutable */
12483 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12484 {
12485 unconst(mSerialPorts[slot]).createObject();
12486 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12487 }
12488 /* create a list of parallel ports that will be mutable */
12489 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12490 {
12491 unconst(mParallelPorts[slot]).createObject();
12492 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12493 }
12494
12495 /* create another USB device filters object that will be mutable */
12496 unconst(mUSBDeviceFilters).createObject();
12497 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12498
12499 /* create a list of network adapters that will be mutable */
12500 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12501 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12502 {
12503 unconst(mNetworkAdapters[slot]).createObject();
12504 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12505 }
12506
12507 /* create another bandwidth control object that will be mutable */
12508 unconst(mBandwidthControl).createObject();
12509 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12510
12511 /* default is to delete saved state on Saved -> PoweredOff transition */
12512 mRemoveSavedState = true;
12513
12514 /* Confirm a successful initialization when it's the case */
12515 autoInitSpan.setSucceeded();
12516
12517 miNATNetworksStarted = 0;
12518
12519 LogFlowThisFuncLeave();
12520 return rc;
12521}
12522
12523/**
12524 * Uninitializes this session object. If the reason is other than
12525 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12526 * or the client watcher code.
12527 *
12528 * @param aReason uninitialization reason
12529 *
12530 * @note Locks mParent + this object for writing.
12531 */
12532void SessionMachine::uninit(Uninit::Reason aReason)
12533{
12534 LogFlowThisFuncEnter();
12535 LogFlowThisFunc(("reason=%d\n", aReason));
12536
12537 /*
12538 * Strongly reference ourselves to prevent this object deletion after
12539 * mData->mSession.mMachine.setNull() below (which can release the last
12540 * reference and call the destructor). Important: this must be done before
12541 * accessing any members (and before AutoUninitSpan that does it as well).
12542 * This self reference will be released as the very last step on return.
12543 */
12544 ComObjPtr<SessionMachine> selfRef;
12545 if (aReason != Uninit::Unexpected)
12546 selfRef = this;
12547
12548 /* Enclose the state transition Ready->InUninit->NotReady */
12549 AutoUninitSpan autoUninitSpan(this);
12550 if (autoUninitSpan.uninitDone())
12551 {
12552 LogFlowThisFunc(("Already uninitialized\n"));
12553 LogFlowThisFuncLeave();
12554 return;
12555 }
12556
12557 if (autoUninitSpan.initFailed())
12558 {
12559 /* We've been called by init() because it's failed. It's not really
12560 * necessary (nor it's safe) to perform the regular uninit sequence
12561 * below, the following is enough.
12562 */
12563 LogFlowThisFunc(("Initialization failed.\n"));
12564 /* destroy the machine client token */
12565 if (mClientToken)
12566 {
12567 delete mClientToken;
12568 mClientToken = NULL;
12569 }
12570 uninitDataAndChildObjects();
12571 mData.free();
12572 unconst(mParent) = NULL;
12573 unconst(mPeer) = NULL;
12574 LogFlowThisFuncLeave();
12575 return;
12576 }
12577
12578 MachineState_T lastState;
12579 {
12580 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12581 lastState = mData->mMachineState;
12582 }
12583 NOREF(lastState);
12584
12585#ifdef VBOX_WITH_USB
12586 // release all captured USB devices, but do this before requesting the locks below
12587 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12588 {
12589 /* Console::captureUSBDevices() is called in the VM process only after
12590 * setting the machine state to Starting or Restoring.
12591 * Console::detachAllUSBDevices() will be called upon successful
12592 * termination. So, we need to release USB devices only if there was
12593 * an abnormal termination of a running VM.
12594 *
12595 * This is identical to SessionMachine::DetachAllUSBDevices except
12596 * for the aAbnormal argument. */
12597 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12598 AssertComRC(rc);
12599 NOREF(rc);
12600
12601 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12602 if (service)
12603 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12604 }
12605#endif /* VBOX_WITH_USB */
12606
12607 // we need to lock this object in uninit() because the lock is shared
12608 // with mPeer (as well as data we modify below). mParent lock is needed
12609 // by several calls to it.
12610 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12611
12612#ifdef VBOX_WITH_RESOURCE_USAGE_API
12613 /*
12614 * It is safe to call Machine::i_unregisterMetrics() here because
12615 * PerformanceCollector::samplerCallback no longer accesses guest methods
12616 * holding the lock.
12617 */
12618 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12619 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12620 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12621 if (mCollectorGuest)
12622 {
12623 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12624 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12625 mCollectorGuest = NULL;
12626 }
12627#endif
12628
12629 if (aReason == Uninit::Abnormal)
12630 {
12631 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12632
12633 /* reset the state to Aborted */
12634 if (mData->mMachineState != MachineState_Aborted)
12635 i_setMachineState(MachineState_Aborted);
12636 }
12637
12638 // any machine settings modified?
12639 if (mData->flModifications)
12640 {
12641 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12642 i_rollback(false /* aNotify */);
12643 }
12644
12645 mData->mSession.mPID = NIL_RTPROCESS;
12646
12647 if (aReason == Uninit::Unexpected)
12648 {
12649 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12650 * client watcher thread to update the set of machines that have open
12651 * sessions. */
12652 mParent->i_updateClientWatcher();
12653 }
12654
12655 /* uninitialize all remote controls */
12656 if (mData->mSession.mRemoteControls.size())
12657 {
12658 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12659 mData->mSession.mRemoteControls.size()));
12660
12661 /* Always restart a the beginning, since the iterator is invalidated
12662 * by using erase(). */
12663 for (Data::Session::RemoteControlList::iterator
12664 it = mData->mSession.mRemoteControls.begin();
12665 it != mData->mSession.mRemoteControls.end();
12666 it = mData->mSession.mRemoteControls.begin())
12667 {
12668 ComPtr<IInternalSessionControl> pControl = *it;
12669 mData->mSession.mRemoteControls.erase(it);
12670 multilock.release();
12671 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12672 HRESULT rc = pControl->Uninitialize();
12673 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12674 if (FAILED(rc))
12675 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12676 multilock.acquire();
12677 }
12678 mData->mSession.mRemoteControls.clear();
12679 }
12680
12681 /* Remove all references to the NAT network service. The service will stop
12682 * if all references (also from other VMs) are removed. */
12683 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12684 {
12685 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12686 {
12687 BOOL enabled;
12688 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12689 if ( FAILED(hrc)
12690 || !enabled)
12691 continue;
12692
12693 NetworkAttachmentType_T type;
12694 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12695 if ( SUCCEEDED(hrc)
12696 && type == NetworkAttachmentType_NATNetwork)
12697 {
12698 Bstr name;
12699 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12700 if (SUCCEEDED(hrc))
12701 {
12702 multilock.release();
12703 Utf8Str strName(name);
12704 LogRel(("VM '%s' stops using NAT network '%s'\n",
12705 mUserData->s.strName.c_str(), strName.c_str()));
12706 mParent->i_natNetworkRefDec(strName);
12707 multilock.acquire();
12708 }
12709 }
12710 }
12711 }
12712
12713 /*
12714 * An expected uninitialization can come only from #i_checkForDeath().
12715 * Otherwise it means that something's gone really wrong (for example,
12716 * the Session implementation has released the VirtualBox reference
12717 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12718 * etc). However, it's also possible, that the client releases the IPC
12719 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12720 * but the VirtualBox release event comes first to the server process.
12721 * This case is practically possible, so we should not assert on an
12722 * unexpected uninit, just log a warning.
12723 */
12724
12725 if (aReason == Uninit::Unexpected)
12726 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12727
12728 if (aReason != Uninit::Normal)
12729 {
12730 mData->mSession.mDirectControl.setNull();
12731 }
12732 else
12733 {
12734 /* this must be null here (see #OnSessionEnd()) */
12735 Assert(mData->mSession.mDirectControl.isNull());
12736 Assert(mData->mSession.mState == SessionState_Unlocking);
12737 Assert(!mData->mSession.mProgress.isNull());
12738 }
12739 if (mData->mSession.mProgress)
12740 {
12741 if (aReason == Uninit::Normal)
12742 mData->mSession.mProgress->i_notifyComplete(S_OK);
12743 else
12744 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12745 COM_IIDOF(ISession),
12746 getComponentName(),
12747 tr("The VM session was aborted"));
12748 mData->mSession.mProgress.setNull();
12749 }
12750
12751 if (mConsoleTaskData.mProgress)
12752 {
12753 Assert(aReason == Uninit::Abnormal);
12754 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12755 COM_IIDOF(ISession),
12756 getComponentName(),
12757 tr("The VM session was aborted"));
12758 mConsoleTaskData.mProgress.setNull();
12759 }
12760
12761 /* remove the association between the peer machine and this session machine */
12762 Assert( (SessionMachine*)mData->mSession.mMachine == this
12763 || aReason == Uninit::Unexpected);
12764
12765 /* reset the rest of session data */
12766 mData->mSession.mLockType = LockType_Null;
12767 mData->mSession.mMachine.setNull();
12768 mData->mSession.mState = SessionState_Unlocked;
12769 mData->mSession.mName.setNull();
12770
12771 /* destroy the machine client token before leaving the exclusive lock */
12772 if (mClientToken)
12773 {
12774 delete mClientToken;
12775 mClientToken = NULL;
12776 }
12777
12778 /* fire an event */
12779 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12780
12781 uninitDataAndChildObjects();
12782
12783 /* free the essential data structure last */
12784 mData.free();
12785
12786 /* release the exclusive lock before setting the below two to NULL */
12787 multilock.release();
12788
12789 unconst(mParent) = NULL;
12790 unconst(mPeer) = NULL;
12791
12792 AuthLibUnload(&mAuthLibCtx);
12793
12794 LogFlowThisFuncLeave();
12795}
12796
12797// util::Lockable interface
12798////////////////////////////////////////////////////////////////////////////////
12799
12800/**
12801 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12802 * with the primary Machine instance (mPeer).
12803 */
12804RWLockHandle *SessionMachine::lockHandle() const
12805{
12806 AssertReturn(mPeer != NULL, NULL);
12807 return mPeer->lockHandle();
12808}
12809
12810// IInternalMachineControl methods
12811////////////////////////////////////////////////////////////////////////////////
12812
12813/**
12814 * Passes collected guest statistics to performance collector object
12815 */
12816HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12817 ULONG aCpuKernel, ULONG aCpuIdle,
12818 ULONG aMemTotal, ULONG aMemFree,
12819 ULONG aMemBalloon, ULONG aMemShared,
12820 ULONG aMemCache, ULONG aPageTotal,
12821 ULONG aAllocVMM, ULONG aFreeVMM,
12822 ULONG aBalloonedVMM, ULONG aSharedVMM,
12823 ULONG aVmNetRx, ULONG aVmNetTx)
12824{
12825#ifdef VBOX_WITH_RESOURCE_USAGE_API
12826 if (mCollectorGuest)
12827 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12828 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12829 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12830 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12831
12832 return S_OK;
12833#else
12834 NOREF(aValidStats);
12835 NOREF(aCpuUser);
12836 NOREF(aCpuKernel);
12837 NOREF(aCpuIdle);
12838 NOREF(aMemTotal);
12839 NOREF(aMemFree);
12840 NOREF(aMemBalloon);
12841 NOREF(aMemShared);
12842 NOREF(aMemCache);
12843 NOREF(aPageTotal);
12844 NOREF(aAllocVMM);
12845 NOREF(aFreeVMM);
12846 NOREF(aBalloonedVMM);
12847 NOREF(aSharedVMM);
12848 NOREF(aVmNetRx);
12849 NOREF(aVmNetTx);
12850 return E_NOTIMPL;
12851#endif
12852}
12853
12854////////////////////////////////////////////////////////////////////////////////
12855//
12856// SessionMachine task records
12857//
12858////////////////////////////////////////////////////////////////////////////////
12859
12860/**
12861 * Task record for saving the machine state.
12862 */
12863class SessionMachine::SaveStateTask
12864 : public Machine::Task
12865{
12866public:
12867 SaveStateTask(SessionMachine *m,
12868 Progress *p,
12869 const Utf8Str &t,
12870 Reason_T enmReason,
12871 const Utf8Str &strStateFilePath)
12872 : Task(m, p, t),
12873 m_enmReason(enmReason),
12874 m_strStateFilePath(strStateFilePath)
12875 {}
12876
12877private:
12878 void handler()
12879 {
12880 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12881 }
12882
12883 Reason_T m_enmReason;
12884 Utf8Str m_strStateFilePath;
12885
12886 friend class SessionMachine;
12887};
12888
12889/**
12890 * Task thread implementation for SessionMachine::SaveState(), called from
12891 * SessionMachine::taskHandler().
12892 *
12893 * @note Locks this object for writing.
12894 *
12895 * @param task
12896 * @return
12897 */
12898void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12899{
12900 LogFlowThisFuncEnter();
12901
12902 AutoCaller autoCaller(this);
12903 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12904 if (FAILED(autoCaller.rc()))
12905 {
12906 /* we might have been uninitialized because the session was accidentally
12907 * closed by the client, so don't assert */
12908 HRESULT rc = setError(E_FAIL,
12909 tr("The session has been accidentally closed"));
12910 task.m_pProgress->i_notifyComplete(rc);
12911 LogFlowThisFuncLeave();
12912 return;
12913 }
12914
12915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12916
12917 HRESULT rc = S_OK;
12918
12919 try
12920 {
12921 ComPtr<IInternalSessionControl> directControl;
12922 if (mData->mSession.mLockType == LockType_VM)
12923 directControl = mData->mSession.mDirectControl;
12924 if (directControl.isNull())
12925 throw setError(VBOX_E_INVALID_VM_STATE,
12926 tr("Trying to save state without a running VM"));
12927 alock.release();
12928 BOOL fSuspendedBySave;
12929 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12930 Assert(!fSuspendedBySave);
12931 alock.acquire();
12932
12933 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12934 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12935 throw E_FAIL);
12936
12937 if (SUCCEEDED(rc))
12938 {
12939 mSSData->strStateFilePath = task.m_strStateFilePath;
12940
12941 /* save all VM settings */
12942 rc = i_saveSettings(NULL);
12943 // no need to check whether VirtualBox.xml needs saving also since
12944 // we can't have a name change pending at this point
12945 }
12946 else
12947 {
12948 // On failure, set the state to the state we had at the beginning.
12949 i_setMachineState(task.m_machineStateBackup);
12950 i_updateMachineStateOnClient();
12951
12952 // Delete the saved state file (might have been already created).
12953 // No need to check whether this is shared with a snapshot here
12954 // because we certainly created a fresh saved state file here.
12955 RTFileDelete(task.m_strStateFilePath.c_str());
12956 }
12957 }
12958 catch (HRESULT aRC) { rc = aRC; }
12959
12960 task.m_pProgress->i_notifyComplete(rc);
12961
12962 LogFlowThisFuncLeave();
12963}
12964
12965/**
12966 * @note Locks this object for writing.
12967 */
12968HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12969{
12970 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12971}
12972
12973HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12974{
12975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12976
12977 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12978 if (FAILED(rc)) return rc;
12979
12980 if ( mData->mMachineState != MachineState_Running
12981 && mData->mMachineState != MachineState_Paused
12982 )
12983 return setError(VBOX_E_INVALID_VM_STATE,
12984 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12985 Global::stringifyMachineState(mData->mMachineState));
12986
12987 ComObjPtr<Progress> pProgress;
12988 pProgress.createObject();
12989 rc = pProgress->init(i_getVirtualBox(),
12990 static_cast<IMachine *>(this) /* aInitiator */,
12991 tr("Saving the execution state of the virtual machine"),
12992 FALSE /* aCancelable */);
12993 if (FAILED(rc))
12994 return rc;
12995
12996 Utf8Str strStateFilePath;
12997 i_composeSavedStateFilename(strStateFilePath);
12998
12999 /* create and start the task on a separate thread (note that it will not
13000 * start working until we release alock) */
13001 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13002 rc = pTask->createThread();
13003 if (FAILED(rc))
13004 return rc;
13005
13006 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13007 i_setMachineState(MachineState_Saving);
13008 i_updateMachineStateOnClient();
13009
13010 pProgress.queryInterfaceTo(aProgress.asOutParam());
13011
13012 return S_OK;
13013}
13014
13015/**
13016 * @note Locks this object for writing.
13017 */
13018HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13019{
13020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13021
13022 HRESULT rc = i_checkStateDependency(MutableStateDep);
13023 if (FAILED(rc)) return rc;
13024
13025 if ( mData->mMachineState != MachineState_PoweredOff
13026 && mData->mMachineState != MachineState_Teleported
13027 && mData->mMachineState != MachineState_Aborted
13028 )
13029 return setError(VBOX_E_INVALID_VM_STATE,
13030 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13031 Global::stringifyMachineState(mData->mMachineState));
13032
13033 com::Utf8Str stateFilePathFull;
13034 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13035 if (RT_FAILURE(vrc))
13036 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13037 tr("Invalid saved state file path '%s' (%Rrc)"),
13038 aSavedStateFile.c_str(),
13039 vrc);
13040
13041 mSSData->strStateFilePath = stateFilePathFull;
13042
13043 /* The below i_setMachineState() will detect the state transition and will
13044 * update the settings file */
13045
13046 return i_setMachineState(MachineState_Saved);
13047}
13048
13049/**
13050 * @note Locks this object for writing.
13051 */
13052HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13053{
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13057 if (FAILED(rc)) return rc;
13058
13059 if (mData->mMachineState != MachineState_Saved)
13060 return setError(VBOX_E_INVALID_VM_STATE,
13061 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13062 Global::stringifyMachineState(mData->mMachineState));
13063
13064 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13065
13066 /*
13067 * Saved -> PoweredOff transition will be detected in the SessionMachine
13068 * and properly handled.
13069 */
13070 rc = i_setMachineState(MachineState_PoweredOff);
13071 return rc;
13072}
13073
13074
13075/**
13076 * @note Locks the same as #i_setMachineState() does.
13077 */
13078HRESULT SessionMachine::updateState(MachineState_T aState)
13079{
13080 return i_setMachineState(aState);
13081}
13082
13083/**
13084 * @note Locks this object for writing.
13085 */
13086HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13087{
13088 IProgress *pProgress(aProgress);
13089
13090 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13091
13092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13093
13094 if (mData->mSession.mState != SessionState_Locked)
13095 return VBOX_E_INVALID_OBJECT_STATE;
13096
13097 if (!mData->mSession.mProgress.isNull())
13098 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13099
13100 /* If we didn't reference the NAT network service yet, add a reference to
13101 * force a start */
13102 if (miNATNetworksStarted < 1)
13103 {
13104 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13105 {
13106 BOOL enabled;
13107 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13108 if ( FAILED(hrc)
13109 || !enabled)
13110 continue;
13111
13112 NetworkAttachmentType_T type;
13113 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13114 if ( SUCCEEDED(hrc)
13115 && type == NetworkAttachmentType_NATNetwork)
13116 {
13117 Bstr name;
13118 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13119 if (SUCCEEDED(hrc))
13120 {
13121 Utf8Str strName(name);
13122 LogRel(("VM '%s' starts using NAT network '%s'\n",
13123 mUserData->s.strName.c_str(), strName.c_str()));
13124 mPeer->lockHandle()->unlockWrite();
13125 mParent->i_natNetworkRefInc(strName);
13126#ifdef RT_LOCK_STRICT
13127 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13128#else
13129 mPeer->lockHandle()->lockWrite();
13130#endif
13131 }
13132 }
13133 }
13134 miNATNetworksStarted++;
13135 }
13136
13137 LogFlowThisFunc(("returns S_OK.\n"));
13138 return S_OK;
13139}
13140
13141/**
13142 * @note Locks this object for writing.
13143 */
13144HRESULT SessionMachine::endPowerUp(LONG aResult)
13145{
13146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 if (mData->mSession.mState != SessionState_Locked)
13149 return VBOX_E_INVALID_OBJECT_STATE;
13150
13151 /* Finalize the LaunchVMProcess progress object. */
13152 if (mData->mSession.mProgress)
13153 {
13154 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13155 mData->mSession.mProgress.setNull();
13156 }
13157
13158 if (SUCCEEDED((HRESULT)aResult))
13159 {
13160#ifdef VBOX_WITH_RESOURCE_USAGE_API
13161 /* The VM has been powered up successfully, so it makes sense
13162 * now to offer the performance metrics for a running machine
13163 * object. Doing it earlier wouldn't be safe. */
13164 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13165 mData->mSession.mPID);
13166#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13167 }
13168
13169 return S_OK;
13170}
13171
13172/**
13173 * @note Locks this object for writing.
13174 */
13175HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13176{
13177 LogFlowThisFuncEnter();
13178
13179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13180
13181 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13182 E_FAIL);
13183
13184 /* create a progress object to track operation completion */
13185 ComObjPtr<Progress> pProgress;
13186 pProgress.createObject();
13187 pProgress->init(i_getVirtualBox(),
13188 static_cast<IMachine *>(this) /* aInitiator */,
13189 tr("Stopping the virtual machine"),
13190 FALSE /* aCancelable */);
13191
13192 /* fill in the console task data */
13193 mConsoleTaskData.mLastState = mData->mMachineState;
13194 mConsoleTaskData.mProgress = pProgress;
13195
13196 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13197 i_setMachineState(MachineState_Stopping);
13198
13199 pProgress.queryInterfaceTo(aProgress.asOutParam());
13200
13201 return S_OK;
13202}
13203
13204/**
13205 * @note Locks this object for writing.
13206 */
13207HRESULT SessionMachine::endPoweringDown(LONG aResult,
13208 const com::Utf8Str &aErrMsg)
13209{
13210 LogFlowThisFuncEnter();
13211
13212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13213
13214 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13215 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13216 && mConsoleTaskData.mLastState != MachineState_Null,
13217 E_FAIL);
13218
13219 /*
13220 * On failure, set the state to the state we had when BeginPoweringDown()
13221 * was called (this is expected by Console::PowerDown() and the associated
13222 * task). On success the VM process already changed the state to
13223 * MachineState_PoweredOff, so no need to do anything.
13224 */
13225 if (FAILED(aResult))
13226 i_setMachineState(mConsoleTaskData.mLastState);
13227
13228 /* notify the progress object about operation completion */
13229 Assert(mConsoleTaskData.mProgress);
13230 if (SUCCEEDED(aResult))
13231 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13232 else
13233 {
13234 if (aErrMsg.length())
13235 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13236 COM_IIDOF(ISession),
13237 getComponentName(),
13238 aErrMsg.c_str());
13239 else
13240 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13241 }
13242
13243 /* clear out the temporary saved state data */
13244 mConsoleTaskData.mLastState = MachineState_Null;
13245 mConsoleTaskData.mProgress.setNull();
13246
13247 LogFlowThisFuncLeave();
13248 return S_OK;
13249}
13250
13251
13252/**
13253 * Goes through the USB filters of the given machine to see if the given
13254 * device matches any filter or not.
13255 *
13256 * @note Locks the same as USBController::hasMatchingFilter() does.
13257 */
13258HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13259 BOOL *aMatched,
13260 ULONG *aMaskedInterfaces)
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264#ifdef VBOX_WITH_USB
13265 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13266#else
13267 NOREF(aDevice);
13268 NOREF(aMaskedInterfaces);
13269 *aMatched = FALSE;
13270#endif
13271
13272 return S_OK;
13273}
13274
13275/**
13276 * @note Locks the same as Host::captureUSBDevice() does.
13277 */
13278HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13279{
13280 LogFlowThisFunc(("\n"));
13281
13282#ifdef VBOX_WITH_USB
13283 /* if captureDeviceForVM() fails, it must have set extended error info */
13284 clearError();
13285 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13286 if (FAILED(rc)) return rc;
13287
13288 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13289 AssertReturn(service, E_FAIL);
13290 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13291#else
13292 NOREF(aId);
13293 return E_NOTIMPL;
13294#endif
13295}
13296
13297/**
13298 * @note Locks the same as Host::detachUSBDevice() does.
13299 */
13300HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13301 BOOL aDone)
13302{
13303 LogFlowThisFunc(("\n"));
13304
13305#ifdef VBOX_WITH_USB
13306 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13307 AssertReturn(service, E_FAIL);
13308 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13309#else
13310 NOREF(aId);
13311 NOREF(aDone);
13312 return E_NOTIMPL;
13313#endif
13314}
13315
13316/**
13317 * Inserts all machine filters to the USB proxy service and then calls
13318 * Host::autoCaptureUSBDevices().
13319 *
13320 * Called by Console from the VM process upon VM startup.
13321 *
13322 * @note Locks what called methods lock.
13323 */
13324HRESULT SessionMachine::autoCaptureUSBDevices()
13325{
13326 LogFlowThisFunc(("\n"));
13327
13328#ifdef VBOX_WITH_USB
13329 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13330 AssertComRC(rc);
13331 NOREF(rc);
13332
13333 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13334 AssertReturn(service, E_FAIL);
13335 return service->autoCaptureDevicesForVM(this);
13336#else
13337 return S_OK;
13338#endif
13339}
13340
13341/**
13342 * Removes all machine filters from the USB proxy service and then calls
13343 * Host::detachAllUSBDevices().
13344 *
13345 * Called by Console from the VM process upon normal VM termination or by
13346 * SessionMachine::uninit() upon abnormal VM termination (from under the
13347 * Machine/SessionMachine lock).
13348 *
13349 * @note Locks what called methods lock.
13350 */
13351HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13352{
13353 LogFlowThisFunc(("\n"));
13354
13355#ifdef VBOX_WITH_USB
13356 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13357 AssertComRC(rc);
13358 NOREF(rc);
13359
13360 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13361 AssertReturn(service, E_FAIL);
13362 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13363#else
13364 NOREF(aDone);
13365 return S_OK;
13366#endif
13367}
13368
13369/**
13370 * @note Locks this object for writing.
13371 */
13372HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13373 ComPtr<IProgress> &aProgress)
13374{
13375 LogFlowThisFuncEnter();
13376
13377 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13378 /*
13379 * We don't assert below because it might happen that a non-direct session
13380 * informs us it is closed right after we've been uninitialized -- it's ok.
13381 */
13382
13383 /* get IInternalSessionControl interface */
13384 ComPtr<IInternalSessionControl> control(aSession);
13385
13386 ComAssertRet(!control.isNull(), E_INVALIDARG);
13387
13388 /* Creating a Progress object requires the VirtualBox lock, and
13389 * thus locking it here is required by the lock order rules. */
13390 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13391
13392 if (control == mData->mSession.mDirectControl)
13393 {
13394 /* The direct session is being normally closed by the client process
13395 * ----------------------------------------------------------------- */
13396
13397 /* go to the closing state (essential for all open*Session() calls and
13398 * for #i_checkForDeath()) */
13399 Assert(mData->mSession.mState == SessionState_Locked);
13400 mData->mSession.mState = SessionState_Unlocking;
13401
13402 /* set direct control to NULL to release the remote instance */
13403 mData->mSession.mDirectControl.setNull();
13404 LogFlowThisFunc(("Direct control is set to NULL\n"));
13405
13406 if (mData->mSession.mProgress)
13407 {
13408 /* finalize the progress, someone might wait if a frontend
13409 * closes the session before powering on the VM. */
13410 mData->mSession.mProgress->notifyComplete(E_FAIL,
13411 COM_IIDOF(ISession),
13412 getComponentName(),
13413 tr("The VM session was closed before any attempt to power it on"));
13414 mData->mSession.mProgress.setNull();
13415 }
13416
13417 /* Create the progress object the client will use to wait until
13418 * #i_checkForDeath() is called to uninitialize this session object after
13419 * it releases the IPC semaphore.
13420 * Note! Because we're "reusing" mProgress here, this must be a proxy
13421 * object just like for LaunchVMProcess. */
13422 Assert(mData->mSession.mProgress.isNull());
13423 ComObjPtr<ProgressProxy> progress;
13424 progress.createObject();
13425 ComPtr<IUnknown> pPeer(mPeer);
13426 progress->init(mParent, pPeer,
13427 Bstr(tr("Closing session")).raw(),
13428 FALSE /* aCancelable */);
13429 progress.queryInterfaceTo(aProgress.asOutParam());
13430 mData->mSession.mProgress = progress;
13431 }
13432 else
13433 {
13434 /* the remote session is being normally closed */
13435 bool found = false;
13436 for (Data::Session::RemoteControlList::iterator
13437 it = mData->mSession.mRemoteControls.begin();
13438 it != mData->mSession.mRemoteControls.end();
13439 ++it)
13440 {
13441 if (control == *it)
13442 {
13443 found = true;
13444 // This MUST be erase(it), not remove(*it) as the latter
13445 // triggers a very nasty use after free due to the place where
13446 // the value "lives".
13447 mData->mSession.mRemoteControls.erase(it);
13448 break;
13449 }
13450 }
13451 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13452 E_INVALIDARG);
13453 }
13454
13455 /* signal the client watcher thread, because the client is going away */
13456 mParent->i_updateClientWatcher();
13457
13458 LogFlowThisFuncLeave();
13459 return S_OK;
13460}
13461
13462HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13463 std::vector<com::Utf8Str> &aValues,
13464 std::vector<LONG64> &aTimestamps,
13465 std::vector<com::Utf8Str> &aFlags)
13466{
13467 LogFlowThisFunc(("\n"));
13468
13469#ifdef VBOX_WITH_GUEST_PROPS
13470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13471
13472 size_t cEntries = mHWData->mGuestProperties.size();
13473 aNames.resize(cEntries);
13474 aValues.resize(cEntries);
13475 aTimestamps.resize(cEntries);
13476 aFlags.resize(cEntries);
13477
13478 size_t i = 0;
13479 for (HWData::GuestPropertyMap::const_iterator
13480 it = mHWData->mGuestProperties.begin();
13481 it != mHWData->mGuestProperties.end();
13482 ++it, ++i)
13483 {
13484 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13485 aNames[i] = it->first;
13486 aValues[i] = it->second.strValue;
13487 aTimestamps[i] = it->second.mTimestamp;
13488
13489 /* If it is NULL, keep it NULL. */
13490 if (it->second.mFlags)
13491 {
13492 GuestPropWriteFlags(it->second.mFlags, szFlags);
13493 aFlags[i] = szFlags;
13494 }
13495 else
13496 aFlags[i] = "";
13497 }
13498 return S_OK;
13499#else
13500 ReturnComNotImplemented();
13501#endif
13502}
13503
13504HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13505 const com::Utf8Str &aValue,
13506 LONG64 aTimestamp,
13507 const com::Utf8Str &aFlags)
13508{
13509 LogFlowThisFunc(("\n"));
13510
13511#ifdef VBOX_WITH_GUEST_PROPS
13512 try
13513 {
13514 /*
13515 * Convert input up front.
13516 */
13517 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13518 if (aFlags.length())
13519 {
13520 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13521 AssertRCReturn(vrc, E_INVALIDARG);
13522 }
13523
13524 /*
13525 * Now grab the object lock, validate the state and do the update.
13526 */
13527
13528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13529
13530 if (!Global::IsOnline(mData->mMachineState))
13531 {
13532 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13533 VBOX_E_INVALID_VM_STATE);
13534 }
13535
13536 i_setModified(IsModified_MachineData);
13537 mHWData.backup();
13538
13539 bool fDelete = !aValue.length();
13540 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13541 if (it != mHWData->mGuestProperties.end())
13542 {
13543 if (!fDelete)
13544 {
13545 it->second.strValue = aValue;
13546 it->second.mTimestamp = aTimestamp;
13547 it->second.mFlags = fFlags;
13548 }
13549 else
13550 mHWData->mGuestProperties.erase(it);
13551
13552 mData->mGuestPropertiesModified = TRUE;
13553 }
13554 else if (!fDelete)
13555 {
13556 HWData::GuestProperty prop;
13557 prop.strValue = aValue;
13558 prop.mTimestamp = aTimestamp;
13559 prop.mFlags = fFlags;
13560
13561 mHWData->mGuestProperties[aName] = prop;
13562 mData->mGuestPropertiesModified = TRUE;
13563 }
13564
13565 alock.release();
13566
13567 mParent->i_onGuestPropertyChange(mData->mUuid,
13568 Bstr(aName).raw(),
13569 Bstr(aValue).raw(),
13570 Bstr(aFlags).raw());
13571 }
13572 catch (...)
13573 {
13574 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13575 }
13576 return S_OK;
13577#else
13578 ReturnComNotImplemented();
13579#endif
13580}
13581
13582
13583HRESULT SessionMachine::lockMedia()
13584{
13585 AutoMultiWriteLock2 alock(this->lockHandle(),
13586 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13587
13588 AssertReturn( mData->mMachineState == MachineState_Starting
13589 || mData->mMachineState == MachineState_Restoring
13590 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13591
13592 clearError();
13593 alock.release();
13594 return i_lockMedia();
13595}
13596
13597HRESULT SessionMachine::unlockMedia()
13598{
13599 HRESULT hrc = i_unlockMedia();
13600 return hrc;
13601}
13602
13603HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13604 ComPtr<IMediumAttachment> &aNewAttachment)
13605{
13606 // request the host lock first, since might be calling Host methods for getting host drives;
13607 // next, protect the media tree all the while we're in here, as well as our member variables
13608 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13609 this->lockHandle(),
13610 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13611
13612 IMediumAttachment *iAttach = aAttachment;
13613 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13614
13615 Utf8Str ctrlName;
13616 LONG lPort;
13617 LONG lDevice;
13618 bool fTempEject;
13619 {
13620 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13621
13622 /* Need to query the details first, as the IMediumAttachment reference
13623 * might be to the original settings, which we are going to change. */
13624 ctrlName = pAttach->i_getControllerName();
13625 lPort = pAttach->i_getPort();
13626 lDevice = pAttach->i_getDevice();
13627 fTempEject = pAttach->i_getTempEject();
13628 }
13629
13630 if (!fTempEject)
13631 {
13632 /* Remember previously mounted medium. The medium before taking the
13633 * backup is not necessarily the same thing. */
13634 ComObjPtr<Medium> oldmedium;
13635 oldmedium = pAttach->i_getMedium();
13636
13637 i_setModified(IsModified_Storage);
13638 mMediumAttachments.backup();
13639
13640 // The backup operation makes the pAttach reference point to the
13641 // old settings. Re-get the correct reference.
13642 pAttach = i_findAttachment(*mMediumAttachments.data(),
13643 ctrlName,
13644 lPort,
13645 lDevice);
13646
13647 {
13648 AutoCaller autoAttachCaller(this);
13649 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13650
13651 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13652 if (!oldmedium.isNull())
13653 oldmedium->i_removeBackReference(mData->mUuid);
13654
13655 pAttach->i_updateMedium(NULL);
13656 pAttach->i_updateEjected();
13657 }
13658
13659 i_setModified(IsModified_Storage);
13660 }
13661 else
13662 {
13663 {
13664 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13665 pAttach->i_updateEjected();
13666 }
13667 }
13668
13669 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13670
13671 return S_OK;
13672}
13673
13674HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13675 com::Utf8Str &aResult)
13676{
13677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13678
13679 HRESULT hr = S_OK;
13680
13681 if (!mAuthLibCtx.hAuthLibrary)
13682 {
13683 /* Load the external authentication library. */
13684 Bstr authLibrary;
13685 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13686
13687 Utf8Str filename = authLibrary;
13688
13689 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13690 if (RT_FAILURE(vrc))
13691 hr = setErrorBoth(E_FAIL, vrc,
13692 tr("Could not load the external authentication library '%s' (%Rrc)"),
13693 filename.c_str(), vrc);
13694 }
13695
13696 /* The auth library might need the machine lock. */
13697 alock.release();
13698
13699 if (FAILED(hr))
13700 return hr;
13701
13702 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13703 {
13704 enum VRDEAuthParams
13705 {
13706 parmUuid = 1,
13707 parmGuestJudgement,
13708 parmUser,
13709 parmPassword,
13710 parmDomain,
13711 parmClientId
13712 };
13713
13714 AuthResult result = AuthResultAccessDenied;
13715
13716 Guid uuid(aAuthParams[parmUuid]);
13717 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13718 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13719
13720 result = AuthLibAuthenticate(&mAuthLibCtx,
13721 uuid.raw(), guestJudgement,
13722 aAuthParams[parmUser].c_str(),
13723 aAuthParams[parmPassword].c_str(),
13724 aAuthParams[parmDomain].c_str(),
13725 u32ClientId);
13726
13727 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13728 size_t cbPassword = aAuthParams[parmPassword].length();
13729 if (cbPassword)
13730 {
13731 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13732 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13733 }
13734
13735 if (result == AuthResultAccessGranted)
13736 aResult = "granted";
13737 else
13738 aResult = "denied";
13739
13740 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13741 aAuthParams[parmUser].c_str(), aResult.c_str()));
13742 }
13743 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13744 {
13745 enum VRDEAuthDisconnectParams
13746 {
13747 parmUuid = 1,
13748 parmClientId
13749 };
13750
13751 Guid uuid(aAuthParams[parmUuid]);
13752 uint32_t u32ClientId = 0;
13753 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13754 }
13755 else
13756 {
13757 hr = E_INVALIDARG;
13758 }
13759
13760 return hr;
13761}
13762
13763// public methods only for internal purposes
13764/////////////////////////////////////////////////////////////////////////////
13765
13766#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13767/**
13768 * Called from the client watcher thread to check for expected or unexpected
13769 * death of the client process that has a direct session to this machine.
13770 *
13771 * On Win32 and on OS/2, this method is called only when we've got the
13772 * mutex (i.e. the client has either died or terminated normally) so it always
13773 * returns @c true (the client is terminated, the session machine is
13774 * uninitialized).
13775 *
13776 * On other platforms, the method returns @c true if the client process has
13777 * terminated normally or abnormally and the session machine was uninitialized,
13778 * and @c false if the client process is still alive.
13779 *
13780 * @note Locks this object for writing.
13781 */
13782bool SessionMachine::i_checkForDeath()
13783{
13784 Uninit::Reason reason;
13785 bool terminated = false;
13786
13787 /* Enclose autoCaller with a block because calling uninit() from under it
13788 * will deadlock. */
13789 {
13790 AutoCaller autoCaller(this);
13791 if (!autoCaller.isOk())
13792 {
13793 /* return true if not ready, to cause the client watcher to exclude
13794 * the corresponding session from watching */
13795 LogFlowThisFunc(("Already uninitialized!\n"));
13796 return true;
13797 }
13798
13799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13800
13801 /* Determine the reason of death: if the session state is Closing here,
13802 * everything is fine. Otherwise it means that the client did not call
13803 * OnSessionEnd() before it released the IPC semaphore. This may happen
13804 * either because the client process has abnormally terminated, or
13805 * because it simply forgot to call ISession::Close() before exiting. We
13806 * threat the latter also as an abnormal termination (see
13807 * Session::uninit() for details). */
13808 reason = mData->mSession.mState == SessionState_Unlocking ?
13809 Uninit::Normal :
13810 Uninit::Abnormal;
13811
13812 if (mClientToken)
13813 terminated = mClientToken->release();
13814 } /* AutoCaller block */
13815
13816 if (terminated)
13817 uninit(reason);
13818
13819 return terminated;
13820}
13821
13822void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13823{
13824 LogFlowThisFunc(("\n"));
13825
13826 strTokenId.setNull();
13827
13828 AutoCaller autoCaller(this);
13829 AssertComRCReturnVoid(autoCaller.rc());
13830
13831 Assert(mClientToken);
13832 if (mClientToken)
13833 mClientToken->getId(strTokenId);
13834}
13835#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13836IToken *SessionMachine::i_getToken()
13837{
13838 LogFlowThisFunc(("\n"));
13839
13840 AutoCaller autoCaller(this);
13841 AssertComRCReturn(autoCaller.rc(), NULL);
13842
13843 Assert(mClientToken);
13844 if (mClientToken)
13845 return mClientToken->getToken();
13846 else
13847 return NULL;
13848}
13849#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13850
13851Machine::ClientToken *SessionMachine::i_getClientToken()
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), NULL);
13857
13858 return mClientToken;
13859}
13860
13861
13862/**
13863 * @note Locks this object for reading.
13864 */
13865HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13871
13872 ComPtr<IInternalSessionControl> directControl;
13873 {
13874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13875 if (mData->mSession.mLockType == LockType_VM)
13876 directControl = mData->mSession.mDirectControl;
13877 }
13878
13879 /* ignore notifications sent after #OnSessionEnd() is called */
13880 if (!directControl)
13881 return S_OK;
13882
13883 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13884}
13885
13886/**
13887 * @note Locks this object for reading.
13888 */
13889HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13890 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13891 IN_BSTR aGuestIp, LONG aGuestPort)
13892{
13893 LogFlowThisFunc(("\n"));
13894
13895 AutoCaller autoCaller(this);
13896 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13897
13898 ComPtr<IInternalSessionControl> directControl;
13899 {
13900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13901 if (mData->mSession.mLockType == LockType_VM)
13902 directControl = mData->mSession.mDirectControl;
13903 }
13904
13905 /* ignore notifications sent after #OnSessionEnd() is called */
13906 if (!directControl)
13907 return S_OK;
13908 /*
13909 * instead acting like callback we ask IVirtualBox deliver corresponding event
13910 */
13911
13912 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13913 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13914 return S_OK;
13915}
13916
13917/**
13918 * @note Locks this object for reading.
13919 */
13920HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 AutoCaller autoCaller(this);
13925 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13926
13927 ComPtr<IInternalSessionControl> directControl;
13928 {
13929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13930 if (mData->mSession.mLockType == LockType_VM)
13931 directControl = mData->mSession.mDirectControl;
13932 }
13933
13934 /* ignore notifications sent after #OnSessionEnd() is called */
13935 if (!directControl)
13936 return S_OK;
13937
13938 return directControl->OnAudioAdapterChange(audioAdapter);
13939}
13940
13941/**
13942 * @note Locks this object for reading.
13943 */
13944HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13945{
13946 LogFlowThisFunc(("\n"));
13947
13948 AutoCaller autoCaller(this);
13949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13950
13951 ComPtr<IInternalSessionControl> directControl;
13952 {
13953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13954 if (mData->mSession.mLockType == LockType_VM)
13955 directControl = mData->mSession.mDirectControl;
13956 }
13957
13958 /* ignore notifications sent after #OnSessionEnd() is called */
13959 if (!directControl)
13960 return S_OK;
13961
13962 return directControl->OnSerialPortChange(serialPort);
13963}
13964
13965/**
13966 * @note Locks this object for reading.
13967 */
13968HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13969{
13970 LogFlowThisFunc(("\n"));
13971
13972 AutoCaller autoCaller(this);
13973 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13974
13975 ComPtr<IInternalSessionControl> directControl;
13976 {
13977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13978 if (mData->mSession.mLockType == LockType_VM)
13979 directControl = mData->mSession.mDirectControl;
13980 }
13981
13982 /* ignore notifications sent after #OnSessionEnd() is called */
13983 if (!directControl)
13984 return S_OK;
13985
13986 return directControl->OnParallelPortChange(parallelPort);
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onStorageControllerChange()
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13998
13999 ComPtr<IInternalSessionControl> directControl;
14000 {
14001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14002 if (mData->mSession.mLockType == LockType_VM)
14003 directControl = mData->mSession.mDirectControl;
14004 }
14005
14006 /* ignore notifications sent after #OnSessionEnd() is called */
14007 if (!directControl)
14008 return S_OK;
14009
14010 return directControl->OnStorageControllerChange();
14011}
14012
14013/**
14014 * @note Locks this object for reading.
14015 */
14016HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14017{
14018 LogFlowThisFunc(("\n"));
14019
14020 AutoCaller autoCaller(this);
14021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14022
14023 ComPtr<IInternalSessionControl> directControl;
14024 {
14025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14026 if (mData->mSession.mLockType == LockType_VM)
14027 directControl = mData->mSession.mDirectControl;
14028 }
14029
14030 mParent->i_onMediumChanged(aAttachment);
14031
14032 /* ignore notifications sent after #OnSessionEnd() is called */
14033 if (!directControl)
14034 return S_OK;
14035
14036 return directControl->OnMediumChange(aAttachment, aForce);
14037}
14038
14039/**
14040 * @note Locks this object for reading.
14041 */
14042HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14043{
14044 LogFlowThisFunc(("\n"));
14045
14046 AutoCaller autoCaller(this);
14047 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14048
14049 ComPtr<IInternalSessionControl> directControl;
14050 {
14051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14052 if (mData->mSession.mLockType == LockType_VM)
14053 directControl = mData->mSession.mDirectControl;
14054 }
14055
14056 /* ignore notifications sent after #OnSessionEnd() is called */
14057 if (!directControl)
14058 return S_OK;
14059
14060 return directControl->OnCPUChange(aCPU, aRemove);
14061}
14062
14063HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14064{
14065 LogFlowThisFunc(("\n"));
14066
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14069
14070 ComPtr<IInternalSessionControl> directControl;
14071 {
14072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14073 if (mData->mSession.mLockType == LockType_VM)
14074 directControl = mData->mSession.mDirectControl;
14075 }
14076
14077 /* ignore notifications sent after #OnSessionEnd() is called */
14078 if (!directControl)
14079 return S_OK;
14080
14081 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14082}
14083
14084/**
14085 * @note Locks this object for reading.
14086 */
14087HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14088{
14089 LogFlowThisFunc(("\n"));
14090
14091 AutoCaller autoCaller(this);
14092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14093
14094 ComPtr<IInternalSessionControl> directControl;
14095 {
14096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14097 if (mData->mSession.mLockType == LockType_VM)
14098 directControl = mData->mSession.mDirectControl;
14099 }
14100
14101 /* ignore notifications sent after #OnSessionEnd() is called */
14102 if (!directControl)
14103 return S_OK;
14104
14105 return directControl->OnVRDEServerChange(aRestart);
14106}
14107
14108/**
14109 * @note Locks this object for reading.
14110 */
14111HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
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 /* ignore notifications sent after #OnSessionEnd() is called */
14126 if (!directControl)
14127 return S_OK;
14128
14129 return directControl->OnRecordingChange(aEnable);
14130}
14131
14132/**
14133 * @note Locks this object for reading.
14134 */
14135HRESULT SessionMachine::i_onUSBControllerChange()
14136{
14137 LogFlowThisFunc(("\n"));
14138
14139 AutoCaller autoCaller(this);
14140 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14141
14142 ComPtr<IInternalSessionControl> directControl;
14143 {
14144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14145 if (mData->mSession.mLockType == LockType_VM)
14146 directControl = mData->mSession.mDirectControl;
14147 }
14148
14149 /* ignore notifications sent after #OnSessionEnd() is called */
14150 if (!directControl)
14151 return S_OK;
14152
14153 return directControl->OnUSBControllerChange();
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::i_onSharedFolderChange()
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturnRC(autoCaller.rc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 if (mData->mSession.mLockType == LockType_VM)
14170 directControl = mData->mSession.mDirectControl;
14171 }
14172
14173 /* ignore notifications sent after #OnSessionEnd() is called */
14174 if (!directControl)
14175 return S_OK;
14176
14177 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14178}
14179
14180/**
14181 * @note Locks this object for reading.
14182 */
14183HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14184{
14185 LogFlowThisFunc(("\n"));
14186
14187 AutoCaller autoCaller(this);
14188 AssertComRCReturnRC(autoCaller.rc());
14189
14190 ComPtr<IInternalSessionControl> directControl;
14191 {
14192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14193 if (mData->mSession.mLockType == LockType_VM)
14194 directControl = mData->mSession.mDirectControl;
14195 }
14196
14197 /* ignore notifications sent after #OnSessionEnd() is called */
14198 if (!directControl)
14199 return S_OK;
14200
14201 return directControl->OnClipboardModeChange(aClipboardMode);
14202}
14203
14204/**
14205 * @note Locks this object for reading.
14206 */
14207HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturnRC(autoCaller.rc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 if (mData->mSession.mLockType == LockType_VM)
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnDnDModeChange(aDnDMode);
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 if (mData->mSession.mLockType == LockType_VM)
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14250}
14251
14252/**
14253 * @note Locks this object for reading.
14254 */
14255HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 if (mData->mSession.mLockType == LockType_VM)
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14276}
14277
14278/**
14279 * Returns @c true if this machine's USB controller reports it has a matching
14280 * filter for the given USB device and @c false otherwise.
14281 *
14282 * @note locks this object for reading.
14283 */
14284bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14285{
14286 AutoCaller autoCaller(this);
14287 /* silently return if not ready -- this method may be called after the
14288 * direct machine session has been called */
14289 if (!autoCaller.isOk())
14290 return false;
14291
14292#ifdef VBOX_WITH_USB
14293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14294
14295 switch (mData->mMachineState)
14296 {
14297 case MachineState_Starting:
14298 case MachineState_Restoring:
14299 case MachineState_TeleportingIn:
14300 case MachineState_Paused:
14301 case MachineState_Running:
14302 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14303 * elsewhere... */
14304 alock.release();
14305 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14306 default: break;
14307 }
14308#else
14309 NOREF(aDevice);
14310 NOREF(aMaskedIfs);
14311#endif
14312 return false;
14313}
14314
14315/**
14316 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14317 */
14318HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14319 IVirtualBoxErrorInfo *aError,
14320 ULONG aMaskedIfs,
14321 const com::Utf8Str &aCaptureFilename)
14322{
14323 LogFlowThisFunc(("\n"));
14324
14325 AutoCaller autoCaller(this);
14326
14327 /* This notification may happen after the machine object has been
14328 * uninitialized (the session was closed), so don't assert. */
14329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14330
14331 ComPtr<IInternalSessionControl> directControl;
14332 {
14333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14334 if (mData->mSession.mLockType == LockType_VM)
14335 directControl = mData->mSession.mDirectControl;
14336 }
14337
14338 /* fail on notifications sent after #OnSessionEnd() is called, it is
14339 * expected by the caller */
14340 if (!directControl)
14341 return E_FAIL;
14342
14343 /* No locks should be held at this point. */
14344 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14345 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14346
14347 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14348}
14349
14350/**
14351 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14352 */
14353HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14354 IVirtualBoxErrorInfo *aError)
14355{
14356 LogFlowThisFunc(("\n"));
14357
14358 AutoCaller autoCaller(this);
14359
14360 /* This notification may happen after the machine object has been
14361 * uninitialized (the session was closed), so don't assert. */
14362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14363
14364 ComPtr<IInternalSessionControl> directControl;
14365 {
14366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14367 if (mData->mSession.mLockType == LockType_VM)
14368 directControl = mData->mSession.mDirectControl;
14369 }
14370
14371 /* fail on notifications sent after #OnSessionEnd() is called, it is
14372 * expected by the caller */
14373 if (!directControl)
14374 return E_FAIL;
14375
14376 /* No locks should be held at this point. */
14377 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14378 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14379
14380 return directControl->OnUSBDeviceDetach(aId, aError);
14381}
14382
14383// protected methods
14384/////////////////////////////////////////////////////////////////////////////
14385
14386/**
14387 * Deletes the given file if it is no longer in use by either the current machine state
14388 * (if the machine is "saved") or any of the machine's snapshots.
14389 *
14390 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14391 * but is different for each SnapshotMachine. When calling this, the order of calling this
14392 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14393 * is therefore critical. I know, it's all rather messy.
14394 *
14395 * @param strStateFile
14396 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14397 * the test for whether the saved state file is in use.
14398 */
14399void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14400 Snapshot *pSnapshotToIgnore)
14401{
14402 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14403 if ( (strStateFile.isNotEmpty())
14404 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14405 )
14406 // ... and it must also not be shared with other snapshots
14407 if ( !mData->mFirstSnapshot
14408 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14409 // this checks the SnapshotMachine's state file paths
14410 )
14411 RTFileDelete(strStateFile.c_str());
14412}
14413
14414/**
14415 * Locks the attached media.
14416 *
14417 * All attached hard disks are locked for writing and DVD/floppy are locked for
14418 * reading. Parents of attached hard disks (if any) are locked for reading.
14419 *
14420 * This method also performs accessibility check of all media it locks: if some
14421 * media is inaccessible, the method will return a failure and a bunch of
14422 * extended error info objects per each inaccessible medium.
14423 *
14424 * Note that this method is atomic: if it returns a success, all media are
14425 * locked as described above; on failure no media is locked at all (all
14426 * succeeded individual locks will be undone).
14427 *
14428 * The caller is responsible for doing the necessary state sanity checks.
14429 *
14430 * The locks made by this method must be undone by calling #unlockMedia() when
14431 * no more needed.
14432 */
14433HRESULT SessionMachine::i_lockMedia()
14434{
14435 AutoCaller autoCaller(this);
14436 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14437
14438 AutoMultiWriteLock2 alock(this->lockHandle(),
14439 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14440
14441 /* bail out if trying to lock things with already set up locking */
14442 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14443
14444 MultiResult mrc(S_OK);
14445
14446 /* Collect locking information for all medium objects attached to the VM. */
14447 for (MediumAttachmentList::const_iterator
14448 it = mMediumAttachments->begin();
14449 it != mMediumAttachments->end();
14450 ++it)
14451 {
14452 MediumAttachment *pAtt = *it;
14453 DeviceType_T devType = pAtt->i_getType();
14454 Medium *pMedium = pAtt->i_getMedium();
14455
14456 MediumLockList *pMediumLockList(new MediumLockList());
14457 // There can be attachments without a medium (floppy/dvd), and thus
14458 // it's impossible to create a medium lock list. It still makes sense
14459 // to have the empty medium lock list in the map in case a medium is
14460 // attached later.
14461 if (pMedium != NULL)
14462 {
14463 MediumType_T mediumType = pMedium->i_getType();
14464 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14465 || mediumType == MediumType_Shareable;
14466 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14467
14468 alock.release();
14469 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14470 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14471 false /* fMediumLockWriteAll */,
14472 NULL,
14473 *pMediumLockList);
14474 alock.acquire();
14475 if (FAILED(mrc))
14476 {
14477 delete pMediumLockList;
14478 mData->mSession.mLockedMedia.Clear();
14479 break;
14480 }
14481 }
14482
14483 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14484 if (FAILED(rc))
14485 {
14486 mData->mSession.mLockedMedia.Clear();
14487 mrc = setError(rc,
14488 tr("Collecting locking information for all attached media failed"));
14489 break;
14490 }
14491 }
14492
14493 if (SUCCEEDED(mrc))
14494 {
14495 /* Now lock all media. If this fails, nothing is locked. */
14496 alock.release();
14497 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14498 alock.acquire();
14499 if (FAILED(rc))
14500 {
14501 mrc = setError(rc,
14502 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14503 }
14504 }
14505
14506 return mrc;
14507}
14508
14509/**
14510 * Undoes the locks made by by #lockMedia().
14511 */
14512HRESULT SessionMachine::i_unlockMedia()
14513{
14514 AutoCaller autoCaller(this);
14515 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14516
14517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14518
14519 /* we may be holding important error info on the current thread;
14520 * preserve it */
14521 ErrorInfoKeeper eik;
14522
14523 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14524 AssertComRC(rc);
14525 return rc;
14526}
14527
14528/**
14529 * Helper to change the machine state (reimplementation).
14530 *
14531 * @note Locks this object for writing.
14532 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14533 * it can cause crashes in random places due to unexpectedly committing
14534 * the current settings. The caller is responsible for that. The call
14535 * to saveStateSettings is fine, because this method does not commit.
14536 */
14537HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14538{
14539 LogFlowThisFuncEnter();
14540 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14541
14542 AutoCaller autoCaller(this);
14543 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14544
14545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14546
14547 MachineState_T oldMachineState = mData->mMachineState;
14548
14549 AssertMsgReturn(oldMachineState != aMachineState,
14550 ("oldMachineState=%s, aMachineState=%s\n",
14551 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14552 E_FAIL);
14553
14554 HRESULT rc = S_OK;
14555
14556 int stsFlags = 0;
14557 bool deleteSavedState = false;
14558
14559 /* detect some state transitions */
14560
14561 if ( ( oldMachineState == MachineState_Saved
14562 && aMachineState == MachineState_Restoring)
14563 || ( ( oldMachineState == MachineState_PoweredOff
14564 || oldMachineState == MachineState_Teleported
14565 || oldMachineState == MachineState_Aborted
14566 )
14567 && ( aMachineState == MachineState_TeleportingIn
14568 || aMachineState == MachineState_Starting
14569 )
14570 )
14571 )
14572 {
14573 /* The EMT thread is about to start */
14574
14575 /* Nothing to do here for now... */
14576
14577 /// @todo NEWMEDIA don't let mDVDDrive and other children
14578 /// change anything when in the Starting/Restoring state
14579 }
14580 else if ( ( oldMachineState == MachineState_Running
14581 || oldMachineState == MachineState_Paused
14582 || oldMachineState == MachineState_Teleporting
14583 || oldMachineState == MachineState_OnlineSnapshotting
14584 || oldMachineState == MachineState_LiveSnapshotting
14585 || oldMachineState == MachineState_Stuck
14586 || oldMachineState == MachineState_Starting
14587 || oldMachineState == MachineState_Stopping
14588 || oldMachineState == MachineState_Saving
14589 || oldMachineState == MachineState_Restoring
14590 || oldMachineState == MachineState_TeleportingPausedVM
14591 || oldMachineState == MachineState_TeleportingIn
14592 )
14593 && ( aMachineState == MachineState_PoweredOff
14594 || aMachineState == MachineState_Saved
14595 || aMachineState == MachineState_Teleported
14596 || aMachineState == MachineState_Aborted
14597 )
14598 )
14599 {
14600 /* The EMT thread has just stopped, unlock attached media. Note that as
14601 * opposed to locking that is done from Console, we do unlocking here
14602 * because the VM process may have aborted before having a chance to
14603 * properly unlock all media it locked. */
14604
14605 unlockMedia();
14606 }
14607
14608 if (oldMachineState == MachineState_Restoring)
14609 {
14610 if (aMachineState != MachineState_Saved)
14611 {
14612 /*
14613 * delete the saved state file once the machine has finished
14614 * restoring from it (note that Console sets the state from
14615 * Restoring to Saved if the VM couldn't restore successfully,
14616 * to give the user an ability to fix an error and retry --
14617 * we keep the saved state file in this case)
14618 */
14619 deleteSavedState = true;
14620 }
14621 }
14622 else if ( oldMachineState == MachineState_Saved
14623 && ( aMachineState == MachineState_PoweredOff
14624 || aMachineState == MachineState_Aborted
14625 || aMachineState == MachineState_Teleported
14626 )
14627 )
14628 {
14629 /*
14630 * delete the saved state after SessionMachine::ForgetSavedState() is called
14631 * or if the VM process (owning a direct VM session) crashed while the
14632 * VM was Saved
14633 */
14634
14635 /// @todo (dmik)
14636 // Not sure that deleting the saved state file just because of the
14637 // client death before it attempted to restore the VM is a good
14638 // thing. But when it crashes we need to go to the Aborted state
14639 // which cannot have the saved state file associated... The only
14640 // way to fix this is to make the Aborted condition not a VM state
14641 // but a bool flag: i.e., when a crash occurs, set it to true and
14642 // change the state to PoweredOff or Saved depending on the
14643 // saved state presence.
14644
14645 deleteSavedState = true;
14646 mData->mCurrentStateModified = TRUE;
14647 stsFlags |= SaveSTS_CurStateModified;
14648 }
14649
14650 if ( aMachineState == MachineState_Starting
14651 || aMachineState == MachineState_Restoring
14652 || aMachineState == MachineState_TeleportingIn
14653 )
14654 {
14655 /* set the current state modified flag to indicate that the current
14656 * state is no more identical to the state in the
14657 * current snapshot */
14658 if (!mData->mCurrentSnapshot.isNull())
14659 {
14660 mData->mCurrentStateModified = TRUE;
14661 stsFlags |= SaveSTS_CurStateModified;
14662 }
14663 }
14664
14665 if (deleteSavedState)
14666 {
14667 if (mRemoveSavedState)
14668 {
14669 Assert(!mSSData->strStateFilePath.isEmpty());
14670
14671 // it is safe to delete the saved state file if ...
14672 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14673 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14674 // ... none of the snapshots share the saved state file
14675 )
14676 RTFileDelete(mSSData->strStateFilePath.c_str());
14677 }
14678
14679 mSSData->strStateFilePath.setNull();
14680 stsFlags |= SaveSTS_StateFilePath;
14681 }
14682
14683 /* redirect to the underlying peer machine */
14684 mPeer->i_setMachineState(aMachineState);
14685
14686 if ( oldMachineState != MachineState_RestoringSnapshot
14687 && ( aMachineState == MachineState_PoweredOff
14688 || aMachineState == MachineState_Teleported
14689 || aMachineState == MachineState_Aborted
14690 || aMachineState == MachineState_Saved))
14691 {
14692 /* the machine has stopped execution
14693 * (or the saved state file was adopted) */
14694 stsFlags |= SaveSTS_StateTimeStamp;
14695 }
14696
14697 if ( ( oldMachineState == MachineState_PoweredOff
14698 || oldMachineState == MachineState_Aborted
14699 || oldMachineState == MachineState_Teleported
14700 )
14701 && aMachineState == MachineState_Saved)
14702 {
14703 /* the saved state file was adopted */
14704 Assert(!mSSData->strStateFilePath.isEmpty());
14705 stsFlags |= SaveSTS_StateFilePath;
14706 }
14707
14708#ifdef VBOX_WITH_GUEST_PROPS
14709 if ( aMachineState == MachineState_PoweredOff
14710 || aMachineState == MachineState_Aborted
14711 || aMachineState == MachineState_Teleported)
14712 {
14713 /* Make sure any transient guest properties get removed from the
14714 * property store on shutdown. */
14715 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14716
14717 /* remove it from the settings representation */
14718 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14719 for (settings::GuestPropertiesList::iterator
14720 it = llGuestProperties.begin();
14721 it != llGuestProperties.end();
14722 /*nothing*/)
14723 {
14724 const settings::GuestProperty &prop = *it;
14725 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14726 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14727 {
14728 it = llGuestProperties.erase(it);
14729 fNeedsSaving = true;
14730 }
14731 else
14732 {
14733 ++it;
14734 }
14735 }
14736
14737 /* Additionally remove it from the HWData representation. Required to
14738 * keep everything in sync, as this is what the API keeps using. */
14739 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14740 for (HWData::GuestPropertyMap::iterator
14741 it = llHWGuestProperties.begin();
14742 it != llHWGuestProperties.end();
14743 /*nothing*/)
14744 {
14745 uint32_t fFlags = it->second.mFlags;
14746 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14747 {
14748 /* iterator where we need to continue after the erase call
14749 * (C++03 is a fact still, and it doesn't return the iterator
14750 * which would allow continuing) */
14751 HWData::GuestPropertyMap::iterator it2 = it;
14752 ++it2;
14753 llHWGuestProperties.erase(it);
14754 it = it2;
14755 fNeedsSaving = true;
14756 }
14757 else
14758 {
14759 ++it;
14760 }
14761 }
14762
14763 if (fNeedsSaving)
14764 {
14765 mData->mCurrentStateModified = TRUE;
14766 stsFlags |= SaveSTS_CurStateModified;
14767 }
14768 }
14769#endif /* VBOX_WITH_GUEST_PROPS */
14770
14771 rc = i_saveStateSettings(stsFlags);
14772
14773 if ( ( oldMachineState != MachineState_PoweredOff
14774 && oldMachineState != MachineState_Aborted
14775 && oldMachineState != MachineState_Teleported
14776 )
14777 && ( aMachineState == MachineState_PoweredOff
14778 || aMachineState == MachineState_Aborted
14779 || aMachineState == MachineState_Teleported
14780 )
14781 )
14782 {
14783 /* we've been shut down for any reason */
14784 /* no special action so far */
14785 }
14786
14787 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14788 LogFlowThisFuncLeave();
14789 return rc;
14790}
14791
14792/**
14793 * Sends the current machine state value to the VM process.
14794 *
14795 * @note Locks this object for reading, then calls a client process.
14796 */
14797HRESULT SessionMachine::i_updateMachineStateOnClient()
14798{
14799 AutoCaller autoCaller(this);
14800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14801
14802 ComPtr<IInternalSessionControl> directControl;
14803 {
14804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14805 AssertReturn(!!mData, E_FAIL);
14806 if (mData->mSession.mLockType == LockType_VM)
14807 directControl = mData->mSession.mDirectControl;
14808
14809 /* directControl may be already set to NULL here in #OnSessionEnd()
14810 * called too early by the direct session process while there is still
14811 * some operation (like deleting the snapshot) in progress. The client
14812 * process in this case is waiting inside Session::close() for the
14813 * "end session" process object to complete, while #uninit() called by
14814 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14815 * operation to complete. For now, we accept this inconsistent behavior
14816 * and simply do nothing here. */
14817
14818 if (mData->mSession.mState == SessionState_Unlocking)
14819 return S_OK;
14820 }
14821
14822 /* ignore notifications sent after #OnSessionEnd() is called */
14823 if (!directControl)
14824 return S_OK;
14825
14826 return directControl->UpdateMachineState(mData->mMachineState);
14827}
14828
14829
14830/*static*/
14831HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14832{
14833 va_list args;
14834 va_start(args, pcszMsg);
14835 HRESULT rc = setErrorInternal(aResultCode,
14836 getStaticClassIID(),
14837 getStaticComponentName(),
14838 Utf8Str(pcszMsg, args),
14839 false /* aWarning */,
14840 true /* aLogIt */);
14841 va_end(args);
14842 return rc;
14843}
14844
14845
14846HRESULT Machine::updateState(MachineState_T aState)
14847{
14848 NOREF(aState);
14849 ReturnComNotImplemented();
14850}
14851
14852HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14853{
14854 NOREF(aProgress);
14855 ReturnComNotImplemented();
14856}
14857
14858HRESULT Machine::endPowerUp(LONG aResult)
14859{
14860 NOREF(aResult);
14861 ReturnComNotImplemented();
14862}
14863
14864HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14865{
14866 NOREF(aProgress);
14867 ReturnComNotImplemented();
14868}
14869
14870HRESULT Machine::endPoweringDown(LONG aResult,
14871 const com::Utf8Str &aErrMsg)
14872{
14873 NOREF(aResult);
14874 NOREF(aErrMsg);
14875 ReturnComNotImplemented();
14876}
14877
14878HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14879 BOOL *aMatched,
14880 ULONG *aMaskedInterfaces)
14881{
14882 NOREF(aDevice);
14883 NOREF(aMatched);
14884 NOREF(aMaskedInterfaces);
14885 ReturnComNotImplemented();
14886
14887}
14888
14889HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14890{
14891 NOREF(aId); NOREF(aCaptureFilename);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14896 BOOL aDone)
14897{
14898 NOREF(aId);
14899 NOREF(aDone);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::autoCaptureUSBDevices()
14904{
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14909{
14910 NOREF(aDone);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14915 ComPtr<IProgress> &aProgress)
14916{
14917 NOREF(aSession);
14918 NOREF(aProgress);
14919 ReturnComNotImplemented();
14920}
14921
14922HRESULT Machine::finishOnlineMergeMedium()
14923{
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14928 std::vector<com::Utf8Str> &aValues,
14929 std::vector<LONG64> &aTimestamps,
14930 std::vector<com::Utf8Str> &aFlags)
14931{
14932 NOREF(aNames);
14933 NOREF(aValues);
14934 NOREF(aTimestamps);
14935 NOREF(aFlags);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14940 const com::Utf8Str &aValue,
14941 LONG64 aTimestamp,
14942 const com::Utf8Str &aFlags)
14943{
14944 NOREF(aName);
14945 NOREF(aValue);
14946 NOREF(aTimestamp);
14947 NOREF(aFlags);
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::lockMedia()
14952{
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::unlockMedia()
14957{
14958 ReturnComNotImplemented();
14959}
14960
14961HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14962 ComPtr<IMediumAttachment> &aNewAttachment)
14963{
14964 NOREF(aAttachment);
14965 NOREF(aNewAttachment);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14970 ULONG aCpuUser,
14971 ULONG aCpuKernel,
14972 ULONG aCpuIdle,
14973 ULONG aMemTotal,
14974 ULONG aMemFree,
14975 ULONG aMemBalloon,
14976 ULONG aMemShared,
14977 ULONG aMemCache,
14978 ULONG aPagedTotal,
14979 ULONG aMemAllocTotal,
14980 ULONG aMemFreeTotal,
14981 ULONG aMemBalloonTotal,
14982 ULONG aMemSharedTotal,
14983 ULONG aVmNetRx,
14984 ULONG aVmNetTx)
14985{
14986 NOREF(aValidStats);
14987 NOREF(aCpuUser);
14988 NOREF(aCpuKernel);
14989 NOREF(aCpuIdle);
14990 NOREF(aMemTotal);
14991 NOREF(aMemFree);
14992 NOREF(aMemBalloon);
14993 NOREF(aMemShared);
14994 NOREF(aMemCache);
14995 NOREF(aPagedTotal);
14996 NOREF(aMemAllocTotal);
14997 NOREF(aMemFreeTotal);
14998 NOREF(aMemBalloonTotal);
14999 NOREF(aMemSharedTotal);
15000 NOREF(aVmNetRx);
15001 NOREF(aVmNetTx);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15006 com::Utf8Str &aResult)
15007{
15008 NOREF(aAuthParams);
15009 NOREF(aResult);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15014{
15015 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15016
15017 AutoCaller autoCaller(this);
15018 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15019
15020 HRESULT rc = S_OK;
15021
15022 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15023 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15024 rc = getUSBDeviceFilters(usbDeviceFilters);
15025 if (FAILED(rc)) return rc;
15026
15027 NOREF(aFlags);
15028 com::Utf8Str osTypeId;
15029 ComObjPtr<GuestOSType> osType = NULL;
15030
15031 /* Get the guest os type as a string from the VB. */
15032 rc = getOSTypeId(osTypeId);
15033 if (FAILED(rc)) return rc;
15034
15035 /* Get the os type obj that coresponds, can be used to get
15036 * the defaults for this guest OS. */
15037 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15038 if (FAILED(rc)) return rc;
15039
15040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15041
15042 /* Let the OS type select 64-bit ness. */
15043 mHWData->mLongMode = osType->i_is64Bit()
15044 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15045
15046 /* Apply network adapters defaults */
15047 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15048 mNetworkAdapters[slot]->i_applyDefaults(osType);
15049
15050 /* Apply serial port defaults */
15051 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15052 mSerialPorts[slot]->i_applyDefaults(osType);
15053
15054 /* Apply parallel port defaults - not OS dependent*/
15055 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15056 mParallelPorts[slot]->i_applyDefaults();
15057
15058
15059 /* Let the OS type enable the X2APIC */
15060 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15061
15062 /* This one covers IOAPICEnabled. */
15063 mBIOSSettings->i_applyDefaults(osType);
15064
15065 /* Initialize default record settings. */
15066 mRecordingSettings->i_applyDefaults();
15067
15068 /* Initialize default BIOS settings here */
15069 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15070 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15071
15072 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15073 if (FAILED(rc)) return rc;
15074
15075 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15076 if (FAILED(rc)) return rc;
15077
15078 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15079 if (FAILED(rc)) return rc;
15080
15081 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15082 if (FAILED(rc)) return rc;
15083
15084 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15085 if (FAILED(rc)) return rc;
15086
15087 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15088 if (FAILED(rc)) return rc;
15089
15090 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15091 if (FAILED(rc)) return rc;
15092
15093 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15094 if (FAILED(rc)) return rc;
15095
15096 BOOL mRTCUseUTC;
15097 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15098 if (FAILED(rc)) return rc;
15099
15100 setRTCUseUTC(mRTCUseUTC);
15101 if (FAILED(rc)) return rc;
15102
15103 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15104 if (FAILED(rc)) return rc;
15105
15106 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15107 if (FAILED(rc)) return rc;
15108
15109 /* Audio stuff. */
15110 AudioCodecType_T audioCodec;
15111 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15112 if (FAILED(rc)) return rc;
15113
15114 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15115 if (FAILED(rc)) return rc;
15116
15117 AudioControllerType_T audioController;
15118 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15119 if (FAILED(rc)) return rc;
15120
15121 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15122 if (FAILED(rc)) return rc;
15123
15124 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15125 if (FAILED(rc)) return rc;
15126
15127 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15128 if (FAILED(rc)) return rc;
15129
15130 /* Storage Controllers */
15131 StorageControllerType_T hdStorageControllerType;
15132 StorageBus_T hdStorageBusType;
15133 StorageControllerType_T dvdStorageControllerType;
15134 StorageBus_T dvdStorageBusType;
15135 BOOL recommendedFloppy;
15136 ComPtr<IStorageController> floppyController;
15137 ComPtr<IStorageController> hdController;
15138 ComPtr<IStorageController> dvdController;
15139 Utf8Str strFloppyName, strDVDName, strHDName;
15140
15141 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15142 strFloppyName = Bstr("Floppy 1").raw();
15143 strDVDName = Bstr("DVD 1").raw();
15144 strHDName = Bstr("HDD 1").raw();
15145
15146 /* Floppy recommended? add one. */
15147 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15148 if (FAILED(rc)) return rc;
15149 if (recommendedFloppy)
15150 {
15151 rc = addStorageController(strFloppyName,
15152 StorageBus_Floppy,
15153 floppyController);
15154 if (FAILED(rc)) return rc;
15155 }
15156
15157 /* Setup one DVD storage controller. */
15158 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15159 if (FAILED(rc)) return rc;
15160
15161 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15162 if (FAILED(rc)) return rc;
15163
15164 rc = addStorageController(strDVDName,
15165 dvdStorageBusType,
15166 dvdController);
15167 if (FAILED(rc)) return rc;
15168
15169 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15170 if (FAILED(rc)) return rc;
15171
15172 /* Setup one HDD storage controller. */
15173 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15174 if (FAILED(rc)) return rc;
15175
15176 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15177 if (FAILED(rc)) return rc;
15178
15179 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15180 {
15181 rc = addStorageController(strHDName,
15182 hdStorageBusType,
15183 hdController);
15184 if (FAILED(rc)) return rc;
15185
15186 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15187 if (FAILED(rc)) return rc;
15188 }
15189 else
15190 {
15191 /* The HD controller is the same as DVD: */
15192 hdController = dvdController;
15193 strHDName = Bstr("DVD 1").raw();
15194 }
15195
15196 /* Limit the AHCI port count if it's used because windows has trouble with
15197 * too many ports and other guest (OS X in particular) may take extra long
15198 * boot: */
15199
15200 // pParent = static_cast<Medium*>(aP)
15201 IStorageController *temp = hdController;
15202 ComObjPtr<StorageController> storageController;
15203 storageController = static_cast<StorageController *>(temp);
15204
15205 // tempHDController = aHDController;
15206 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15207 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15208 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15209 storageController->COMSETTER(PortCount)(1);
15210
15211 /* USB stuff */
15212
15213 bool ohciEnabled = false;
15214
15215 ComPtr<IUSBController> usbController;
15216 BOOL recommendedUSB3;
15217 BOOL recommendedUSB;
15218 BOOL usbProxyAvailable;
15219
15220 getUSBProxyAvailable(&usbProxyAvailable);
15221 if (FAILED(rc)) return rc;
15222
15223 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15224 if (FAILED(rc)) return rc;
15225 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15226 if (FAILED(rc)) return rc;
15227
15228 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15229 {
15230#ifdef VBOX_WITH_EXTPACK
15231 /* USB 3.0 is only available if the proper ExtPack is installed. */
15232 ExtPackManager *aManager = mParent->i_getExtPackManager();
15233 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15234 {
15235 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15236 if (FAILED(rc)) return rc;
15237
15238 /* xHci includes OHCI */
15239 ohciEnabled = true;
15240 }
15241#endif
15242 }
15243 if ( !ohciEnabled
15244 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15245 {
15246 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15247 if (FAILED(rc)) return rc;
15248 ohciEnabled = true;
15249
15250#ifdef VBOX_WITH_EXTPACK
15251 /* USB 2.0 is only available if the proper ExtPack is installed.
15252 * Note. Configuring EHCI here and providing messages about
15253 * the missing extpack isn't exactly clean, but it is a
15254 * necessary evil to patch over legacy compatability issues
15255 * introduced by the new distribution model. */
15256 ExtPackManager *manager = mParent->i_getExtPackManager();
15257 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15258 {
15259 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15260 if (FAILED(rc)) return rc;
15261 }
15262#endif
15263 }
15264
15265 /* Set recommended human interface device types: */
15266 BOOL recommendedUSBHID;
15267 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15268 if (FAILED(rc)) return rc;
15269
15270 if (recommendedUSBHID)
15271 {
15272 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15273 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15274 if (!ohciEnabled && !usbDeviceFilters.isNull())
15275 {
15276 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15277 if (FAILED(rc)) return rc;
15278 }
15279 }
15280
15281 BOOL recommendedUSBTablet;
15282 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15283 if (FAILED(rc)) return rc;
15284
15285 if (recommendedUSBTablet)
15286 {
15287 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15288 if (!ohciEnabled && !usbDeviceFilters.isNull())
15289 {
15290 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15291 if (FAILED(rc)) return rc;
15292 }
15293 }
15294 return S_OK;
15295}
15296
15297/* This isn't handled entirely by the wrapper generator yet. */
15298#ifdef VBOX_WITH_XPCOM
15299NS_DECL_CLASSINFO(SessionMachine)
15300NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15301
15302NS_DECL_CLASSINFO(SnapshotMachine)
15303NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15304#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