VirtualBox

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

Last change on this file since 61888 was 61788, checked in by vboxsync, 9 years ago

re-enable x2APIC for new Linux VMs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 515.5 KB
Line 
1/* $Id: MachineImpl.cpp 61788 2016-06-21 12:08:15Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2016 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
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Apply BIOS defaults */
348 mBIOSSettings->i_applyDefaults(aOsType);
349
350 /* Apply network adapters defaults */
351 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
352 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
353
354 /* Apply serial port defaults */
355 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
356 mSerialPorts[slot]->i_applyDefaults(aOsType);
357
358 /* Let the OS type select 64-bit ness. */
359 mHWData->mLongMode = aOsType->i_is64Bit()
360 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361
362 /* Let the OS type enable the X2APIC */
363 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
364 }
365
366 /* Apply parallel port defaults */
367 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
368 mParallelPorts[slot]->i_applyDefaults();
369
370 /* At this point the changing of the current state modification
371 * flag is allowed. */
372 i_allowStateModification();
373
374 /* commit all changes made during the initialization */
375 i_commit();
376 }
377
378 /* Confirm a successful initialization when it's the case */
379 if (SUCCEEDED(rc))
380 {
381 if (mData->mAccessible)
382 autoInitSpan.setSucceeded();
383 else
384 autoInitSpan.setLimited();
385 }
386
387 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
388 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
389 mData->mRegistered,
390 mData->mAccessible,
391 rc));
392
393 LogFlowThisFuncLeave();
394
395 return rc;
396}
397
398/**
399 * Initializes a new instance with data from machine XML (formerly Init_Registered).
400 * Gets called in two modes:
401 *
402 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
403 * UUID is specified and we mark the machine as "registered";
404 *
405 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
406 * and the machine remains unregistered until RegisterMachine() is called.
407 *
408 * @param aParent Associated parent object
409 * @param aConfigFile Local file system path to the VM settings file (can
410 * be relative to the VirtualBox config directory).
411 * @param aId UUID of the machine or NULL (see above).
412 *
413 * @return Success indicator. if not S_OK, the machine object is invalid
414 */
415HRESULT Machine::initFromSettings(VirtualBox *aParent,
416 const Utf8Str &strConfigFile,
417 const Guid *aId)
418{
419 LogFlowThisFuncEnter();
420 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
421
422 /* Enclose the state transition NotReady->InInit->Ready */
423 AutoInitSpan autoInitSpan(this);
424 AssertReturn(autoInitSpan.isOk(), E_FAIL);
425
426 HRESULT rc = initImpl(aParent, strConfigFile);
427 if (FAILED(rc)) return rc;
428
429 if (aId)
430 {
431 // loading a registered VM:
432 unconst(mData->mUuid) = *aId;
433 mData->mRegistered = TRUE;
434 // now load the settings from XML:
435 rc = i_registeredInit();
436 // this calls initDataAndChildObjects() and loadSettings()
437 }
438 else
439 {
440 // opening an unregistered VM (VirtualBox::OpenMachine()):
441 rc = initDataAndChildObjects();
442
443 if (SUCCEEDED(rc))
444 {
445 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
446 mData->mAccessible = TRUE;
447
448 try
449 {
450 // load and parse machine XML; this will throw on XML or logic errors
451 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
452
453 // reject VM UUID duplicates, they can happen if someone
454 // tries to register an already known VM config again
455 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
456 true /* fPermitInaccessible */,
457 false /* aDoSetError */,
458 NULL) != VBOX_E_OBJECT_NOT_FOUND)
459 {
460 throw setError(E_FAIL,
461 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
462 mData->m_strConfigFile.c_str());
463 }
464
465 // use UUID from machine config
466 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
467
468 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
469 NULL /* puuidRegistry */);
470 if (FAILED(rc)) throw rc;
471
472 /* At this point the changing of the current state modification
473 * flag is allowed. */
474 i_allowStateModification();
475
476 i_commit();
477 }
478 catch (HRESULT err)
479 {
480 /* we assume that error info is set by the thrower */
481 rc = err;
482 }
483 catch (...)
484 {
485 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
486 }
487 }
488 }
489
490 /* Confirm a successful initialization when it's the case */
491 if (SUCCEEDED(rc))
492 {
493 if (mData->mAccessible)
494 autoInitSpan.setSucceeded();
495 else
496 {
497 autoInitSpan.setLimited();
498
499 // uninit media from this machine's media registry, or else
500 // reloading the settings will fail
501 mParent->i_unregisterMachineMedia(i_getId());
502 }
503 }
504
505 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
506 "rc=%08X\n",
507 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
508 mData->mRegistered, mData->mAccessible, rc));
509
510 LogFlowThisFuncLeave();
511
512 return rc;
513}
514
515/**
516 * Initializes a new instance from a machine config that is already in memory
517 * (import OVF case). Since we are importing, the UUID in the machine
518 * config is ignored and we always generate a fresh one.
519 *
520 * @param strName Name for the new machine; this overrides what is specified in config and is used
521 * for the settings file as well.
522 * @param config Machine configuration loaded and parsed from XML.
523 *
524 * @return Success indicator. if not S_OK, the machine object is invalid
525 */
526HRESULT Machine::init(VirtualBox *aParent,
527 const Utf8Str &strName,
528 const settings::MachineConfigFile &config)
529{
530 LogFlowThisFuncEnter();
531
532 /* Enclose the state transition NotReady->InInit->Ready */
533 AutoInitSpan autoInitSpan(this);
534 AssertReturn(autoInitSpan.isOk(), E_FAIL);
535
536 Utf8Str strConfigFile;
537 aParent->i_getDefaultMachineFolder(strConfigFile);
538 strConfigFile.append(RTPATH_DELIMITER);
539 strConfigFile.append(strName);
540 strConfigFile.append(RTPATH_DELIMITER);
541 strConfigFile.append(strName);
542 strConfigFile.append(".vbox");
543
544 HRESULT rc = initImpl(aParent, strConfigFile);
545 if (FAILED(rc)) return rc;
546
547 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
548 if (FAILED(rc)) return rc;
549
550 rc = initDataAndChildObjects();
551
552 if (SUCCEEDED(rc))
553 {
554 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
555 mData->mAccessible = TRUE;
556
557 // create empty machine config for instance data
558 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
559
560 // generate fresh UUID, ignore machine config
561 unconst(mData->mUuid).create();
562
563 rc = i_loadMachineDataFromSettings(config,
564 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
565
566 // override VM name as well, it may be different
567 mUserData->s.strName = strName;
568
569 if (SUCCEEDED(rc))
570 {
571 /* At this point the changing of the current state modification
572 * flag is allowed. */
573 i_allowStateModification();
574
575 /* commit all changes made during the initialization */
576 i_commit();
577 }
578 }
579
580 /* Confirm a successful initialization when it's the case */
581 if (SUCCEEDED(rc))
582 {
583 if (mData->mAccessible)
584 autoInitSpan.setSucceeded();
585 else
586 {
587 /* Ignore all errors from unregistering, they would destroy
588- * the more interesting error information we already have,
589- * pinpointing the issue with the VM config. */
590 ErrorInfoKeeper eik;
591
592 autoInitSpan.setLimited();
593
594 // uninit media from this machine's media registry, or else
595 // reloading the settings will fail
596 mParent->i_unregisterMachineMedia(i_getId());
597 }
598 }
599
600 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
601 "rc=%08X\n",
602 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
603 mData->mRegistered, mData->mAccessible, rc));
604
605 LogFlowThisFuncLeave();
606
607 return rc;
608}
609
610/**
611 * Shared code between the various init() implementations.
612 * @param aParent
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 setError(VBOX_E_FILE_ERROR,
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 = setError(VBOX_E_FILE_ERROR,
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 = setError(VBOX_E_FILE_ERROR,
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 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 it = mUserData->s.llGroups.begin();
1056 it != mUserData->s.llGroups.end(); ++it, ++i)
1057 aGroups[i] = (*it);
1058
1059 return S_OK;
1060}
1061
1062HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1063{
1064 StringsList llGroups;
1065 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1066 if (FAILED(rc))
1067 return rc;
1068
1069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 rc = i_checkStateDependency(MutableOrSavedStateDep);
1072 if (FAILED(rc)) return rc;
1073
1074 i_setModified(IsModified_MachineData);
1075 mUserData.backup();
1076 mUserData->s.llGroups = llGroups;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1082{
1083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1084
1085 aOSTypeId = mUserData->s.strOsType;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1091{
1092 /* look up the object by Id to check it is valid */
1093 ComPtr<IGuestOSType> guestOSType;
1094 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1095 if (FAILED(rc)) return rc;
1096
1097 /* when setting, always use the "etalon" value for consistency -- lookup
1098 * by ID is case-insensitive and the input value may have different case */
1099 Bstr osTypeId;
1100 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1101 if (FAILED(rc)) return rc;
1102
1103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1104
1105 rc = i_checkStateDependency(MutableStateDep);
1106 if (FAILED(rc)) return rc;
1107
1108 i_setModified(IsModified_MachineData);
1109 mUserData.backup();
1110 mUserData->s.strOsType = osTypeId;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1116{
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 *aFirmwareType = mHWData->mFirmwareType;
1120
1121 return S_OK;
1122}
1123
1124HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1125{
1126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 HRESULT rc = i_checkStateDependency(MutableStateDep);
1129 if (FAILED(rc)) return rc;
1130
1131 i_setModified(IsModified_MachineData);
1132 mHWData.backup();
1133 mHWData->mFirmwareType = aFirmwareType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1139{
1140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1143
1144 return S_OK;
1145}
1146
1147HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1148{
1149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1150
1151 HRESULT rc = i_checkStateDependency(MutableStateDep);
1152 if (FAILED(rc)) return rc;
1153
1154 i_setModified(IsModified_MachineData);
1155 mHWData.backup();
1156 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1162{
1163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 *aPointingHIDType = mHWData->mPointingHIDType;
1166
1167 return S_OK;
1168}
1169
1170HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1171{
1172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 HRESULT rc = i_checkStateDependency(MutableStateDep);
1175 if (FAILED(rc)) return rc;
1176
1177 i_setModified(IsModified_MachineData);
1178 mHWData.backup();
1179 mHWData->mPointingHIDType = aPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1185{
1186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 *aChipsetType = mHWData->mChipsetType;
1189
1190 return S_OK;
1191}
1192
1193HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1194{
1195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 HRESULT rc = i_checkStateDependency(MutableStateDep);
1198 if (FAILED(rc)) return rc;
1199
1200 if (aChipsetType != mHWData->mChipsetType)
1201 {
1202 i_setModified(IsModified_MachineData);
1203 mHWData.backup();
1204 mHWData->mChipsetType = aChipsetType;
1205
1206 // Resize network adapter array, to be finalized on commit/rollback.
1207 // We must not throw away entries yet, otherwise settings are lost
1208 // without a way to roll back.
1209 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1210 size_t oldCount = mNetworkAdapters.size();
1211 if (newCount > oldCount)
1212 {
1213 mNetworkAdapters.resize(newCount);
1214 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1215 {
1216 unconst(mNetworkAdapters[slot]).createObject();
1217 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1218 }
1219 }
1220 }
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1226{
1227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 aParavirtDebug = mHWData->mParavirtDebug;
1230 return S_OK;
1231}
1232
1233HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1234{
1235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1236
1237 HRESULT rc = i_checkStateDependency(MutableStateDep);
1238 if (FAILED(rc)) return rc;
1239
1240 /** @todo Parse/validate options? */
1241 if (aParavirtDebug != mHWData->mParavirtDebug)
1242 {
1243 i_setModified(IsModified_MachineData);
1244 mHWData.backup();
1245 mHWData->mParavirtDebug = aParavirtDebug;
1246 }
1247
1248 return S_OK;
1249}
1250
1251HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 *aParavirtProvider = mHWData->mParavirtProvider;
1256
1257 return S_OK;
1258}
1259
1260HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1261{
1262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 HRESULT rc = i_checkStateDependency(MutableStateDep);
1265 if (FAILED(rc)) return rc;
1266
1267 if (aParavirtProvider != mHWData->mParavirtProvider)
1268 {
1269 i_setModified(IsModified_MachineData);
1270 mHWData.backup();
1271 mHWData->mParavirtProvider = aParavirtProvider;
1272 }
1273
1274 return S_OK;
1275}
1276
1277HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1278{
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 *aParavirtProvider = mHWData->mParavirtProvider;
1282 switch (mHWData->mParavirtProvider)
1283 {
1284 case ParavirtProvider_None:
1285 case ParavirtProvider_HyperV:
1286 case ParavirtProvider_KVM:
1287 case ParavirtProvider_Minimal:
1288 break;
1289
1290 /* Resolve dynamic provider types to the effective types. */
1291 default:
1292 {
1293 ComPtr<IGuestOSType> ptrGuestOSType;
1294 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1295 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1296
1297 Bstr guestTypeFamilyId;
1298 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1299 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1300 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1301
1302 switch (mHWData->mParavirtProvider)
1303 {
1304 case ParavirtProvider_Legacy:
1305 {
1306 if (fOsXGuest)
1307 *aParavirtProvider = ParavirtProvider_Minimal;
1308 else
1309 *aParavirtProvider = ParavirtProvider_None;
1310 break;
1311 }
1312
1313 case ParavirtProvider_Default:
1314 {
1315 if (fOsXGuest)
1316 *aParavirtProvider = ParavirtProvider_Minimal;
1317 else if ( mUserData->s.strOsType == "Windows10"
1318 || mUserData->s.strOsType == "Windows10_64"
1319 || mUserData->s.strOsType == "Windows81"
1320 || mUserData->s.strOsType == "Windows81_64"
1321 || mUserData->s.strOsType == "Windows8"
1322 || mUserData->s.strOsType == "Windows8_64"
1323 || mUserData->s.strOsType == "Windows7"
1324 || mUserData->s.strOsType == "Windows7_64"
1325 || mUserData->s.strOsType == "WindowsVista"
1326 || mUserData->s.strOsType == "WindowsVista_64"
1327 || mUserData->s.strOsType == "Windows2012"
1328 || mUserData->s.strOsType == "Windows2012_64"
1329 || mUserData->s.strOsType == "Windows2008"
1330 || mUserData->s.strOsType == "Windows2008_64")
1331 {
1332 *aParavirtProvider = ParavirtProvider_HyperV;
1333 }
1334 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1335 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1336 || mUserData->s.strOsType == "Linux"
1337 || mUserData->s.strOsType == "Linux_64"
1338 || mUserData->s.strOsType == "ArchLinux"
1339 || mUserData->s.strOsType == "ArchLinux_64"
1340 || mUserData->s.strOsType == "Debian"
1341 || mUserData->s.strOsType == "Debian_64"
1342 || mUserData->s.strOsType == "Fedora"
1343 || mUserData->s.strOsType == "Fedora_64"
1344 || mUserData->s.strOsType == "Gentoo"
1345 || mUserData->s.strOsType == "Gentoo_64"
1346 || mUserData->s.strOsType == "Mandriva"
1347 || mUserData->s.strOsType == "Mandriva_64"
1348 || mUserData->s.strOsType == "OpenSUSE"
1349 || mUserData->s.strOsType == "OpenSUSE_64"
1350 || mUserData->s.strOsType == "Oracle"
1351 || mUserData->s.strOsType == "Oracle_64"
1352 || mUserData->s.strOsType == "RedHat"
1353 || mUserData->s.strOsType == "RedHat_64"
1354 || mUserData->s.strOsType == "Turbolinux"
1355 || mUserData->s.strOsType == "Turbolinux_64"
1356 || mUserData->s.strOsType == "Ubuntu"
1357 || mUserData->s.strOsType == "Ubuntu_64"
1358 || mUserData->s.strOsType == "Xandros"
1359 || mUserData->s.strOsType == "Xandros_64")
1360 {
1361 *aParavirtProvider = ParavirtProvider_KVM;
1362 }
1363 else
1364 *aParavirtProvider = ParavirtProvider_None;
1365 break;
1366 }
1367 }
1368 break;
1369 }
1370 }
1371
1372 Assert( *aParavirtProvider == ParavirtProvider_None
1373 || *aParavirtProvider == ParavirtProvider_Minimal
1374 || *aParavirtProvider == ParavirtProvider_HyperV
1375 || *aParavirtProvider == ParavirtProvider_KVM);
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 aHardwareVersion = mHWData->mHWVersion;
1384
1385 return S_OK;
1386}
1387
1388HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1389{
1390 /* check known version */
1391 Utf8Str hwVersion = aHardwareVersion;
1392 if ( hwVersion.compare("1") != 0
1393 && hwVersion.compare("2") != 0)
1394 return setError(E_INVALIDARG,
1395 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = i_checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 i_setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 mHWData->mHWVersion = aHardwareVersion;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 if (!mHWData->mHardwareUUID.isZero())
1414 aHardwareUUID = mHWData->mHardwareUUID;
1415 else
1416 aHardwareUUID = mData->mUuid;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1422{
1423 if (!aHardwareUUID.isValid())
1424 return E_INVALIDARG;
1425
1426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 HRESULT rc = i_checkStateDependency(MutableStateDep);
1429 if (FAILED(rc)) return rc;
1430
1431 i_setModified(IsModified_MachineData);
1432 mHWData.backup();
1433 if (aHardwareUUID == mData->mUuid)
1434 mHWData->mHardwareUUID.clear();
1435 else
1436 mHWData->mHardwareUUID = aHardwareUUID;
1437
1438 return S_OK;
1439}
1440
1441HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1442{
1443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 *aMemorySize = mHWData->mMemorySize;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::setMemorySize(ULONG aMemorySize)
1451{
1452 /* check RAM limits */
1453 if ( aMemorySize < MM_RAM_MIN_IN_MB
1454 || aMemorySize > MM_RAM_MAX_IN_MB
1455 )
1456 return setError(E_INVALIDARG,
1457 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1458 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mMemorySize = aMemorySize;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 *aCPUCount = mHWData->mCPUCount;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::setCPUCount(ULONG aCPUCount)
1482{
1483 /* check CPU limits */
1484 if ( aCPUCount < SchemaDefs::MinCPUCount
1485 || aCPUCount > SchemaDefs::MaxCPUCount
1486 )
1487 return setError(E_INVALIDARG,
1488 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1489 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1490
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1494 if (mHWData->mCPUHotPlugEnabled)
1495 {
1496 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1497 {
1498 if (mHWData->mCPUAttached[idx])
1499 return setError(E_INVALIDARG,
1500 tr("There is still a CPU attached to socket %lu."
1501 "Detach the CPU before removing the socket"),
1502 aCPUCount, idx+1);
1503 }
1504 }
1505
1506 HRESULT rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 i_setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCPUCount = aCPUCount;
1512
1513 return S_OK;
1514}
1515
1516HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1517{
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1526{
1527 HRESULT rc = S_OK;
1528
1529 /* check throttle limits */
1530 if ( aCPUExecutionCap < 1
1531 || aCPUExecutionCap > 100
1532 )
1533 return setError(E_INVALIDARG,
1534 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1535 aCPUExecutionCap, 1, 100);
1536
1537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 alock.release();
1540 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1541 alock.acquire();
1542 if (FAILED(rc)) return rc;
1543
1544 i_setModified(IsModified_MachineData);
1545 mHWData.backup();
1546 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1547
1548 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1549 if (Global::IsOnline(mData->mMachineState))
1550 i_saveSettings(NULL);
1551
1552 return S_OK;
1553}
1554
1555HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1565{
1566 HRESULT rc = S_OK;
1567
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1574 {
1575 if (aCPUHotPlugEnabled)
1576 {
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579
1580 /* Add the amount of CPUs currently attached */
1581 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1582 mHWData->mCPUAttached[i] = true;
1583 }
1584 else
1585 {
1586 /*
1587 * We can disable hotplug only if the amount of maximum CPUs is equal
1588 * to the amount of attached CPUs
1589 */
1590 unsigned cCpusAttached = 0;
1591 unsigned iHighestId = 0;
1592
1593 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1594 {
1595 if (mHWData->mCPUAttached[i])
1596 {
1597 cCpusAttached++;
1598 iHighestId = i;
1599 }
1600 }
1601
1602 if ( (cCpusAttached != mHWData->mCPUCount)
1603 || (iHighestId >= mHWData->mCPUCount))
1604 return setError(E_INVALIDARG,
1605 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 }
1610 }
1611
1612 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1613
1614 return rc;
1615}
1616
1617HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1618{
1619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1620
1621 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1622
1623 return S_OK;
1624}
1625
1626HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1627{
1628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1631 if (SUCCEEDED(hrc))
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1636 }
1637 return hrc;
1638}
1639
1640HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1641{
1642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1643 aCPUProfile = mHWData->mCpuProfile;
1644 return S_OK;
1645}
1646
1647HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1648{
1649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1650 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1651 if (SUCCEEDED(hrc))
1652 {
1653 i_setModified(IsModified_MachineData);
1654 mHWData.backup();
1655 /* Empty equals 'host'. */
1656 if (aCPUProfile.isNotEmpty())
1657 mHWData->mCpuProfile = aCPUProfile;
1658 else
1659 mHWData->mCpuProfile = "host";
1660 }
1661 return hrc;
1662}
1663
1664HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1665{
1666#ifdef VBOX_WITH_USB_CARDREADER
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEmulatedUSBCardReaderEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1679{
1680#ifdef VBOX_WITH_USB_CARDREADER
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 i_setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1689
1690 return S_OK;
1691#else
1692 NOREF(aEmulatedUSBCardReaderEnabled);
1693 return E_NOTIMPL;
1694#endif
1695}
1696
1697HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 *aHPETEnabled = mHWData->mHPETEnabled;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1707{
1708 HRESULT rc = S_OK;
1709
1710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 rc = i_checkStateDependency(MutableStateDep);
1713 if (FAILED(rc)) return rc;
1714
1715 i_setModified(IsModified_MachineData);
1716 mHWData.backup();
1717
1718 mHWData->mHPETEnabled = aHPETEnabled;
1719
1720 return rc;
1721}
1722
1723HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1728 return S_OK;
1729}
1730
1731HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1732{
1733 HRESULT rc = S_OK;
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 i_setModified(IsModified_MachineData);
1738 mHWData.backup();
1739 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1740
1741 alock.release();
1742 rc = i_onVideoCaptureChange();
1743 alock.acquire();
1744 if (FAILED(rc))
1745 {
1746 /*
1747 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1748 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1749 * determine if it should start or stop capturing. Therefore we need to manually
1750 * undo change.
1751 */
1752 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1753 return rc;
1754 }
1755
1756 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1757 if (Global::IsOnline(mData->mMachineState))
1758 i_saveSettings(NULL);
1759
1760 return rc;
1761}
1762
1763HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1767 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1768 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1773{
1774 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1775 bool fChanged = false;
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1780 {
1781 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1782 {
1783 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1784 fChanged = true;
1785 }
1786 }
1787 if (fChanged)
1788 {
1789 alock.release();
1790 HRESULT rc = i_onVideoCaptureChange();
1791 alock.acquire();
1792 if (FAILED(rc)) return rc;
1793 i_setModified(IsModified_MachineData);
1794
1795 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1796 if (Global::IsOnline(mData->mMachineState))
1797 i_saveSettings(NULL);
1798 }
1799
1800 return S_OK;
1801}
1802
1803HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1804{
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806 if (mHWData->mVideoCaptureFile.isEmpty())
1807 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1808 else
1809 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1814{
1815 Utf8Str strFile(aVideoCaptureFile);
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 if (!RTPathStartsWithRoot(strFile.c_str()))
1823 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1824
1825 if (!strFile.isEmpty())
1826 {
1827 Utf8Str defaultFile;
1828 i_getDefaultVideoCaptureFile(defaultFile);
1829 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1830 strFile.setNull();
1831 }
1832
1833 i_setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mVideoCaptureFile = strFile;
1836
1837 return S_OK;
1838}
1839
1840HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1841{
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1848{
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 if ( Global::IsOnline(mData->mMachineState)
1852 && mHWData->mVideoCaptureEnabled)
1853 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1854
1855 i_setModified(IsModified_MachineData);
1856 mHWData.backup();
1857 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1858
1859 return S_OK;
1860}
1861
1862HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 if ( Global::IsOnline(mData->mMachineState)
1874 && mHWData->mVideoCaptureEnabled)
1875 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1876
1877 i_setModified(IsModified_MachineData);
1878 mHWData.backup();
1879 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1892{
1893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 if ( Global::IsOnline(mData->mMachineState)
1896 && mHWData->mVideoCaptureEnabled)
1897 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1898
1899 i_setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1902
1903 return S_OK;
1904}
1905
1906HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1907{
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1910 return S_OK;
1911}
1912
1913HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1914{
1915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 if ( Global::IsOnline(mData->mMachineState)
1918 && mHWData->mVideoCaptureEnabled)
1919 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1920
1921 i_setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1929{
1930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1931 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1932 return S_OK;
1933}
1934
1935HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1936{
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 if ( Global::IsOnline(mData->mMachineState)
1940 && mHWData->mVideoCaptureEnabled)
1941 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1942
1943 i_setModified(IsModified_MachineData);
1944 mHWData.backup();
1945 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1954 return S_OK;
1955}
1956
1957HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1958{
1959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 if ( Global::IsOnline(mData->mMachineState)
1962 && mHWData->mVideoCaptureEnabled)
1963 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1964
1965 i_setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1968
1969 return S_OK;
1970}
1971
1972HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1981{
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 if ( Global::IsOnline(mData->mMachineState)
1985 && mHWData->mVideoCaptureEnabled)
1986 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1987
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1991
1992 return S_OK;
1993}
1994
1995HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1996{
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2000
2001 return S_OK;
2002}
2003
2004HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2005{
2006 switch (aGraphicsControllerType)
2007 {
2008 case GraphicsControllerType_Null:
2009 case GraphicsControllerType_VBoxVGA:
2010#ifdef VBOX_WITH_VMSVGA
2011 case GraphicsControllerType_VMSVGA:
2012#endif
2013 break;
2014 default:
2015 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2016 }
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 HRESULT rc = i_checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2026
2027 return S_OK;
2028}
2029
2030HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2031{
2032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2033
2034 *aVRAMSize = mHWData->mVRAMSize;
2035
2036 return S_OK;
2037}
2038
2039HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2040{
2041 /* check VRAM limits */
2042 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2043 return setError(E_INVALIDARG,
2044 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2045 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2046
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 HRESULT rc = i_checkStateDependency(MutableStateDep);
2050 if (FAILED(rc)) return rc;
2051
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mVRAMSize = aVRAMSize;
2055
2056 return S_OK;
2057}
2058
2059/** @todo this method should not be public */
2060HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2061{
2062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2065
2066 return S_OK;
2067}
2068
2069/**
2070 * Set the memory balloon size.
2071 *
2072 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2073 * we have to make sure that we never call IGuest from here.
2074 */
2075HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2076{
2077 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2078#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2079 /* check limits */
2080 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2081 return setError(E_INVALIDARG,
2082 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2083 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2084
2085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2086
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2090
2091 return S_OK;
2092#else
2093 NOREF(aMemoryBalloonSize);
2094 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2095#endif
2096}
2097
2098HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2099{
2100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2103 return S_OK;
2104}
2105
2106HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2107{
2108#ifdef VBOX_WITH_PAGE_SHARING
2109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2115 return S_OK;
2116#else
2117 NOREF(aPageFusionEnabled);
2118 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2119#endif
2120}
2121
2122HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2123{
2124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2125
2126 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2127
2128 return S_OK;
2129}
2130
2131HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /** @todo check validity! */
2139
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2143
2144 return S_OK;
2145}
2146
2147
2148HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2149{
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2153
2154 return S_OK;
2155}
2156
2157HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2158{
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = i_checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 /** @todo check validity! */
2165 i_setModified(IsModified_MachineData);
2166 mHWData.backup();
2167 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2168
2169 return S_OK;
2170}
2171
2172HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2173{
2174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 *aMonitorCount = mHWData->mMonitorCount;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2182{
2183 /* make sure monitor count is a sensible number */
2184 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2185 return setError(E_INVALIDARG,
2186 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2187 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2188
2189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 HRESULT rc = i_checkStateDependency(MutableStateDep);
2192 if (FAILED(rc)) return rc;
2193
2194 i_setModified(IsModified_MachineData);
2195 mHWData.backup();
2196 mHWData->mMonitorCount = aMonitorCount;
2197
2198 return S_OK;
2199}
2200
2201HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2202{
2203 /* mBIOSSettings is constant during life time, no need to lock */
2204 aBIOSSettings = mBIOSSettings;
2205
2206 return S_OK;
2207}
2208
2209HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2210{
2211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2212
2213 switch (aProperty)
2214 {
2215 case CPUPropertyType_PAE:
2216 *aValue = mHWData->mPAEEnabled;
2217 break;
2218
2219 case CPUPropertyType_LongMode:
2220 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2221 *aValue = TRUE;
2222 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2223 *aValue = FALSE;
2224#if HC_ARCH_BITS == 64
2225 else
2226 *aValue = TRUE;
2227#else
2228 else
2229 {
2230 *aValue = FALSE;
2231
2232 ComPtr<IGuestOSType> ptrGuestOSType;
2233 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2234 if (SUCCEEDED(hrc2))
2235 {
2236 BOOL fIs64Bit = FALSE;
2237 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2238 if (SUCCEEDED(hrc2) && fIs64Bit)
2239 {
2240 ComObjPtr<Host> ptrHost = mParent->i_host();
2241 alock.release();
2242
2243 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2244 if (FAILED(hrc2))
2245 *aValue = FALSE;
2246 }
2247 }
2248 }
2249#endif
2250 break;
2251
2252 case CPUPropertyType_TripleFaultReset:
2253 *aValue = mHWData->mTripleFaultReset;
2254 break;
2255
2256 case CPUPropertyType_APIC:
2257 *aValue = mHWData->mAPIC;
2258 break;
2259
2260 case CPUPropertyType_X2APIC:
2261 *aValue = mHWData->mX2APIC;
2262 break;
2263
2264 default:
2265 return E_INVALIDARG;
2266 }
2267 return S_OK;
2268}
2269
2270HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2271{
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 HRESULT rc = i_checkStateDependency(MutableStateDep);
2275 if (FAILED(rc)) return rc;
2276
2277 switch (aProperty)
2278 {
2279 case CPUPropertyType_PAE:
2280 i_setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mPAEEnabled = !!aValue;
2283 break;
2284
2285 case CPUPropertyType_LongMode:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2289 break;
2290
2291 case CPUPropertyType_TripleFaultReset:
2292 i_setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mTripleFaultReset = !!aValue;
2295 break;
2296
2297 case CPUPropertyType_APIC:
2298 if (mHWData->mX2APIC)
2299 aValue = TRUE;
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mAPIC = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_X2APIC:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mX2APIC = !!aValue;
2309 if (aValue)
2310 mHWData->mAPIC = !!aValue;
2311 break;
2312
2313 default:
2314 return E_INVALIDARG;
2315 }
2316 return S_OK;
2317}
2318
2319HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2320{
2321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2322
2323 switch(aId)
2324 {
2325 case 0x0:
2326 case 0x1:
2327 case 0x2:
2328 case 0x3:
2329 case 0x4:
2330 case 0x5:
2331 case 0x6:
2332 case 0x7:
2333 case 0x8:
2334 case 0x9:
2335 case 0xA:
2336 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2337 return E_INVALIDARG;
2338
2339 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2340 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2341 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2342 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2343 break;
2344
2345 case 0x80000000:
2346 case 0x80000001:
2347 case 0x80000002:
2348 case 0x80000003:
2349 case 0x80000004:
2350 case 0x80000005:
2351 case 0x80000006:
2352 case 0x80000007:
2353 case 0x80000008:
2354 case 0x80000009:
2355 case 0x8000000A:
2356 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2357 return E_INVALIDARG;
2358
2359 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2360 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2361 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2362 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2363 break;
2364
2365 default:
2366 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2367 }
2368 return S_OK;
2369}
2370
2371
2372HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2373{
2374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2375
2376 HRESULT rc = i_checkStateDependency(MutableStateDep);
2377 if (FAILED(rc)) return rc;
2378
2379 switch(aId)
2380 {
2381 case 0x0:
2382 case 0x1:
2383 case 0x2:
2384 case 0x3:
2385 case 0x4:
2386 case 0x5:
2387 case 0x6:
2388 case 0x7:
2389 case 0x8:
2390 case 0x9:
2391 case 0xA:
2392 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2393 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2394 i_setModified(IsModified_MachineData);
2395 mHWData.backup();
2396 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2397 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2398 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2399 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2400 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2401 break;
2402
2403 case 0x80000000:
2404 case 0x80000001:
2405 case 0x80000002:
2406 case 0x80000003:
2407 case 0x80000004:
2408 case 0x80000005:
2409 case 0x80000006:
2410 case 0x80000007:
2411 case 0x80000008:
2412 case 0x80000009:
2413 case 0x8000000A:
2414 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2415 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2416 i_setModified(IsModified_MachineData);
2417 mHWData.backup();
2418 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2419 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2420 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2421 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2422 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2423 break;
2424
2425 default:
2426 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2427 }
2428 return S_OK;
2429}
2430
2431HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2432{
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434
2435 HRESULT rc = i_checkStateDependency(MutableStateDep);
2436 if (FAILED(rc)) return rc;
2437
2438 switch(aId)
2439 {
2440 case 0x0:
2441 case 0x1:
2442 case 0x2:
2443 case 0x3:
2444 case 0x4:
2445 case 0x5:
2446 case 0x6:
2447 case 0x7:
2448 case 0x8:
2449 case 0x9:
2450 case 0xA:
2451 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2452 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2453 i_setModified(IsModified_MachineData);
2454 mHWData.backup();
2455 /* Invalidate leaf. */
2456 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2457 break;
2458
2459 case 0x80000000:
2460 case 0x80000001:
2461 case 0x80000002:
2462 case 0x80000003:
2463 case 0x80000004:
2464 case 0x80000005:
2465 case 0x80000006:
2466 case 0x80000007:
2467 case 0x80000008:
2468 case 0x80000009:
2469 case 0x8000000A:
2470 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2471 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2472 i_setModified(IsModified_MachineData);
2473 mHWData.backup();
2474 /* Invalidate leaf. */
2475 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2476 break;
2477
2478 default:
2479 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2480 }
2481 return S_OK;
2482}
2483
2484HRESULT Machine::removeAllCPUIDLeaves()
2485{
2486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 HRESULT rc = i_checkStateDependency(MutableStateDep);
2489 if (FAILED(rc)) return rc;
2490
2491 i_setModified(IsModified_MachineData);
2492 mHWData.backup();
2493
2494 /* Invalidate all standard leafs. */
2495 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2496 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2497
2498 /* Invalidate all extended leafs. */
2499 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2500 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2501
2502 return S_OK;
2503}
2504HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2505{
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 switch(aProperty)
2509 {
2510 case HWVirtExPropertyType_Enabled:
2511 *aValue = mHWData->mHWVirtExEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_VPID:
2515 *aValue = mHWData->mHWVirtExVPIDEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_NestedPaging:
2519 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_UnrestrictedExecution:
2523 *aValue = mHWData->mHWVirtExUXEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_LargePages:
2527 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2528#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2529 *aValue = FALSE;
2530#endif
2531 break;
2532
2533 case HWVirtExPropertyType_Force:
2534 *aValue = mHWData->mHWVirtExForceEnabled;
2535 break;
2536
2537 default:
2538 return E_INVALIDARG;
2539 }
2540 return S_OK;
2541}
2542
2543HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2544{
2545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 HRESULT rc = i_checkStateDependency(MutableStateDep);
2548 if (FAILED(rc)) return rc;
2549
2550 switch(aProperty)
2551 {
2552 case HWVirtExPropertyType_Enabled:
2553 i_setModified(IsModified_MachineData);
2554 mHWData.backup();
2555 mHWData->mHWVirtExEnabled = !!aValue;
2556 break;
2557
2558 case HWVirtExPropertyType_VPID:
2559 i_setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2562 break;
2563
2564 case HWVirtExPropertyType_NestedPaging:
2565 i_setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2568 break;
2569
2570 case HWVirtExPropertyType_UnrestrictedExecution:
2571 i_setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExUXEnabled = !!aValue;
2574 break;
2575
2576 case HWVirtExPropertyType_LargePages:
2577 i_setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2580 break;
2581
2582 case HWVirtExPropertyType_Force:
2583 i_setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExForceEnabled = !!aValue;
2586 break;
2587
2588 default:
2589 return E_INVALIDARG;
2590 }
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2605{
2606 /* @todo (r=dmik):
2607 * 1. Allow to change the name of the snapshot folder containing snapshots
2608 * 2. Rename the folder on disk instead of just changing the property
2609 * value (to be smart and not to leave garbage). Note that it cannot be
2610 * done here because the change may be rolled back. Thus, the right
2611 * place is #saveSettings().
2612 */
2613
2614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 HRESULT rc = i_checkStateDependency(MutableStateDep);
2617 if (FAILED(rc)) return rc;
2618
2619 if (!mData->mCurrentSnapshot.isNull())
2620 return setError(E_FAIL,
2621 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2622
2623 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2624
2625 if (strSnapshotFolder.isEmpty())
2626 strSnapshotFolder = "Snapshots";
2627 int vrc = i_calculateFullPath(strSnapshotFolder,
2628 strSnapshotFolder);
2629 if (RT_FAILURE(vrc))
2630 return setError(E_FAIL,
2631 tr("Invalid snapshot folder '%s' (%Rrc)"),
2632 strSnapshotFolder.c_str(), vrc);
2633
2634 i_setModified(IsModified_MachineData);
2635 mUserData.backup();
2636
2637 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2643{
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 aMediumAttachments.resize(mMediaData->mAttachments.size());
2647 size_t i = 0;
2648 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2649 it != mMediaData->mAttachments.end(); ++it, ++i)
2650 aMediumAttachments[i] = *it;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 Assert(!!mVRDEServer);
2660
2661 aVRDEServer = mVRDEServer;
2662
2663 return S_OK;
2664}
2665
2666HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2667{
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 aAudioAdapter = mAudioAdapter;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2676{
2677#ifdef VBOX_WITH_VUSB
2678 clearError();
2679 MultiResult rc(S_OK);
2680
2681# ifdef VBOX_WITH_USB
2682 rc = mParent->i_host()->i_checkUSBProxyService();
2683 if (FAILED(rc)) return rc;
2684# endif
2685
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 USBControllerList data = *mUSBControllers.data();
2689 aUSBControllers.resize(data.size());
2690 size_t i = 0;
2691 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2692 aUSBControllers[i] = *it;
2693
2694 return S_OK;
2695#else
2696 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2697 * extended error info to indicate that USB is simply not available
2698 * (w/o treating it as a failure), for example, as in OSE */
2699 NOREF(aUSBControllers);
2700 ReturnComNotImplemented();
2701#endif /* VBOX_WITH_VUSB */
2702}
2703
2704HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2705{
2706#ifdef VBOX_WITH_VUSB
2707 clearError();
2708 MultiResult rc(S_OK);
2709
2710# ifdef VBOX_WITH_USB
2711 rc = mParent->i_host()->i_checkUSBProxyService();
2712 if (FAILED(rc)) return rc;
2713# endif
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 aUSBDeviceFilters = mUSBDeviceFilters;
2718 return rc;
2719#else
2720 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2721 * extended error info to indicate that USB is simply not available
2722 * (w/o treating it as a failure), for example, as in OSE */
2723 NOREF(aUSBDeviceFilters);
2724 ReturnComNotImplemented();
2725#endif /* VBOX_WITH_VUSB */
2726}
2727
2728HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSettingsFilePath = mData->m_strConfigFileFull;
2733
2734 return S_OK;
2735}
2736
2737HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2738{
2739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2740
2741 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2742 if (FAILED(rc)) return rc;
2743
2744 if (!mData->pMachineConfigFile->fileExists())
2745 // this is a new machine, and no config file exists yet:
2746 *aSettingsModified = TRUE;
2747 else
2748 *aSettingsModified = (mData->flModifications != 0);
2749
2750 return S_OK;
2751}
2752
2753HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2754{
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756
2757 *aSessionState = mData->mSession.mState;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aSessionName = mData->mSession.mName;
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 *aSessionPID = mData->mSession.mPID;
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::getState(MachineState_T *aState)
2781{
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 *aState = mData->mMachineState;
2785 Assert(mData->mMachineState != MachineState_Null);
2786
2787 return S_OK;
2788}
2789
2790HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2791{
2792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 aStateFilePath = mSSData->strStateFilePath;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 i_getLogFolder(aLogFolder);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 aCurrentSnapshot = mData->mCurrentSnapshot;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2831 ? 0
2832 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2833
2834 return S_OK;
2835}
2836
2837HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 /* Note: for machines with no snapshots, we always return FALSE
2842 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2843 * reasons :) */
2844
2845 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2846 ? FALSE
2847 : mData->mCurrentStateModified;
2848
2849 return S_OK;
2850}
2851
2852HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2853{
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 aSharedFolders.resize(mHWData->mSharedFolders.size());
2857 size_t i = 0;
2858 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2859 it != mHWData->mSharedFolders.end(); ++i, ++it)
2860 aSharedFolders[i] = *it;
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2866{
2867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 *aClipboardMode = mHWData->mClipboardMode;
2870
2871 return S_OK;
2872}
2873
2874HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2875{
2876 HRESULT rc = S_OK;
2877
2878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 alock.release();
2881 rc = i_onClipboardModeChange(aClipboardMode);
2882 alock.acquire();
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mHWData.backup();
2887 mHWData->mClipboardMode = aClipboardMode;
2888
2889 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2890 if (Global::IsOnline(mData->mMachineState))
2891 i_saveSettings(NULL);
2892
2893 return S_OK;
2894}
2895
2896HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2897{
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 *aDnDMode = mHWData->mDnDMode;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2906{
2907 HRESULT rc = S_OK;
2908
2909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 alock.release();
2912 rc = i_onDnDModeChange(aDnDMode);
2913
2914 alock.acquire();
2915 if (FAILED(rc)) return rc;
2916
2917 i_setModified(IsModified_MachineData);
2918 mHWData.backup();
2919 mHWData->mDnDMode = aDnDMode;
2920
2921 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2922 if (Global::IsOnline(mData->mMachineState))
2923 i_saveSettings(NULL);
2924
2925 return S_OK;
2926}
2927
2928HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2929{
2930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2931 StorageControllerList data = *mStorageControllers.data();
2932 size_t i = 0;
2933 aStorageControllers.resize(data.size());
2934 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2935 aStorageControllers[i] = *it;
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 *aEnabled = mUserData->s.fTeleporterEnabled;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 /* Only allow it to be set to true when PoweredOff or Aborted.
2953 (Clearing it is always permitted.) */
2954 if ( aTeleporterEnabled
2955 && mData->mRegistered
2956 && ( !i_isSessionMachine()
2957 || ( mData->mMachineState != MachineState_PoweredOff
2958 && mData->mMachineState != MachineState_Teleported
2959 && mData->mMachineState != MachineState_Aborted
2960 )
2961 )
2962 )
2963 return setError(VBOX_E_INVALID_VM_STATE,
2964 tr("The machine is not powered off (state is %s)"),
2965 Global::stringifyMachineState(mData->mMachineState));
2966
2967 i_setModified(IsModified_MachineData);
2968 mUserData.backup();
2969 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2970
2971 return S_OK;
2972}
2973
2974HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2975{
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2984{
2985 if (aTeleporterPort >= _64K)
2986 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2987
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3005
3006 return S_OK;
3007}
3008
3009HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3010{
3011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3024{
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3027
3028 return S_OK;
3029}
3030
3031HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3032{
3033 /*
3034 * Hash the password first.
3035 */
3036 com::Utf8Str aT = aTeleporterPassword;
3037
3038 if (!aT.isEmpty())
3039 {
3040 if (VBoxIsPasswordHashed(&aT))
3041 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3042 VBoxHashPassword(&aT);
3043 }
3044
3045 /*
3046 * Do the update.
3047 */
3048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3049 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3050 if (SUCCEEDED(hrc))
3051 {
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.strTeleporterPassword = aT;
3055 }
3056
3057 return hrc;
3058}
3059
3060HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3061{
3062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 /* @todo deal with running state change. */
3073 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3074 if (FAILED(rc)) return rc;
3075
3076 i_setModified(IsModified_MachineData);
3077 mUserData.backup();
3078 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 /* @todo deal with running state change. */
3095 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3096 if (FAILED(rc)) return rc;
3097
3098 i_setModified(IsModified_MachineData);
3099 mUserData.backup();
3100 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3101 return S_OK;
3102}
3103
3104HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3113{
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 /* @todo deal with running state change. */
3117 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3123 return S_OK;
3124}
3125
3126HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3127{
3128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3129
3130 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3131
3132 return S_OK;
3133}
3134
3135HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3136{
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* @todo deal with running state change. */
3140 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 i_setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3146
3147 return S_OK;
3148}
3149
3150HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3151{
3152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3155 return S_OK;
3156}
3157
3158HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3159{
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /* @todo deal with running state change. */
3163 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3164 if (FAILED(rc)) return rc;
3165
3166 i_setModified(IsModified_MachineData);
3167 mUserData.backup();
3168 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3169 return S_OK;
3170}
3171
3172HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3173{
3174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3177
3178 return S_OK;
3179}
3180
3181HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3182{
3183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 /* Only allow it to be set to true when PoweredOff or Aborted.
3186 (Clearing it is always permitted.) */
3187 if ( aRTCUseUTC
3188 && mData->mRegistered
3189 && ( !i_isSessionMachine()
3190 || ( mData->mMachineState != MachineState_PoweredOff
3191 && mData->mMachineState != MachineState_Teleported
3192 && mData->mMachineState != MachineState_Aborted
3193 )
3194 )
3195 )
3196 return setError(VBOX_E_INVALID_VM_STATE,
3197 tr("The machine is not powered off (state is %s)"),
3198 Global::stringifyMachineState(mData->mMachineState));
3199
3200 i_setModified(IsModified_MachineData);
3201 mUserData.backup();
3202 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3208{
3209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3212
3213 return S_OK;
3214}
3215
3216HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3217{
3218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 HRESULT rc = i_checkStateDependency(MutableStateDep);
3221 if (FAILED(rc)) return rc;
3222
3223 i_setModified(IsModified_MachineData);
3224 mHWData.backup();
3225 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3226
3227 return S_OK;
3228}
3229
3230HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3231{
3232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3233
3234 *aIOCacheSize = mHWData->mIOCacheSize;
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3240{
3241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243 HRESULT rc = i_checkStateDependency(MutableStateDep);
3244 if (FAILED(rc)) return rc;
3245
3246 i_setModified(IsModified_MachineData);
3247 mHWData.backup();
3248 mHWData->mIOCacheSize = aIOCacheSize;
3249
3250 return S_OK;
3251}
3252
3253
3254/**
3255 * @note Locks objects!
3256 */
3257HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3258 LockType_T aLockType)
3259{
3260 /* check the session state */
3261 SessionState_T state;
3262 HRESULT rc = aSession->COMGETTER(State)(&state);
3263 if (FAILED(rc)) return rc;
3264
3265 if (state != SessionState_Unlocked)
3266 return setError(VBOX_E_INVALID_OBJECT_STATE,
3267 tr("The given session is busy"));
3268
3269 // get the client's IInternalSessionControl interface
3270 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3271 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3272 E_INVALIDARG);
3273
3274 // session name (only used in some code paths)
3275 Utf8Str strSessionName;
3276
3277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3278
3279 if (!mData->mRegistered)
3280 return setError(E_UNEXPECTED,
3281 tr("The machine '%s' is not registered"),
3282 mUserData->s.strName.c_str());
3283
3284 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3285
3286 SessionState_T oldState = mData->mSession.mState;
3287 /* Hack: in case the session is closing and there is a progress object
3288 * which allows waiting for the session to be closed, take the opportunity
3289 * and do a limited wait (max. 1 second). This helps a lot when the system
3290 * is busy and thus session closing can take a little while. */
3291 if ( mData->mSession.mState == SessionState_Unlocking
3292 && mData->mSession.mProgress)
3293 {
3294 alock.release();
3295 mData->mSession.mProgress->WaitForCompletion(1000);
3296 alock.acquire();
3297 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3298 }
3299
3300 // try again now
3301 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3302 // (i.e. session machine exists)
3303 && (aLockType == LockType_Shared) // caller wants a shared link to the
3304 // existing session that holds the write lock:
3305 )
3306 {
3307 // OK, share the session... we are now dealing with three processes:
3308 // 1) VBoxSVC (where this code runs);
3309 // 2) process C: the caller's client process (who wants a shared session);
3310 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3311
3312 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3313 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3314 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3315 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3316 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3317
3318 /*
3319 * Release the lock before calling the client process. It's safe here
3320 * since the only thing to do after we get the lock again is to add
3321 * the remote control to the list (which doesn't directly influence
3322 * anything).
3323 */
3324 alock.release();
3325
3326 // get the console of the session holding the write lock (this is a remote call)
3327 ComPtr<IConsole> pConsoleW;
3328 if (mData->mSession.mLockType == LockType_VM)
3329 {
3330 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3331 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3332 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3333 if (FAILED(rc))
3334 // the failure may occur w/o any error info (from RPC), so provide one
3335 return setError(VBOX_E_VM_ERROR,
3336 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3337 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3338 }
3339
3340 // share the session machine and W's console with the caller's session
3341 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3342 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3343 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3344
3345 if (FAILED(rc))
3346 // the failure may occur w/o any error info (from RPC), so provide one
3347 return setError(VBOX_E_VM_ERROR,
3348 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3349 alock.acquire();
3350
3351 // need to revalidate the state after acquiring the lock again
3352 if (mData->mSession.mState != SessionState_Locked)
3353 {
3354 pSessionControl->Uninitialize();
3355 return setError(VBOX_E_INVALID_SESSION_STATE,
3356 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3357 mUserData->s.strName.c_str());
3358 }
3359
3360 // add the caller's session to the list
3361 mData->mSession.mRemoteControls.push_back(pSessionControl);
3362 }
3363 else if ( mData->mSession.mState == SessionState_Locked
3364 || mData->mSession.mState == SessionState_Unlocking
3365 )
3366 {
3367 // sharing not permitted, or machine still unlocking:
3368 return setError(VBOX_E_INVALID_OBJECT_STATE,
3369 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3370 mUserData->s.strName.c_str());
3371 }
3372 else
3373 {
3374 // machine is not locked: then write-lock the machine (create the session machine)
3375
3376 // must not be busy
3377 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3378
3379 // get the caller's session PID
3380 RTPROCESS pid = NIL_RTPROCESS;
3381 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3382 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3383 Assert(pid != NIL_RTPROCESS);
3384
3385 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3386
3387 if (fLaunchingVMProcess)
3388 {
3389 if (mData->mSession.mPID == NIL_RTPROCESS)
3390 {
3391 // two or more clients racing for a lock, the one which set the
3392 // session state to Spawning will win, the others will get an
3393 // error as we can't decide here if waiting a little would help
3394 // (only for shared locks this would avoid an error)
3395 return setError(VBOX_E_INVALID_OBJECT_STATE,
3396 tr("The machine '%s' already has a lock request pending"),
3397 mUserData->s.strName.c_str());
3398 }
3399
3400 // this machine is awaiting for a spawning session to be opened:
3401 // then the calling process must be the one that got started by
3402 // LaunchVMProcess()
3403
3404 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3405 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3406
3407#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3408 /* Hardened windows builds spawns three processes when a VM is
3409 launched, the 3rd one is the one that will end up here. */
3410 RTPROCESS ppid;
3411 int rc = RTProcQueryParent(pid, &ppid);
3412 if (RT_SUCCESS(rc))
3413 rc = RTProcQueryParent(ppid, &ppid);
3414 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3415 || rc == VERR_ACCESS_DENIED)
3416 {
3417 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3418 mData->mSession.mPID = pid;
3419 }
3420#endif
3421
3422 if (mData->mSession.mPID != pid)
3423 return setError(E_ACCESSDENIED,
3424 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3425 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3426 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3427 }
3428
3429 // create the mutable SessionMachine from the current machine
3430 ComObjPtr<SessionMachine> sessionMachine;
3431 sessionMachine.createObject();
3432 rc = sessionMachine->init(this);
3433 AssertComRC(rc);
3434
3435 /* NOTE: doing return from this function after this point but
3436 * before the end is forbidden since it may call SessionMachine::uninit()
3437 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3438 * lock while still holding the Machine lock in alock so that a deadlock
3439 * is possible due to the wrong lock order. */
3440
3441 if (SUCCEEDED(rc))
3442 {
3443 /*
3444 * Set the session state to Spawning to protect against subsequent
3445 * attempts to open a session and to unregister the machine after
3446 * we release the lock.
3447 */
3448 SessionState_T origState = mData->mSession.mState;
3449 mData->mSession.mState = SessionState_Spawning;
3450
3451#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3452 /* Get the client token ID to be passed to the client process */
3453 Utf8Str strTokenId;
3454 sessionMachine->i_getTokenId(strTokenId);
3455 Assert(!strTokenId.isEmpty());
3456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3457 /* Get the client token to be passed to the client process */
3458 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3459 /* The token is now "owned" by pToken, fix refcount */
3460 if (!pToken.isNull())
3461 pToken->Release();
3462#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3463
3464 /*
3465 * Release the lock before calling the client process -- it will call
3466 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3467 * because the state is Spawning, so that LaunchVMProcess() and
3468 * LockMachine() calls will fail. This method, called before we
3469 * acquire the lock again, will fail because of the wrong PID.
3470 *
3471 * Note that mData->mSession.mRemoteControls accessed outside
3472 * the lock may not be modified when state is Spawning, so it's safe.
3473 */
3474 alock.release();
3475
3476 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3477#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3478 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3479#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3480 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3481 /* Now the token is owned by the client process. */
3482 pToken.setNull();
3483#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3485
3486 /* The failure may occur w/o any error info (from RPC), so provide one */
3487 if (FAILED(rc))
3488 setError(VBOX_E_VM_ERROR,
3489 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3490
3491 // get session name, either to remember or to compare against
3492 // the already known session name.
3493 {
3494 Bstr bstrSessionName;
3495 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3496 if (SUCCEEDED(rc2))
3497 strSessionName = bstrSessionName;
3498 }
3499
3500 if ( SUCCEEDED(rc)
3501 && fLaunchingVMProcess
3502 )
3503 {
3504 /* complete the remote session initialization */
3505
3506 /* get the console from the direct session */
3507 ComPtr<IConsole> console;
3508 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3509 ComAssertComRC(rc);
3510
3511 if (SUCCEEDED(rc) && !console)
3512 {
3513 ComAssert(!!console);
3514 rc = E_FAIL;
3515 }
3516
3517 /* assign machine & console to the remote session */
3518 if (SUCCEEDED(rc))
3519 {
3520 /*
3521 * after LaunchVMProcess(), the first and the only
3522 * entry in remoteControls is that remote session
3523 */
3524 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3525 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3526 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3527
3528 /* The failure may occur w/o any error info (from RPC), so provide one */
3529 if (FAILED(rc))
3530 setError(VBOX_E_VM_ERROR,
3531 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3532 }
3533
3534 if (FAILED(rc))
3535 pSessionControl->Uninitialize();
3536 }
3537
3538 /* acquire the lock again */
3539 alock.acquire();
3540
3541 /* Restore the session state */
3542 mData->mSession.mState = origState;
3543 }
3544
3545 // finalize spawning anyway (this is why we don't return on errors above)
3546 if (fLaunchingVMProcess)
3547 {
3548 Assert(mData->mSession.mName == strSessionName);
3549 /* Note that the progress object is finalized later */
3550 /** @todo Consider checking mData->mSession.mProgress for cancellation
3551 * around here. */
3552
3553 /* We don't reset mSession.mPID here because it is necessary for
3554 * SessionMachine::uninit() to reap the child process later. */
3555
3556 if (FAILED(rc))
3557 {
3558 /* Close the remote session, remove the remote control from the list
3559 * and reset session state to Closed (@note keep the code in sync
3560 * with the relevant part in checkForSpawnFailure()). */
3561
3562 Assert(mData->mSession.mRemoteControls.size() == 1);
3563 if (mData->mSession.mRemoteControls.size() == 1)
3564 {
3565 ErrorInfoKeeper eik;
3566 mData->mSession.mRemoteControls.front()->Uninitialize();
3567 }
3568
3569 mData->mSession.mRemoteControls.clear();
3570 mData->mSession.mState = SessionState_Unlocked;
3571 }
3572 }
3573 else
3574 {
3575 /* memorize PID of the directly opened session */
3576 if (SUCCEEDED(rc))
3577 mData->mSession.mPID = pid;
3578 }
3579
3580 if (SUCCEEDED(rc))
3581 {
3582 mData->mSession.mLockType = aLockType;
3583 /* memorize the direct session control and cache IUnknown for it */
3584 mData->mSession.mDirectControl = pSessionControl;
3585 mData->mSession.mState = SessionState_Locked;
3586 if (!fLaunchingVMProcess)
3587 mData->mSession.mName = strSessionName;
3588 /* associate the SessionMachine with this Machine */
3589 mData->mSession.mMachine = sessionMachine;
3590
3591 /* request an IUnknown pointer early from the remote party for later
3592 * identity checks (it will be internally cached within mDirectControl
3593 * at least on XPCOM) */
3594 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3595 NOREF(unk);
3596 }
3597
3598 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3599 * would break the lock order */
3600 alock.release();
3601
3602 /* uninitialize the created session machine on failure */
3603 if (FAILED(rc))
3604 sessionMachine->uninit();
3605 }
3606
3607 if (SUCCEEDED(rc))
3608 {
3609 /*
3610 * tell the client watcher thread to update the set of
3611 * machines that have open sessions
3612 */
3613 mParent->i_updateClientWatcher();
3614
3615 if (oldState != SessionState_Locked)
3616 /* fire an event */
3617 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3618 }
3619
3620 return rc;
3621}
3622
3623/**
3624 * @note Locks objects!
3625 */
3626HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3627 const com::Utf8Str &aName,
3628 const com::Utf8Str &aEnvironment,
3629 ComPtr<IProgress> &aProgress)
3630{
3631 Utf8Str strFrontend(aName);
3632 /* "emergencystop" doesn't need the session, so skip the checks/interface
3633 * retrieval. This code doesn't quite fit in here, but introducing a
3634 * special API method would be even more effort, and would require explicit
3635 * support by every API client. It's better to hide the feature a bit. */
3636 if (strFrontend != "emergencystop")
3637 CheckComArgNotNull(aSession);
3638
3639 HRESULT rc = S_OK;
3640 if (strFrontend.isEmpty())
3641 {
3642 Bstr bstrFrontend;
3643 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3644 if (FAILED(rc))
3645 return rc;
3646 strFrontend = bstrFrontend;
3647 if (strFrontend.isEmpty())
3648 {
3649 ComPtr<ISystemProperties> systemProperties;
3650 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3651 if (FAILED(rc))
3652 return rc;
3653 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3654 if (FAILED(rc))
3655 return rc;
3656 strFrontend = bstrFrontend;
3657 }
3658 /* paranoia - emergencystop is not a valid default */
3659 if (strFrontend == "emergencystop")
3660 strFrontend = Utf8Str::Empty;
3661 }
3662 /* default frontend: Qt GUI */
3663 if (strFrontend.isEmpty())
3664 strFrontend = "GUI/Qt";
3665
3666 if (strFrontend != "emergencystop")
3667 {
3668 /* check the session state */
3669 SessionState_T state;
3670 rc = aSession->COMGETTER(State)(&state);
3671 if (FAILED(rc))
3672 return rc;
3673
3674 if (state != SessionState_Unlocked)
3675 return setError(VBOX_E_INVALID_OBJECT_STATE,
3676 tr("The given session is busy"));
3677
3678 /* get the IInternalSessionControl interface */
3679 ComPtr<IInternalSessionControl> control(aSession);
3680 ComAssertMsgRet(!control.isNull(),
3681 ("No IInternalSessionControl interface"),
3682 E_INVALIDARG);
3683
3684 /* get the teleporter enable state for the progress object init. */
3685 BOOL fTeleporterEnabled;
3686 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3687 if (FAILED(rc))
3688 return rc;
3689
3690 /* create a progress object */
3691 ComObjPtr<ProgressProxy> progress;
3692 progress.createObject();
3693 rc = progress->init(mParent,
3694 static_cast<IMachine*>(this),
3695 Bstr(tr("Starting VM")).raw(),
3696 TRUE /* aCancelable */,
3697 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3698 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3699 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3700 2 /* uFirstOperationWeight */,
3701 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3702
3703 if (SUCCEEDED(rc))
3704 {
3705 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3706 if (SUCCEEDED(rc))
3707 {
3708 aProgress = progress;
3709
3710 /* signal the client watcher thread */
3711 mParent->i_updateClientWatcher();
3712
3713 /* fire an event */
3714 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3715 }
3716 }
3717 }
3718 else
3719 {
3720 /* no progress object - either instant success or failure */
3721 aProgress = NULL;
3722
3723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3724
3725 if (mData->mSession.mState != SessionState_Locked)
3726 return setError(VBOX_E_INVALID_OBJECT_STATE,
3727 tr("The machine '%s' is not locked by a session"),
3728 mUserData->s.strName.c_str());
3729
3730 /* must have a VM process associated - do not kill normal API clients
3731 * with an open session */
3732 if (!Global::IsOnline(mData->mMachineState))
3733 return setError(VBOX_E_INVALID_OBJECT_STATE,
3734 tr("The machine '%s' does not have a VM process"),
3735 mUserData->s.strName.c_str());
3736
3737 /* forcibly terminate the VM process */
3738 if (mData->mSession.mPID != NIL_RTPROCESS)
3739 RTProcTerminate(mData->mSession.mPID);
3740
3741 /* signal the client watcher thread, as most likely the client has
3742 * been terminated */
3743 mParent->i_updateClientWatcher();
3744 }
3745
3746 return rc;
3747}
3748
3749HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3750{
3751 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3752 return setError(E_INVALIDARG,
3753 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3754 aPosition, SchemaDefs::MaxBootPosition);
3755
3756 if (aDevice == DeviceType_USB)
3757 return setError(E_NOTIMPL,
3758 tr("Booting from USB device is currently not supported"));
3759
3760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3761
3762 HRESULT rc = i_checkStateDependency(MutableStateDep);
3763 if (FAILED(rc)) return rc;
3764
3765 i_setModified(IsModified_MachineData);
3766 mHWData.backup();
3767 mHWData->mBootOrder[aPosition - 1] = aDevice;
3768
3769 return S_OK;
3770}
3771
3772HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3773{
3774 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3775 return setError(E_INVALIDARG,
3776 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3777 aPosition, SchemaDefs::MaxBootPosition);
3778
3779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3780
3781 *aDevice = mHWData->mBootOrder[aPosition - 1];
3782
3783 return S_OK;
3784}
3785
3786HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3787 LONG aControllerPort,
3788 LONG aDevice,
3789 DeviceType_T aType,
3790 const ComPtr<IMedium> &aMedium)
3791{
3792 IMedium *aM = aMedium;
3793 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3794 aName.c_str(), aControllerPort, aDevice, aType, aM));
3795
3796 // request the host lock first, since might be calling Host methods for getting host drives;
3797 // next, protect the media tree all the while we're in here, as well as our member variables
3798 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3799 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3800
3801 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3802 if (FAILED(rc)) return rc;
3803
3804 /// @todo NEWMEDIA implicit machine registration
3805 if (!mData->mRegistered)
3806 return setError(VBOX_E_INVALID_OBJECT_STATE,
3807 tr("Cannot attach storage devices to an unregistered machine"));
3808
3809 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3810
3811 /* Check for an existing controller. */
3812 ComObjPtr<StorageController> ctl;
3813 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3814 if (FAILED(rc)) return rc;
3815
3816 StorageControllerType_T ctrlType;
3817 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3818 if (FAILED(rc))
3819 return setError(E_FAIL,
3820 tr("Could not get type of controller '%s'"),
3821 aName.c_str());
3822
3823 bool fSilent = false;
3824 Utf8Str strReconfig;
3825
3826 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3827 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3828 if ( mData->mMachineState == MachineState_Paused
3829 && strReconfig == "1")
3830 fSilent = true;
3831
3832 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3833 bool fHotplug = false;
3834 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3835 fHotplug = true;
3836
3837 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3838 return setError(VBOX_E_INVALID_VM_STATE,
3839 tr("Controller '%s' does not support hotplugging"),
3840 aName.c_str());
3841
3842 // check that the port and device are not out of range
3843 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3844 if (FAILED(rc)) return rc;
3845
3846 /* check if the device slot is already busy */
3847 MediumAttachment *pAttachTemp;
3848 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3849 Bstr(aName).raw(),
3850 aControllerPort,
3851 aDevice)))
3852 {
3853 Medium *pMedium = pAttachTemp->i_getMedium();
3854 if (pMedium)
3855 {
3856 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3857 return setError(VBOX_E_OBJECT_IN_USE,
3858 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3859 pMedium->i_getLocationFull().c_str(),
3860 aControllerPort,
3861 aDevice,
3862 aName.c_str());
3863 }
3864 else
3865 return setError(VBOX_E_OBJECT_IN_USE,
3866 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3867 aControllerPort, aDevice, aName.c_str());
3868 }
3869
3870 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3871 if (aMedium && medium.isNull())
3872 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3873
3874 AutoCaller mediumCaller(medium);
3875 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3876
3877 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3878
3879 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3880 && !medium.isNull()
3881 )
3882 return setError(VBOX_E_OBJECT_IN_USE,
3883 tr("Medium '%s' is already attached to this virtual machine"),
3884 medium->i_getLocationFull().c_str());
3885
3886 if (!medium.isNull())
3887 {
3888 MediumType_T mtype = medium->i_getType();
3889 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3890 // For DVDs it's not written to the config file, so needs no global config
3891 // version bump. For floppies it's a new attribute "type", which is ignored
3892 // by older VirtualBox version, so needs no global config version bump either.
3893 // For hard disks this type is not accepted.
3894 if (mtype == MediumType_MultiAttach)
3895 {
3896 // This type is new with VirtualBox 4.0 and therefore requires settings
3897 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3898 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3899 // two reasons: The medium type is a property of the media registry tree, which
3900 // can reside in the global config file (for pre-4.0 media); we would therefore
3901 // possibly need to bump the global config version. We don't want to do that though
3902 // because that might make downgrading to pre-4.0 impossible.
3903 // As a result, we can only use these two new types if the medium is NOT in the
3904 // global registry:
3905 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3906 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3907 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3908 )
3909 return setError(VBOX_E_INVALID_OBJECT_STATE,
3910 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3911 "to machines that were created with VirtualBox 4.0 or later"),
3912 medium->i_getLocationFull().c_str());
3913 }
3914 }
3915
3916 bool fIndirect = false;
3917 if (!medium.isNull())
3918 fIndirect = medium->i_isReadOnly();
3919 bool associate = true;
3920
3921 do
3922 {
3923 if ( aType == DeviceType_HardDisk
3924 && mMediaData.isBackedUp())
3925 {
3926 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3927
3928 /* check if the medium was attached to the VM before we started
3929 * changing attachments in which case the attachment just needs to
3930 * be restored */
3931 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3932 {
3933 AssertReturn(!fIndirect, E_FAIL);
3934
3935 /* see if it's the same bus/channel/device */
3936 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3937 {
3938 /* the simplest case: restore the whole attachment
3939 * and return, nothing else to do */
3940 mMediaData->mAttachments.push_back(pAttachTemp);
3941
3942 /* Reattach the medium to the VM. */
3943 if (fHotplug || fSilent)
3944 {
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 MediumLockList *pMediumLockList(new MediumLockList());
3950
3951 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3952 medium /* pToLockWrite */,
3953 false /* fMediumLockWriteAll */,
3954 NULL,
3955 *pMediumLockList);
3956 alock.acquire();
3957 if (FAILED(rc))
3958 delete pMediumLockList;
3959 else
3960 {
3961 mData->mSession.mLockedMedia.Unlock();
3962 alock.release();
3963 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3964 mData->mSession.mLockedMedia.Lock();
3965 alock.acquire();
3966 }
3967 alock.release();
3968
3969 if (SUCCEEDED(rc))
3970 {
3971 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3972 /* Remove lock list in case of error. */
3973 if (FAILED(rc))
3974 {
3975 mData->mSession.mLockedMedia.Unlock();
3976 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3977 mData->mSession.mLockedMedia.Lock();
3978 }
3979 }
3980 }
3981
3982 return S_OK;
3983 }
3984
3985 /* bus/channel/device differ; we need a new attachment object,
3986 * but don't try to associate it again */
3987 associate = false;
3988 break;
3989 }
3990 }
3991
3992 /* go further only if the attachment is to be indirect */
3993 if (!fIndirect)
3994 break;
3995
3996 /* perform the so called smart attachment logic for indirect
3997 * attachments. Note that smart attachment is only applicable to base
3998 * hard disks. */
3999
4000 if (medium->i_getParent().isNull())
4001 {
4002 /* first, investigate the backup copy of the current hard disk
4003 * attachments to make it possible to re-attach existing diffs to
4004 * another device slot w/o losing their contents */
4005 if (mMediaData.isBackedUp())
4006 {
4007 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4008
4009 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4010 uint32_t foundLevel = 0;
4011
4012 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4013 {
4014 uint32_t level = 0;
4015 MediumAttachment *pAttach = *it;
4016 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4017 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4018 if (pMedium.isNull())
4019 continue;
4020
4021 if (pMedium->i_getBase(&level) == medium)
4022 {
4023 /* skip the hard disk if its currently attached (we
4024 * cannot attach the same hard disk twice) */
4025 if (i_findAttachment(mMediaData->mAttachments,
4026 pMedium))
4027 continue;
4028
4029 /* matched device, channel and bus (i.e. attached to the
4030 * same place) will win and immediately stop the search;
4031 * otherwise the attachment that has the youngest
4032 * descendant of medium will be used
4033 */
4034 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4035 {
4036 /* the simplest case: restore the whole attachment
4037 * and return, nothing else to do */
4038 mMediaData->mAttachments.push_back(*it);
4039
4040 /* Reattach the medium to the VM. */
4041 if (fHotplug || fSilent)
4042 {
4043 mediumLock.release();
4044 treeLock.release();
4045 alock.release();
4046
4047 MediumLockList *pMediumLockList(new MediumLockList());
4048
4049 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4050 medium /* pToLockWrite */,
4051 false /* fMediumLockWriteAll */,
4052 NULL,
4053 *pMediumLockList);
4054 alock.acquire();
4055 if (FAILED(rc))
4056 delete pMediumLockList;
4057 else
4058 {
4059 mData->mSession.mLockedMedia.Unlock();
4060 alock.release();
4061 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4062 mData->mSession.mLockedMedia.Lock();
4063 alock.acquire();
4064 }
4065 alock.release();
4066
4067 if (SUCCEEDED(rc))
4068 {
4069 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4070 /* Remove lock list in case of error. */
4071 if (FAILED(rc))
4072 {
4073 mData->mSession.mLockedMedia.Unlock();
4074 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4075 mData->mSession.mLockedMedia.Lock();
4076 }
4077 }
4078 }
4079
4080 return S_OK;
4081 }
4082 else if ( foundIt == oldAtts.end()
4083 || level > foundLevel /* prefer younger */
4084 )
4085 {
4086 foundIt = it;
4087 foundLevel = level;
4088 }
4089 }
4090 }
4091
4092 if (foundIt != oldAtts.end())
4093 {
4094 /* use the previously attached hard disk */
4095 medium = (*foundIt)->i_getMedium();
4096 mediumCaller.attach(medium);
4097 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4098 mediumLock.attach(medium);
4099 /* not implicit, doesn't require association with this VM */
4100 fIndirect = false;
4101 associate = false;
4102 /* go right to the MediumAttachment creation */
4103 break;
4104 }
4105 }
4106
4107 /* must give up the medium lock and medium tree lock as below we
4108 * go over snapshots, which needs a lock with higher lock order. */
4109 mediumLock.release();
4110 treeLock.release();
4111
4112 /* then, search through snapshots for the best diff in the given
4113 * hard disk's chain to base the new diff on */
4114
4115 ComObjPtr<Medium> base;
4116 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4117 while (snap)
4118 {
4119 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4120
4121 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4122
4123 MediumAttachment *pAttachFound = NULL;
4124 uint32_t foundLevel = 0;
4125
4126 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4127 {
4128 MediumAttachment *pAttach = *it;
4129 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4130 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4131 if (pMedium.isNull())
4132 continue;
4133
4134 uint32_t level = 0;
4135 if (pMedium->i_getBase(&level) == medium)
4136 {
4137 /* matched device, channel and bus (i.e. attached to the
4138 * same place) will win and immediately stop the search;
4139 * otherwise the attachment that has the youngest
4140 * descendant of medium will be used
4141 */
4142 if ( pAttach->i_getDevice() == aDevice
4143 && pAttach->i_getPort() == aControllerPort
4144 && pAttach->i_getControllerName() == aName
4145 )
4146 {
4147 pAttachFound = pAttach;
4148 break;
4149 }
4150 else if ( !pAttachFound
4151 || level > foundLevel /* prefer younger */
4152 )
4153 {
4154 pAttachFound = pAttach;
4155 foundLevel = level;
4156 }
4157 }
4158 }
4159
4160 if (pAttachFound)
4161 {
4162 base = pAttachFound->i_getMedium();
4163 break;
4164 }
4165
4166 snap = snap->i_getParent();
4167 }
4168
4169 /* re-lock medium tree and the medium, as we need it below */
4170 treeLock.acquire();
4171 mediumLock.acquire();
4172
4173 /* found a suitable diff, use it as a base */
4174 if (!base.isNull())
4175 {
4176 medium = base;
4177 mediumCaller.attach(medium);
4178 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4179 mediumLock.attach(medium);
4180 }
4181 }
4182
4183 Utf8Str strFullSnapshotFolder;
4184 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4185
4186 ComObjPtr<Medium> diff;
4187 diff.createObject();
4188 // store this diff in the same registry as the parent
4189 Guid uuidRegistryParent;
4190 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4191 {
4192 // parent image has no registry: this can happen if we're attaching a new immutable
4193 // image that has not yet been attached (medium then points to the base and we're
4194 // creating the diff image for the immutable, and the parent is not yet registered);
4195 // put the parent in the machine registry then
4196 mediumLock.release();
4197 treeLock.release();
4198 alock.release();
4199 i_addMediumToRegistry(medium);
4200 alock.acquire();
4201 treeLock.acquire();
4202 mediumLock.acquire();
4203 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4204 }
4205 rc = diff->init(mParent,
4206 medium->i_getPreferredDiffFormat(),
4207 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4208 uuidRegistryParent,
4209 DeviceType_HardDisk);
4210 if (FAILED(rc)) return rc;
4211
4212 /* Apply the normal locking logic to the entire chain. */
4213 MediumLockList *pMediumLockList(new MediumLockList());
4214 mediumLock.release();
4215 treeLock.release();
4216 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4217 diff /* pToLockWrite */,
4218 false /* fMediumLockWriteAll */,
4219 medium,
4220 *pMediumLockList);
4221 treeLock.acquire();
4222 mediumLock.acquire();
4223 if (SUCCEEDED(rc))
4224 {
4225 mediumLock.release();
4226 treeLock.release();
4227 rc = pMediumLockList->Lock();
4228 treeLock.acquire();
4229 mediumLock.acquire();
4230 if (FAILED(rc))
4231 setError(rc,
4232 tr("Could not lock medium when creating diff '%s'"),
4233 diff->i_getLocationFull().c_str());
4234 else
4235 {
4236 /* will release the lock before the potentially lengthy
4237 * operation, so protect with the special state */
4238 MachineState_T oldState = mData->mMachineState;
4239 i_setMachineState(MachineState_SettingUp);
4240
4241 mediumLock.release();
4242 treeLock.release();
4243 alock.release();
4244
4245 rc = medium->i_createDiffStorage(diff,
4246 medium->i_getPreferredDiffVariant(),
4247 pMediumLockList,
4248 NULL /* aProgress */,
4249 true /* aWait */);
4250
4251 alock.acquire();
4252 treeLock.acquire();
4253 mediumLock.acquire();
4254
4255 i_setMachineState(oldState);
4256 }
4257 }
4258
4259 /* Unlock the media and free the associated memory. */
4260 delete pMediumLockList;
4261
4262 if (FAILED(rc)) return rc;
4263
4264 /* use the created diff for the actual attachment */
4265 medium = diff;
4266 mediumCaller.attach(medium);
4267 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4268 mediumLock.attach(medium);
4269 }
4270 while (0);
4271
4272 ComObjPtr<MediumAttachment> attachment;
4273 attachment.createObject();
4274 rc = attachment->init(this,
4275 medium,
4276 aName,
4277 aControllerPort,
4278 aDevice,
4279 aType,
4280 fIndirect,
4281 false /* fPassthrough */,
4282 false /* fTempEject */,
4283 false /* fNonRotational */,
4284 false /* fDiscard */,
4285 fHotplug /* fHotPluggable */,
4286 Utf8Str::Empty);
4287 if (FAILED(rc)) return rc;
4288
4289 if (associate && !medium.isNull())
4290 {
4291 // as the last step, associate the medium to the VM
4292 rc = medium->i_addBackReference(mData->mUuid);
4293 // here we can fail because of Deleting, or being in process of creating a Diff
4294 if (FAILED(rc)) return rc;
4295
4296 mediumLock.release();
4297 treeLock.release();
4298 alock.release();
4299 i_addMediumToRegistry(medium);
4300 alock.acquire();
4301 treeLock.acquire();
4302 mediumLock.acquire();
4303 }
4304
4305 /* success: finally remember the attachment */
4306 i_setModified(IsModified_Storage);
4307 mMediaData.backup();
4308 mMediaData->mAttachments.push_back(attachment);
4309
4310 mediumLock.release();
4311 treeLock.release();
4312 alock.release();
4313
4314 if (fHotplug || fSilent)
4315 {
4316 if (!medium.isNull())
4317 {
4318 MediumLockList *pMediumLockList(new MediumLockList());
4319
4320 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4321 medium /* pToLockWrite */,
4322 false /* fMediumLockWriteAll */,
4323 NULL,
4324 *pMediumLockList);
4325 alock.acquire();
4326 if (FAILED(rc))
4327 delete pMediumLockList;
4328 else
4329 {
4330 mData->mSession.mLockedMedia.Unlock();
4331 alock.release();
4332 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4333 mData->mSession.mLockedMedia.Lock();
4334 alock.acquire();
4335 }
4336 alock.release();
4337 }
4338
4339 if (SUCCEEDED(rc))
4340 {
4341 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4342 /* Remove lock list in case of error. */
4343 if (FAILED(rc))
4344 {
4345 mData->mSession.mLockedMedia.Unlock();
4346 mData->mSession.mLockedMedia.Remove(attachment);
4347 mData->mSession.mLockedMedia.Lock();
4348 }
4349 }
4350 }
4351
4352 /* Save modified registries, but skip this machine as it's the caller's
4353 * job to save its settings like all other settings changes. */
4354 mParent->i_unmarkRegistryModified(i_getId());
4355 mParent->i_saveModifiedRegistries();
4356
4357 return rc;
4358}
4359
4360HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4361 LONG aDevice)
4362{
4363 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4364 aName.c_str(), aControllerPort, aDevice));
4365
4366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4367
4368 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4369 if (FAILED(rc)) return rc;
4370
4371 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4372
4373 /* Check for an existing controller. */
4374 ComObjPtr<StorageController> ctl;
4375 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4376 if (FAILED(rc)) return rc;
4377
4378 StorageControllerType_T ctrlType;
4379 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4380 if (FAILED(rc))
4381 return setError(E_FAIL,
4382 tr("Could not get type of controller '%s'"),
4383 aName.c_str());
4384
4385 bool fSilent = false;
4386 Utf8Str strReconfig;
4387
4388 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4389 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4390 if ( mData->mMachineState == MachineState_Paused
4391 && strReconfig == "1")
4392 fSilent = true;
4393
4394 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4395 bool fHotplug = false;
4396 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4397 fHotplug = true;
4398
4399 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4400 return setError(VBOX_E_INVALID_VM_STATE,
4401 tr("Controller '%s' does not support hotplugging"),
4402 aName.c_str());
4403
4404 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4405 Bstr(aName).raw(),
4406 aControllerPort,
4407 aDevice);
4408 if (!pAttach)
4409 return setError(VBOX_E_OBJECT_NOT_FOUND,
4410 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4411 aDevice, aControllerPort, aName.c_str());
4412
4413 if (fHotplug && !pAttach->i_getHotPluggable())
4414 return setError(VBOX_E_NOT_SUPPORTED,
4415 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418 /*
4419 * The VM has to detach the device before we delete any implicit diffs.
4420 * If this fails we can roll back without loosing data.
4421 */
4422 if (fHotplug || fSilent)
4423 {
4424 alock.release();
4425 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4426 alock.acquire();
4427 }
4428 if (FAILED(rc)) return rc;
4429
4430 /* If we are here everything went well and we can delete the implicit now. */
4431 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4432
4433 alock.release();
4434
4435 /* Save modified registries, but skip this machine as it's the caller's
4436 * job to save its settings like all other settings changes. */
4437 mParent->i_unmarkRegistryModified(i_getId());
4438 mParent->i_saveModifiedRegistries();
4439
4440 return rc;
4441}
4442
4443HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4444 LONG aDevice, BOOL aPassthrough)
4445{
4446 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4447 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4448
4449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4450
4451 HRESULT rc = i_checkStateDependency(MutableStateDep);
4452 if (FAILED(rc)) return rc;
4453
4454 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4455
4456 if (Global::IsOnlineOrTransient(mData->mMachineState))
4457 return setError(VBOX_E_INVALID_VM_STATE,
4458 tr("Invalid machine state: %s"),
4459 Global::stringifyMachineState(mData->mMachineState));
4460
4461 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4462 Bstr(aName).raw(),
4463 aControllerPort,
4464 aDevice);
4465 if (!pAttach)
4466 return setError(VBOX_E_OBJECT_NOT_FOUND,
4467 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4468 aDevice, aControllerPort, aName.c_str());
4469
4470
4471 i_setModified(IsModified_Storage);
4472 mMediaData.backup();
4473
4474 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4475
4476 if (pAttach->i_getType() != DeviceType_DVD)
4477 return setError(E_INVALIDARG,
4478 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4479 aDevice, aControllerPort, aName.c_str());
4480 pAttach->i_updatePassthrough(!!aPassthrough);
4481
4482 return S_OK;
4483}
4484
4485HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4486 LONG aDevice, BOOL aTemporaryEject)
4487{
4488
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4490 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4491
4492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4493
4494 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4495 if (FAILED(rc)) return rc;
4496
4497 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4498 Bstr(aName).raw(),
4499 aControllerPort,
4500 aDevice);
4501 if (!pAttach)
4502 return setError(VBOX_E_OBJECT_NOT_FOUND,
4503 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4504 aDevice, aControllerPort, aName.c_str());
4505
4506
4507 i_setModified(IsModified_Storage);
4508 mMediaData.backup();
4509
4510 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4511
4512 if (pAttach->i_getType() != DeviceType_DVD)
4513 return setError(E_INVALIDARG,
4514 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4515 aDevice, aControllerPort, aName.c_str());
4516 pAttach->i_updateTempEject(!!aTemporaryEject);
4517
4518 return S_OK;
4519}
4520
4521HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4522 LONG aDevice, BOOL aNonRotational)
4523{
4524
4525 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4526 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4527
4528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4529
4530 HRESULT rc = i_checkStateDependency(MutableStateDep);
4531 if (FAILED(rc)) return rc;
4532
4533 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4534
4535 if (Global::IsOnlineOrTransient(mData->mMachineState))
4536 return setError(VBOX_E_INVALID_VM_STATE,
4537 tr("Invalid machine state: %s"),
4538 Global::stringifyMachineState(mData->mMachineState));
4539
4540 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4541 Bstr(aName).raw(),
4542 aControllerPort,
4543 aDevice);
4544 if (!pAttach)
4545 return setError(VBOX_E_OBJECT_NOT_FOUND,
4546 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4547 aDevice, aControllerPort, aName.c_str());
4548
4549
4550 i_setModified(IsModified_Storage);
4551 mMediaData.backup();
4552
4553 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4554
4555 if (pAttach->i_getType() != DeviceType_HardDisk)
4556 return setError(E_INVALIDARG,
4557 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"),
4558 aDevice, aControllerPort, aName.c_str());
4559 pAttach->i_updateNonRotational(!!aNonRotational);
4560
4561 return S_OK;
4562}
4563
4564HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4565 LONG aDevice, BOOL aDiscard)
4566{
4567
4568 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4569 aName.c_str(), aControllerPort, aDevice, aDiscard));
4570
4571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4572
4573 HRESULT rc = i_checkStateDependency(MutableStateDep);
4574 if (FAILED(rc)) return rc;
4575
4576 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4577
4578 if (Global::IsOnlineOrTransient(mData->mMachineState))
4579 return setError(VBOX_E_INVALID_VM_STATE,
4580 tr("Invalid machine state: %s"),
4581 Global::stringifyMachineState(mData->mMachineState));
4582
4583 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4584 Bstr(aName).raw(),
4585 aControllerPort,
4586 aDevice);
4587 if (!pAttach)
4588 return setError(VBOX_E_OBJECT_NOT_FOUND,
4589 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4590 aDevice, aControllerPort, aName.c_str());
4591
4592
4593 i_setModified(IsModified_Storage);
4594 mMediaData.backup();
4595
4596 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4597
4598 if (pAttach->i_getType() != DeviceType_HardDisk)
4599 return setError(E_INVALIDARG,
4600 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"),
4601 aDevice, aControllerPort, aName.c_str());
4602 pAttach->i_updateDiscard(!!aDiscard);
4603
4604 return S_OK;
4605}
4606
4607HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4608 LONG aDevice, BOOL aHotPluggable)
4609{
4610 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4611 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4612
4613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4614
4615 HRESULT rc = i_checkStateDependency(MutableStateDep);
4616 if (FAILED(rc)) return rc;
4617
4618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4619
4620 if (Global::IsOnlineOrTransient(mData->mMachineState))
4621 return setError(VBOX_E_INVALID_VM_STATE,
4622 tr("Invalid machine state: %s"),
4623 Global::stringifyMachineState(mData->mMachineState));
4624
4625 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4626 Bstr(aName).raw(),
4627 aControllerPort,
4628 aDevice);
4629 if (!pAttach)
4630 return setError(VBOX_E_OBJECT_NOT_FOUND,
4631 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4632 aDevice, aControllerPort, aName.c_str());
4633
4634 /* Check for an existing controller. */
4635 ComObjPtr<StorageController> ctl;
4636 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4637 if (FAILED(rc)) return rc;
4638
4639 StorageControllerType_T ctrlType;
4640 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4641 if (FAILED(rc))
4642 return setError(E_FAIL,
4643 tr("Could not get type of controller '%s'"),
4644 aName.c_str());
4645
4646 if (!i_isControllerHotplugCapable(ctrlType))
4647 return setError(VBOX_E_NOT_SUPPORTED,
4648 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4649 aName.c_str());
4650
4651 i_setModified(IsModified_Storage);
4652 mMediaData.backup();
4653
4654 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4655
4656 if (pAttach->i_getType() == DeviceType_Floppy)
4657 return setError(E_INVALIDARG,
4658 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"),
4659 aDevice, aControllerPort, aName.c_str());
4660 pAttach->i_updateHotPluggable(!!aHotPluggable);
4661
4662 return S_OK;
4663}
4664
4665HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4666 LONG aDevice)
4667{
4668 int rc = S_OK;
4669 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4670 aName.c_str(), aControllerPort, aDevice));
4671
4672 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4673
4674 return rc;
4675}
4676
4677HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4678 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4679{
4680 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4681 aName.c_str(), aControllerPort, aDevice));
4682
4683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4684
4685 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4686 if (FAILED(rc)) return rc;
4687
4688 if (Global::IsOnlineOrTransient(mData->mMachineState))
4689 return setError(VBOX_E_INVALID_VM_STATE,
4690 tr("Invalid machine state: %s"),
4691 Global::stringifyMachineState(mData->mMachineState));
4692
4693 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4694 Bstr(aName).raw(),
4695 aControllerPort,
4696 aDevice);
4697 if (!pAttach)
4698 return setError(VBOX_E_OBJECT_NOT_FOUND,
4699 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4700 aDevice, aControllerPort, aName.c_str());
4701
4702
4703 i_setModified(IsModified_Storage);
4704 mMediaData.backup();
4705
4706 IBandwidthGroup *iB = aBandwidthGroup;
4707 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4708 if (aBandwidthGroup && group.isNull())
4709 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4710
4711 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4712
4713 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4714 if (strBandwidthGroupOld.isNotEmpty())
4715 {
4716 /* Get the bandwidth group object and release it - this must not fail. */
4717 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4718 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4719 Assert(SUCCEEDED(rc));
4720
4721 pBandwidthGroupOld->i_release();
4722 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4723 }
4724
4725 if (!group.isNull())
4726 {
4727 group->i_reference();
4728 pAttach->i_updateBandwidthGroup(group->i_getName());
4729 }
4730
4731 return S_OK;
4732}
4733
4734HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4735 LONG aControllerPort,
4736 LONG aDevice,
4737 DeviceType_T aType)
4738{
4739 HRESULT rc = S_OK;
4740
4741 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4742 aName.c_str(), aControllerPort, aDevice, aType));
4743
4744 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4745
4746 return rc;
4747}
4748
4749
4750HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4751 LONG aControllerPort,
4752 LONG aDevice,
4753 BOOL aForce)
4754{
4755 int rc = S_OK;
4756 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4757 aName.c_str(), aControllerPort, aForce));
4758
4759 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4760
4761 return rc;
4762}
4763
4764HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4765 LONG aControllerPort,
4766 LONG aDevice,
4767 const ComPtr<IMedium> &aMedium,
4768 BOOL aForce)
4769{
4770 int rc = S_OK;
4771 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4772 aName.c_str(), aControllerPort, aDevice, aForce));
4773
4774 // request the host lock first, since might be calling Host methods for getting host drives;
4775 // next, protect the media tree all the while we're in here, as well as our member variables
4776 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4777 this->lockHandle(),
4778 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4779
4780 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4781 Bstr(aName).raw(),
4782 aControllerPort,
4783 aDevice);
4784 if (pAttach.isNull())
4785 return setError(VBOX_E_OBJECT_NOT_FOUND,
4786 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4787 aDevice, aControllerPort, aName.c_str());
4788
4789 /* Remember previously mounted medium. The medium before taking the
4790 * backup is not necessarily the same thing. */
4791 ComObjPtr<Medium> oldmedium;
4792 oldmedium = pAttach->i_getMedium();
4793
4794 IMedium *iM = aMedium;
4795 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4796 if (aMedium && pMedium.isNull())
4797 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4798
4799 AutoCaller mediumCaller(pMedium);
4800 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4801
4802 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4803 if (pMedium)
4804 {
4805 DeviceType_T mediumType = pAttach->i_getType();
4806 switch (mediumType)
4807 {
4808 case DeviceType_DVD:
4809 case DeviceType_Floppy:
4810 break;
4811
4812 default:
4813 return setError(VBOX_E_INVALID_OBJECT_STATE,
4814 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4815 aControllerPort,
4816 aDevice,
4817 aName.c_str());
4818 }
4819 }
4820
4821 i_setModified(IsModified_Storage);
4822 mMediaData.backup();
4823
4824 {
4825 // The backup operation makes the pAttach reference point to the
4826 // old settings. Re-get the correct reference.
4827 pAttach = i_findAttachment(mMediaData->mAttachments,
4828 Bstr(aName).raw(),
4829 aControllerPort,
4830 aDevice);
4831 if (!oldmedium.isNull())
4832 oldmedium->i_removeBackReference(mData->mUuid);
4833 if (!pMedium.isNull())
4834 {
4835 pMedium->i_addBackReference(mData->mUuid);
4836
4837 mediumLock.release();
4838 multiLock.release();
4839 i_addMediumToRegistry(pMedium);
4840 multiLock.acquire();
4841 mediumLock.acquire();
4842 }
4843
4844 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4845 pAttach->i_updateMedium(pMedium);
4846 }
4847
4848 i_setModified(IsModified_Storage);
4849
4850 mediumLock.release();
4851 multiLock.release();
4852 rc = i_onMediumChange(pAttach, aForce);
4853 multiLock.acquire();
4854 mediumLock.acquire();
4855
4856 /* On error roll back this change only. */
4857 if (FAILED(rc))
4858 {
4859 if (!pMedium.isNull())
4860 pMedium->i_removeBackReference(mData->mUuid);
4861 pAttach = i_findAttachment(mMediaData->mAttachments,
4862 Bstr(aName).raw(),
4863 aControllerPort,
4864 aDevice);
4865 /* If the attachment is gone in the meantime, bail out. */
4866 if (pAttach.isNull())
4867 return rc;
4868 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4869 if (!oldmedium.isNull())
4870 oldmedium->i_addBackReference(mData->mUuid);
4871 pAttach->i_updateMedium(oldmedium);
4872 }
4873
4874 mediumLock.release();
4875 multiLock.release();
4876
4877 /* Save modified registries, but skip this machine as it's the caller's
4878 * job to save its settings like all other settings changes. */
4879 mParent->i_unmarkRegistryModified(i_getId());
4880 mParent->i_saveModifiedRegistries();
4881
4882 return rc;
4883}
4884HRESULT Machine::getMedium(const com::Utf8Str &aName,
4885 LONG aControllerPort,
4886 LONG aDevice,
4887 ComPtr<IMedium> &aMedium)
4888{
4889 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4890 aName.c_str(), aControllerPort, aDevice));
4891
4892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4893
4894 aMedium = NULL;
4895
4896 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4897 Bstr(aName).raw(),
4898 aControllerPort,
4899 aDevice);
4900 if (pAttach.isNull())
4901 return setError(VBOX_E_OBJECT_NOT_FOUND,
4902 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4903 aDevice, aControllerPort, aName.c_str());
4904
4905 aMedium = pAttach->i_getMedium();
4906
4907 return S_OK;
4908}
4909
4910HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4911{
4912
4913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4914
4915 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4916
4917 return S_OK;
4918}
4919
4920HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4921{
4922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4923
4924 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4925
4926 return S_OK;
4927}
4928
4929HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4930{
4931 /* Do not assert if slot is out of range, just return the advertised
4932 status. testdriver/vbox.py triggers this in logVmInfo. */
4933 if (aSlot >= mNetworkAdapters.size())
4934 return setError(E_INVALIDARG,
4935 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4936 aSlot, mNetworkAdapters.size());
4937
4938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4939
4940 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4941
4942 return S_OK;
4943}
4944
4945HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4946{
4947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4948
4949 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4950 size_t i = 0;
4951 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4952 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4953 ++it, ++i)
4954 aKeys[i] = it->first;
4955
4956 return S_OK;
4957}
4958
4959 /**
4960 * @note Locks this object for reading.
4961 */
4962HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4963 com::Utf8Str &aValue)
4964{
4965 /* start with nothing found */
4966 aValue = "";
4967
4968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4969
4970 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4971 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4972 // found:
4973 aValue = it->second; // source is a Utf8Str
4974
4975 /* return the result to caller (may be empty) */
4976 return S_OK;
4977}
4978
4979 /**
4980 * @note Locks mParent for writing + this object for writing.
4981 */
4982HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4983{
4984 Utf8Str strOldValue; // empty
4985
4986 // locking note: we only hold the read lock briefly to look up the old value,
4987 // then release it and call the onExtraCanChange callbacks. There is a small
4988 // chance of a race insofar as the callback might be called twice if two callers
4989 // change the same key at the same time, but that's a much better solution
4990 // than the deadlock we had here before. The actual changing of the extradata
4991 // is then performed under the write lock and race-free.
4992
4993 // look up the old value first; if nothing has changed then we need not do anything
4994 {
4995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4996
4997 // For snapshots don't even think about allowing changes, extradata
4998 // is global for a machine, so there is nothing snapshot specific.
4999 if (i_isSnapshotMachine())
5000 return setError(VBOX_E_INVALID_VM_STATE,
5001 tr("Cannot set extradata for a snapshot"));
5002
5003 // check if the right IMachine instance is used
5004 if (mData->mRegistered && !i_isSessionMachine())
5005 return setError(VBOX_E_INVALID_VM_STATE,
5006 tr("Cannot set extradata for an immutable machine"));
5007
5008 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5009 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5010 strOldValue = it->second;
5011 }
5012
5013 bool fChanged;
5014 if ((fChanged = (strOldValue != aValue)))
5015 {
5016 // ask for permission from all listeners outside the locks;
5017 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5018 // lock to copy the list of callbacks to invoke
5019 Bstr error;
5020 Bstr bstrValue(aValue);
5021
5022 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5023 {
5024 const char *sep = error.isEmpty() ? "" : ": ";
5025 CBSTR err = error.raw();
5026 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5027 return setError(E_ACCESSDENIED,
5028 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5029 aKey.c_str(),
5030 aValue.c_str(),
5031 sep,
5032 err);
5033 }
5034
5035 // data is changing and change not vetoed: then write it out under the lock
5036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5037
5038 if (aValue.isEmpty())
5039 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5040 else
5041 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5042 // creates a new key if needed
5043
5044 bool fNeedsGlobalSaveSettings = false;
5045 // This saving of settings is tricky: there is no "old state" for the
5046 // extradata items at all (unlike all other settings), so the old/new
5047 // settings comparison would give a wrong result!
5048 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5049
5050 if (fNeedsGlobalSaveSettings)
5051 {
5052 // save the global settings; for that we should hold only the VirtualBox lock
5053 alock.release();
5054 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5055 mParent->i_saveSettings();
5056 }
5057 }
5058
5059 // fire notification outside the lock
5060 if (fChanged)
5061 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5062
5063 return S_OK;
5064}
5065
5066HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5067{
5068 aProgress = NULL;
5069 NOREF(aSettingsFilePath);
5070 ReturnComNotImplemented();
5071}
5072
5073HRESULT Machine::saveSettings()
5074{
5075 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5076
5077 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5078 if (FAILED(rc)) return rc;
5079
5080 /* the settings file path may never be null */
5081 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5082
5083 /* save all VM data excluding snapshots */
5084 bool fNeedsGlobalSaveSettings = false;
5085 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5086 mlock.release();
5087
5088 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5089 {
5090 // save the global settings; for that we should hold only the VirtualBox lock
5091 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5092 rc = mParent->i_saveSettings();
5093 }
5094
5095 return rc;
5096}
5097
5098
5099HRESULT Machine::discardSettings()
5100{
5101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5102
5103 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5104 if (FAILED(rc)) return rc;
5105
5106 /*
5107 * during this rollback, the session will be notified if data has
5108 * been actually changed
5109 */
5110 i_rollback(true /* aNotify */);
5111
5112 return S_OK;
5113}
5114
5115/** @note Locks objects! */
5116HRESULT Machine::unregister(AutoCaller &autoCaller,
5117 CleanupMode_T aCleanupMode,
5118 std::vector<ComPtr<IMedium> > &aMedia)
5119{
5120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5121
5122 Guid id(i_getId());
5123
5124 if (mData->mSession.mState != SessionState_Unlocked)
5125 return setError(VBOX_E_INVALID_OBJECT_STATE,
5126 tr("Cannot unregister the machine '%s' while it is locked"),
5127 mUserData->s.strName.c_str());
5128
5129 // wait for state dependents to drop to zero
5130 i_ensureNoStateDependencies();
5131
5132 if (!mData->mAccessible)
5133 {
5134 // inaccessible maschines can only be unregistered; uninitialize ourselves
5135 // here because currently there may be no unregistered that are inaccessible
5136 // (this state combination is not supported). Note releasing the caller and
5137 // leaving the lock before calling uninit()
5138 alock.release();
5139 autoCaller.release();
5140
5141 uninit();
5142
5143 mParent->i_unregisterMachine(this, id);
5144 // calls VirtualBox::i_saveSettings()
5145
5146 return S_OK;
5147 }
5148
5149 HRESULT rc = S_OK;
5150
5151 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5152 // discard saved state
5153 if (mData->mMachineState == MachineState_Saved)
5154 {
5155 // add the saved state file to the list of files the caller should delete
5156 Assert(!mSSData->strStateFilePath.isEmpty());
5157 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5158
5159 mSSData->strStateFilePath.setNull();
5160
5161 // unconditionally set the machine state to powered off, we now
5162 // know no session has locked the machine
5163 mData->mMachineState = MachineState_PoweredOff;
5164 }
5165
5166 size_t cSnapshots = 0;
5167 if (mData->mFirstSnapshot)
5168 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5169 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5170 // fail now before we start detaching media
5171 return setError(VBOX_E_INVALID_OBJECT_STATE,
5172 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5173 mUserData->s.strName.c_str(), cSnapshots);
5174
5175 // This list collects the medium objects from all medium attachments
5176 // which we will detach from the machine and its snapshots, in a specific
5177 // order which allows for closing all media without getting "media in use"
5178 // errors, simply by going through the list from the front to the back:
5179 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5180 // and must be closed before the parent media from the snapshots, or closing the parents
5181 // will fail because they still have children);
5182 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5183 // the root ("first") snapshot of the machine.
5184 MediaList llMedia;
5185
5186 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5187 && mMediaData->mAttachments.size()
5188 )
5189 {
5190 // we have media attachments: detach them all and add the Medium objects to our list
5191 if (aCleanupMode != CleanupMode_UnregisterOnly)
5192 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5193 else
5194 return setError(VBOX_E_INVALID_OBJECT_STATE,
5195 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5196 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5197 }
5198
5199 if (cSnapshots)
5200 {
5201 // add the media from the medium attachments of the snapshots to llMedia
5202 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5203 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5204 // into the children first
5205
5206 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5207 MachineState_T oldState = mData->mMachineState;
5208 mData->mMachineState = MachineState_DeletingSnapshot;
5209
5210 // make a copy of the first snapshot so the refcount does not drop to 0
5211 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5212 // because of the AutoCaller voodoo)
5213 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5214
5215 // GO!
5216 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5217
5218 mData->mMachineState = oldState;
5219 }
5220
5221 if (FAILED(rc))
5222 {
5223 i_rollbackMedia();
5224 return rc;
5225 }
5226
5227 // commit all the media changes made above
5228 i_commitMedia();
5229
5230 mData->mRegistered = false;
5231
5232 // machine lock no longer needed
5233 alock.release();
5234
5235 // return media to caller
5236 size_t i = 0;
5237 aMedia.resize(llMedia.size());
5238 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5239 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5240
5241 mParent->i_unregisterMachine(this, id);
5242 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5243
5244 return S_OK;
5245}
5246
5247/**
5248 * Task record for deleting a machine config.
5249 */
5250struct Machine::DeleteConfigTask
5251 : public Machine::Task
5252{
5253 DeleteConfigTask(Machine *m,
5254 Progress *p,
5255 const Utf8Str &t,
5256 const RTCList<ComPtr<IMedium> > &llMediums,
5257 const StringsList &llFilesToDelete)
5258 : Task(m, p, t),
5259 m_llMediums(llMediums),
5260 m_llFilesToDelete(llFilesToDelete)
5261 {}
5262
5263 void handler()
5264 {
5265 m_pMachine->i_deleteConfigHandler(*this);
5266 }
5267
5268 RTCList<ComPtr<IMedium> > m_llMediums;
5269 StringsList m_llFilesToDelete;
5270};
5271
5272/**
5273 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5274 * SessionMachine::taskHandler().
5275 *
5276 * @note Locks this object for writing.
5277 *
5278 * @param task
5279 * @return
5280 */
5281void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5282{
5283 LogFlowThisFuncEnter();
5284
5285 AutoCaller autoCaller(this);
5286 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5287 if (FAILED(autoCaller.rc()))
5288 {
5289 /* we might have been uninitialized because the session was accidentally
5290 * closed by the client, so don't assert */
5291 HRESULT rc = setError(E_FAIL,
5292 tr("The session has been accidentally closed"));
5293 task.m_pProgress->i_notifyComplete(rc);
5294 LogFlowThisFuncLeave();
5295 return;
5296 }
5297
5298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5299
5300 HRESULT rc = S_OK;
5301
5302 try
5303 {
5304 ULONG uLogHistoryCount = 3;
5305 ComPtr<ISystemProperties> systemProperties;
5306 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5307 if (FAILED(rc)) throw rc;
5308
5309 if (!systemProperties.isNull())
5310 {
5311 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5312 if (FAILED(rc)) throw rc;
5313 }
5314
5315 MachineState_T oldState = mData->mMachineState;
5316 i_setMachineState(MachineState_SettingUp);
5317 alock.release();
5318 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5319 {
5320 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5321 {
5322 AutoCaller mac(pMedium);
5323 if (FAILED(mac.rc())) throw mac.rc();
5324 Utf8Str strLocation = pMedium->i_getLocationFull();
5325 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5326 if (FAILED(rc)) throw rc;
5327 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5328 }
5329 if (pMedium->i_isMediumFormatFile())
5330 {
5331 ComPtr<IProgress> pProgress2;
5332 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5333 if (FAILED(rc)) throw rc;
5334 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5335 if (FAILED(rc)) throw rc;
5336 /* Check the result of the asynchronous process. */
5337 LONG iRc;
5338 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5339 if (FAILED(rc)) throw rc;
5340 /* If the thread of the progress object has an error, then
5341 * retrieve the error info from there, or it'll be lost. */
5342 if (FAILED(iRc))
5343 throw setError(ProgressErrorInfo(pProgress2));
5344 }
5345
5346 /* Close the medium, deliberately without checking the return
5347 * code, and without leaving any trace in the error info, as
5348 * a failure here is a very minor issue, which shouldn't happen
5349 * as above we even managed to delete the medium. */
5350 {
5351 ErrorInfoKeeper eik;
5352 pMedium->Close();
5353 }
5354 }
5355 i_setMachineState(oldState);
5356 alock.acquire();
5357
5358 // delete the files pushed on the task list by Machine::Delete()
5359 // (this includes saved states of the machine and snapshots and
5360 // medium storage files from the IMedium list passed in, and the
5361 // machine XML file)
5362 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5363 while (it != task.m_llFilesToDelete.end())
5364 {
5365 const Utf8Str &strFile = *it;
5366 LogFunc(("Deleting file %s\n", strFile.c_str()));
5367 int vrc = RTFileDelete(strFile.c_str());
5368 if (RT_FAILURE(vrc))
5369 throw setError(VBOX_E_IPRT_ERROR,
5370 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5371
5372 ++it;
5373 if (it == task.m_llFilesToDelete.end())
5374 {
5375 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5376 if (FAILED(rc)) throw rc;
5377 break;
5378 }
5379
5380 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5381 if (FAILED(rc)) throw rc;
5382 }
5383
5384 /* delete the settings only when the file actually exists */
5385 if (mData->pMachineConfigFile->fileExists())
5386 {
5387 /* Delete any backup or uncommitted XML files. Ignore failures.
5388 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5389 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5390 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5391 RTFileDelete(otherXml.c_str());
5392 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5393 RTFileDelete(otherXml.c_str());
5394
5395 /* delete the Logs folder, nothing important should be left
5396 * there (we don't check for errors because the user might have
5397 * some private files there that we don't want to delete) */
5398 Utf8Str logFolder;
5399 getLogFolder(logFolder);
5400 Assert(logFolder.length());
5401 if (RTDirExists(logFolder.c_str()))
5402 {
5403 /* Delete all VBox.log[.N] files from the Logs folder
5404 * (this must be in sync with the rotation logic in
5405 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5406 * files that may have been created by the GUI. */
5407 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5408 logFolder.c_str(), RTPATH_DELIMITER);
5409 RTFileDelete(log.c_str());
5410 log = Utf8StrFmt("%s%cVBox.png",
5411 logFolder.c_str(), RTPATH_DELIMITER);
5412 RTFileDelete(log.c_str());
5413 for (int i = uLogHistoryCount; i > 0; i--)
5414 {
5415 log = Utf8StrFmt("%s%cVBox.log.%d",
5416 logFolder.c_str(), RTPATH_DELIMITER, i);
5417 RTFileDelete(log.c_str());
5418 log = Utf8StrFmt("%s%cVBox.png.%d",
5419 logFolder.c_str(), RTPATH_DELIMITER, i);
5420 RTFileDelete(log.c_str());
5421 }
5422#if defined(RT_OS_WINDOWS)
5423 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5424 RTFileDelete(log.c_str());
5425 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5426 RTFileDelete(log.c_str());
5427#endif
5428
5429 RTDirRemove(logFolder.c_str());
5430 }
5431
5432 /* delete the Snapshots folder, nothing important should be left
5433 * there (we don't check for errors because the user might have
5434 * some private files there that we don't want to delete) */
5435 Utf8Str strFullSnapshotFolder;
5436 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5437 Assert(!strFullSnapshotFolder.isEmpty());
5438 if (RTDirExists(strFullSnapshotFolder.c_str()))
5439 RTDirRemove(strFullSnapshotFolder.c_str());
5440
5441 // delete the directory that contains the settings file, but only
5442 // if it matches the VM name
5443 Utf8Str settingsDir;
5444 if (i_isInOwnDir(&settingsDir))
5445 RTDirRemove(settingsDir.c_str());
5446 }
5447
5448 alock.release();
5449
5450 mParent->i_saveModifiedRegistries();
5451 }
5452 catch (HRESULT aRC) { rc = aRC; }
5453
5454 task.m_pProgress->i_notifyComplete(rc);
5455
5456 LogFlowThisFuncLeave();
5457}
5458
5459HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5460{
5461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5462
5463 HRESULT rc = i_checkStateDependency(MutableStateDep);
5464 if (FAILED(rc)) return rc;
5465
5466 if (mData->mRegistered)
5467 return setError(VBOX_E_INVALID_VM_STATE,
5468 tr("Cannot delete settings of a registered machine"));
5469
5470 // collect files to delete
5471 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5472 if (mData->pMachineConfigFile->fileExists())
5473 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5474
5475 RTCList<ComPtr<IMedium> > llMediums;
5476 for (size_t i = 0; i < aMedia.size(); ++i)
5477 {
5478 IMedium *pIMedium(aMedia[i]);
5479 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5480 if (pMedium.isNull())
5481 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5482 SafeArray<BSTR> ids;
5483 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5484 if (FAILED(rc)) return rc;
5485 /* At this point the medium should not have any back references
5486 * anymore. If it has it is attached to another VM and *must* not
5487 * deleted. */
5488 if (ids.size() < 1)
5489 llMediums.append(pMedium);
5490 }
5491
5492 ComObjPtr<Progress> pProgress;
5493 pProgress.createObject();
5494 rc = pProgress->init(i_getVirtualBox(),
5495 static_cast<IMachine*>(this) /* aInitiator */,
5496 Bstr(tr("Deleting files")).raw(),
5497 true /* fCancellable */,
5498 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5499 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5500 if (FAILED(rc))
5501 return rc;
5502
5503 /* create and start the task on a separate thread (note that it will not
5504 * start working until we release alock) */
5505 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5506 rc = pTask->createThread();
5507 if (FAILED(rc))
5508 return rc;
5509
5510 pProgress.queryInterfaceTo(aProgress.asOutParam());
5511
5512 LogFlowFuncLeave();
5513
5514 return S_OK;
5515}
5516
5517HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5518{
5519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5520
5521 ComObjPtr<Snapshot> pSnapshot;
5522 HRESULT rc;
5523
5524 if (aNameOrId.isEmpty())
5525 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5526 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5527 else
5528 {
5529 Guid uuid(aNameOrId);
5530 if (uuid.isValid())
5531 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5532 else
5533 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5534 }
5535 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5536
5537 return rc;
5538}
5539
5540HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5541{
5542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5543
5544 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5545 if (FAILED(rc)) return rc;
5546
5547 ComObjPtr<SharedFolder> sharedFolder;
5548 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5549 if (SUCCEEDED(rc))
5550 return setError(VBOX_E_OBJECT_IN_USE,
5551 tr("Shared folder named '%s' already exists"),
5552 aName.c_str());
5553
5554 sharedFolder.createObject();
5555 rc = sharedFolder->init(i_getMachine(),
5556 aName,
5557 aHostPath,
5558 !!aWritable,
5559 !!aAutomount,
5560 true /* fFailOnError */);
5561 if (FAILED(rc)) return rc;
5562
5563 i_setModified(IsModified_SharedFolders);
5564 mHWData.backup();
5565 mHWData->mSharedFolders.push_back(sharedFolder);
5566
5567 /* inform the direct session if any */
5568 alock.release();
5569 i_onSharedFolderChange();
5570
5571 return S_OK;
5572}
5573
5574HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5575{
5576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5577
5578 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5579 if (FAILED(rc)) return rc;
5580
5581 ComObjPtr<SharedFolder> sharedFolder;
5582 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5583 if (FAILED(rc)) return rc;
5584
5585 i_setModified(IsModified_SharedFolders);
5586 mHWData.backup();
5587 mHWData->mSharedFolders.remove(sharedFolder);
5588
5589 /* inform the direct session if any */
5590 alock.release();
5591 i_onSharedFolderChange();
5592
5593 return S_OK;
5594}
5595
5596HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5597{
5598 /* start with No */
5599 *aCanShow = FALSE;
5600
5601 ComPtr<IInternalSessionControl> directControl;
5602 {
5603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5604
5605 if (mData->mSession.mState != SessionState_Locked)
5606 return setError(VBOX_E_INVALID_VM_STATE,
5607 tr("Machine is not locked for session (session state: %s)"),
5608 Global::stringifySessionState(mData->mSession.mState));
5609
5610 if (mData->mSession.mLockType == LockType_VM)
5611 directControl = mData->mSession.mDirectControl;
5612 }
5613
5614 /* ignore calls made after #OnSessionEnd() is called */
5615 if (!directControl)
5616 return S_OK;
5617
5618 LONG64 dummy;
5619 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5620}
5621
5622HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5623{
5624 ComPtr<IInternalSessionControl> directControl;
5625 {
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627
5628 if (mData->mSession.mState != SessionState_Locked)
5629 return setError(E_FAIL,
5630 tr("Machine is not locked for session (session state: %s)"),
5631 Global::stringifySessionState(mData->mSession.mState));
5632
5633 if (mData->mSession.mLockType == LockType_VM)
5634 directControl = mData->mSession.mDirectControl;
5635 }
5636
5637 /* ignore calls made after #OnSessionEnd() is called */
5638 if (!directControl)
5639 return S_OK;
5640
5641 BOOL dummy;
5642 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5643}
5644
5645#ifdef VBOX_WITH_GUEST_PROPS
5646/**
5647 * Look up a guest property in VBoxSVC's internal structures.
5648 */
5649HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5650 com::Utf8Str &aValue,
5651 LONG64 *aTimestamp,
5652 com::Utf8Str &aFlags) const
5653{
5654 using namespace guestProp;
5655
5656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5657 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5658
5659 if (it != mHWData->mGuestProperties.end())
5660 {
5661 char szFlags[MAX_FLAGS_LEN + 1];
5662 aValue = it->second.strValue;
5663 *aTimestamp = it->second.mTimestamp;
5664 writeFlags(it->second.mFlags, szFlags);
5665 aFlags = Utf8Str(szFlags);
5666 }
5667
5668 return S_OK;
5669}
5670
5671/**
5672 * Query the VM that a guest property belongs to for the property.
5673 * @returns E_ACCESSDENIED if the VM process is not available or not
5674 * currently handling queries and the lookup should then be done in
5675 * VBoxSVC.
5676 */
5677HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5678 com::Utf8Str &aValue,
5679 LONG64 *aTimestamp,
5680 com::Utf8Str &aFlags) const
5681{
5682 HRESULT rc = S_OK;
5683 BSTR bValue = NULL;
5684 BSTR bFlags = NULL;
5685
5686 ComPtr<IInternalSessionControl> directControl;
5687 {
5688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5689 if (mData->mSession.mLockType == LockType_VM)
5690 directControl = mData->mSession.mDirectControl;
5691 }
5692
5693 /* ignore calls made after #OnSessionEnd() is called */
5694 if (!directControl)
5695 rc = E_ACCESSDENIED;
5696 else
5697 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5698 0 /* accessMode */,
5699 &bValue, aTimestamp, &bFlags);
5700
5701 aValue = bValue;
5702 aFlags = bFlags;
5703
5704 return rc;
5705}
5706#endif // VBOX_WITH_GUEST_PROPS
5707
5708HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5709 com::Utf8Str &aValue,
5710 LONG64 *aTimestamp,
5711 com::Utf8Str &aFlags)
5712{
5713#ifndef VBOX_WITH_GUEST_PROPS
5714 ReturnComNotImplemented();
5715#else // VBOX_WITH_GUEST_PROPS
5716
5717 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5718
5719 if (rc == E_ACCESSDENIED)
5720 /* The VM is not running or the service is not (yet) accessible */
5721 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5722 return rc;
5723#endif // VBOX_WITH_GUEST_PROPS
5724}
5725
5726HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5727{
5728 LONG64 dummyTimestamp;
5729 com::Utf8Str dummyFlags;
5730 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5731 return rc;
5732
5733}
5734HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5735{
5736 com::Utf8Str dummyFlags;
5737 com::Utf8Str dummyValue;
5738 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5739 return rc;
5740}
5741
5742#ifdef VBOX_WITH_GUEST_PROPS
5743/**
5744 * Set a guest property in VBoxSVC's internal structures.
5745 */
5746HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5747 const com::Utf8Str &aFlags, bool fDelete)
5748{
5749 using namespace guestProp;
5750
5751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5752 HRESULT rc = S_OK;
5753
5754 rc = i_checkStateDependency(MutableOrSavedStateDep);
5755 if (FAILED(rc)) return rc;
5756
5757 try
5758 {
5759 uint32_t fFlags = NILFLAG;
5760 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5761 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5762
5763 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5764 if (it == mHWData->mGuestProperties.end())
5765 {
5766 if (!fDelete)
5767 {
5768 i_setModified(IsModified_MachineData);
5769 mHWData.backupEx();
5770
5771 RTTIMESPEC time;
5772 HWData::GuestProperty prop;
5773 prop.strValue = Bstr(aValue).raw();
5774 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5775 prop.mFlags = fFlags;
5776 mHWData->mGuestProperties[aName] = prop;
5777 }
5778 }
5779 else
5780 {
5781 if (it->second.mFlags & (RDONLYHOST))
5782 {
5783 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5784 }
5785 else
5786 {
5787 i_setModified(IsModified_MachineData);
5788 mHWData.backupEx();
5789
5790 /* The backupEx() operation invalidates our iterator,
5791 * so get a new one. */
5792 it = mHWData->mGuestProperties.find(aName);
5793 Assert(it != mHWData->mGuestProperties.end());
5794
5795 if (!fDelete)
5796 {
5797 RTTIMESPEC time;
5798 it->second.strValue = aValue;
5799 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5800 it->second.mFlags = fFlags;
5801 }
5802 else
5803 mHWData->mGuestProperties.erase(it);
5804 }
5805 }
5806
5807 if (SUCCEEDED(rc))
5808 {
5809 alock.release();
5810
5811 mParent->i_onGuestPropertyChange(mData->mUuid,
5812 Bstr(aName).raw(),
5813 Bstr(aValue).raw(),
5814 Bstr(aFlags).raw());
5815 }
5816 }
5817 catch (std::bad_alloc &)
5818 {
5819 rc = E_OUTOFMEMORY;
5820 }
5821
5822 return rc;
5823}
5824
5825/**
5826 * Set a property on the VM that that property belongs to.
5827 * @returns E_ACCESSDENIED if the VM process is not available or not
5828 * currently handling queries and the setting should then be done in
5829 * VBoxSVC.
5830 */
5831HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5832 const com::Utf8Str &aFlags, bool fDelete)
5833{
5834 HRESULT rc;
5835
5836 try
5837 {
5838 ComPtr<IInternalSessionControl> directControl;
5839 {
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841 if (mData->mSession.mLockType == LockType_VM)
5842 directControl = mData->mSession.mDirectControl;
5843 }
5844
5845 BSTR dummy = NULL; /* will not be changed (setter) */
5846 LONG64 dummy64;
5847 if (!directControl)
5848 rc = E_ACCESSDENIED;
5849 else
5850 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5851 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5852 fDelete? 2: 1 /* accessMode */,
5853 &dummy, &dummy64, &dummy);
5854 }
5855 catch (std::bad_alloc &)
5856 {
5857 rc = E_OUTOFMEMORY;
5858 }
5859
5860 return rc;
5861}
5862#endif // VBOX_WITH_GUEST_PROPS
5863
5864HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5865 const com::Utf8Str &aFlags)
5866{
5867#ifndef VBOX_WITH_GUEST_PROPS
5868 ReturnComNotImplemented();
5869#else // VBOX_WITH_GUEST_PROPS
5870 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5871 if (rc == E_ACCESSDENIED)
5872 /* The VM is not running or the service is not (yet) accessible */
5873 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5874 return rc;
5875#endif // VBOX_WITH_GUEST_PROPS
5876}
5877
5878HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5879{
5880 return setGuestProperty(aProperty, aValue, "");
5881}
5882
5883HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5884{
5885#ifndef VBOX_WITH_GUEST_PROPS
5886 ReturnComNotImplemented();
5887#else // VBOX_WITH_GUEST_PROPS
5888 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5889 if (rc == E_ACCESSDENIED)
5890 /* The VM is not running or the service is not (yet) accessible */
5891 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5892 return rc;
5893#endif // VBOX_WITH_GUEST_PROPS
5894}
5895
5896#ifdef VBOX_WITH_GUEST_PROPS
5897/**
5898 * Enumerate the guest properties in VBoxSVC's internal structures.
5899 */
5900HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5901 std::vector<com::Utf8Str> &aNames,
5902 std::vector<com::Utf8Str> &aValues,
5903 std::vector<LONG64> &aTimestamps,
5904 std::vector<com::Utf8Str> &aFlags)
5905{
5906 using namespace guestProp;
5907
5908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5909 Utf8Str strPatterns(aPatterns);
5910
5911 HWData::GuestPropertyMap propMap;
5912
5913 /*
5914 * Look for matching patterns and build up a list.
5915 */
5916 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5917 while (it != mHWData->mGuestProperties.end())
5918 {
5919 if ( strPatterns.isEmpty()
5920 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5921 RTSTR_MAX,
5922 it->first.c_str(),
5923 RTSTR_MAX,
5924 NULL)
5925 )
5926 propMap.insert(*it);
5927 ++it;
5928 }
5929
5930 alock.release();
5931
5932 /*
5933 * And build up the arrays for returning the property information.
5934 */
5935 size_t cEntries = propMap.size();
5936
5937 aNames.resize(cEntries);
5938 aValues.resize(cEntries);
5939 aTimestamps.resize(cEntries);
5940 aFlags.resize(cEntries);
5941
5942 char szFlags[MAX_FLAGS_LEN + 1];
5943 size_t i= 0;
5944 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5945 {
5946 aNames[i] = it->first;
5947 aValues[i] = it->second.strValue;
5948 aTimestamps[i] = it->second.mTimestamp;
5949 writeFlags(it->second.mFlags, szFlags);
5950 aFlags[i] = Utf8Str(szFlags);
5951 }
5952
5953 return S_OK;
5954}
5955
5956/**
5957 * Enumerate the properties managed by a VM.
5958 * @returns E_ACCESSDENIED if the VM process is not available or not
5959 * currently handling queries and the setting should then be done in
5960 * VBoxSVC.
5961 */
5962HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5963 std::vector<com::Utf8Str> &aNames,
5964 std::vector<com::Utf8Str> &aValues,
5965 std::vector<LONG64> &aTimestamps,
5966 std::vector<com::Utf8Str> &aFlags)
5967{
5968 HRESULT rc;
5969 ComPtr<IInternalSessionControl> directControl;
5970 {
5971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5972 if (mData->mSession.mLockType == LockType_VM)
5973 directControl = mData->mSession.mDirectControl;
5974 }
5975
5976 com::SafeArray<BSTR> bNames;
5977 com::SafeArray<BSTR> bValues;
5978 com::SafeArray<LONG64> bTimestamps;
5979 com::SafeArray<BSTR> bFlags;
5980
5981 if (!directControl)
5982 rc = E_ACCESSDENIED;
5983 else
5984 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5985 ComSafeArrayAsOutParam(bNames),
5986 ComSafeArrayAsOutParam(bValues),
5987 ComSafeArrayAsOutParam(bTimestamps),
5988 ComSafeArrayAsOutParam(bFlags));
5989 size_t i;
5990 aNames.resize(bNames.size());
5991 for (i = 0; i < bNames.size(); ++i)
5992 aNames[i] = Utf8Str(bNames[i]);
5993 aValues.resize(bValues.size());
5994 for (i = 0; i < bValues.size(); ++i)
5995 aValues[i] = Utf8Str(bValues[i]);
5996 aTimestamps.resize(bTimestamps.size());
5997 for (i = 0; i < bTimestamps.size(); ++i)
5998 aTimestamps[i] = bTimestamps[i];
5999 aFlags.resize(bFlags.size());
6000 for (i = 0; i < bFlags.size(); ++i)
6001 aFlags[i] = Utf8Str(bFlags[i]);
6002
6003 return rc;
6004}
6005#endif // VBOX_WITH_GUEST_PROPS
6006HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6007 std::vector<com::Utf8Str> &aNames,
6008 std::vector<com::Utf8Str> &aValues,
6009 std::vector<LONG64> &aTimestamps,
6010 std::vector<com::Utf8Str> &aFlags)
6011{
6012#ifndef VBOX_WITH_GUEST_PROPS
6013 ReturnComNotImplemented();
6014#else // VBOX_WITH_GUEST_PROPS
6015
6016 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6017
6018 if (rc == E_ACCESSDENIED)
6019 /* The VM is not running or the service is not (yet) accessible */
6020 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6021 return rc;
6022#endif // VBOX_WITH_GUEST_PROPS
6023}
6024
6025HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6026 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6027{
6028 MediaData::AttachmentList atts;
6029
6030 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6031 if (FAILED(rc)) return rc;
6032
6033 size_t i = 0;
6034 aMediumAttachments.resize(atts.size());
6035 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6036 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6037
6038 return S_OK;
6039}
6040
6041HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6042 LONG aControllerPort,
6043 LONG aDevice,
6044 ComPtr<IMediumAttachment> &aAttachment)
6045{
6046 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6047 aName.c_str(), aControllerPort, aDevice));
6048
6049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6050
6051 aAttachment = NULL;
6052
6053 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6054 Bstr(aName).raw(),
6055 aControllerPort,
6056 aDevice);
6057 if (pAttach.isNull())
6058 return setError(VBOX_E_OBJECT_NOT_FOUND,
6059 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6060 aDevice, aControllerPort, aName.c_str());
6061
6062 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6063
6064 return S_OK;
6065}
6066
6067
6068HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6069 StorageBus_T aConnectionType,
6070 ComPtr<IStorageController> &aController)
6071{
6072 if ( (aConnectionType <= StorageBus_Null)
6073 || (aConnectionType > StorageBus_USB))
6074 return setError(E_INVALIDARG,
6075 tr("Invalid connection type: %d"),
6076 aConnectionType);
6077
6078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6079
6080 HRESULT rc = i_checkStateDependency(MutableStateDep);
6081 if (FAILED(rc)) return rc;
6082
6083 /* try to find one with the name first. */
6084 ComObjPtr<StorageController> ctrl;
6085
6086 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6087 if (SUCCEEDED(rc))
6088 return setError(VBOX_E_OBJECT_IN_USE,
6089 tr("Storage controller named '%s' already exists"),
6090 aName.c_str());
6091
6092 ctrl.createObject();
6093
6094 /* get a new instance number for the storage controller */
6095 ULONG ulInstance = 0;
6096 bool fBootable = true;
6097 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6098 it != mStorageControllers->end();
6099 ++it)
6100 {
6101 if ((*it)->i_getStorageBus() == aConnectionType)
6102 {
6103 ULONG ulCurInst = (*it)->i_getInstance();
6104
6105 if (ulCurInst >= ulInstance)
6106 ulInstance = ulCurInst + 1;
6107
6108 /* Only one controller of each type can be marked as bootable. */
6109 if ((*it)->i_getBootable())
6110 fBootable = false;
6111 }
6112 }
6113
6114 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6115 if (FAILED(rc)) return rc;
6116
6117 i_setModified(IsModified_Storage);
6118 mStorageControllers.backup();
6119 mStorageControllers->push_back(ctrl);
6120
6121 ctrl.queryInterfaceTo(aController.asOutParam());
6122
6123 /* inform the direct session if any */
6124 alock.release();
6125 i_onStorageControllerChange();
6126
6127 return S_OK;
6128}
6129
6130HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6131 ComPtr<IStorageController> &aStorageController)
6132{
6133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 ComObjPtr<StorageController> ctrl;
6136
6137 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6138 if (SUCCEEDED(rc))
6139 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6140
6141 return rc;
6142}
6143
6144HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6145 ULONG aInstance,
6146 ComPtr<IStorageController> &aStorageController)
6147{
6148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6151 it != mStorageControllers->end();
6152 ++it)
6153 {
6154 if ( (*it)->i_getStorageBus() == aConnectionType
6155 && (*it)->i_getInstance() == aInstance)
6156 {
6157 (*it).queryInterfaceTo(aStorageController.asOutParam());
6158 return S_OK;
6159 }
6160 }
6161
6162 return setError(VBOX_E_OBJECT_NOT_FOUND,
6163 tr("Could not find a storage controller with instance number '%lu'"),
6164 aInstance);
6165}
6166
6167HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6168{
6169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6170
6171 HRESULT rc = i_checkStateDependency(MutableStateDep);
6172 if (FAILED(rc)) return rc;
6173
6174 ComObjPtr<StorageController> ctrl;
6175
6176 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6177 if (SUCCEEDED(rc))
6178 {
6179 /* Ensure that only one controller of each type is marked as bootable. */
6180 if (aBootable == TRUE)
6181 {
6182 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6183 it != mStorageControllers->end();
6184 ++it)
6185 {
6186 ComObjPtr<StorageController> aCtrl = (*it);
6187
6188 if ( (aCtrl->i_getName() != aName)
6189 && aCtrl->i_getBootable() == TRUE
6190 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6191 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6192 {
6193 aCtrl->i_setBootable(FALSE);
6194 break;
6195 }
6196 }
6197 }
6198
6199 if (SUCCEEDED(rc))
6200 {
6201 ctrl->i_setBootable(aBootable);
6202 i_setModified(IsModified_Storage);
6203 }
6204 }
6205
6206 if (SUCCEEDED(rc))
6207 {
6208 /* inform the direct session if any */
6209 alock.release();
6210 i_onStorageControllerChange();
6211 }
6212
6213 return rc;
6214}
6215
6216HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6217{
6218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6219
6220 HRESULT rc = i_checkStateDependency(MutableStateDep);
6221 if (FAILED(rc)) return rc;
6222
6223 ComObjPtr<StorageController> ctrl;
6224 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6225 if (FAILED(rc)) return rc;
6226
6227 {
6228 /* find all attached devices to the appropriate storage controller and detach them all */
6229 // make a temporary list because detachDevice invalidates iterators into
6230 // mMediaData->mAttachments
6231 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6232
6233 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6234 it != llAttachments2.end();
6235 ++it)
6236 {
6237 MediumAttachment *pAttachTemp = *it;
6238
6239 AutoCaller localAutoCaller(pAttachTemp);
6240 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6241
6242 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6243
6244 if (pAttachTemp->i_getControllerName() == aName)
6245 {
6246 rc = i_detachDevice(pAttachTemp, alock, NULL);
6247 if (FAILED(rc)) return rc;
6248 }
6249 }
6250 }
6251
6252 /* We can remove it now. */
6253 i_setModified(IsModified_Storage);
6254 mStorageControllers.backup();
6255
6256 ctrl->i_unshare();
6257
6258 mStorageControllers->remove(ctrl);
6259
6260 /* inform the direct session if any */
6261 alock.release();
6262 i_onStorageControllerChange();
6263
6264 return S_OK;
6265}
6266
6267HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6268 ComPtr<IUSBController> &aController)
6269{
6270 if ( (aType <= USBControllerType_Null)
6271 || (aType >= USBControllerType_Last))
6272 return setError(E_INVALIDARG,
6273 tr("Invalid USB controller type: %d"),
6274 aType);
6275
6276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6277
6278 HRESULT rc = i_checkStateDependency(MutableStateDep);
6279 if (FAILED(rc)) return rc;
6280
6281 /* try to find one with the same type first. */
6282 ComObjPtr<USBController> ctrl;
6283
6284 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6285 if (SUCCEEDED(rc))
6286 return setError(VBOX_E_OBJECT_IN_USE,
6287 tr("USB controller named '%s' already exists"),
6288 aName.c_str());
6289
6290 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6291 ULONG maxInstances;
6292 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6293 if (FAILED(rc))
6294 return rc;
6295
6296 ULONG cInstances = i_getUSBControllerCountByType(aType);
6297 if (cInstances >= maxInstances)
6298 return setError(E_INVALIDARG,
6299 tr("Too many USB controllers of this type"));
6300
6301 ctrl.createObject();
6302
6303 rc = ctrl->init(this, aName, aType);
6304 if (FAILED(rc)) return rc;
6305
6306 i_setModified(IsModified_USB);
6307 mUSBControllers.backup();
6308 mUSBControllers->push_back(ctrl);
6309
6310 ctrl.queryInterfaceTo(aController.asOutParam());
6311
6312 /* inform the direct session if any */
6313 alock.release();
6314 i_onUSBControllerChange();
6315
6316 return S_OK;
6317}
6318
6319HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6320{
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 ComObjPtr<USBController> ctrl;
6324
6325 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6326 if (SUCCEEDED(rc))
6327 ctrl.queryInterfaceTo(aController.asOutParam());
6328
6329 return rc;
6330}
6331
6332HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6333 ULONG *aControllers)
6334{
6335 if ( (aType <= USBControllerType_Null)
6336 || (aType >= USBControllerType_Last))
6337 return setError(E_INVALIDARG,
6338 tr("Invalid USB controller type: %d"),
6339 aType);
6340
6341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6342
6343 ComObjPtr<USBController> ctrl;
6344
6345 *aControllers = i_getUSBControllerCountByType(aType);
6346
6347 return S_OK;
6348}
6349
6350HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6351{
6352
6353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6354
6355 HRESULT rc = i_checkStateDependency(MutableStateDep);
6356 if (FAILED(rc)) return rc;
6357
6358 ComObjPtr<USBController> ctrl;
6359 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6360 if (FAILED(rc)) return rc;
6361
6362 i_setModified(IsModified_USB);
6363 mUSBControllers.backup();
6364
6365 ctrl->i_unshare();
6366
6367 mUSBControllers->remove(ctrl);
6368
6369 /* inform the direct session if any */
6370 alock.release();
6371 i_onUSBControllerChange();
6372
6373 return S_OK;
6374}
6375
6376HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6377 ULONG *aOriginX,
6378 ULONG *aOriginY,
6379 ULONG *aWidth,
6380 ULONG *aHeight,
6381 BOOL *aEnabled)
6382{
6383 uint32_t u32OriginX= 0;
6384 uint32_t u32OriginY= 0;
6385 uint32_t u32Width = 0;
6386 uint32_t u32Height = 0;
6387 uint16_t u16Flags = 0;
6388
6389 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6390 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6391 if (RT_FAILURE(vrc))
6392 {
6393#ifdef RT_OS_WINDOWS
6394 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6395 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6396 * So just assign fEnable to TRUE again.
6397 * The right fix would be to change GUI API wrappers to make sure that parameters
6398 * are changed only if API succeeds.
6399 */
6400 *aEnabled = TRUE;
6401#endif
6402 return setError(VBOX_E_IPRT_ERROR,
6403 tr("Saved guest size is not available (%Rrc)"),
6404 vrc);
6405 }
6406
6407 *aOriginX = u32OriginX;
6408 *aOriginY = u32OriginY;
6409 *aWidth = u32Width;
6410 *aHeight = u32Height;
6411 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6412
6413 return S_OK;
6414}
6415
6416HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6417 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6418{
6419 if (aScreenId != 0)
6420 return E_NOTIMPL;
6421
6422 if ( aBitmapFormat != BitmapFormat_BGR0
6423 && aBitmapFormat != BitmapFormat_BGRA
6424 && aBitmapFormat != BitmapFormat_RGBA
6425 && aBitmapFormat != BitmapFormat_PNG)
6426 return setError(E_NOTIMPL,
6427 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6428
6429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6430
6431 uint8_t *pu8Data = NULL;
6432 uint32_t cbData = 0;
6433 uint32_t u32Width = 0;
6434 uint32_t u32Height = 0;
6435
6436 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6437
6438 if (RT_FAILURE(vrc))
6439 return setError(VBOX_E_IPRT_ERROR,
6440 tr("Saved thumbnail data is not available (%Rrc)"),
6441 vrc);
6442
6443 HRESULT hr = S_OK;
6444
6445 *aWidth = u32Width;
6446 *aHeight = u32Height;
6447
6448 if (cbData > 0)
6449 {
6450 /* Convert pixels to the format expected by the API caller. */
6451 if (aBitmapFormat == BitmapFormat_BGR0)
6452 {
6453 /* [0] B, [1] G, [2] R, [3] 0. */
6454 aData.resize(cbData);
6455 memcpy(&aData.front(), pu8Data, cbData);
6456 }
6457 else if (aBitmapFormat == BitmapFormat_BGRA)
6458 {
6459 /* [0] B, [1] G, [2] R, [3] A. */
6460 aData.resize(cbData);
6461 for (uint32_t i = 0; i < cbData; i += 4)
6462 {
6463 aData[i] = pu8Data[i];
6464 aData[i + 1] = pu8Data[i + 1];
6465 aData[i + 2] = pu8Data[i + 2];
6466 aData[i + 3] = 0xff;
6467 }
6468 }
6469 else if (aBitmapFormat == BitmapFormat_RGBA)
6470 {
6471 /* [0] R, [1] G, [2] B, [3] A. */
6472 aData.resize(cbData);
6473 for (uint32_t i = 0; i < cbData; i += 4)
6474 {
6475 aData[i] = pu8Data[i + 2];
6476 aData[i + 1] = pu8Data[i + 1];
6477 aData[i + 2] = pu8Data[i];
6478 aData[i + 3] = 0xff;
6479 }
6480 }
6481 else if (aBitmapFormat == BitmapFormat_PNG)
6482 {
6483 uint8_t *pu8PNG = NULL;
6484 uint32_t cbPNG = 0;
6485 uint32_t cxPNG = 0;
6486 uint32_t cyPNG = 0;
6487
6488 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6489
6490 if (RT_SUCCESS(vrc))
6491 {
6492 aData.resize(cbPNG);
6493 if (cbPNG)
6494 memcpy(&aData.front(), pu8PNG, cbPNG);
6495 }
6496 else
6497 hr = setError(VBOX_E_IPRT_ERROR,
6498 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6499 vrc);
6500
6501 RTMemFree(pu8PNG);
6502 }
6503 }
6504
6505 freeSavedDisplayScreenshot(pu8Data);
6506
6507 return hr;
6508}
6509
6510HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6511 ULONG *aWidth,
6512 ULONG *aHeight,
6513 std::vector<BitmapFormat_T> &aBitmapFormats)
6514{
6515 if (aScreenId != 0)
6516 return E_NOTIMPL;
6517
6518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6519
6520 uint8_t *pu8Data = NULL;
6521 uint32_t cbData = 0;
6522 uint32_t u32Width = 0;
6523 uint32_t u32Height = 0;
6524
6525 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6526
6527 if (RT_FAILURE(vrc))
6528 return setError(VBOX_E_IPRT_ERROR,
6529 tr("Saved screenshot data is not available (%Rrc)"),
6530 vrc);
6531
6532 *aWidth = u32Width;
6533 *aHeight = u32Height;
6534 aBitmapFormats.resize(1);
6535 aBitmapFormats[0] = BitmapFormat_PNG;
6536
6537 freeSavedDisplayScreenshot(pu8Data);
6538
6539 return S_OK;
6540}
6541
6542HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6543 BitmapFormat_T aBitmapFormat,
6544 ULONG *aWidth,
6545 ULONG *aHeight,
6546 std::vector<BYTE> &aData)
6547{
6548 if (aScreenId != 0)
6549 return E_NOTIMPL;
6550
6551 if (aBitmapFormat != BitmapFormat_PNG)
6552 return E_NOTIMPL;
6553
6554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 uint8_t *pu8Data = NULL;
6557 uint32_t cbData = 0;
6558 uint32_t u32Width = 0;
6559 uint32_t u32Height = 0;
6560
6561 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6562
6563 if (RT_FAILURE(vrc))
6564 return setError(VBOX_E_IPRT_ERROR,
6565 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6566 vrc);
6567
6568 *aWidth = u32Width;
6569 *aHeight = u32Height;
6570
6571 aData.resize(cbData);
6572 if (cbData)
6573 memcpy(&aData.front(), pu8Data, cbData);
6574
6575 freeSavedDisplayScreenshot(pu8Data);
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::hotPlugCPU(ULONG aCpu)
6581{
6582 HRESULT rc = S_OK;
6583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6584
6585 if (!mHWData->mCPUHotPlugEnabled)
6586 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6587
6588 if (aCpu >= mHWData->mCPUCount)
6589 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6590
6591 if (mHWData->mCPUAttached[aCpu])
6592 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6593
6594 alock.release();
6595 rc = i_onCPUChange(aCpu, false);
6596 alock.acquire();
6597 if (FAILED(rc)) return rc;
6598
6599 i_setModified(IsModified_MachineData);
6600 mHWData.backup();
6601 mHWData->mCPUAttached[aCpu] = true;
6602
6603 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6604 if (Global::IsOnline(mData->mMachineState))
6605 i_saveSettings(NULL);
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6611{
6612 HRESULT rc = S_OK;
6613
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615
6616 if (!mHWData->mCPUHotPlugEnabled)
6617 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6618
6619 if (aCpu >= SchemaDefs::MaxCPUCount)
6620 return setError(E_INVALIDARG,
6621 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6622 SchemaDefs::MaxCPUCount);
6623
6624 if (!mHWData->mCPUAttached[aCpu])
6625 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6626
6627 /* CPU 0 can't be detached */
6628 if (aCpu == 0)
6629 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6630
6631 alock.release();
6632 rc = i_onCPUChange(aCpu, true);
6633 alock.acquire();
6634 if (FAILED(rc)) return rc;
6635
6636 i_setModified(IsModified_MachineData);
6637 mHWData.backup();
6638 mHWData->mCPUAttached[aCpu] = false;
6639
6640 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6641 if (Global::IsOnline(mData->mMachineState))
6642 i_saveSettings(NULL);
6643
6644 return S_OK;
6645}
6646
6647HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6648{
6649 *aAttached = false;
6650
6651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 /* If hotplug is enabled the CPU is always enabled. */
6654 if (!mHWData->mCPUHotPlugEnabled)
6655 {
6656 if (aCpu < mHWData->mCPUCount)
6657 *aAttached = true;
6658 }
6659 else
6660 {
6661 if (aCpu < SchemaDefs::MaxCPUCount)
6662 *aAttached = mHWData->mCPUAttached[aCpu];
6663 }
6664
6665 return S_OK;
6666}
6667
6668HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6669{
6670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6671
6672 Utf8Str log = i_getLogFilename(aIdx);
6673 if (!RTFileExists(log.c_str()))
6674 log.setNull();
6675 aFilename = log;
6676
6677 return S_OK;
6678}
6679
6680HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6681{
6682 if (aSize < 0)
6683 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6684
6685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6686
6687 HRESULT rc = S_OK;
6688 Utf8Str log = i_getLogFilename(aIdx);
6689
6690 /* do not unnecessarily hold the lock while doing something which does
6691 * not need the lock and potentially takes a long time. */
6692 alock.release();
6693
6694 /* Limit the chunk size to 32K for now, as that gives better performance
6695 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6696 * One byte expands to approx. 25 bytes of breathtaking XML. */
6697 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6698 aData.resize(cbData);
6699
6700 RTFILE LogFile;
6701 int vrc = RTFileOpen(&LogFile, log.c_str(),
6702 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6703 if (RT_SUCCESS(vrc))
6704 {
6705 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6706 if (RT_SUCCESS(vrc))
6707 aData.resize(cbData);
6708 else
6709 rc = setError(VBOX_E_IPRT_ERROR,
6710 tr("Could not read log file '%s' (%Rrc)"),
6711 log.c_str(), vrc);
6712 RTFileClose(LogFile);
6713 }
6714 else
6715 rc = setError(VBOX_E_IPRT_ERROR,
6716 tr("Could not open log file '%s' (%Rrc)"),
6717 log.c_str(), vrc);
6718
6719 if (FAILED(rc))
6720 aData.resize(0);
6721
6722 return rc;
6723}
6724
6725
6726/**
6727 * Currently this method doesn't attach device to the running VM,
6728 * just makes sure it's plugged on next VM start.
6729 */
6730HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6731{
6732 // lock scope
6733 {
6734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6735
6736 HRESULT rc = i_checkStateDependency(MutableStateDep);
6737 if (FAILED(rc)) return rc;
6738
6739 ChipsetType_T aChipset = ChipsetType_PIIX3;
6740 COMGETTER(ChipsetType)(&aChipset);
6741
6742 if (aChipset != ChipsetType_ICH9)
6743 {
6744 return setError(E_INVALIDARG,
6745 tr("Host PCI attachment only supported with ICH9 chipset"));
6746 }
6747
6748 // check if device with this host PCI address already attached
6749 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6750 it != mHWData->mPCIDeviceAssignments.end();
6751 ++it)
6752 {
6753 LONG iHostAddress = -1;
6754 ComPtr<PCIDeviceAttachment> pAttach;
6755 pAttach = *it;
6756 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6757 if (iHostAddress == aHostAddress)
6758 return setError(E_INVALIDARG,
6759 tr("Device with host PCI address already attached to this VM"));
6760 }
6761
6762 ComObjPtr<PCIDeviceAttachment> pda;
6763 char name[32];
6764
6765 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6766 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6767 Bstr bname(name);
6768 pda.createObject();
6769 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6770 i_setModified(IsModified_MachineData);
6771 mHWData.backup();
6772 mHWData->mPCIDeviceAssignments.push_back(pda);
6773 }
6774
6775 return S_OK;
6776}
6777
6778/**
6779 * Currently this method doesn't detach device from the running VM,
6780 * just makes sure it's not plugged on next VM start.
6781 */
6782HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6783{
6784 ComObjPtr<PCIDeviceAttachment> pAttach;
6785 bool fRemoved = false;
6786 HRESULT rc;
6787
6788 // lock scope
6789 {
6790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 rc = i_checkStateDependency(MutableStateDep);
6793 if (FAILED(rc)) return rc;
6794
6795 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6796 it != mHWData->mPCIDeviceAssignments.end();
6797 ++it)
6798 {
6799 LONG iHostAddress = -1;
6800 pAttach = *it;
6801 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6802 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6803 {
6804 i_setModified(IsModified_MachineData);
6805 mHWData.backup();
6806 mHWData->mPCIDeviceAssignments.remove(pAttach);
6807 fRemoved = true;
6808 break;
6809 }
6810 }
6811 }
6812
6813
6814 /* Fire event outside of the lock */
6815 if (fRemoved)
6816 {
6817 Assert(!pAttach.isNull());
6818 ComPtr<IEventSource> es;
6819 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6820 Assert(SUCCEEDED(rc));
6821 Bstr mid;
6822 rc = this->COMGETTER(Id)(mid.asOutParam());
6823 Assert(SUCCEEDED(rc));
6824 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6825 }
6826
6827 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6828 tr("No host PCI device %08x attached"),
6829 aHostAddress
6830 );
6831}
6832
6833HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6834{
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836
6837 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6838
6839 size_t i = 0;
6840 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6841 it != mHWData->mPCIDeviceAssignments.end();
6842 ++i, ++it)
6843 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6844
6845 return S_OK;
6846}
6847
6848HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6849{
6850 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6851
6852 return S_OK;
6853}
6854
6855HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6856{
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6865{
6866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6868 if (SUCCEEDED(hrc))
6869 {
6870 hrc = mHWData.backupEx();
6871 if (SUCCEEDED(hrc))
6872 {
6873 i_setModified(IsModified_MachineData);
6874 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6875 }
6876 }
6877 return hrc;
6878}
6879
6880HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6884 return S_OK;
6885}
6886
6887HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6888{
6889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6891 if (SUCCEEDED(hrc))
6892 {
6893 hrc = mHWData.backupEx();
6894 if (SUCCEEDED(hrc))
6895 {
6896 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6897 if (SUCCEEDED(hrc))
6898 i_setModified(IsModified_MachineData);
6899 }
6900 }
6901 return hrc;
6902}
6903
6904HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6905{
6906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6907
6908 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6909
6910 return S_OK;
6911}
6912
6913HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6914{
6915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6916 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6917 if (SUCCEEDED(hrc))
6918 {
6919 hrc = mHWData.backupEx();
6920 if (SUCCEEDED(hrc))
6921 {
6922 i_setModified(IsModified_MachineData);
6923 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6924 }
6925 }
6926 return hrc;
6927}
6928
6929HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6930{
6931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6932
6933 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6934
6935 return S_OK;
6936}
6937
6938HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6939{
6940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6941
6942 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6943 if ( SUCCEEDED(hrc)
6944 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6945 {
6946 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6947 int vrc;
6948
6949 if (aAutostartEnabled)
6950 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6951 else
6952 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6953
6954 if (RT_SUCCESS(vrc))
6955 {
6956 hrc = mHWData.backupEx();
6957 if (SUCCEEDED(hrc))
6958 {
6959 i_setModified(IsModified_MachineData);
6960 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6961 }
6962 }
6963 else if (vrc == VERR_NOT_SUPPORTED)
6964 hrc = setError(VBOX_E_NOT_SUPPORTED,
6965 tr("The VM autostart feature is not supported on this platform"));
6966 else if (vrc == VERR_PATH_NOT_FOUND)
6967 hrc = setError(E_FAIL,
6968 tr("The path to the autostart database is not set"));
6969 else
6970 hrc = setError(E_UNEXPECTED,
6971 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6972 aAutostartEnabled ? "Adding" : "Removing",
6973 mUserData->s.strName.c_str(), vrc);
6974 }
6975 return hrc;
6976}
6977
6978HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6979{
6980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6983
6984 return S_OK;
6985}
6986
6987HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6988{
6989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6990 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6991 if (SUCCEEDED(hrc))
6992 {
6993 hrc = mHWData.backupEx();
6994 if (SUCCEEDED(hrc))
6995 {
6996 i_setModified(IsModified_MachineData);
6997 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6998 }
6999 }
7000 return hrc;
7001}
7002
7003HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7004{
7005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7006
7007 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7008
7009 return S_OK;
7010}
7011
7012HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7013{
7014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7015 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7016 if ( SUCCEEDED(hrc)
7017 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7018 {
7019 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7020 int vrc;
7021
7022 if (aAutostopType != AutostopType_Disabled)
7023 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7024 else
7025 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7026
7027 if (RT_SUCCESS(vrc))
7028 {
7029 hrc = mHWData.backupEx();
7030 if (SUCCEEDED(hrc))
7031 {
7032 i_setModified(IsModified_MachineData);
7033 mHWData->mAutostart.enmAutostopType = aAutostopType;
7034 }
7035 }
7036 else if (vrc == VERR_NOT_SUPPORTED)
7037 hrc = setError(VBOX_E_NOT_SUPPORTED,
7038 tr("The VM autostop feature is not supported on this platform"));
7039 else if (vrc == VERR_PATH_NOT_FOUND)
7040 hrc = setError(E_FAIL,
7041 tr("The path to the autostart database is not set"));
7042 else
7043 hrc = setError(E_UNEXPECTED,
7044 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7045 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7046 mUserData->s.strName.c_str(), vrc);
7047 }
7048 return hrc;
7049}
7050
7051HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7052{
7053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7054
7055 aDefaultFrontend = mHWData->mDefaultFrontend;
7056
7057 return S_OK;
7058}
7059
7060HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7061{
7062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7063 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7064 if (SUCCEEDED(hrc))
7065 {
7066 hrc = mHWData.backupEx();
7067 if (SUCCEEDED(hrc))
7068 {
7069 i_setModified(IsModified_MachineData);
7070 mHWData->mDefaultFrontend = aDefaultFrontend;
7071 }
7072 }
7073 return hrc;
7074}
7075
7076HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7077{
7078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7079 size_t cbIcon = mUserData->s.ovIcon.size();
7080 aIcon.resize(cbIcon);
7081 if (cbIcon)
7082 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7083 return S_OK;
7084}
7085
7086HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7087{
7088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7089 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7090 if (SUCCEEDED(hrc))
7091 {
7092 i_setModified(IsModified_MachineData);
7093 mUserData.backup();
7094 size_t cbIcon = aIcon.size();
7095 mUserData->s.ovIcon.resize(cbIcon);
7096 if (cbIcon)
7097 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7098 }
7099 return hrc;
7100}
7101
7102HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7103{
7104#ifdef VBOX_WITH_USB
7105 *aUSBProxyAvailable = true;
7106#else
7107 *aUSBProxyAvailable = false;
7108#endif
7109 return S_OK;
7110}
7111
7112HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7113{
7114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7115
7116 aVMProcessPriority = mUserData->s.strVMPriority;
7117
7118 return S_OK;
7119}
7120
7121HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7122{
7123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7124 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7125 if (SUCCEEDED(hrc))
7126 {
7127 /** @todo r=klaus: currently this is marked as not implemented, as
7128 * the code for setting the priority of the process is not there
7129 * (neither when starting the VM nor at runtime). */
7130 ReturnComNotImplemented();
7131 hrc = mUserData.backupEx();
7132 if (SUCCEEDED(hrc))
7133 {
7134 i_setModified(IsModified_MachineData);
7135 mUserData->s.strVMPriority = aVMProcessPriority;
7136 }
7137 }
7138 return hrc;
7139}
7140
7141HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7142 ComPtr<IProgress> &aProgress)
7143{
7144 ComObjPtr<Progress> pP;
7145 Progress *ppP = pP;
7146 IProgress *iP = static_cast<IProgress *>(ppP);
7147 IProgress **pProgress = &iP;
7148
7149 IMachine *pTarget = aTarget;
7150
7151 /* Convert the options. */
7152 RTCList<CloneOptions_T> optList;
7153 if (aOptions.size())
7154 for (size_t i = 0; i < aOptions.size(); ++i)
7155 optList.append(aOptions[i]);
7156
7157 if (optList.contains(CloneOptions_Link))
7158 {
7159 if (!i_isSnapshotMachine())
7160 return setError(E_INVALIDARG,
7161 tr("Linked clone can only be created from a snapshot"));
7162 if (aMode != CloneMode_MachineState)
7163 return setError(E_INVALIDARG,
7164 tr("Linked clone can only be created for a single machine state"));
7165 }
7166 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7167
7168 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7169
7170 HRESULT rc = pWorker->start(pProgress);
7171
7172 pP = static_cast<Progress *>(*pProgress);
7173 pP.queryInterfaceTo(aProgress.asOutParam());
7174
7175 return rc;
7176
7177}
7178
7179HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7180{
7181 NOREF(aProgress);
7182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7183
7184 // This check should always fail.
7185 HRESULT rc = i_checkStateDependency(MutableStateDep);
7186 if (FAILED(rc)) return rc;
7187
7188 AssertFailedReturn(E_NOTIMPL);
7189}
7190
7191HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7192{
7193 NOREF(aSavedStateFile);
7194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7195
7196 // This check should always fail.
7197 HRESULT rc = i_checkStateDependency(MutableStateDep);
7198 if (FAILED(rc)) return rc;
7199
7200 AssertFailedReturn(E_NOTIMPL);
7201}
7202
7203HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7204{
7205 NOREF(aFRemoveFile);
7206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7207
7208 // This check should always fail.
7209 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7210 if (FAILED(rc)) return rc;
7211
7212 AssertFailedReturn(E_NOTIMPL);
7213}
7214
7215// public methods for internal purposes
7216/////////////////////////////////////////////////////////////////////////////
7217
7218/**
7219 * Adds the given IsModified_* flag to the dirty flags of the machine.
7220 * This must be called either during i_loadSettings or under the machine write lock.
7221 * @param fl
7222 */
7223void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7224{
7225 mData->flModifications |= fl;
7226 if (fAllowStateModification && i_isStateModificationAllowed())
7227 mData->mCurrentStateModified = true;
7228}
7229
7230/**
7231 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7232 * care of the write locking.
7233 *
7234 * @param fModifications The flag to add.
7235 */
7236void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7237{
7238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7239 i_setModified(fModification, fAllowStateModification);
7240}
7241
7242/**
7243 * Saves the registry entry of this machine to the given configuration node.
7244 *
7245 * @param aEntryNode Node to save the registry entry to.
7246 *
7247 * @note locks this object for reading.
7248 */
7249HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7250{
7251 AutoLimitedCaller autoCaller(this);
7252 AssertComRCReturnRC(autoCaller.rc());
7253
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255
7256 data.uuid = mData->mUuid;
7257 data.strSettingsFile = mData->m_strConfigFile;
7258
7259 return S_OK;
7260}
7261
7262/**
7263 * Calculates the absolute path of the given path taking the directory of the
7264 * machine settings file as the current directory.
7265 *
7266 * @param aPath Path to calculate the absolute path for.
7267 * @param aResult Where to put the result (used only on success, can be the
7268 * same Utf8Str instance as passed in @a aPath).
7269 * @return IPRT result.
7270 *
7271 * @note Locks this object for reading.
7272 */
7273int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7274{
7275 AutoCaller autoCaller(this);
7276 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7277
7278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7279
7280 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7281
7282 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7283
7284 strSettingsDir.stripFilename();
7285 char folder[RTPATH_MAX];
7286 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7287 if (RT_SUCCESS(vrc))
7288 aResult = folder;
7289
7290 return vrc;
7291}
7292
7293/**
7294 * Copies strSource to strTarget, making it relative to the machine folder
7295 * if it is a subdirectory thereof, or simply copying it otherwise.
7296 *
7297 * @param strSource Path to evaluate and copy.
7298 * @param strTarget Buffer to receive target path.
7299 *
7300 * @note Locks this object for reading.
7301 */
7302void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7303 Utf8Str &strTarget)
7304{
7305 AutoCaller autoCaller(this);
7306 AssertComRCReturn(autoCaller.rc(), (void)0);
7307
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7311 // use strTarget as a temporary buffer to hold the machine settings dir
7312 strTarget = mData->m_strConfigFileFull;
7313 strTarget.stripFilename();
7314 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7315 {
7316 // is relative: then append what's left
7317 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7318 // for empty paths (only possible for subdirs) use "." to avoid
7319 // triggering default settings for not present config attributes.
7320 if (strTarget.isEmpty())
7321 strTarget = ".";
7322 }
7323 else
7324 // is not relative: then overwrite
7325 strTarget = strSource;
7326}
7327
7328/**
7329 * Returns the full path to the machine's log folder in the
7330 * \a aLogFolder argument.
7331 */
7332void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7333{
7334 AutoCaller autoCaller(this);
7335 AssertComRCReturnVoid(autoCaller.rc());
7336
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338
7339 char szTmp[RTPATH_MAX];
7340 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7341 if (RT_SUCCESS(vrc))
7342 {
7343 if (szTmp[0] && !mUserData.isNull())
7344 {
7345 char szTmp2[RTPATH_MAX];
7346 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7347 if (RT_SUCCESS(vrc))
7348 aLogFolder = BstrFmt("%s%c%s",
7349 szTmp2,
7350 RTPATH_DELIMITER,
7351 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7352 }
7353 else
7354 vrc = VERR_PATH_IS_RELATIVE;
7355 }
7356
7357 if (RT_FAILURE(vrc))
7358 {
7359 // fallback if VBOX_USER_LOGHOME is not set or invalid
7360 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7361 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7362 aLogFolder.append(RTPATH_DELIMITER);
7363 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7364 }
7365}
7366
7367/**
7368 * Returns the full path to the machine's log file for an given index.
7369 */
7370Utf8Str Machine::i_getLogFilename(ULONG idx)
7371{
7372 Utf8Str logFolder;
7373 getLogFolder(logFolder);
7374 Assert(logFolder.length());
7375
7376 Utf8Str log;
7377 if (idx == 0)
7378 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7379#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7380 else if (idx == 1)
7381 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7382 else
7383 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7384#else
7385 else
7386 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7387#endif
7388 return log;
7389}
7390
7391/**
7392 * Returns the full path to the machine's hardened log file.
7393 */
7394Utf8Str Machine::i_getHardeningLogFilename(void)
7395{
7396 Utf8Str strFilename;
7397 getLogFolder(strFilename);
7398 Assert(strFilename.length());
7399 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7400 return strFilename;
7401}
7402
7403
7404/**
7405 * Composes a unique saved state filename based on the current system time. The filename is
7406 * granular to the second so this will work so long as no more than one snapshot is taken on
7407 * a machine per second.
7408 *
7409 * Before version 4.1, we used this formula for saved state files:
7410 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7411 * which no longer works because saved state files can now be shared between the saved state of the
7412 * "saved" machine and an online snapshot, and the following would cause problems:
7413 * 1) save machine
7414 * 2) create online snapshot from that machine state --> reusing saved state file
7415 * 3) save machine again --> filename would be reused, breaking the online snapshot
7416 *
7417 * So instead we now use a timestamp.
7418 *
7419 * @param str
7420 */
7421
7422void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7423{
7424 AutoCaller autoCaller(this);
7425 AssertComRCReturnVoid(autoCaller.rc());
7426
7427 {
7428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7429 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7430 }
7431
7432 RTTIMESPEC ts;
7433 RTTimeNow(&ts);
7434 RTTIME time;
7435 RTTimeExplode(&time, &ts);
7436
7437 strStateFilePath += RTPATH_DELIMITER;
7438 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7439 time.i32Year, time.u8Month, time.u8MonthDay,
7440 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7441}
7442
7443/**
7444 * Returns the full path to the default video capture file.
7445 */
7446void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7447{
7448 AutoCaller autoCaller(this);
7449 AssertComRCReturnVoid(autoCaller.rc());
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7454 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7455 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7456}
7457
7458/**
7459 * Returns whether at least one USB controller is present for the VM.
7460 */
7461bool Machine::i_isUSBControllerPresent()
7462{
7463 AutoCaller autoCaller(this);
7464 AssertComRCReturn(autoCaller.rc(), false);
7465
7466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7467
7468 return (mUSBControllers->size() > 0);
7469}
7470
7471/**
7472 * @note Locks this object for writing, calls the client process
7473 * (inside the lock).
7474 */
7475HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7476 const Utf8Str &strFrontend,
7477 const Utf8Str &strEnvironment,
7478 ProgressProxy *aProgress)
7479{
7480 LogFlowThisFuncEnter();
7481
7482 AssertReturn(aControl, E_FAIL);
7483 AssertReturn(aProgress, E_FAIL);
7484 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7485
7486 AutoCaller autoCaller(this);
7487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7488
7489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7490
7491 if (!mData->mRegistered)
7492 return setError(E_UNEXPECTED,
7493 tr("The machine '%s' is not registered"),
7494 mUserData->s.strName.c_str());
7495
7496 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7497
7498 /* The process started when launching a VM with separate UI/VM processes is always
7499 * the UI process, i.e. needs special handling as it won't claim the session. */
7500 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7501
7502 if (fSeparate)
7503 {
7504 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7505 return setError(VBOX_E_INVALID_OBJECT_STATE,
7506 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7507 mUserData->s.strName.c_str());
7508 }
7509 else
7510 {
7511 if ( mData->mSession.mState == SessionState_Locked
7512 || mData->mSession.mState == SessionState_Spawning
7513 || mData->mSession.mState == SessionState_Unlocking)
7514 return setError(VBOX_E_INVALID_OBJECT_STATE,
7515 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7516 mUserData->s.strName.c_str());
7517
7518 /* may not be busy */
7519 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7520 }
7521
7522 /* get the path to the executable */
7523 char szPath[RTPATH_MAX];
7524 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7525 size_t cchBufLeft = strlen(szPath);
7526 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7527 szPath[cchBufLeft] = 0;
7528 char *pszNamePart = szPath + cchBufLeft;
7529 cchBufLeft = sizeof(szPath) - cchBufLeft;
7530
7531 int vrc = VINF_SUCCESS;
7532 RTPROCESS pid = NIL_RTPROCESS;
7533
7534 RTENV env = RTENV_DEFAULT;
7535
7536 if (!strEnvironment.isEmpty())
7537 {
7538 char *newEnvStr = NULL;
7539
7540 do
7541 {
7542 /* clone the current environment */
7543 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7544 AssertRCBreakStmt(vrc2, vrc = vrc2);
7545
7546 newEnvStr = RTStrDup(strEnvironment.c_str());
7547 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7548
7549 /* put new variables to the environment
7550 * (ignore empty variable names here since RTEnv API
7551 * intentionally doesn't do that) */
7552 char *var = newEnvStr;
7553 for (char *p = newEnvStr; *p; ++p)
7554 {
7555 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7556 {
7557 *p = '\0';
7558 if (*var)
7559 {
7560 char *val = strchr(var, '=');
7561 if (val)
7562 {
7563 *val++ = '\0';
7564 vrc2 = RTEnvSetEx(env, var, val);
7565 }
7566 else
7567 vrc2 = RTEnvUnsetEx(env, var);
7568 if (RT_FAILURE(vrc2))
7569 break;
7570 }
7571 var = p + 1;
7572 }
7573 }
7574 if (RT_SUCCESS(vrc2) && *var)
7575 vrc2 = RTEnvPutEx(env, var);
7576
7577 AssertRCBreakStmt(vrc2, vrc = vrc2);
7578 }
7579 while (0);
7580
7581 if (newEnvStr != NULL)
7582 RTStrFree(newEnvStr);
7583 }
7584
7585 /* Hardening logging */
7586#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7587 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7588 {
7589 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7590 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7591 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7592 {
7593 Utf8Str strStartupLogDir = strHardeningLogFile;
7594 strStartupLogDir.stripFilename();
7595 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7596 file without stripping the file. */
7597 }
7598 strSupHardeningLogArg.append(strHardeningLogFile);
7599
7600 /* Remove legacy log filename to avoid confusion. */
7601 Utf8Str strOldStartupLogFile;
7602 getLogFolder(strOldStartupLogFile);
7603 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7604 RTFileDelete(strOldStartupLogFile.c_str());
7605 }
7606 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7607#else
7608 const char *pszSupHardeningLogArg = NULL;
7609#endif
7610
7611 Utf8Str strCanonicalName;
7612
7613#ifdef VBOX_WITH_QTGUI
7614 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7615 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7616 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7617 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7619 {
7620 strCanonicalName = "GUI/Qt";
7621# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7622 /* Modify the base path so that we don't need to use ".." below. */
7623 RTPathStripTrailingSlash(szPath);
7624 RTPathStripFilename(szPath);
7625 cchBufLeft = strlen(szPath);
7626 pszNamePart = szPath + cchBufLeft;
7627 cchBufLeft = sizeof(szPath) - cchBufLeft;
7628
7629# define OSX_APP_NAME "VirtualBoxVM"
7630# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7631
7632 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7633 if ( strAppOverride.contains(".")
7634 || strAppOverride.contains("/")
7635 || strAppOverride.contains("\\")
7636 || strAppOverride.contains(":"))
7637 strAppOverride.setNull();
7638 Utf8Str strAppPath;
7639 if (!strAppOverride.isEmpty())
7640 {
7641 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7642 Utf8Str strFullPath(szPath);
7643 strFullPath.append(strAppPath);
7644 /* there is a race, but people using this deserve the failure */
7645 if (!RTFileExists(strFullPath.c_str()))
7646 strAppOverride.setNull();
7647 }
7648 if (strAppOverride.isEmpty())
7649 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7650 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7651 strcpy(pszNamePart, strAppPath.c_str());
7652# else
7653 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7654 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7655 strcpy(pszNamePart, s_szVirtualBox_exe);
7656# endif
7657
7658 Utf8Str idStr = mData->mUuid.toString();
7659 const char *apszArgs[] =
7660 {
7661 szPath,
7662 "--comment", mUserData->s.strName.c_str(),
7663 "--startvm", idStr.c_str(),
7664 "--no-startvm-errormsgbox",
7665 NULL, /* For "--separate". */
7666 NULL, /* For "--sup-startup-log". */
7667 NULL
7668 };
7669 unsigned iArg = 6;
7670 if (fSeparate)
7671 apszArgs[iArg++] = "--separate";
7672 apszArgs[iArg++] = pszSupHardeningLogArg;
7673
7674 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7675 }
7676#else /* !VBOX_WITH_QTGUI */
7677 if (0)
7678 ;
7679#endif /* VBOX_WITH_QTGUI */
7680
7681 else
7682
7683#ifdef VBOX_WITH_VBOXSDL
7684 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7685 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7686 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7687 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7688 {
7689 strCanonicalName = "GUI/SDL";
7690 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7691 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7692 strcpy(pszNamePart, s_szVBoxSDL_exe);
7693
7694 Utf8Str idStr = mData->mUuid.toString();
7695 const char *apszArgs[] =
7696 {
7697 szPath,
7698 "--comment", mUserData->s.strName.c_str(),
7699 "--startvm", idStr.c_str(),
7700 NULL, /* For "--separate". */
7701 NULL, /* For "--sup-startup-log". */
7702 NULL
7703 };
7704 unsigned iArg = 5;
7705 if (fSeparate)
7706 apszArgs[iArg++] = "--separate";
7707 apszArgs[iArg++] = pszSupHardeningLogArg;
7708
7709 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7710 }
7711#else /* !VBOX_WITH_VBOXSDL */
7712 if (0)
7713 ;
7714#endif /* !VBOX_WITH_VBOXSDL */
7715
7716 else
7717
7718#ifdef VBOX_WITH_HEADLESS
7719 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7720 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7721 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7722 )
7723 {
7724 strCanonicalName = "headless";
7725 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7726 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7727 * and a VM works even if the server has not been installed.
7728 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7729 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7730 * differently in 4.0 and 3.x.
7731 */
7732 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7733 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7734 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7735
7736 Utf8Str idStr = mData->mUuid.toString();
7737 const char *apszArgs[] =
7738 {
7739 szPath,
7740 "--comment", mUserData->s.strName.c_str(),
7741 "--startvm", idStr.c_str(),
7742 "--vrde", "config",
7743 NULL, /* For "--capture". */
7744 NULL, /* For "--sup-startup-log". */
7745 NULL
7746 };
7747 unsigned iArg = 7;
7748 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7749 apszArgs[iArg++] = "--capture";
7750 apszArgs[iArg++] = pszSupHardeningLogArg;
7751
7752# ifdef RT_OS_WINDOWS
7753 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7754# else
7755 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7756# endif
7757 }
7758#else /* !VBOX_WITH_HEADLESS */
7759 if (0)
7760 ;
7761#endif /* !VBOX_WITH_HEADLESS */
7762 else
7763 {
7764 RTEnvDestroy(env);
7765 return setError(E_INVALIDARG,
7766 tr("Invalid frontend name: '%s'"),
7767 strFrontend.c_str());
7768 }
7769
7770 RTEnvDestroy(env);
7771
7772 if (RT_FAILURE(vrc))
7773 return setError(VBOX_E_IPRT_ERROR,
7774 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7775 mUserData->s.strName.c_str(), vrc);
7776
7777 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7778
7779 if (!fSeparate)
7780 {
7781 /*
7782 * Note that we don't release the lock here before calling the client,
7783 * because it doesn't need to call us back if called with a NULL argument.
7784 * Releasing the lock here is dangerous because we didn't prepare the
7785 * launch data yet, but the client we've just started may happen to be
7786 * too fast and call LockMachine() that will fail (because of PID, etc.),
7787 * so that the Machine will never get out of the Spawning session state.
7788 */
7789
7790 /* inform the session that it will be a remote one */
7791 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7792#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7793 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7794#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7795 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7796#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7797 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7798
7799 if (FAILED(rc))
7800 {
7801 /* restore the session state */
7802 mData->mSession.mState = SessionState_Unlocked;
7803 alock.release();
7804 mParent->i_addProcessToReap(pid);
7805 /* The failure may occur w/o any error info (from RPC), so provide one */
7806 return setError(VBOX_E_VM_ERROR,
7807 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7808 }
7809
7810 /* attach launch data to the machine */
7811 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7812 mData->mSession.mRemoteControls.push_back(aControl);
7813 mData->mSession.mProgress = aProgress;
7814 mData->mSession.mPID = pid;
7815 mData->mSession.mState = SessionState_Spawning;
7816 Assert(strCanonicalName.isNotEmpty());
7817 mData->mSession.mName = strCanonicalName;
7818 }
7819 else
7820 {
7821 /* For separate UI process we declare the launch as completed instantly, as the
7822 * actual headless VM start may or may not come. No point in remembering anything
7823 * yet, as what matters for us is when the headless VM gets started. */
7824 aProgress->i_notifyComplete(S_OK);
7825 }
7826
7827 alock.release();
7828 mParent->i_addProcessToReap(pid);
7829
7830 LogFlowThisFuncLeave();
7831 return S_OK;
7832}
7833
7834/**
7835 * Returns @c true if the given session machine instance has an open direct
7836 * session (and optionally also for direct sessions which are closing) and
7837 * returns the session control machine instance if so.
7838 *
7839 * Note that when the method returns @c false, the arguments remain unchanged.
7840 *
7841 * @param aMachine Session machine object.
7842 * @param aControl Direct session control object (optional).
7843 * @param aRequireVM If true then only allow VM sessions.
7844 * @param aAllowClosing If true then additionally a session which is currently
7845 * being closed will also be allowed.
7846 *
7847 * @note locks this object for reading.
7848 */
7849bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7850 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7851 bool aRequireVM /*= false*/,
7852 bool aAllowClosing /*= false*/)
7853{
7854 AutoLimitedCaller autoCaller(this);
7855 AssertComRCReturn(autoCaller.rc(), false);
7856
7857 /* just return false for inaccessible machines */
7858 if (getObjectState().getState() != ObjectState::Ready)
7859 return false;
7860
7861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7862
7863 if ( ( mData->mSession.mState == SessionState_Locked
7864 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7865 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7866 )
7867 {
7868 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7869
7870 aMachine = mData->mSession.mMachine;
7871
7872 if (aControl != NULL)
7873 *aControl = mData->mSession.mDirectControl;
7874
7875 return true;
7876 }
7877
7878 return false;
7879}
7880
7881/**
7882 * Returns @c true if the given machine has an spawning direct session.
7883 *
7884 * @note locks this object for reading.
7885 */
7886bool Machine::i_isSessionSpawning()
7887{
7888 AutoLimitedCaller autoCaller(this);
7889 AssertComRCReturn(autoCaller.rc(), false);
7890
7891 /* just return false for inaccessible machines */
7892 if (getObjectState().getState() != ObjectState::Ready)
7893 return false;
7894
7895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7896
7897 if (mData->mSession.mState == SessionState_Spawning)
7898 return true;
7899
7900 return false;
7901}
7902
7903/**
7904 * Called from the client watcher thread to check for unexpected client process
7905 * death during Session_Spawning state (e.g. before it successfully opened a
7906 * direct session).
7907 *
7908 * On Win32 and on OS/2, this method is called only when we've got the
7909 * direct client's process termination notification, so it always returns @c
7910 * true.
7911 *
7912 * On other platforms, this method returns @c true if the client process is
7913 * terminated and @c false if it's still alive.
7914 *
7915 * @note Locks this object for writing.
7916 */
7917bool Machine::i_checkForSpawnFailure()
7918{
7919 AutoCaller autoCaller(this);
7920 if (!autoCaller.isOk())
7921 {
7922 /* nothing to do */
7923 LogFlowThisFunc(("Already uninitialized!\n"));
7924 return true;
7925 }
7926
7927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7928
7929 if (mData->mSession.mState != SessionState_Spawning)
7930 {
7931 /* nothing to do */
7932 LogFlowThisFunc(("Not spawning any more!\n"));
7933 return true;
7934 }
7935
7936 HRESULT rc = S_OK;
7937
7938 /* PID not yet initialized, skip check. */
7939 if (mData->mSession.mPID == NIL_RTPROCESS)
7940 return false;
7941
7942 RTPROCSTATUS status;
7943 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7944
7945 if (vrc != VERR_PROCESS_RUNNING)
7946 {
7947 Utf8Str strExtraInfo;
7948
7949#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7950 /* If the startup logfile exists and is of non-zero length, tell the
7951 user to look there for more details to encourage them to attach it
7952 when reporting startup issues. */
7953 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7954 uint64_t cbStartupLogFile = 0;
7955 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7956 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7957 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7958#endif
7959
7960 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7961 rc = setError(E_FAIL,
7962 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7963 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7964 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7965 rc = setError(E_FAIL,
7966 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7967 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7968 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7969 rc = setError(E_FAIL,
7970 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7971 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7972 else
7973 rc = setError(E_FAIL,
7974 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7975 i_getName().c_str(), vrc, strExtraInfo.c_str());
7976 }
7977
7978 if (FAILED(rc))
7979 {
7980 /* Close the remote session, remove the remote control from the list
7981 * and reset session state to Closed (@note keep the code in sync with
7982 * the relevant part in LockMachine()). */
7983
7984 Assert(mData->mSession.mRemoteControls.size() == 1);
7985 if (mData->mSession.mRemoteControls.size() == 1)
7986 {
7987 ErrorInfoKeeper eik;
7988 mData->mSession.mRemoteControls.front()->Uninitialize();
7989 }
7990
7991 mData->mSession.mRemoteControls.clear();
7992 mData->mSession.mState = SessionState_Unlocked;
7993
7994 /* finalize the progress after setting the state */
7995 if (!mData->mSession.mProgress.isNull())
7996 {
7997 mData->mSession.mProgress->notifyComplete(rc);
7998 mData->mSession.mProgress.setNull();
7999 }
8000
8001 mData->mSession.mPID = NIL_RTPROCESS;
8002
8003 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8004 return true;
8005 }
8006
8007 return false;
8008}
8009
8010/**
8011 * Checks whether the machine can be registered. If so, commits and saves
8012 * all settings.
8013 *
8014 * @note Must be called from mParent's write lock. Locks this object and
8015 * children for writing.
8016 */
8017HRESULT Machine::i_prepareRegister()
8018{
8019 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8020
8021 AutoLimitedCaller autoCaller(this);
8022 AssertComRCReturnRC(autoCaller.rc());
8023
8024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8025
8026 /* wait for state dependents to drop to zero */
8027 i_ensureNoStateDependencies();
8028
8029 if (!mData->mAccessible)
8030 return setError(VBOX_E_INVALID_OBJECT_STATE,
8031 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8032 mUserData->s.strName.c_str(),
8033 mData->mUuid.toString().c_str());
8034
8035 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8036
8037 if (mData->mRegistered)
8038 return setError(VBOX_E_INVALID_OBJECT_STATE,
8039 tr("The machine '%s' with UUID {%s} is already registered"),
8040 mUserData->s.strName.c_str(),
8041 mData->mUuid.toString().c_str());
8042
8043 HRESULT rc = S_OK;
8044
8045 // Ensure the settings are saved. If we are going to be registered and
8046 // no config file exists yet, create it by calling i_saveSettings() too.
8047 if ( (mData->flModifications)
8048 || (!mData->pMachineConfigFile->fileExists())
8049 )
8050 {
8051 rc = i_saveSettings(NULL);
8052 // no need to check whether VirtualBox.xml needs saving too since
8053 // we can't have a machine XML file rename pending
8054 if (FAILED(rc)) return rc;
8055 }
8056
8057 /* more config checking goes here */
8058
8059 if (SUCCEEDED(rc))
8060 {
8061 /* we may have had implicit modifications we want to fix on success */
8062 i_commit();
8063
8064 mData->mRegistered = true;
8065 }
8066 else
8067 {
8068 /* we may have had implicit modifications we want to cancel on failure*/
8069 i_rollback(false /* aNotify */);
8070 }
8071
8072 return rc;
8073}
8074
8075/**
8076 * Increases the number of objects dependent on the machine state or on the
8077 * registered state. Guarantees that these two states will not change at least
8078 * until #releaseStateDependency() is called.
8079 *
8080 * Depending on the @a aDepType value, additional state checks may be made.
8081 * These checks will set extended error info on failure. See
8082 * #checkStateDependency() for more info.
8083 *
8084 * If this method returns a failure, the dependency is not added and the caller
8085 * is not allowed to rely on any particular machine state or registration state
8086 * value and may return the failed result code to the upper level.
8087 *
8088 * @param aDepType Dependency type to add.
8089 * @param aState Current machine state (NULL if not interested).
8090 * @param aRegistered Current registered state (NULL if not interested).
8091 *
8092 * @note Locks this object for writing.
8093 */
8094HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8095 MachineState_T *aState /* = NULL */,
8096 BOOL *aRegistered /* = NULL */)
8097{
8098 AutoCaller autoCaller(this);
8099 AssertComRCReturnRC(autoCaller.rc());
8100
8101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8102
8103 HRESULT rc = i_checkStateDependency(aDepType);
8104 if (FAILED(rc)) return rc;
8105
8106 {
8107 if (mData->mMachineStateChangePending != 0)
8108 {
8109 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8110 * drop to zero so don't add more. It may make sense to wait a bit
8111 * and retry before reporting an error (since the pending state
8112 * transition should be really quick) but let's just assert for
8113 * now to see if it ever happens on practice. */
8114
8115 AssertFailed();
8116
8117 return setError(E_ACCESSDENIED,
8118 tr("Machine state change is in progress. Please retry the operation later."));
8119 }
8120
8121 ++mData->mMachineStateDeps;
8122 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8123 }
8124
8125 if (aState)
8126 *aState = mData->mMachineState;
8127 if (aRegistered)
8128 *aRegistered = mData->mRegistered;
8129
8130 return S_OK;
8131}
8132
8133/**
8134 * Decreases the number of objects dependent on the machine state.
8135 * Must always complete the #addStateDependency() call after the state
8136 * dependency is no more necessary.
8137 */
8138void Machine::i_releaseStateDependency()
8139{
8140 AutoCaller autoCaller(this);
8141 AssertComRCReturnVoid(autoCaller.rc());
8142
8143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8144
8145 /* releaseStateDependency() w/o addStateDependency()? */
8146 AssertReturnVoid(mData->mMachineStateDeps != 0);
8147 -- mData->mMachineStateDeps;
8148
8149 if (mData->mMachineStateDeps == 0)
8150 {
8151 /* inform i_ensureNoStateDependencies() that there are no more deps */
8152 if (mData->mMachineStateChangePending != 0)
8153 {
8154 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8155 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8156 }
8157 }
8158}
8159
8160Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8161{
8162 /* start with nothing found */
8163 Utf8Str strResult("");
8164
8165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8166
8167 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8168 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8169 // found:
8170 strResult = it->second; // source is a Utf8Str
8171
8172 return strResult;
8173}
8174
8175// protected methods
8176/////////////////////////////////////////////////////////////////////////////
8177
8178/**
8179 * Performs machine state checks based on the @a aDepType value. If a check
8180 * fails, this method will set extended error info, otherwise it will return
8181 * S_OK. It is supposed, that on failure, the caller will immediately return
8182 * the return value of this method to the upper level.
8183 *
8184 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8185 *
8186 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8187 * current state of this machine object allows to change settings of the
8188 * machine (i.e. the machine is not registered, or registered but not running
8189 * and not saved). It is useful to call this method from Machine setters
8190 * before performing any change.
8191 *
8192 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8193 * as for MutableStateDep except that if the machine is saved, S_OK is also
8194 * returned. This is useful in setters which allow changing machine
8195 * properties when it is in the saved state.
8196 *
8197 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8198 * if the current state of this machine object allows to change runtime
8199 * changeable settings of the machine (i.e. the machine is not registered, or
8200 * registered but either running or not running and not saved). It is useful
8201 * to call this method from Machine setters before performing any changes to
8202 * runtime changeable settings.
8203 *
8204 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8205 * the same as for MutableOrRunningStateDep except that if the machine is
8206 * saved, S_OK is also returned. This is useful in setters which allow
8207 * changing runtime and saved state changeable machine properties.
8208 *
8209 * @param aDepType Dependency type to check.
8210 *
8211 * @note Non Machine based classes should use #addStateDependency() and
8212 * #releaseStateDependency() methods or the smart AutoStateDependency
8213 * template.
8214 *
8215 * @note This method must be called from under this object's read or write
8216 * lock.
8217 */
8218HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8219{
8220 switch (aDepType)
8221 {
8222 case AnyStateDep:
8223 {
8224 break;
8225 }
8226 case MutableStateDep:
8227 {
8228 if ( mData->mRegistered
8229 && ( !i_isSessionMachine()
8230 || ( mData->mMachineState != MachineState_Aborted
8231 && mData->mMachineState != MachineState_Teleported
8232 && mData->mMachineState != MachineState_PoweredOff
8233 )
8234 )
8235 )
8236 return setError(VBOX_E_INVALID_VM_STATE,
8237 tr("The machine is not mutable (state is %s)"),
8238 Global::stringifyMachineState(mData->mMachineState));
8239 break;
8240 }
8241 case MutableOrSavedStateDep:
8242 {
8243 if ( mData->mRegistered
8244 && ( !i_isSessionMachine()
8245 || ( mData->mMachineState != MachineState_Aborted
8246 && mData->mMachineState != MachineState_Teleported
8247 && mData->mMachineState != MachineState_Saved
8248 && mData->mMachineState != MachineState_PoweredOff
8249 )
8250 )
8251 )
8252 return setError(VBOX_E_INVALID_VM_STATE,
8253 tr("The machine is not mutable or saved (state is %s)"),
8254 Global::stringifyMachineState(mData->mMachineState));
8255 break;
8256 }
8257 case MutableOrRunningStateDep:
8258 {
8259 if ( mData->mRegistered
8260 && ( !i_isSessionMachine()
8261 || ( mData->mMachineState != MachineState_Aborted
8262 && mData->mMachineState != MachineState_Teleported
8263 && mData->mMachineState != MachineState_PoweredOff
8264 && !Global::IsOnline(mData->mMachineState)
8265 )
8266 )
8267 )
8268 return setError(VBOX_E_INVALID_VM_STATE,
8269 tr("The machine is not mutable or running (state is %s)"),
8270 Global::stringifyMachineState(mData->mMachineState));
8271 break;
8272 }
8273 case MutableOrSavedOrRunningStateDep:
8274 {
8275 if ( mData->mRegistered
8276 && ( !i_isSessionMachine()
8277 || ( mData->mMachineState != MachineState_Aborted
8278 && mData->mMachineState != MachineState_Teleported
8279 && mData->mMachineState != MachineState_Saved
8280 && mData->mMachineState != MachineState_PoweredOff
8281 && !Global::IsOnline(mData->mMachineState)
8282 )
8283 )
8284 )
8285 return setError(VBOX_E_INVALID_VM_STATE,
8286 tr("The machine is not mutable, saved or running (state is %s)"),
8287 Global::stringifyMachineState(mData->mMachineState));
8288 break;
8289 }
8290 }
8291
8292 return S_OK;
8293}
8294
8295/**
8296 * Helper to initialize all associated child objects and allocate data
8297 * structures.
8298 *
8299 * This method must be called as a part of the object's initialization procedure
8300 * (usually done in the #init() method).
8301 *
8302 * @note Must be called only from #init() or from #registeredInit().
8303 */
8304HRESULT Machine::initDataAndChildObjects()
8305{
8306 AutoCaller autoCaller(this);
8307 AssertComRCReturnRC(autoCaller.rc());
8308 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8309 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8310
8311 AssertReturn(!mData->mAccessible, E_FAIL);
8312
8313 /* allocate data structures */
8314 mSSData.allocate();
8315 mUserData.allocate();
8316 mHWData.allocate();
8317 mMediaData.allocate();
8318 mStorageControllers.allocate();
8319 mUSBControllers.allocate();
8320
8321 /* initialize mOSTypeId */
8322 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8323
8324 /* create associated BIOS settings object */
8325 unconst(mBIOSSettings).createObject();
8326 mBIOSSettings->init(this);
8327
8328 /* create an associated VRDE object (default is disabled) */
8329 unconst(mVRDEServer).createObject();
8330 mVRDEServer->init(this);
8331
8332 /* create associated serial port objects */
8333 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8334 {
8335 unconst(mSerialPorts[slot]).createObject();
8336 mSerialPorts[slot]->init(this, slot);
8337 }
8338
8339 /* create associated parallel port objects */
8340 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8341 {
8342 unconst(mParallelPorts[slot]).createObject();
8343 mParallelPorts[slot]->init(this, slot);
8344 }
8345
8346 /* create the audio adapter object (always present, default is disabled) */
8347 unconst(mAudioAdapter).createObject();
8348 mAudioAdapter->init(this);
8349
8350 /* create the USB device filters object (always present) */
8351 unconst(mUSBDeviceFilters).createObject();
8352 mUSBDeviceFilters->init(this);
8353
8354 /* create associated network adapter objects */
8355 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8357 {
8358 unconst(mNetworkAdapters[slot]).createObject();
8359 mNetworkAdapters[slot]->init(this, slot);
8360 }
8361
8362 /* create the bandwidth control */
8363 unconst(mBandwidthControl).createObject();
8364 mBandwidthControl->init(this);
8365
8366 return S_OK;
8367}
8368
8369/**
8370 * Helper to uninitialize all associated child objects and to free all data
8371 * structures.
8372 *
8373 * This method must be called as a part of the object's uninitialization
8374 * procedure (usually done in the #uninit() method).
8375 *
8376 * @note Must be called only from #uninit() or from #registeredInit().
8377 */
8378void Machine::uninitDataAndChildObjects()
8379{
8380 AutoCaller autoCaller(this);
8381 AssertComRCReturnVoid(autoCaller.rc());
8382 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8383 || getObjectState().getState() == ObjectState::Limited);
8384
8385 /* tell all our other child objects we've been uninitialized */
8386 if (mBandwidthControl)
8387 {
8388 mBandwidthControl->uninit();
8389 unconst(mBandwidthControl).setNull();
8390 }
8391
8392 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8393 {
8394 if (mNetworkAdapters[slot])
8395 {
8396 mNetworkAdapters[slot]->uninit();
8397 unconst(mNetworkAdapters[slot]).setNull();
8398 }
8399 }
8400
8401 if (mUSBDeviceFilters)
8402 {
8403 mUSBDeviceFilters->uninit();
8404 unconst(mUSBDeviceFilters).setNull();
8405 }
8406
8407 if (mAudioAdapter)
8408 {
8409 mAudioAdapter->uninit();
8410 unconst(mAudioAdapter).setNull();
8411 }
8412
8413 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8414 {
8415 if (mParallelPorts[slot])
8416 {
8417 mParallelPorts[slot]->uninit();
8418 unconst(mParallelPorts[slot]).setNull();
8419 }
8420 }
8421
8422 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8423 {
8424 if (mSerialPorts[slot])
8425 {
8426 mSerialPorts[slot]->uninit();
8427 unconst(mSerialPorts[slot]).setNull();
8428 }
8429 }
8430
8431 if (mVRDEServer)
8432 {
8433 mVRDEServer->uninit();
8434 unconst(mVRDEServer).setNull();
8435 }
8436
8437 if (mBIOSSettings)
8438 {
8439 mBIOSSettings->uninit();
8440 unconst(mBIOSSettings).setNull();
8441 }
8442
8443 /* Deassociate media (only when a real Machine or a SnapshotMachine
8444 * instance is uninitialized; SessionMachine instances refer to real
8445 * Machine media). This is necessary for a clean re-initialization of
8446 * the VM after successfully re-checking the accessibility state. Note
8447 * that in case of normal Machine or SnapshotMachine uninitialization (as
8448 * a result of unregistering or deleting the snapshot), outdated media
8449 * attachments will already be uninitialized and deleted, so this
8450 * code will not affect them. */
8451 if ( !!mMediaData
8452 && (!i_isSessionMachine())
8453 )
8454 {
8455 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8456 it != mMediaData->mAttachments.end();
8457 ++it)
8458 {
8459 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8460 if (pMedium.isNull())
8461 continue;
8462 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8463 AssertComRC(rc);
8464 }
8465 }
8466
8467 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8468 {
8469 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8470 if (mData->mFirstSnapshot)
8471 {
8472 // snapshots tree is protected by machine write lock; strictly
8473 // this isn't necessary here since we're deleting the entire
8474 // machine, but otherwise we assert in Snapshot::uninit()
8475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8476 mData->mFirstSnapshot->uninit();
8477 mData->mFirstSnapshot.setNull();
8478 }
8479
8480 mData->mCurrentSnapshot.setNull();
8481 }
8482
8483 /* free data structures (the essential mData structure is not freed here
8484 * since it may be still in use) */
8485 mMediaData.free();
8486 mStorageControllers.free();
8487 mUSBControllers.free();
8488 mHWData.free();
8489 mUserData.free();
8490 mSSData.free();
8491}
8492
8493/**
8494 * Returns a pointer to the Machine object for this machine that acts like a
8495 * parent for complex machine data objects such as shared folders, etc.
8496 *
8497 * For primary Machine objects and for SnapshotMachine objects, returns this
8498 * object's pointer itself. For SessionMachine objects, returns the peer
8499 * (primary) machine pointer.
8500 */
8501Machine* Machine::i_getMachine()
8502{
8503 if (i_isSessionMachine())
8504 return (Machine*)mPeer;
8505 return this;
8506}
8507
8508/**
8509 * Makes sure that there are no machine state dependents. If necessary, waits
8510 * for the number of dependents to drop to zero.
8511 *
8512 * Make sure this method is called from under this object's write lock to
8513 * guarantee that no new dependents may be added when this method returns
8514 * control to the caller.
8515 *
8516 * @note Locks this object for writing. The lock will be released while waiting
8517 * (if necessary).
8518 *
8519 * @warning To be used only in methods that change the machine state!
8520 */
8521void Machine::i_ensureNoStateDependencies()
8522{
8523 AssertReturnVoid(isWriteLockOnCurrentThread());
8524
8525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8526
8527 /* Wait for all state dependents if necessary */
8528 if (mData->mMachineStateDeps != 0)
8529 {
8530 /* lazy semaphore creation */
8531 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8532 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8533
8534 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8535 mData->mMachineStateDeps));
8536
8537 ++mData->mMachineStateChangePending;
8538
8539 /* reset the semaphore before waiting, the last dependent will signal
8540 * it */
8541 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8542
8543 alock.release();
8544
8545 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8546
8547 alock.acquire();
8548
8549 -- mData->mMachineStateChangePending;
8550 }
8551}
8552
8553/**
8554 * Changes the machine state and informs callbacks.
8555 *
8556 * This method is not intended to fail so it either returns S_OK or asserts (and
8557 * returns a failure).
8558 *
8559 * @note Locks this object for writing.
8560 */
8561HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8562{
8563 LogFlowThisFuncEnter();
8564 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8565 Assert(aMachineState != MachineState_Null);
8566
8567 AutoCaller autoCaller(this);
8568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8569
8570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8571
8572 /* wait for state dependents to drop to zero */
8573 i_ensureNoStateDependencies();
8574
8575 MachineState_T const enmOldState = mData->mMachineState;
8576 if (enmOldState != aMachineState)
8577 {
8578 mData->mMachineState = aMachineState;
8579 RTTimeNow(&mData->mLastStateChange);
8580
8581#ifdef VBOX_WITH_DTRACE_R3_MAIN
8582 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8583#endif
8584 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8585 }
8586
8587 LogFlowThisFuncLeave();
8588 return S_OK;
8589}
8590
8591/**
8592 * Searches for a shared folder with the given logical name
8593 * in the collection of shared folders.
8594 *
8595 * @param aName logical name of the shared folder
8596 * @param aSharedFolder where to return the found object
8597 * @param aSetError whether to set the error info if the folder is
8598 * not found
8599 * @return
8600 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8601 *
8602 * @note
8603 * must be called from under the object's lock!
8604 */
8605HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8606 ComObjPtr<SharedFolder> &aSharedFolder,
8607 bool aSetError /* = false */)
8608{
8609 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8610 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8611 it != mHWData->mSharedFolders.end();
8612 ++it)
8613 {
8614 SharedFolder *pSF = *it;
8615 AutoCaller autoCaller(pSF);
8616 if (pSF->i_getName() == aName)
8617 {
8618 aSharedFolder = pSF;
8619 rc = S_OK;
8620 break;
8621 }
8622 }
8623
8624 if (aSetError && FAILED(rc))
8625 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8626
8627 return rc;
8628}
8629
8630/**
8631 * Initializes all machine instance data from the given settings structures
8632 * from XML. The exception is the machine UUID which needs special handling
8633 * depending on the caller's use case, so the caller needs to set that herself.
8634 *
8635 * This gets called in several contexts during machine initialization:
8636 *
8637 * -- When machine XML exists on disk already and needs to be loaded into memory,
8638 * for example, from registeredInit() to load all registered machines on
8639 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8640 * attached to the machine should be part of some media registry already.
8641 *
8642 * -- During OVF import, when a machine config has been constructed from an
8643 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8644 * ensure that the media listed as attachments in the config (which have
8645 * been imported from the OVF) receive the correct registry ID.
8646 *
8647 * -- During VM cloning.
8648 *
8649 * @param config Machine settings from XML.
8650 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8651 * for each attached medium in the config.
8652 * @return
8653 */
8654HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8655 const Guid *puuidRegistry)
8656{
8657 // copy name, description, OS type, teleporter, UTC etc.
8658 mUserData->s = config.machineUserData;
8659
8660 // look up the object by Id to check it is valid
8661 ComPtr<IGuestOSType> guestOSType;
8662 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8663 guestOSType.asOutParam());
8664 if (FAILED(rc)) return rc;
8665
8666 // stateFile (optional)
8667 if (config.strStateFile.isEmpty())
8668 mSSData->strStateFilePath.setNull();
8669 else
8670 {
8671 Utf8Str stateFilePathFull(config.strStateFile);
8672 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8673 if (RT_FAILURE(vrc))
8674 return setError(E_FAIL,
8675 tr("Invalid saved state file path '%s' (%Rrc)"),
8676 config.strStateFile.c_str(),
8677 vrc);
8678 mSSData->strStateFilePath = stateFilePathFull;
8679 }
8680
8681 // snapshot folder needs special processing so set it again
8682 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8683 if (FAILED(rc)) return rc;
8684
8685 /* Copy the extra data items (Not in any case config is already the same as
8686 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8687 * make sure the extra data map is copied). */
8688 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8689
8690 /* currentStateModified (optional, default is true) */
8691 mData->mCurrentStateModified = config.fCurrentStateModified;
8692
8693 mData->mLastStateChange = config.timeLastStateChange;
8694
8695 /*
8696 * note: all mUserData members must be assigned prior this point because
8697 * we need to commit changes in order to let mUserData be shared by all
8698 * snapshot machine instances.
8699 */
8700 mUserData.commitCopy();
8701
8702 // machine registry, if present (must be loaded before snapshots)
8703 if (config.canHaveOwnMediaRegistry())
8704 {
8705 // determine machine folder
8706 Utf8Str strMachineFolder = i_getSettingsFileFull();
8707 strMachineFolder.stripFilename();
8708 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8709 config.mediaRegistry,
8710 strMachineFolder);
8711 if (FAILED(rc)) return rc;
8712 }
8713
8714 /* Snapshot node (optional) */
8715 size_t cRootSnapshots;
8716 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8717 {
8718 // there must be only one root snapshot
8719 Assert(cRootSnapshots == 1);
8720
8721 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8722
8723 rc = i_loadSnapshot(snap,
8724 config.uuidCurrentSnapshot,
8725 NULL); // no parent == first snapshot
8726 if (FAILED(rc)) return rc;
8727 }
8728
8729 // hardware data
8730 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8731 if (FAILED(rc)) return rc;
8732
8733 /*
8734 * NOTE: the assignment below must be the last thing to do,
8735 * otherwise it will be not possible to change the settings
8736 * somewhere in the code above because all setters will be
8737 * blocked by i_checkStateDependency(MutableStateDep).
8738 */
8739
8740 /* set the machine state to Aborted or Saved when appropriate */
8741 if (config.fAborted)
8742 {
8743 mSSData->strStateFilePath.setNull();
8744
8745 /* no need to use i_setMachineState() during init() */
8746 mData->mMachineState = MachineState_Aborted;
8747 }
8748 else if (!mSSData->strStateFilePath.isEmpty())
8749 {
8750 /* no need to use i_setMachineState() during init() */
8751 mData->mMachineState = MachineState_Saved;
8752 }
8753
8754 // after loading settings, we are no longer different from the XML on disk
8755 mData->flModifications = 0;
8756
8757 return S_OK;
8758}
8759
8760/**
8761 * Recursively loads all snapshots starting from the given.
8762 *
8763 * @param aNode <Snapshot> node.
8764 * @param aCurSnapshotId Current snapshot ID from the settings file.
8765 * @param aParentSnapshot Parent snapshot.
8766 */
8767HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8768 const Guid &aCurSnapshotId,
8769 Snapshot *aParentSnapshot)
8770{
8771 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8772 AssertReturn(!i_isSessionMachine(), E_FAIL);
8773
8774 HRESULT rc = S_OK;
8775
8776 Utf8Str strStateFile;
8777 if (!data.strStateFile.isEmpty())
8778 {
8779 /* optional */
8780 strStateFile = data.strStateFile;
8781 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8782 if (RT_FAILURE(vrc))
8783 return setError(E_FAIL,
8784 tr("Invalid saved state file path '%s' (%Rrc)"),
8785 strStateFile.c_str(),
8786 vrc);
8787 }
8788
8789 /* create a snapshot machine object */
8790 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8791 pSnapshotMachine.createObject();
8792 rc = pSnapshotMachine->initFromSettings(this,
8793 data.hardware,
8794 &data.debugging,
8795 &data.autostart,
8796 data.uuid.ref(),
8797 strStateFile);
8798 if (FAILED(rc)) return rc;
8799
8800 /* create a snapshot object */
8801 ComObjPtr<Snapshot> pSnapshot;
8802 pSnapshot.createObject();
8803 /* initialize the snapshot */
8804 rc = pSnapshot->init(mParent, // VirtualBox object
8805 data.uuid,
8806 data.strName,
8807 data.strDescription,
8808 data.timestamp,
8809 pSnapshotMachine,
8810 aParentSnapshot);
8811 if (FAILED(rc)) return rc;
8812
8813 /* memorize the first snapshot if necessary */
8814 if (!mData->mFirstSnapshot)
8815 mData->mFirstSnapshot = pSnapshot;
8816
8817 /* memorize the current snapshot when appropriate */
8818 if ( !mData->mCurrentSnapshot
8819 && pSnapshot->i_getId() == aCurSnapshotId
8820 )
8821 mData->mCurrentSnapshot = pSnapshot;
8822
8823 // now create the children
8824 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8825 it != data.llChildSnapshots.end();
8826 ++it)
8827 {
8828 const settings::Snapshot &childData = *it;
8829 // recurse
8830 rc = i_loadSnapshot(childData,
8831 aCurSnapshotId,
8832 pSnapshot); // parent = the one we created above
8833 if (FAILED(rc)) return rc;
8834 }
8835
8836 return rc;
8837}
8838
8839/**
8840 * Loads settings into mHWData.
8841 *
8842 * @param data Reference to the hardware settings.
8843 * @param pDbg Pointer to the debugging settings.
8844 * @param pAutostart Pointer to the autostart settings.
8845 */
8846HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8847 const Guid *puuidSnapshot,
8848 const settings::Hardware &data,
8849 const settings::Debugging *pDbg,
8850 const settings::Autostart *pAutostart)
8851{
8852 AssertReturn(!i_isSessionMachine(), E_FAIL);
8853
8854 HRESULT rc = S_OK;
8855
8856 try
8857 {
8858 /* The hardware version attribute (optional). */
8859 mHWData->mHWVersion = data.strVersion;
8860 mHWData->mHardwareUUID = data.uuid;
8861
8862 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8863 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8864 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8865 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8866 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8867 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8868 mHWData->mPAEEnabled = data.fPAE;
8869 mHWData->mLongMode = data.enmLongMode;
8870 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8871 mHWData->mAPIC = data.fAPIC;
8872 mHWData->mX2APIC = data.fX2APIC;
8873 mHWData->mCPUCount = data.cCPUs;
8874 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8875 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8876 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8877 mHWData->mCpuProfile = data.strCpuProfile;
8878
8879 // cpu
8880 if (mHWData->mCPUHotPlugEnabled)
8881 {
8882 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8883 it != data.llCpus.end();
8884 ++it)
8885 {
8886 const settings::Cpu &cpu = *it;
8887
8888 mHWData->mCPUAttached[cpu.ulId] = true;
8889 }
8890 }
8891
8892 // cpuid leafs
8893 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8894 it != data.llCpuIdLeafs.end();
8895 ++it)
8896 {
8897 const settings::CpuIdLeaf &leaf = *it;
8898
8899 switch (leaf.ulId)
8900 {
8901 case 0x0:
8902 case 0x1:
8903 case 0x2:
8904 case 0x3:
8905 case 0x4:
8906 case 0x5:
8907 case 0x6:
8908 case 0x7:
8909 case 0x8:
8910 case 0x9:
8911 case 0xA:
8912 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8913 break;
8914
8915 case 0x80000000:
8916 case 0x80000001:
8917 case 0x80000002:
8918 case 0x80000003:
8919 case 0x80000004:
8920 case 0x80000005:
8921 case 0x80000006:
8922 case 0x80000007:
8923 case 0x80000008:
8924 case 0x80000009:
8925 case 0x8000000A:
8926 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8927 break;
8928
8929 default:
8930 /* just ignore */
8931 break;
8932 }
8933 }
8934
8935 mHWData->mMemorySize = data.ulMemorySizeMB;
8936 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8937
8938 // boot order
8939 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8940 {
8941 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8942 if (it == data.mapBootOrder.end())
8943 mHWData->mBootOrder[i] = DeviceType_Null;
8944 else
8945 mHWData->mBootOrder[i] = it->second;
8946 }
8947
8948 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8949 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8950 mHWData->mMonitorCount = data.cMonitors;
8951 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8952 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8953 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8954 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8955 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8956 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8957 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8958 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8959 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8960 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8961 if (!data.strVideoCaptureFile.isEmpty())
8962 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8963 else
8964 mHWData->mVideoCaptureFile.setNull();
8965 mHWData->mFirmwareType = data.firmwareType;
8966 mHWData->mPointingHIDType = data.pointingHIDType;
8967 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8968 mHWData->mChipsetType = data.chipsetType;
8969 mHWData->mParavirtProvider = data.paravirtProvider;
8970 mHWData->mParavirtDebug = data.strParavirtDebug;
8971 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8972 mHWData->mHPETEnabled = data.fHPETEnabled;
8973
8974 /* VRDEServer */
8975 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8976 if (FAILED(rc)) return rc;
8977
8978 /* BIOS */
8979 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8980 if (FAILED(rc)) return rc;
8981
8982 // Bandwidth control (must come before network adapters)
8983 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8984 if (FAILED(rc)) return rc;
8985
8986 /* Shared folders */
8987 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8988 it != data.usbSettings.llUSBControllers.end();
8989 ++it)
8990 {
8991 const settings::USBController &settingsCtrl = *it;
8992 ComObjPtr<USBController> newCtrl;
8993
8994 newCtrl.createObject();
8995 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8996 mUSBControllers->push_back(newCtrl);
8997 }
8998
8999 /* USB device filters */
9000 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9001 if (FAILED(rc)) return rc;
9002
9003 // network adapters
9004 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9005 size_t oldCount = mNetworkAdapters.size();
9006 if (newCount > oldCount)
9007 {
9008 mNetworkAdapters.resize(newCount);
9009 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9010 {
9011 unconst(mNetworkAdapters[slot]).createObject();
9012 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9013 }
9014 }
9015 else if (newCount < oldCount)
9016 mNetworkAdapters.resize(newCount);
9017 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9018 it != data.llNetworkAdapters.end();
9019 ++it)
9020 {
9021 const settings::NetworkAdapter &nic = *it;
9022
9023 /* slot unicity is guaranteed by XML Schema */
9024 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9025 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9026 if (FAILED(rc)) return rc;
9027 }
9028
9029 // serial ports
9030 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9031 it != data.llSerialPorts.end();
9032 ++it)
9033 {
9034 const settings::SerialPort &s = *it;
9035
9036 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9037 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9038 if (FAILED(rc)) return rc;
9039 }
9040
9041 // parallel ports (optional)
9042 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9043 it != data.llParallelPorts.end();
9044 ++it)
9045 {
9046 const settings::ParallelPort &p = *it;
9047
9048 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9049 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9050 if (FAILED(rc)) return rc;
9051 }
9052
9053 /* AudioAdapter */
9054 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9055 if (FAILED(rc)) return rc;
9056
9057 /* storage controllers */
9058 rc = i_loadStorageControllers(data.storage,
9059 puuidRegistry,
9060 puuidSnapshot);
9061 if (FAILED(rc)) return rc;
9062
9063 /* Shared folders */
9064 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9065 it != data.llSharedFolders.end();
9066 ++it)
9067 {
9068 const settings::SharedFolder &sf = *it;
9069
9070 ComObjPtr<SharedFolder> sharedFolder;
9071 /* Check for double entries. Not allowed! */
9072 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9073 if (SUCCEEDED(rc))
9074 return setError(VBOX_E_OBJECT_IN_USE,
9075 tr("Shared folder named '%s' already exists"),
9076 sf.strName.c_str());
9077
9078 /* Create the new shared folder. Don't break on error. This will be
9079 * reported when the machine starts. */
9080 sharedFolder.createObject();
9081 rc = sharedFolder->init(i_getMachine(),
9082 sf.strName,
9083 sf.strHostPath,
9084 RT_BOOL(sf.fWritable),
9085 RT_BOOL(sf.fAutoMount),
9086 false /* fFailOnError */);
9087 if (FAILED(rc)) return rc;
9088 mHWData->mSharedFolders.push_back(sharedFolder);
9089 }
9090
9091 // Clipboard
9092 mHWData->mClipboardMode = data.clipboardMode;
9093
9094 // drag'n'drop
9095 mHWData->mDnDMode = data.dndMode;
9096
9097 // guest settings
9098 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9099
9100 // IO settings
9101 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9102 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9103
9104 // Host PCI devices
9105 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9106 it != data.pciAttachments.end();
9107 ++it)
9108 {
9109 const settings::HostPCIDeviceAttachment &hpda = *it;
9110 ComObjPtr<PCIDeviceAttachment> pda;
9111
9112 pda.createObject();
9113 pda->i_loadSettings(this, hpda);
9114 mHWData->mPCIDeviceAssignments.push_back(pda);
9115 }
9116
9117 /*
9118 * (The following isn't really real hardware, but it lives in HWData
9119 * for reasons of convenience.)
9120 */
9121
9122#ifdef VBOX_WITH_GUEST_PROPS
9123 /* Guest properties (optional) */
9124
9125 /* Only load transient guest properties for configs which have saved
9126 * state, because there shouldn't be any for powered off VMs. The same
9127 * logic applies for snapshots, as offline snapshots shouldn't have
9128 * any such properties. They confuse the code in various places.
9129 * Note: can't rely on the machine state, as it isn't set yet. */
9130 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9131 /* apologies for the hacky unconst() usage, but this needs hacking
9132 * actually inconsistent settings into consistency, otherwise there
9133 * will be some corner cases where the inconsistency survives
9134 * surprisingly long without getting fixed, especially for snapshots
9135 * as there are no config changes. */
9136 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9137 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9138 it != llGuestProperties.end();
9139 /*nothing*/)
9140 {
9141 const settings::GuestProperty &prop = *it;
9142 uint32_t fFlags = guestProp::NILFLAG;
9143 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9144 if ( fSkipTransientGuestProperties
9145 && ( fFlags & guestProp::TRANSIENT
9146 || fFlags & guestProp::TRANSRESET))
9147 {
9148 it = llGuestProperties.erase(it);
9149 continue;
9150 }
9151 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9152 mHWData->mGuestProperties[prop.strName] = property;
9153 ++it;
9154 }
9155#endif /* VBOX_WITH_GUEST_PROPS defined */
9156
9157 rc = i_loadDebugging(pDbg);
9158 if (FAILED(rc))
9159 return rc;
9160
9161 mHWData->mAutostart = *pAutostart;
9162
9163 /* default frontend */
9164 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9165 }
9166 catch(std::bad_alloc &)
9167 {
9168 return E_OUTOFMEMORY;
9169 }
9170
9171 AssertComRC(rc);
9172 return rc;
9173}
9174
9175/**
9176 * Called from Machine::loadHardware() to load the debugging settings of the
9177 * machine.
9178 *
9179 * @param pDbg Pointer to the settings.
9180 */
9181HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9182{
9183 mHWData->mDebugging = *pDbg;
9184 /* no more processing currently required, this will probably change. */
9185 return S_OK;
9186}
9187
9188/**
9189 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9190 *
9191 * @param data
9192 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9193 * @param puuidSnapshot
9194 * @return
9195 */
9196HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9197 const Guid *puuidRegistry,
9198 const Guid *puuidSnapshot)
9199{
9200 AssertReturn(!i_isSessionMachine(), E_FAIL);
9201
9202 HRESULT rc = S_OK;
9203
9204 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9205 it != data.llStorageControllers.end();
9206 ++it)
9207 {
9208 const settings::StorageController &ctlData = *it;
9209
9210 ComObjPtr<StorageController> pCtl;
9211 /* Try to find one with the name first. */
9212 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9213 if (SUCCEEDED(rc))
9214 return setError(VBOX_E_OBJECT_IN_USE,
9215 tr("Storage controller named '%s' already exists"),
9216 ctlData.strName.c_str());
9217
9218 pCtl.createObject();
9219 rc = pCtl->init(this,
9220 ctlData.strName,
9221 ctlData.storageBus,
9222 ctlData.ulInstance,
9223 ctlData.fBootable);
9224 if (FAILED(rc)) return rc;
9225
9226 mStorageControllers->push_back(pCtl);
9227
9228 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9229 if (FAILED(rc)) return rc;
9230
9231 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9232 if (FAILED(rc)) return rc;
9233
9234 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9235 if (FAILED(rc)) return rc;
9236
9237 /* Load the attached devices now. */
9238 rc = i_loadStorageDevices(pCtl,
9239 ctlData,
9240 puuidRegistry,
9241 puuidSnapshot);
9242 if (FAILED(rc)) return rc;
9243 }
9244
9245 return S_OK;
9246}
9247
9248/**
9249 * Called from i_loadStorageControllers for a controller's devices.
9250 *
9251 * @param aStorageController
9252 * @param data
9253 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9254 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9255 * @return
9256 */
9257HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9258 const settings::StorageController &data,
9259 const Guid *puuidRegistry,
9260 const Guid *puuidSnapshot)
9261{
9262 HRESULT rc = S_OK;
9263
9264 /* paranoia: detect duplicate attachments */
9265 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9266 it != data.llAttachedDevices.end();
9267 ++it)
9268 {
9269 const settings::AttachedDevice &ad = *it;
9270
9271 for (settings::AttachedDevicesList::const_iterator it2 = it;
9272 it2 != data.llAttachedDevices.end();
9273 ++it2)
9274 {
9275 if (it == it2)
9276 continue;
9277
9278 const settings::AttachedDevice &ad2 = *it2;
9279
9280 if ( ad.lPort == ad2.lPort
9281 && ad.lDevice == ad2.lDevice)
9282 {
9283 return setError(E_FAIL,
9284 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9285 aStorageController->i_getName().c_str(),
9286 ad.lPort,
9287 ad.lDevice,
9288 mUserData->s.strName.c_str());
9289 }
9290 }
9291 }
9292
9293 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9294 it != data.llAttachedDevices.end();
9295 ++it)
9296 {
9297 const settings::AttachedDevice &dev = *it;
9298 ComObjPtr<Medium> medium;
9299
9300 switch (dev.deviceType)
9301 {
9302 case DeviceType_Floppy:
9303 case DeviceType_DVD:
9304 if (dev.strHostDriveSrc.isNotEmpty())
9305 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9306 false /* fRefresh */, medium);
9307 else
9308 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9309 dev.uuid,
9310 false /* fRefresh */,
9311 false /* aSetError */,
9312 medium);
9313 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9314 // This is not an error. The host drive or UUID might have vanished, so just go
9315 // ahead without this removeable medium attachment
9316 rc = S_OK;
9317 break;
9318
9319 case DeviceType_HardDisk:
9320 {
9321 /* find a hard disk by UUID */
9322 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9323 if (FAILED(rc))
9324 {
9325 if (i_isSnapshotMachine())
9326 {
9327 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9328 // so the user knows that the bad disk is in a snapshot somewhere
9329 com::ErrorInfo info;
9330 return setError(E_FAIL,
9331 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9332 puuidSnapshot->raw(),
9333 info.getText().raw());
9334 }
9335 else
9336 return rc;
9337 }
9338
9339 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9340
9341 if (medium->i_getType() == MediumType_Immutable)
9342 {
9343 if (i_isSnapshotMachine())
9344 return setError(E_FAIL,
9345 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9346 "of the virtual machine '%s' ('%s')"),
9347 medium->i_getLocationFull().c_str(),
9348 dev.uuid.raw(),
9349 puuidSnapshot->raw(),
9350 mUserData->s.strName.c_str(),
9351 mData->m_strConfigFileFull.c_str());
9352
9353 return setError(E_FAIL,
9354 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9355 medium->i_getLocationFull().c_str(),
9356 dev.uuid.raw(),
9357 mUserData->s.strName.c_str(),
9358 mData->m_strConfigFileFull.c_str());
9359 }
9360
9361 if (medium->i_getType() == MediumType_MultiAttach)
9362 {
9363 if (i_isSnapshotMachine())
9364 return setError(E_FAIL,
9365 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9366 "of the virtual machine '%s' ('%s')"),
9367 medium->i_getLocationFull().c_str(),
9368 dev.uuid.raw(),
9369 puuidSnapshot->raw(),
9370 mUserData->s.strName.c_str(),
9371 mData->m_strConfigFileFull.c_str());
9372
9373 return setError(E_FAIL,
9374 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9375 medium->i_getLocationFull().c_str(),
9376 dev.uuid.raw(),
9377 mUserData->s.strName.c_str(),
9378 mData->m_strConfigFileFull.c_str());
9379 }
9380
9381 if ( !i_isSnapshotMachine()
9382 && medium->i_getChildren().size() != 0
9383 )
9384 return setError(E_FAIL,
9385 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9386 "because it has %d differencing child hard disks"),
9387 medium->i_getLocationFull().c_str(),
9388 dev.uuid.raw(),
9389 mUserData->s.strName.c_str(),
9390 mData->m_strConfigFileFull.c_str(),
9391 medium->i_getChildren().size());
9392
9393 if (i_findAttachment(mMediaData->mAttachments,
9394 medium))
9395 return setError(E_FAIL,
9396 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9397 medium->i_getLocationFull().c_str(),
9398 dev.uuid.raw(),
9399 mUserData->s.strName.c_str(),
9400 mData->m_strConfigFileFull.c_str());
9401
9402 break;
9403 }
9404
9405 default:
9406 return setError(E_FAIL,
9407 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9408 medium->i_getLocationFull().c_str(),
9409 mUserData->s.strName.c_str(),
9410 mData->m_strConfigFileFull.c_str());
9411 }
9412
9413 if (FAILED(rc))
9414 break;
9415
9416 /* Bandwidth groups are loaded at this point. */
9417 ComObjPtr<BandwidthGroup> pBwGroup;
9418
9419 if (!dev.strBwGroup.isEmpty())
9420 {
9421 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9422 if (FAILED(rc))
9423 return setError(E_FAIL,
9424 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9425 medium->i_getLocationFull().c_str(),
9426 dev.strBwGroup.c_str(),
9427 mUserData->s.strName.c_str(),
9428 mData->m_strConfigFileFull.c_str());
9429 pBwGroup->i_reference();
9430 }
9431
9432 const Bstr controllerName = aStorageController->i_getName();
9433 ComObjPtr<MediumAttachment> pAttachment;
9434 pAttachment.createObject();
9435 rc = pAttachment->init(this,
9436 medium,
9437 controllerName,
9438 dev.lPort,
9439 dev.lDevice,
9440 dev.deviceType,
9441 false,
9442 dev.fPassThrough,
9443 dev.fTempEject,
9444 dev.fNonRotational,
9445 dev.fDiscard,
9446 dev.fHotPluggable,
9447 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9448 if (FAILED(rc)) break;
9449
9450 /* associate the medium with this machine and snapshot */
9451 if (!medium.isNull())
9452 {
9453 AutoCaller medCaller(medium);
9454 if (FAILED(medCaller.rc())) return medCaller.rc();
9455 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9456
9457 if (i_isSnapshotMachine())
9458 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9459 else
9460 rc = medium->i_addBackReference(mData->mUuid);
9461 /* If the medium->addBackReference fails it sets an appropriate
9462 * error message, so no need to do any guesswork here. */
9463
9464 if (puuidRegistry)
9465 // caller wants registry ID to be set on all attached media (OVF import case)
9466 medium->i_addRegistry(*puuidRegistry);
9467 }
9468
9469 if (FAILED(rc))
9470 break;
9471
9472 /* back up mMediaData to let registeredInit() properly rollback on failure
9473 * (= limited accessibility) */
9474 i_setModified(IsModified_Storage);
9475 mMediaData.backup();
9476 mMediaData->mAttachments.push_back(pAttachment);
9477 }
9478
9479 return rc;
9480}
9481
9482/**
9483 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9484 *
9485 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9486 * @param aSnapshot where to return the found snapshot
9487 * @param aSetError true to set extended error info on failure
9488 */
9489HRESULT Machine::i_findSnapshotById(const Guid &aId,
9490 ComObjPtr<Snapshot> &aSnapshot,
9491 bool aSetError /* = false */)
9492{
9493 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9494
9495 if (!mData->mFirstSnapshot)
9496 {
9497 if (aSetError)
9498 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9499 return E_FAIL;
9500 }
9501
9502 if (aId.isZero())
9503 aSnapshot = mData->mFirstSnapshot;
9504 else
9505 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9506
9507 if (!aSnapshot)
9508 {
9509 if (aSetError)
9510 return setError(E_FAIL,
9511 tr("Could not find a snapshot with UUID {%s}"),
9512 aId.toString().c_str());
9513 return E_FAIL;
9514 }
9515
9516 return S_OK;
9517}
9518
9519/**
9520 * Returns the snapshot with the given name or fails of no such snapshot.
9521 *
9522 * @param aName snapshot name to find
9523 * @param aSnapshot where to return the found snapshot
9524 * @param aSetError true to set extended error info on failure
9525 */
9526HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9527 ComObjPtr<Snapshot> &aSnapshot,
9528 bool aSetError /* = false */)
9529{
9530 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9531
9532 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9533
9534 if (!mData->mFirstSnapshot)
9535 {
9536 if (aSetError)
9537 return setError(VBOX_E_OBJECT_NOT_FOUND,
9538 tr("This machine does not have any snapshots"));
9539 return VBOX_E_OBJECT_NOT_FOUND;
9540 }
9541
9542 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9543
9544 if (!aSnapshot)
9545 {
9546 if (aSetError)
9547 return setError(VBOX_E_OBJECT_NOT_FOUND,
9548 tr("Could not find a snapshot named '%s'"), strName.c_str());
9549 return VBOX_E_OBJECT_NOT_FOUND;
9550 }
9551
9552 return S_OK;
9553}
9554
9555/**
9556 * Returns a storage controller object with the given name.
9557 *
9558 * @param aName storage controller name to find
9559 * @param aStorageController where to return the found storage controller
9560 * @param aSetError true to set extended error info on failure
9561 */
9562HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9563 ComObjPtr<StorageController> &aStorageController,
9564 bool aSetError /* = false */)
9565{
9566 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9567
9568 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9569 it != mStorageControllers->end();
9570 ++it)
9571 {
9572 if ((*it)->i_getName() == aName)
9573 {
9574 aStorageController = (*it);
9575 return S_OK;
9576 }
9577 }
9578
9579 if (aSetError)
9580 return setError(VBOX_E_OBJECT_NOT_FOUND,
9581 tr("Could not find a storage controller named '%s'"),
9582 aName.c_str());
9583 return VBOX_E_OBJECT_NOT_FOUND;
9584}
9585
9586/**
9587 * Returns a USB controller object with the given name.
9588 *
9589 * @param aName USB controller name to find
9590 * @param aUSBController where to return the found USB controller
9591 * @param aSetError true to set extended error info on failure
9592 */
9593HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9594 ComObjPtr<USBController> &aUSBController,
9595 bool aSetError /* = false */)
9596{
9597 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9598
9599 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9600 it != mUSBControllers->end();
9601 ++it)
9602 {
9603 if ((*it)->i_getName() == aName)
9604 {
9605 aUSBController = (*it);
9606 return S_OK;
9607 }
9608 }
9609
9610 if (aSetError)
9611 return setError(VBOX_E_OBJECT_NOT_FOUND,
9612 tr("Could not find a storage controller named '%s'"),
9613 aName.c_str());
9614 return VBOX_E_OBJECT_NOT_FOUND;
9615}
9616
9617/**
9618 * Returns the number of USB controller instance of the given type.
9619 *
9620 * @param enmType USB controller type.
9621 */
9622ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9623{
9624 ULONG cCtrls = 0;
9625
9626 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9627 it != mUSBControllers->end();
9628 ++it)
9629 {
9630 if ((*it)->i_getControllerType() == enmType)
9631 cCtrls++;
9632 }
9633
9634 return cCtrls;
9635}
9636
9637HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9638 MediaData::AttachmentList &atts)
9639{
9640 AutoCaller autoCaller(this);
9641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9642
9643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9644
9645 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9646 it != mMediaData->mAttachments.end();
9647 ++it)
9648 {
9649 const ComObjPtr<MediumAttachment> &pAtt = *it;
9650 // should never happen, but deal with NULL pointers in the list.
9651 AssertContinue(!pAtt.isNull());
9652
9653 // getControllerName() needs caller+read lock
9654 AutoCaller autoAttCaller(pAtt);
9655 if (FAILED(autoAttCaller.rc()))
9656 {
9657 atts.clear();
9658 return autoAttCaller.rc();
9659 }
9660 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9661
9662 if (pAtt->i_getControllerName() == aName)
9663 atts.push_back(pAtt);
9664 }
9665
9666 return S_OK;
9667}
9668
9669
9670/**
9671 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9672 * file if the machine name was changed and about creating a new settings file
9673 * if this is a new machine.
9674 *
9675 * @note Must be never called directly but only from #saveSettings().
9676 */
9677HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9678{
9679 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9680
9681 HRESULT rc = S_OK;
9682
9683 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9684
9685 /// @todo need to handle primary group change, too
9686
9687 /* attempt to rename the settings file if machine name is changed */
9688 if ( mUserData->s.fNameSync
9689 && mUserData.isBackedUp()
9690 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9691 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9692 )
9693 {
9694 bool dirRenamed = false;
9695 bool fileRenamed = false;
9696
9697 Utf8Str configFile, newConfigFile;
9698 Utf8Str configFilePrev, newConfigFilePrev;
9699 Utf8Str configDir, newConfigDir;
9700
9701 do
9702 {
9703 int vrc = VINF_SUCCESS;
9704
9705 Utf8Str name = mUserData.backedUpData()->s.strName;
9706 Utf8Str newName = mUserData->s.strName;
9707 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9708 if (group == "/")
9709 group.setNull();
9710 Utf8Str newGroup = mUserData->s.llGroups.front();
9711 if (newGroup == "/")
9712 newGroup.setNull();
9713
9714 configFile = mData->m_strConfigFileFull;
9715
9716 /* first, rename the directory if it matches the group and machine name */
9717 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9718 group.c_str(), RTPATH_DELIMITER, name.c_str());
9719 /** @todo hack, make somehow use of ComposeMachineFilename */
9720 if (mUserData->s.fDirectoryIncludesUUID)
9721 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9722 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9723 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9724 /** @todo hack, make somehow use of ComposeMachineFilename */
9725 if (mUserData->s.fDirectoryIncludesUUID)
9726 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9727 configDir = configFile;
9728 configDir.stripFilename();
9729 newConfigDir = configDir;
9730 if ( configDir.length() >= groupPlusName.length()
9731 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9732 groupPlusName.c_str()))
9733 {
9734 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9735 Utf8Str newConfigBaseDir(newConfigDir);
9736 newConfigDir.append(newGroupPlusName);
9737 /* consistency: use \ if appropriate on the platform */
9738 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9739 /* new dir and old dir cannot be equal here because of 'if'
9740 * above and because name != newName */
9741 Assert(configDir != newConfigDir);
9742 if (!fSettingsFileIsNew)
9743 {
9744 /* perform real rename only if the machine is not new */
9745 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9746 if ( vrc == VERR_FILE_NOT_FOUND
9747 || vrc == VERR_PATH_NOT_FOUND)
9748 {
9749 /* create the parent directory, then retry renaming */
9750 Utf8Str parent(newConfigDir);
9751 parent.stripFilename();
9752 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9753 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9754 }
9755 if (RT_FAILURE(vrc))
9756 {
9757 rc = setError(E_FAIL,
9758 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9759 configDir.c_str(),
9760 newConfigDir.c_str(),
9761 vrc);
9762 break;
9763 }
9764 /* delete subdirectories which are no longer needed */
9765 Utf8Str dir(configDir);
9766 dir.stripFilename();
9767 while (dir != newConfigBaseDir && dir != ".")
9768 {
9769 vrc = RTDirRemove(dir.c_str());
9770 if (RT_FAILURE(vrc))
9771 break;
9772 dir.stripFilename();
9773 }
9774 dirRenamed = true;
9775 }
9776 }
9777
9778 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9779 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9780
9781 /* then try to rename the settings file itself */
9782 if (newConfigFile != configFile)
9783 {
9784 /* get the path to old settings file in renamed directory */
9785 configFile = Utf8StrFmt("%s%c%s",
9786 newConfigDir.c_str(),
9787 RTPATH_DELIMITER,
9788 RTPathFilename(configFile.c_str()));
9789 if (!fSettingsFileIsNew)
9790 {
9791 /* perform real rename only if the machine is not new */
9792 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9793 if (RT_FAILURE(vrc))
9794 {
9795 rc = setError(E_FAIL,
9796 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9797 configFile.c_str(),
9798 newConfigFile.c_str(),
9799 vrc);
9800 break;
9801 }
9802 fileRenamed = true;
9803 configFilePrev = configFile;
9804 configFilePrev += "-prev";
9805 newConfigFilePrev = newConfigFile;
9806 newConfigFilePrev += "-prev";
9807 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9808 }
9809 }
9810
9811 // update m_strConfigFileFull amd mConfigFile
9812 mData->m_strConfigFileFull = newConfigFile;
9813 // compute the relative path too
9814 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9815
9816 // store the old and new so that VirtualBox::i_saveSettings() can update
9817 // the media registry
9818 if ( mData->mRegistered
9819 && (configDir != newConfigDir || configFile != newConfigFile))
9820 {
9821 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9822
9823 if (pfNeedsGlobalSaveSettings)
9824 *pfNeedsGlobalSaveSettings = true;
9825 }
9826
9827 // in the saved state file path, replace the old directory with the new directory
9828 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9829 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9830
9831 // and do the same thing for the saved state file paths of all the online snapshots
9832 if (mData->mFirstSnapshot)
9833 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9834 newConfigDir.c_str());
9835 }
9836 while (0);
9837
9838 if (FAILED(rc))
9839 {
9840 /* silently try to rename everything back */
9841 if (fileRenamed)
9842 {
9843 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9844 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9845 }
9846 if (dirRenamed)
9847 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9848 }
9849
9850 if (FAILED(rc)) return rc;
9851 }
9852
9853 if (fSettingsFileIsNew)
9854 {
9855 /* create a virgin config file */
9856 int vrc = VINF_SUCCESS;
9857
9858 /* ensure the settings directory exists */
9859 Utf8Str path(mData->m_strConfigFileFull);
9860 path.stripFilename();
9861 if (!RTDirExists(path.c_str()))
9862 {
9863 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9864 if (RT_FAILURE(vrc))
9865 {
9866 return setError(E_FAIL,
9867 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9868 path.c_str(),
9869 vrc);
9870 }
9871 }
9872
9873 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9874 path = Utf8Str(mData->m_strConfigFileFull);
9875 RTFILE f = NIL_RTFILE;
9876 vrc = RTFileOpen(&f, path.c_str(),
9877 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9878 if (RT_FAILURE(vrc))
9879 return setError(E_FAIL,
9880 tr("Could not create the settings file '%s' (%Rrc)"),
9881 path.c_str(),
9882 vrc);
9883 RTFileClose(f);
9884 }
9885
9886 return rc;
9887}
9888
9889/**
9890 * Saves and commits machine data, user data and hardware data.
9891 *
9892 * Note that on failure, the data remains uncommitted.
9893 *
9894 * @a aFlags may combine the following flags:
9895 *
9896 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9897 * Used when saving settings after an operation that makes them 100%
9898 * correspond to the settings from the current snapshot.
9899 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9900 * #isReallyModified() returns false. This is necessary for cases when we
9901 * change machine data directly, not through the backup()/commit() mechanism.
9902 * - SaveS_Force: settings will be saved without doing a deep compare of the
9903 * settings structures. This is used when this is called because snapshots
9904 * have changed to avoid the overhead of the deep compare.
9905 *
9906 * @note Must be called from under this object's write lock. Locks children for
9907 * writing.
9908 *
9909 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9910 * initialized to false and that will be set to true by this function if
9911 * the caller must invoke VirtualBox::i_saveSettings() because the global
9912 * settings have changed. This will happen if a machine rename has been
9913 * saved and the global machine and media registries will therefore need
9914 * updating.
9915 */
9916HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9917 int aFlags /*= 0*/)
9918{
9919 LogFlowThisFuncEnter();
9920
9921 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9922
9923 /* make sure child objects are unable to modify the settings while we are
9924 * saving them */
9925 i_ensureNoStateDependencies();
9926
9927 AssertReturn(!i_isSnapshotMachine(),
9928 E_FAIL);
9929
9930 HRESULT rc = S_OK;
9931 bool fNeedsWrite = false;
9932
9933 /* First, prepare to save settings. It will care about renaming the
9934 * settings directory and file if the machine name was changed and about
9935 * creating a new settings file if this is a new machine. */
9936 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9937 if (FAILED(rc)) return rc;
9938
9939 // keep a pointer to the current settings structures
9940 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9941 settings::MachineConfigFile *pNewConfig = NULL;
9942
9943 try
9944 {
9945 // make a fresh one to have everyone write stuff into
9946 pNewConfig = new settings::MachineConfigFile(NULL);
9947 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9948
9949 // now go and copy all the settings data from COM to the settings structures
9950 // (this calles i_saveSettings() on all the COM objects in the machine)
9951 i_copyMachineDataToSettings(*pNewConfig);
9952
9953 if (aFlags & SaveS_ResetCurStateModified)
9954 {
9955 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9956 mData->mCurrentStateModified = FALSE;
9957 fNeedsWrite = true; // always, no need to compare
9958 }
9959 else if (aFlags & SaveS_Force)
9960 {
9961 fNeedsWrite = true; // always, no need to compare
9962 }
9963 else
9964 {
9965 if (!mData->mCurrentStateModified)
9966 {
9967 // do a deep compare of the settings that we just saved with the settings
9968 // previously stored in the config file; this invokes MachineConfigFile::operator==
9969 // which does a deep compare of all the settings, which is expensive but less expensive
9970 // than writing out XML in vain
9971 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9972
9973 // could still be modified if any settings changed
9974 mData->mCurrentStateModified = fAnySettingsChanged;
9975
9976 fNeedsWrite = fAnySettingsChanged;
9977 }
9978 else
9979 fNeedsWrite = true;
9980 }
9981
9982 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9983
9984 if (fNeedsWrite)
9985 // now spit it all out!
9986 pNewConfig->write(mData->m_strConfigFileFull);
9987
9988 mData->pMachineConfigFile = pNewConfig;
9989 delete pOldConfig;
9990 i_commit();
9991
9992 // after saving settings, we are no longer different from the XML on disk
9993 mData->flModifications = 0;
9994 }
9995 catch (HRESULT err)
9996 {
9997 // we assume that error info is set by the thrower
9998 rc = err;
9999
10000 // restore old config
10001 delete pNewConfig;
10002 mData->pMachineConfigFile = pOldConfig;
10003 }
10004 catch (...)
10005 {
10006 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10007 }
10008
10009 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10010 {
10011 /* Fire the data change event, even on failure (since we've already
10012 * committed all data). This is done only for SessionMachines because
10013 * mutable Machine instances are always not registered (i.e. private
10014 * to the client process that creates them) and thus don't need to
10015 * inform callbacks. */
10016 if (i_isSessionMachine())
10017 mParent->i_onMachineDataChange(mData->mUuid);
10018 }
10019
10020 LogFlowThisFunc(("rc=%08X\n", rc));
10021 LogFlowThisFuncLeave();
10022 return rc;
10023}
10024
10025/**
10026 * Implementation for saving the machine settings into the given
10027 * settings::MachineConfigFile instance. This copies machine extradata
10028 * from the previous machine config file in the instance data, if any.
10029 *
10030 * This gets called from two locations:
10031 *
10032 * -- Machine::i_saveSettings(), during the regular XML writing;
10033 *
10034 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10035 * exported to OVF and we write the VirtualBox proprietary XML
10036 * into a <vbox:Machine> tag.
10037 *
10038 * This routine fills all the fields in there, including snapshots, *except*
10039 * for the following:
10040 *
10041 * -- fCurrentStateModified. There is some special logic associated with that.
10042 *
10043 * The caller can then call MachineConfigFile::write() or do something else
10044 * with it.
10045 *
10046 * Caller must hold the machine lock!
10047 *
10048 * This throws XML errors and HRESULT, so the caller must have a catch block!
10049 */
10050void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10051{
10052 // deep copy extradata
10053 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10054
10055 config.uuid = mData->mUuid;
10056
10057 // copy name, description, OS type, teleport, UTC etc.
10058 config.machineUserData = mUserData->s;
10059
10060 if ( mData->mMachineState == MachineState_Saved
10061 || mData->mMachineState == MachineState_Restoring
10062 // when doing certain snapshot operations we may or may not have
10063 // a saved state in the current state, so keep everything as is
10064 || ( ( mData->mMachineState == MachineState_Snapshotting
10065 || mData->mMachineState == MachineState_DeletingSnapshot
10066 || mData->mMachineState == MachineState_RestoringSnapshot)
10067 && (!mSSData->strStateFilePath.isEmpty())
10068 )
10069 )
10070 {
10071 Assert(!mSSData->strStateFilePath.isEmpty());
10072 /* try to make the file name relative to the settings file dir */
10073 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10074 }
10075 else
10076 {
10077 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10078 config.strStateFile.setNull();
10079 }
10080
10081 if (mData->mCurrentSnapshot)
10082 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10083 else
10084 config.uuidCurrentSnapshot.clear();
10085
10086 config.timeLastStateChange = mData->mLastStateChange;
10087 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10088 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10089
10090 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10091 if (FAILED(rc)) throw rc;
10092
10093 // save machine's media registry if this is VirtualBox 4.0 or later
10094 if (config.canHaveOwnMediaRegistry())
10095 {
10096 // determine machine folder
10097 Utf8Str strMachineFolder = i_getSettingsFileFull();
10098 strMachineFolder.stripFilename();
10099 mParent->i_saveMediaRegistry(config.mediaRegistry,
10100 i_getId(), // only media with registry ID == machine UUID
10101 strMachineFolder);
10102 // this throws HRESULT
10103 }
10104
10105 // save snapshots
10106 rc = i_saveAllSnapshots(config);
10107 if (FAILED(rc)) throw rc;
10108}
10109
10110/**
10111 * Saves all snapshots of the machine into the given machine config file. Called
10112 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10113 * @param config
10114 * @return
10115 */
10116HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10117{
10118 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10119
10120 HRESULT rc = S_OK;
10121
10122 try
10123 {
10124 config.llFirstSnapshot.clear();
10125
10126 if (mData->mFirstSnapshot)
10127 {
10128 // the settings use a list for "the first snapshot"
10129 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10130
10131 // get reference to the snapshot on the list and work on that
10132 // element straight in the list to avoid excessive copying later
10133 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10134 if (FAILED(rc)) throw rc;
10135 }
10136
10137// if (mType == IsSessionMachine)
10138// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10139
10140 }
10141 catch (HRESULT err)
10142 {
10143 /* we assume that error info is set by the thrower */
10144 rc = err;
10145 }
10146 catch (...)
10147 {
10148 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10149 }
10150
10151 return rc;
10152}
10153
10154/**
10155 * Saves the VM hardware configuration. It is assumed that the
10156 * given node is empty.
10157 *
10158 * @param data Reference to the settings object for the hardware config.
10159 * @param pDbg Pointer to the settings object for the debugging config
10160 * which happens to live in mHWData.
10161 * @param pAutostart Pointer to the settings object for the autostart config
10162 * which happens to live in mHWData.
10163 */
10164HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10165 settings::Autostart *pAutostart)
10166{
10167 HRESULT rc = S_OK;
10168
10169 try
10170 {
10171 /* The hardware version attribute (optional).
10172 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10173 if ( mHWData->mHWVersion == "1"
10174 && mSSData->strStateFilePath.isEmpty()
10175 )
10176 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10177 other point needs to be found where this can be done. */
10178
10179 data.strVersion = mHWData->mHWVersion;
10180 data.uuid = mHWData->mHardwareUUID;
10181
10182 // CPU
10183 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10184 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10185 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10186 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10187 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10188 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10189 data.fPAE = !!mHWData->mPAEEnabled;
10190 data.enmLongMode = mHWData->mLongMode;
10191 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10192 data.fAPIC = !!mHWData->mAPIC;
10193 data.fX2APIC = !!mHWData->mX2APIC;
10194 data.cCPUs = mHWData->mCPUCount;
10195 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10196 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10197 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10198 data.strCpuProfile = mHWData->mCpuProfile;
10199
10200 data.llCpus.clear();
10201 if (data.fCpuHotPlug)
10202 {
10203 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10204 {
10205 if (mHWData->mCPUAttached[idx])
10206 {
10207 settings::Cpu cpu;
10208 cpu.ulId = idx;
10209 data.llCpus.push_back(cpu);
10210 }
10211 }
10212 }
10213
10214 /* Standard and Extended CPUID leafs. */
10215 data.llCpuIdLeafs.clear();
10216 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10217 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10218 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10219 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10220 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10221 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10222
10223 // memory
10224 data.ulMemorySizeMB = mHWData->mMemorySize;
10225 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10226
10227 // firmware
10228 data.firmwareType = mHWData->mFirmwareType;
10229
10230 // HID
10231 data.pointingHIDType = mHWData->mPointingHIDType;
10232 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10233
10234 // chipset
10235 data.chipsetType = mHWData->mChipsetType;
10236
10237 // paravirt
10238 data.paravirtProvider = mHWData->mParavirtProvider;
10239 data.strParavirtDebug = mHWData->mParavirtDebug;
10240
10241 // emulated USB card reader
10242 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10243
10244 // HPET
10245 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10246
10247 // boot order
10248 data.mapBootOrder.clear();
10249 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10250 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10251
10252 // display
10253 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10254 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10255 data.cMonitors = mHWData->mMonitorCount;
10256 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10257 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10258 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10259 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10260 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10261 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10262 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10263 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10264 {
10265 if (mHWData->maVideoCaptureScreens[i])
10266 ASMBitSet(&data.u64VideoCaptureScreens, i);
10267 else
10268 ASMBitClear(&data.u64VideoCaptureScreens, i);
10269 }
10270 /* store relative video capture file if possible */
10271 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10272
10273 /* VRDEServer settings (optional) */
10274 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10275 if (FAILED(rc)) throw rc;
10276
10277 /* BIOS (required) */
10278 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10279 if (FAILED(rc)) throw rc;
10280
10281 /* USB Controller (required) */
10282 data.usbSettings.llUSBControllers.clear();
10283 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10284 {
10285 ComObjPtr<USBController> ctrl = *it;
10286 settings::USBController settingsCtrl;
10287
10288 settingsCtrl.strName = ctrl->i_getName();
10289 settingsCtrl.enmType = ctrl->i_getControllerType();
10290
10291 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10292 }
10293
10294 /* USB device filters (required) */
10295 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10296 if (FAILED(rc)) throw rc;
10297
10298 /* Network adapters (required) */
10299 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10300 data.llNetworkAdapters.clear();
10301 /* Write out only the nominal number of network adapters for this
10302 * chipset type. Since Machine::commit() hasn't been called there
10303 * may be extra NIC settings in the vector. */
10304 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10305 {
10306 settings::NetworkAdapter nic;
10307 nic.ulSlot = (uint32_t)slot;
10308 /* paranoia check... must not be NULL, but must not crash either. */
10309 if (mNetworkAdapters[slot])
10310 {
10311 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10312 if (FAILED(rc)) throw rc;
10313
10314 data.llNetworkAdapters.push_back(nic);
10315 }
10316 }
10317
10318 /* Serial ports */
10319 data.llSerialPorts.clear();
10320 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10321 {
10322 if (mSerialPorts[slot]->i_hasDefaults())
10323 continue;
10324
10325 settings::SerialPort s;
10326 s.ulSlot = slot;
10327 rc = mSerialPorts[slot]->i_saveSettings(s);
10328 if (FAILED(rc)) return rc;
10329
10330 data.llSerialPorts.push_back(s);
10331 }
10332
10333 /* Parallel ports */
10334 data.llParallelPorts.clear();
10335 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10336 {
10337 if (mParallelPorts[slot]->i_hasDefaults())
10338 continue;
10339
10340 settings::ParallelPort p;
10341 p.ulSlot = slot;
10342 rc = mParallelPorts[slot]->i_saveSettings(p);
10343 if (FAILED(rc)) return rc;
10344
10345 data.llParallelPorts.push_back(p);
10346 }
10347
10348 /* Audio adapter */
10349 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10350 if (FAILED(rc)) return rc;
10351
10352 rc = i_saveStorageControllers(data.storage);
10353 if (FAILED(rc)) return rc;
10354
10355 /* Shared folders */
10356 data.llSharedFolders.clear();
10357 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10358 it != mHWData->mSharedFolders.end();
10359 ++it)
10360 {
10361 SharedFolder *pSF = *it;
10362 AutoCaller sfCaller(pSF);
10363 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10364 settings::SharedFolder sf;
10365 sf.strName = pSF->i_getName();
10366 sf.strHostPath = pSF->i_getHostPath();
10367 sf.fWritable = !!pSF->i_isWritable();
10368 sf.fAutoMount = !!pSF->i_isAutoMounted();
10369
10370 data.llSharedFolders.push_back(sf);
10371 }
10372
10373 // clipboard
10374 data.clipboardMode = mHWData->mClipboardMode;
10375
10376 // drag'n'drop
10377 data.dndMode = mHWData->mDnDMode;
10378
10379 /* Guest */
10380 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10381
10382 // IO settings
10383 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10384 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10385
10386 /* BandwidthControl (required) */
10387 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10388 if (FAILED(rc)) throw rc;
10389
10390 /* Host PCI devices */
10391 data.pciAttachments.clear();
10392 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10393 it != mHWData->mPCIDeviceAssignments.end();
10394 ++it)
10395 {
10396 ComObjPtr<PCIDeviceAttachment> pda = *it;
10397 settings::HostPCIDeviceAttachment hpda;
10398
10399 rc = pda->i_saveSettings(hpda);
10400 if (FAILED(rc)) throw rc;
10401
10402 data.pciAttachments.push_back(hpda);
10403 }
10404
10405 // guest properties
10406 data.llGuestProperties.clear();
10407#ifdef VBOX_WITH_GUEST_PROPS
10408 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10409 it != mHWData->mGuestProperties.end();
10410 ++it)
10411 {
10412 HWData::GuestProperty property = it->second;
10413
10414 /* Remove transient guest properties at shutdown unless we
10415 * are saving state. Note that restoring snapshot intentionally
10416 * keeps them, they will be removed if appropriate once the final
10417 * machine state is set (as crashes etc. need to work). */
10418 if ( ( mData->mMachineState == MachineState_PoweredOff
10419 || mData->mMachineState == MachineState_Aborted
10420 || mData->mMachineState == MachineState_Teleported)
10421 && ( property.mFlags & guestProp::TRANSIENT
10422 || property.mFlags & guestProp::TRANSRESET))
10423 continue;
10424 settings::GuestProperty prop;
10425 prop.strName = it->first;
10426 prop.strValue = property.strValue;
10427 prop.timestamp = property.mTimestamp;
10428 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10429 guestProp::writeFlags(property.mFlags, szFlags);
10430 prop.strFlags = szFlags;
10431
10432 data.llGuestProperties.push_back(prop);
10433 }
10434
10435 /* I presume this doesn't require a backup(). */
10436 mData->mGuestPropertiesModified = FALSE;
10437#endif /* VBOX_WITH_GUEST_PROPS defined */
10438
10439 *pDbg = mHWData->mDebugging;
10440 *pAutostart = mHWData->mAutostart;
10441
10442 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10443 }
10444 catch(std::bad_alloc &)
10445 {
10446 return E_OUTOFMEMORY;
10447 }
10448
10449 AssertComRC(rc);
10450 return rc;
10451}
10452
10453/**
10454 * Saves the storage controller configuration.
10455 *
10456 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10457 */
10458HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10459{
10460 data.llStorageControllers.clear();
10461
10462 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10463 it != mStorageControllers->end();
10464 ++it)
10465 {
10466 HRESULT rc;
10467 ComObjPtr<StorageController> pCtl = *it;
10468
10469 settings::StorageController ctl;
10470 ctl.strName = pCtl->i_getName();
10471 ctl.controllerType = pCtl->i_getControllerType();
10472 ctl.storageBus = pCtl->i_getStorageBus();
10473 ctl.ulInstance = pCtl->i_getInstance();
10474 ctl.fBootable = pCtl->i_getBootable();
10475
10476 /* Save the port count. */
10477 ULONG portCount;
10478 rc = pCtl->COMGETTER(PortCount)(&portCount);
10479 ComAssertComRCRet(rc, rc);
10480 ctl.ulPortCount = portCount;
10481
10482 /* Save fUseHostIOCache */
10483 BOOL fUseHostIOCache;
10484 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10485 ComAssertComRCRet(rc, rc);
10486 ctl.fUseHostIOCache = !!fUseHostIOCache;
10487
10488 /* save the devices now. */
10489 rc = i_saveStorageDevices(pCtl, ctl);
10490 ComAssertComRCRet(rc, rc);
10491
10492 data.llStorageControllers.push_back(ctl);
10493 }
10494
10495 return S_OK;
10496}
10497
10498/**
10499 * Saves the hard disk configuration.
10500 */
10501HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10502 settings::StorageController &data)
10503{
10504 MediaData::AttachmentList atts;
10505
10506 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10507 if (FAILED(rc)) return rc;
10508
10509 data.llAttachedDevices.clear();
10510 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10511 it != atts.end();
10512 ++it)
10513 {
10514 settings::AttachedDevice dev;
10515 IMediumAttachment *iA = *it;
10516 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10517 Medium *pMedium = pAttach->i_getMedium();
10518
10519 dev.deviceType = pAttach->i_getType();
10520 dev.lPort = pAttach->i_getPort();
10521 dev.lDevice = pAttach->i_getDevice();
10522 dev.fPassThrough = pAttach->i_getPassthrough();
10523 dev.fHotPluggable = pAttach->i_getHotPluggable();
10524 if (pMedium)
10525 {
10526 if (pMedium->i_isHostDrive())
10527 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10528 else
10529 dev.uuid = pMedium->i_getId();
10530 dev.fTempEject = pAttach->i_getTempEject();
10531 dev.fNonRotational = pAttach->i_getNonRotational();
10532 dev.fDiscard = pAttach->i_getDiscard();
10533 }
10534
10535 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10536
10537 data.llAttachedDevices.push_back(dev);
10538 }
10539
10540 return S_OK;
10541}
10542
10543/**
10544 * Saves machine state settings as defined by aFlags
10545 * (SaveSTS_* values).
10546 *
10547 * @param aFlags Combination of SaveSTS_* flags.
10548 *
10549 * @note Locks objects for writing.
10550 */
10551HRESULT Machine::i_saveStateSettings(int aFlags)
10552{
10553 if (aFlags == 0)
10554 return S_OK;
10555
10556 AutoCaller autoCaller(this);
10557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10558
10559 /* This object's write lock is also necessary to serialize file access
10560 * (prevent concurrent reads and writes) */
10561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10562
10563 HRESULT rc = S_OK;
10564
10565 Assert(mData->pMachineConfigFile);
10566
10567 try
10568 {
10569 if (aFlags & SaveSTS_CurStateModified)
10570 mData->pMachineConfigFile->fCurrentStateModified = true;
10571
10572 if (aFlags & SaveSTS_StateFilePath)
10573 {
10574 if (!mSSData->strStateFilePath.isEmpty())
10575 /* try to make the file name relative to the settings file dir */
10576 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10577 else
10578 mData->pMachineConfigFile->strStateFile.setNull();
10579 }
10580
10581 if (aFlags & SaveSTS_StateTimeStamp)
10582 {
10583 Assert( mData->mMachineState != MachineState_Aborted
10584 || mSSData->strStateFilePath.isEmpty());
10585
10586 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10587
10588 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10589//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10590 }
10591
10592 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10593 }
10594 catch (...)
10595 {
10596 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10597 }
10598
10599 return rc;
10600}
10601
10602/**
10603 * Ensures that the given medium is added to a media registry. If this machine
10604 * was created with 4.0 or later, then the machine registry is used. Otherwise
10605 * the global VirtualBox media registry is used.
10606 *
10607 * Caller must NOT hold machine lock, media tree or any medium locks!
10608 *
10609 * @param pMedium
10610 */
10611void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10612{
10613 /* Paranoia checks: do not hold machine or media tree locks. */
10614 AssertReturnVoid(!isWriteLockOnCurrentThread());
10615 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10616
10617 ComObjPtr<Medium> pBase;
10618 {
10619 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10620 pBase = pMedium->i_getBase();
10621 }
10622
10623 /* Paranoia checks: do not hold medium locks. */
10624 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10625 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10626
10627 // decide which medium registry to use now that the medium is attached:
10628 Guid uuid;
10629 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10630 // machine XML is VirtualBox 4.0 or higher:
10631 uuid = i_getId(); // machine UUID
10632 else
10633 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10634
10635 if (pMedium->i_addRegistry(uuid))
10636 mParent->i_markRegistryModified(uuid);
10637
10638 /* For more complex hard disk structures it can happen that the base
10639 * medium isn't yet associated with any medium registry. Do that now. */
10640 if (pMedium != pBase)
10641 {
10642 /* Tree lock needed by Medium::addRegistry when recursing. */
10643 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10644 if (pBase->i_addRegistryRecursive(uuid))
10645 {
10646 treeLock.release();
10647 mParent->i_markRegistryModified(uuid);
10648 }
10649 }
10650}
10651
10652/**
10653 * Creates differencing hard disks for all normal hard disks attached to this
10654 * machine and a new set of attachments to refer to created disks.
10655 *
10656 * Used when taking a snapshot or when deleting the current state. Gets called
10657 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10658 *
10659 * This method assumes that mMediaData contains the original hard disk attachments
10660 * it needs to create diffs for. On success, these attachments will be replaced
10661 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10662 * called to delete created diffs which will also rollback mMediaData and restore
10663 * whatever was backed up before calling this method.
10664 *
10665 * Attachments with non-normal hard disks are left as is.
10666 *
10667 * If @a aOnline is @c false then the original hard disks that require implicit
10668 * diffs will be locked for reading. Otherwise it is assumed that they are
10669 * already locked for writing (when the VM was started). Note that in the latter
10670 * case it is responsibility of the caller to lock the newly created diffs for
10671 * writing if this method succeeds.
10672 *
10673 * @param aProgress Progress object to run (must contain at least as
10674 * many operations left as the number of hard disks
10675 * attached).
10676 * @param aOnline Whether the VM was online prior to this operation.
10677 *
10678 * @note The progress object is not marked as completed, neither on success nor
10679 * on failure. This is a responsibility of the caller.
10680 *
10681 * @note Locks this object and the media tree for writing.
10682 */
10683HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10684 ULONG aWeight,
10685 bool aOnline)
10686{
10687 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10688
10689 AutoCaller autoCaller(this);
10690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10691
10692 AutoMultiWriteLock2 alock(this->lockHandle(),
10693 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10694
10695 /* must be in a protective state because we release the lock below */
10696 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10697 || mData->mMachineState == MachineState_OnlineSnapshotting
10698 || mData->mMachineState == MachineState_LiveSnapshotting
10699 || mData->mMachineState == MachineState_RestoringSnapshot
10700 || mData->mMachineState == MachineState_DeletingSnapshot
10701 , E_FAIL);
10702
10703 HRESULT rc = S_OK;
10704
10705 // use appropriate locked media map (online or offline)
10706 MediumLockListMap lockedMediaOffline;
10707 MediumLockListMap *lockedMediaMap;
10708 if (aOnline)
10709 lockedMediaMap = &mData->mSession.mLockedMedia;
10710 else
10711 lockedMediaMap = &lockedMediaOffline;
10712
10713 try
10714 {
10715 if (!aOnline)
10716 {
10717 /* lock all attached hard disks early to detect "in use"
10718 * situations before creating actual diffs */
10719 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10720 it != mMediaData->mAttachments.end();
10721 ++it)
10722 {
10723 MediumAttachment* pAtt = *it;
10724 if (pAtt->i_getType() == DeviceType_HardDisk)
10725 {
10726 Medium* pMedium = pAtt->i_getMedium();
10727 Assert(pMedium);
10728
10729 MediumLockList *pMediumLockList(new MediumLockList());
10730 alock.release();
10731 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10732 NULL /* pToLockWrite */,
10733 false /* fMediumLockWriteAll */,
10734 NULL,
10735 *pMediumLockList);
10736 alock.acquire();
10737 if (FAILED(rc))
10738 {
10739 delete pMediumLockList;
10740 throw rc;
10741 }
10742 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10743 if (FAILED(rc))
10744 {
10745 throw setError(rc,
10746 tr("Collecting locking information for all attached media failed"));
10747 }
10748 }
10749 }
10750
10751 /* Now lock all media. If this fails, nothing is locked. */
10752 alock.release();
10753 rc = lockedMediaMap->Lock();
10754 alock.acquire();
10755 if (FAILED(rc))
10756 {
10757 throw setError(rc,
10758 tr("Locking of attached media failed"));
10759 }
10760 }
10761
10762 /* remember the current list (note that we don't use backup() since
10763 * mMediaData may be already backed up) */
10764 MediaData::AttachmentList atts = mMediaData->mAttachments;
10765
10766 /* start from scratch */
10767 mMediaData->mAttachments.clear();
10768
10769 /* go through remembered attachments and create diffs for normal hard
10770 * disks and attach them */
10771 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10772 it != atts.end();
10773 ++it)
10774 {
10775 MediumAttachment* pAtt = *it;
10776
10777 DeviceType_T devType = pAtt->i_getType();
10778 Medium* pMedium = pAtt->i_getMedium();
10779
10780 if ( devType != DeviceType_HardDisk
10781 || pMedium == NULL
10782 || pMedium->i_getType() != MediumType_Normal)
10783 {
10784 /* copy the attachment as is */
10785
10786 /** @todo the progress object created in SessionMachine::TakeSnaphot
10787 * only expects operations for hard disks. Later other
10788 * device types need to show up in the progress as well. */
10789 if (devType == DeviceType_HardDisk)
10790 {
10791 if (pMedium == NULL)
10792 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10793 aWeight); // weight
10794 else
10795 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10796 pMedium->i_getBase()->i_getName().c_str()).raw(),
10797 aWeight); // weight
10798 }
10799
10800 mMediaData->mAttachments.push_back(pAtt);
10801 continue;
10802 }
10803
10804 /* need a diff */
10805 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10806 pMedium->i_getBase()->i_getName().c_str()).raw(),
10807 aWeight); // weight
10808
10809 Utf8Str strFullSnapshotFolder;
10810 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10811
10812 ComObjPtr<Medium> diff;
10813 diff.createObject();
10814 // store the diff in the same registry as the parent
10815 // (this cannot fail here because we can't create implicit diffs for
10816 // unregistered images)
10817 Guid uuidRegistryParent;
10818 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10819 Assert(fInRegistry); NOREF(fInRegistry);
10820 rc = diff->init(mParent,
10821 pMedium->i_getPreferredDiffFormat(),
10822 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10823 uuidRegistryParent,
10824 DeviceType_HardDisk);
10825 if (FAILED(rc)) throw rc;
10826
10827 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10828 * the push_back? Looks like we're going to release medium with the
10829 * wrong kind of lock (general issue with if we fail anywhere at all)
10830 * and an orphaned VDI in the snapshots folder. */
10831
10832 /* update the appropriate lock list */
10833 MediumLockList *pMediumLockList;
10834 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10835 AssertComRCThrowRC(rc);
10836 if (aOnline)
10837 {
10838 alock.release();
10839 /* The currently attached medium will be read-only, change
10840 * the lock type to read. */
10841 rc = pMediumLockList->Update(pMedium, false);
10842 alock.acquire();
10843 AssertComRCThrowRC(rc);
10844 }
10845
10846 /* release the locks before the potentially lengthy operation */
10847 alock.release();
10848 rc = pMedium->i_createDiffStorage(diff,
10849 pMedium->i_getPreferredDiffVariant(),
10850 pMediumLockList,
10851 NULL /* aProgress */,
10852 true /* aWait */);
10853 alock.acquire();
10854 if (FAILED(rc)) throw rc;
10855
10856 /* actual lock list update is done in Machine::i_commitMedia */
10857
10858 rc = diff->i_addBackReference(mData->mUuid);
10859 AssertComRCThrowRC(rc);
10860
10861 /* add a new attachment */
10862 ComObjPtr<MediumAttachment> attachment;
10863 attachment.createObject();
10864 rc = attachment->init(this,
10865 diff,
10866 pAtt->i_getControllerName(),
10867 pAtt->i_getPort(),
10868 pAtt->i_getDevice(),
10869 DeviceType_HardDisk,
10870 true /* aImplicit */,
10871 false /* aPassthrough */,
10872 false /* aTempEject */,
10873 pAtt->i_getNonRotational(),
10874 pAtt->i_getDiscard(),
10875 pAtt->i_getHotPluggable(),
10876 pAtt->i_getBandwidthGroup());
10877 if (FAILED(rc)) throw rc;
10878
10879 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10880 AssertComRCThrowRC(rc);
10881 mMediaData->mAttachments.push_back(attachment);
10882 }
10883 }
10884 catch (HRESULT aRC) { rc = aRC; }
10885
10886 /* unlock all hard disks we locked when there is no VM */
10887 if (!aOnline)
10888 {
10889 ErrorInfoKeeper eik;
10890
10891 HRESULT rc1 = lockedMediaMap->Clear();
10892 AssertComRC(rc1);
10893 }
10894
10895 return rc;
10896}
10897
10898/**
10899 * Deletes implicit differencing hard disks created either by
10900 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10901 *
10902 * Note that to delete hard disks created by #AttachDevice() this method is
10903 * called from #fixupMedia() when the changes are rolled back.
10904 *
10905 * @note Locks this object and the media tree for writing.
10906 */
10907HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10908{
10909 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10910
10911 AutoCaller autoCaller(this);
10912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10913
10914 AutoMultiWriteLock2 alock(this->lockHandle(),
10915 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10916
10917 /* We absolutely must have backed up state. */
10918 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10919
10920 /* Check if there are any implicitly created diff images. */
10921 bool fImplicitDiffs = false;
10922 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10923 it != mMediaData->mAttachments.end();
10924 ++it)
10925 {
10926 const ComObjPtr<MediumAttachment> &pAtt = *it;
10927 if (pAtt->i_isImplicit())
10928 {
10929 fImplicitDiffs = true;
10930 break;
10931 }
10932 }
10933 /* If there is nothing to do, leave early. This saves lots of image locking
10934 * effort. It also avoids a MachineStateChanged event without real reason.
10935 * This is important e.g. when loading a VM config, because there should be
10936 * no events. Otherwise API clients can become thoroughly confused for
10937 * inaccessible VMs (the code for loading VM configs uses this method for
10938 * cleanup if the config makes no sense), as they take such events as an
10939 * indication that the VM is alive, and they would force the VM config to
10940 * be reread, leading to an endless loop. */
10941 if (!fImplicitDiffs)
10942 return S_OK;
10943
10944 HRESULT rc = S_OK;
10945 MachineState_T oldState = mData->mMachineState;
10946
10947 /* will release the lock before the potentially lengthy operation,
10948 * so protect with the special state (unless already protected) */
10949 if ( oldState != MachineState_Snapshotting
10950 && oldState != MachineState_OnlineSnapshotting
10951 && oldState != MachineState_LiveSnapshotting
10952 && oldState != MachineState_RestoringSnapshot
10953 && oldState != MachineState_DeletingSnapshot
10954 && oldState != MachineState_DeletingSnapshotOnline
10955 && oldState != MachineState_DeletingSnapshotPaused
10956 )
10957 i_setMachineState(MachineState_SettingUp);
10958
10959 // use appropriate locked media map (online or offline)
10960 MediumLockListMap lockedMediaOffline;
10961 MediumLockListMap *lockedMediaMap;
10962 if (aOnline)
10963 lockedMediaMap = &mData->mSession.mLockedMedia;
10964 else
10965 lockedMediaMap = &lockedMediaOffline;
10966
10967 try
10968 {
10969 if (!aOnline)
10970 {
10971 /* lock all attached hard disks early to detect "in use"
10972 * situations before deleting actual diffs */
10973 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10974 it != mMediaData->mAttachments.end();
10975 ++it)
10976 {
10977 MediumAttachment* pAtt = *it;
10978 if (pAtt->i_getType() == DeviceType_HardDisk)
10979 {
10980 Medium* pMedium = pAtt->i_getMedium();
10981 Assert(pMedium);
10982
10983 MediumLockList *pMediumLockList(new MediumLockList());
10984 alock.release();
10985 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10986 NULL /* pToLockWrite */,
10987 false /* fMediumLockWriteAll */,
10988 NULL,
10989 *pMediumLockList);
10990 alock.acquire();
10991
10992 if (FAILED(rc))
10993 {
10994 delete pMediumLockList;
10995 throw rc;
10996 }
10997
10998 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10999 if (FAILED(rc))
11000 throw rc;
11001 }
11002 }
11003
11004 if (FAILED(rc))
11005 throw rc;
11006 } // end of offline
11007
11008 /* Lock lists are now up to date and include implicitly created media */
11009
11010 /* Go through remembered attachments and delete all implicitly created
11011 * diffs and fix up the attachment information */
11012 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11013 MediaData::AttachmentList implicitAtts;
11014 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11015 it != mMediaData->mAttachments.end();
11016 ++it)
11017 {
11018 ComObjPtr<MediumAttachment> pAtt = *it;
11019 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11020 if (pMedium.isNull())
11021 continue;
11022
11023 // Implicit attachments go on the list for deletion and back references are removed.
11024 if (pAtt->i_isImplicit())
11025 {
11026 /* Deassociate and mark for deletion */
11027 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11028 rc = pMedium->i_removeBackReference(mData->mUuid);
11029 if (FAILED(rc))
11030 throw rc;
11031 implicitAtts.push_back(pAtt);
11032 continue;
11033 }
11034
11035 /* Was this medium attached before? */
11036 if (!i_findAttachment(oldAtts, pMedium))
11037 {
11038 /* no: de-associate */
11039 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11040 rc = pMedium->i_removeBackReference(mData->mUuid);
11041 if (FAILED(rc))
11042 throw rc;
11043 continue;
11044 }
11045 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11046 }
11047
11048 /* If there are implicit attachments to delete, throw away the lock
11049 * map contents (which will unlock all media) since the medium
11050 * attachments will be rolled back. Below we need to completely
11051 * recreate the lock map anyway since it is infinitely complex to
11052 * do this incrementally (would need reconstructing each attachment
11053 * change, which would be extremely hairy). */
11054 if (implicitAtts.size() != 0)
11055 {
11056 ErrorInfoKeeper eik;
11057
11058 HRESULT rc1 = lockedMediaMap->Clear();
11059 AssertComRC(rc1);
11060 }
11061
11062 /* rollback hard disk changes */
11063 mMediaData.rollback();
11064
11065 MultiResult mrc(S_OK);
11066
11067 // Delete unused implicit diffs.
11068 if (implicitAtts.size() != 0)
11069 {
11070 alock.release();
11071
11072 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11073 {
11074 // Remove medium associated with this attachment.
11075 ComObjPtr<MediumAttachment> pAtt = *it;
11076 Assert(pAtt);
11077 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11078 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11079 Assert(pMedium);
11080
11081 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11082 // continue on delete failure, just collect error messages
11083 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11084 pMedium->i_getLocationFull().c_str() ));
11085 mrc = rc;
11086 }
11087 // Clear the list of deleted implicit attachments now, while not
11088 // holding the lock, as it will ultimately trigger Medium::uninit()
11089 // calls which assume that the media tree lock isn't held.
11090 implicitAtts.clear();
11091
11092 alock.acquire();
11093
11094 /* if there is a VM recreate media lock map as mentioned above,
11095 * otherwise it is a waste of time and we leave things unlocked */
11096 if (aOnline)
11097 {
11098 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11099 /* must never be NULL, but better safe than sorry */
11100 if (!pMachine.isNull())
11101 {
11102 alock.release();
11103 rc = mData->mSession.mMachine->i_lockMedia();
11104 alock.acquire();
11105 if (FAILED(rc))
11106 throw rc;
11107 }
11108 }
11109 }
11110 }
11111 catch (HRESULT aRC) {rc = aRC;}
11112
11113 if (mData->mMachineState == MachineState_SettingUp)
11114 i_setMachineState(oldState);
11115
11116 /* unlock all hard disks we locked when there is no VM */
11117 if (!aOnline)
11118 {
11119 ErrorInfoKeeper eik;
11120
11121 HRESULT rc1 = lockedMediaMap->Clear();
11122 AssertComRC(rc1);
11123 }
11124
11125 return rc;
11126}
11127
11128
11129/**
11130 * Looks through the given list of media attachments for one with the given parameters
11131 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11132 * can be searched as well if needed.
11133 *
11134 * @param list
11135 * @param aControllerName
11136 * @param aControllerPort
11137 * @param aDevice
11138 * @return
11139 */
11140MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11141 const Utf8Str &aControllerName,
11142 LONG aControllerPort,
11143 LONG aDevice)
11144{
11145 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11146 {
11147 MediumAttachment *pAttach = *it;
11148 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11149 return pAttach;
11150 }
11151
11152 return NULL;
11153}
11154
11155/**
11156 * Looks through the given list of media attachments for one with the given parameters
11157 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11158 * can be searched as well if needed.
11159 *
11160 * @param list
11161 * @param aControllerName
11162 * @param aControllerPort
11163 * @param aDevice
11164 * @return
11165 */
11166MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11167 ComObjPtr<Medium> pMedium)
11168{
11169 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11170 {
11171 MediumAttachment *pAttach = *it;
11172 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11173 if (pMediumThis == pMedium)
11174 return pAttach;
11175 }
11176
11177 return NULL;
11178}
11179
11180/**
11181 * Looks through the given list of media attachments for one with the given parameters
11182 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11183 * can be searched as well if needed.
11184 *
11185 * @param list
11186 * @param aControllerName
11187 * @param aControllerPort
11188 * @param aDevice
11189 * @return
11190 */
11191MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11192 Guid &id)
11193{
11194 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11195 {
11196 MediumAttachment *pAttach = *it;
11197 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11198 if (pMediumThis->i_getId() == id)
11199 return pAttach;
11200 }
11201
11202 return NULL;
11203}
11204
11205/**
11206 * Main implementation for Machine::DetachDevice. This also gets called
11207 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11208 *
11209 * @param pAttach Medium attachment to detach.
11210 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11211 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11212 * SnapshotMachine, and this must be its snapshot.
11213 * @return
11214 */
11215HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11216 AutoWriteLock &writeLock,
11217 Snapshot *pSnapshot)
11218{
11219 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11220 DeviceType_T mediumType = pAttach->i_getType();
11221
11222 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11223
11224 if (pAttach->i_isImplicit())
11225 {
11226 /* attempt to implicitly delete the implicitly created diff */
11227
11228 /// @todo move the implicit flag from MediumAttachment to Medium
11229 /// and forbid any hard disk operation when it is implicit. Or maybe
11230 /// a special media state for it to make it even more simple.
11231
11232 Assert(mMediaData.isBackedUp());
11233
11234 /* will release the lock before the potentially lengthy operation, so
11235 * protect with the special state */
11236 MachineState_T oldState = mData->mMachineState;
11237 i_setMachineState(MachineState_SettingUp);
11238
11239 writeLock.release();
11240
11241 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11242 true /*aWait*/);
11243
11244 writeLock.acquire();
11245
11246 i_setMachineState(oldState);
11247
11248 if (FAILED(rc)) return rc;
11249 }
11250
11251 i_setModified(IsModified_Storage);
11252 mMediaData.backup();
11253 mMediaData->mAttachments.remove(pAttach);
11254
11255 if (!oldmedium.isNull())
11256 {
11257 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11258 if (pSnapshot)
11259 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11260 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11261 else if (mediumType != DeviceType_HardDisk)
11262 oldmedium->i_removeBackReference(mData->mUuid);
11263 }
11264
11265 return S_OK;
11266}
11267
11268/**
11269 * Goes thru all media of the given list and
11270 *
11271 * 1) calls i_detachDevice() on each of them for this machine and
11272 * 2) adds all Medium objects found in the process to the given list,
11273 * depending on cleanupMode.
11274 *
11275 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11276 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11277 * media to the list.
11278 *
11279 * This gets called from Machine::Unregister, both for the actual Machine and
11280 * the SnapshotMachine objects that might be found in the snapshots.
11281 *
11282 * Requires caller and locking. The machine lock must be passed in because it
11283 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11284 *
11285 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11286 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11287 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11288 * Full, then all media get added;
11289 * otherwise no media get added.
11290 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11291 * @return
11292 */
11293HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11294 Snapshot *pSnapshot,
11295 CleanupMode_T cleanupMode,
11296 MediaList &llMedia)
11297{
11298 Assert(isWriteLockOnCurrentThread());
11299
11300 HRESULT rc;
11301
11302 // make a temporary list because i_detachDevice invalidates iterators into
11303 // mMediaData->mAttachments
11304 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11305
11306 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11307 {
11308 ComObjPtr<MediumAttachment> &pAttach = *it;
11309 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11310
11311 if (!pMedium.isNull())
11312 {
11313 AutoCaller mac(pMedium);
11314 if (FAILED(mac.rc())) return mac.rc();
11315 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11316 DeviceType_T devType = pMedium->i_getDeviceType();
11317 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11318 && devType == DeviceType_HardDisk)
11319 || (cleanupMode == CleanupMode_Full)
11320 )
11321 {
11322 llMedia.push_back(pMedium);
11323 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11324 /* Not allowed to keep this lock as below we need the parent
11325 * medium lock, and the lock order is parent to child. */
11326 lock.release();
11327 /*
11328 * Search for medias which are not attached to any machine, but
11329 * in the chain to an attached disk. Mediums are only consided
11330 * if they are:
11331 * - have only one child
11332 * - no references to any machines
11333 * - are of normal medium type
11334 */
11335 while (!pParent.isNull())
11336 {
11337 AutoCaller mac1(pParent);
11338 if (FAILED(mac1.rc())) return mac1.rc();
11339 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11340 if (pParent->i_getChildren().size() == 1)
11341 {
11342 if ( pParent->i_getMachineBackRefCount() == 0
11343 && pParent->i_getType() == MediumType_Normal
11344 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11345 llMedia.push_back(pParent);
11346 }
11347 else
11348 break;
11349 pParent = pParent->i_getParent();
11350 }
11351 }
11352 }
11353
11354 // real machine: then we need to use the proper method
11355 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11356
11357 if (FAILED(rc))
11358 return rc;
11359 }
11360
11361 return S_OK;
11362}
11363
11364/**
11365 * Perform deferred hard disk detachments.
11366 *
11367 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11368 * backed up).
11369 *
11370 * If @a aOnline is @c true then this method will also unlock the old hard disks
11371 * for which the new implicit diffs were created and will lock these new diffs for
11372 * writing.
11373 *
11374 * @param aOnline Whether the VM was online prior to this operation.
11375 *
11376 * @note Locks this object for writing!
11377 */
11378void Machine::i_commitMedia(bool aOnline /*= false*/)
11379{
11380 AutoCaller autoCaller(this);
11381 AssertComRCReturnVoid(autoCaller.rc());
11382
11383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11384
11385 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11386
11387 HRESULT rc = S_OK;
11388
11389 /* no attach/detach operations -- nothing to do */
11390 if (!mMediaData.isBackedUp())
11391 return;
11392
11393 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11394 bool fMediaNeedsLocking = false;
11395
11396 /* enumerate new attachments */
11397 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11398 it != mMediaData->mAttachments.end();
11399 ++it)
11400 {
11401 MediumAttachment *pAttach = *it;
11402
11403 pAttach->i_commit();
11404
11405 Medium* pMedium = pAttach->i_getMedium();
11406 bool fImplicit = pAttach->i_isImplicit();
11407
11408 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11409 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11410 fImplicit));
11411
11412 /** @todo convert all this Machine-based voodoo to MediumAttachment
11413 * based commit logic. */
11414 if (fImplicit)
11415 {
11416 /* convert implicit attachment to normal */
11417 pAttach->i_setImplicit(false);
11418
11419 if ( aOnline
11420 && pMedium
11421 && pAttach->i_getType() == DeviceType_HardDisk
11422 )
11423 {
11424 /* update the appropriate lock list */
11425 MediumLockList *pMediumLockList;
11426 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11427 AssertComRC(rc);
11428 if (pMediumLockList)
11429 {
11430 /* unlock if there's a need to change the locking */
11431 if (!fMediaNeedsLocking)
11432 {
11433 rc = mData->mSession.mLockedMedia.Unlock();
11434 AssertComRC(rc);
11435 fMediaNeedsLocking = true;
11436 }
11437 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11438 AssertComRC(rc);
11439 rc = pMediumLockList->Append(pMedium, true);
11440 AssertComRC(rc);
11441 }
11442 }
11443
11444 continue;
11445 }
11446
11447 if (pMedium)
11448 {
11449 /* was this medium attached before? */
11450 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11451 {
11452 MediumAttachment *pOldAttach = *oldIt;
11453 if (pOldAttach->i_getMedium() == pMedium)
11454 {
11455 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11456
11457 /* yes: remove from old to avoid de-association */
11458 oldAtts.erase(oldIt);
11459 break;
11460 }
11461 }
11462 }
11463 }
11464
11465 /* enumerate remaining old attachments and de-associate from the
11466 * current machine state */
11467 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11468 {
11469 MediumAttachment *pAttach = *it;
11470 Medium* pMedium = pAttach->i_getMedium();
11471
11472 /* Detach only hard disks, since DVD/floppy media is detached
11473 * instantly in MountMedium. */
11474 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11475 {
11476 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11477
11478 /* now de-associate from the current machine state */
11479 rc = pMedium->i_removeBackReference(mData->mUuid);
11480 AssertComRC(rc);
11481
11482 if (aOnline)
11483 {
11484 /* unlock since medium is not used anymore */
11485 MediumLockList *pMediumLockList;
11486 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11487 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11488 {
11489 /* this happens for online snapshots, there the attachment
11490 * is changing, but only to a diff image created under
11491 * the old one, so there is no separate lock list */
11492 Assert(!pMediumLockList);
11493 }
11494 else
11495 {
11496 AssertComRC(rc);
11497 if (pMediumLockList)
11498 {
11499 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11500 AssertComRC(rc);
11501 }
11502 }
11503 }
11504 }
11505 }
11506
11507 /* take media locks again so that the locking state is consistent */
11508 if (fMediaNeedsLocking)
11509 {
11510 Assert(aOnline);
11511 rc = mData->mSession.mLockedMedia.Lock();
11512 AssertComRC(rc);
11513 }
11514
11515 /* commit the hard disk changes */
11516 mMediaData.commit();
11517
11518 if (i_isSessionMachine())
11519 {
11520 /*
11521 * Update the parent machine to point to the new owner.
11522 * This is necessary because the stored parent will point to the
11523 * session machine otherwise and cause crashes or errors later
11524 * when the session machine gets invalid.
11525 */
11526 /** @todo Change the MediumAttachment class to behave like any other
11527 * class in this regard by creating peer MediumAttachment
11528 * objects for session machines and share the data with the peer
11529 * machine.
11530 */
11531 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11532 it != mMediaData->mAttachments.end();
11533 ++it)
11534 (*it)->i_updateParentMachine(mPeer);
11535
11536 /* attach new data to the primary machine and reshare it */
11537 mPeer->mMediaData.attach(mMediaData);
11538 }
11539
11540 return;
11541}
11542
11543/**
11544 * Perform deferred deletion of implicitly created diffs.
11545 *
11546 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11547 * backed up).
11548 *
11549 * @note Locks this object for writing!
11550 */
11551void Machine::i_rollbackMedia()
11552{
11553 AutoCaller autoCaller(this);
11554 AssertComRCReturnVoid(autoCaller.rc());
11555
11556 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11557 LogFlowThisFunc(("Entering rollbackMedia\n"));
11558
11559 HRESULT rc = S_OK;
11560
11561 /* no attach/detach operations -- nothing to do */
11562 if (!mMediaData.isBackedUp())
11563 return;
11564
11565 /* enumerate new attachments */
11566 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11567 it != mMediaData->mAttachments.end();
11568 ++it)
11569 {
11570 MediumAttachment *pAttach = *it;
11571 /* Fix up the backrefs for DVD/floppy media. */
11572 if (pAttach->i_getType() != DeviceType_HardDisk)
11573 {
11574 Medium* pMedium = pAttach->i_getMedium();
11575 if (pMedium)
11576 {
11577 rc = pMedium->i_removeBackReference(mData->mUuid);
11578 AssertComRC(rc);
11579 }
11580 }
11581
11582 (*it)->i_rollback();
11583
11584 pAttach = *it;
11585 /* Fix up the backrefs for DVD/floppy media. */
11586 if (pAttach->i_getType() != DeviceType_HardDisk)
11587 {
11588 Medium* pMedium = pAttach->i_getMedium();
11589 if (pMedium)
11590 {
11591 rc = pMedium->i_addBackReference(mData->mUuid);
11592 AssertComRC(rc);
11593 }
11594 }
11595 }
11596
11597 /** @todo convert all this Machine-based voodoo to MediumAttachment
11598 * based rollback logic. */
11599 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11600
11601 return;
11602}
11603
11604/**
11605 * Returns true if the settings file is located in the directory named exactly
11606 * as the machine; this means, among other things, that the machine directory
11607 * should be auto-renamed.
11608 *
11609 * @param aSettingsDir if not NULL, the full machine settings file directory
11610 * name will be assigned there.
11611 *
11612 * @note Doesn't lock anything.
11613 * @note Not thread safe (must be called from this object's lock).
11614 */
11615bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11616{
11617 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11618 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11619 if (aSettingsDir)
11620 *aSettingsDir = strMachineDirName;
11621 strMachineDirName.stripPath(); // vmname
11622 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11623 strConfigFileOnly.stripPath() // vmname.vbox
11624 .stripSuffix(); // vmname
11625 /** @todo hack, make somehow use of ComposeMachineFilename */
11626 if (mUserData->s.fDirectoryIncludesUUID)
11627 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11628
11629 AssertReturn(!strMachineDirName.isEmpty(), false);
11630 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11631
11632 return strMachineDirName == strConfigFileOnly;
11633}
11634
11635/**
11636 * Discards all changes to machine settings.
11637 *
11638 * @param aNotify Whether to notify the direct session about changes or not.
11639 *
11640 * @note Locks objects for writing!
11641 */
11642void Machine::i_rollback(bool aNotify)
11643{
11644 AutoCaller autoCaller(this);
11645 AssertComRCReturn(autoCaller.rc(), (void)0);
11646
11647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11648
11649 if (!mStorageControllers.isNull())
11650 {
11651 if (mStorageControllers.isBackedUp())
11652 {
11653 /* unitialize all new devices (absent in the backed up list). */
11654 StorageControllerList::const_iterator it = mStorageControllers->begin();
11655 StorageControllerList *backedList = mStorageControllers.backedUpData();
11656 while (it != mStorageControllers->end())
11657 {
11658 if ( std::find(backedList->begin(), backedList->end(), *it)
11659 == backedList->end()
11660 )
11661 {
11662 (*it)->uninit();
11663 }
11664 ++it;
11665 }
11666
11667 /* restore the list */
11668 mStorageControllers.rollback();
11669 }
11670
11671 /* rollback any changes to devices after restoring the list */
11672 if (mData->flModifications & IsModified_Storage)
11673 {
11674 StorageControllerList::const_iterator it = mStorageControllers->begin();
11675 while (it != mStorageControllers->end())
11676 {
11677 (*it)->i_rollback();
11678 ++it;
11679 }
11680 }
11681 }
11682
11683 if (!mUSBControllers.isNull())
11684 {
11685 if (mUSBControllers.isBackedUp())
11686 {
11687 /* unitialize all new devices (absent in the backed up list). */
11688 USBControllerList::const_iterator it = mUSBControllers->begin();
11689 USBControllerList *backedList = mUSBControllers.backedUpData();
11690 while (it != mUSBControllers->end())
11691 {
11692 if ( std::find(backedList->begin(), backedList->end(), *it)
11693 == backedList->end()
11694 )
11695 {
11696 (*it)->uninit();
11697 }
11698 ++it;
11699 }
11700
11701 /* restore the list */
11702 mUSBControllers.rollback();
11703 }
11704
11705 /* rollback any changes to devices after restoring the list */
11706 if (mData->flModifications & IsModified_USB)
11707 {
11708 USBControllerList::const_iterator it = mUSBControllers->begin();
11709 while (it != mUSBControllers->end())
11710 {
11711 (*it)->i_rollback();
11712 ++it;
11713 }
11714 }
11715 }
11716
11717 mUserData.rollback();
11718
11719 mHWData.rollback();
11720
11721 if (mData->flModifications & IsModified_Storage)
11722 i_rollbackMedia();
11723
11724 if (mBIOSSettings)
11725 mBIOSSettings->i_rollback();
11726
11727 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11728 mVRDEServer->i_rollback();
11729
11730 if (mAudioAdapter)
11731 mAudioAdapter->i_rollback();
11732
11733 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11734 mUSBDeviceFilters->i_rollback();
11735
11736 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11737 mBandwidthControl->i_rollback();
11738
11739 if (!mHWData.isNull())
11740 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11741 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11742 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11743 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11744
11745 if (mData->flModifications & IsModified_NetworkAdapters)
11746 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11747 if ( mNetworkAdapters[slot]
11748 && mNetworkAdapters[slot]->i_isModified())
11749 {
11750 mNetworkAdapters[slot]->i_rollback();
11751 networkAdapters[slot] = mNetworkAdapters[slot];
11752 }
11753
11754 if (mData->flModifications & IsModified_SerialPorts)
11755 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11756 if ( mSerialPorts[slot]
11757 && mSerialPorts[slot]->i_isModified())
11758 {
11759 mSerialPorts[slot]->i_rollback();
11760 serialPorts[slot] = mSerialPorts[slot];
11761 }
11762
11763 if (mData->flModifications & IsModified_ParallelPorts)
11764 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11765 if ( mParallelPorts[slot]
11766 && mParallelPorts[slot]->i_isModified())
11767 {
11768 mParallelPorts[slot]->i_rollback();
11769 parallelPorts[slot] = mParallelPorts[slot];
11770 }
11771
11772 if (aNotify)
11773 {
11774 /* inform the direct session about changes */
11775
11776 ComObjPtr<Machine> that = this;
11777 uint32_t flModifications = mData->flModifications;
11778 alock.release();
11779
11780 if (flModifications & IsModified_SharedFolders)
11781 that->i_onSharedFolderChange();
11782
11783 if (flModifications & IsModified_VRDEServer)
11784 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11785 if (flModifications & IsModified_USB)
11786 that->i_onUSBControllerChange();
11787
11788 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11789 if (networkAdapters[slot])
11790 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11791 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11792 if (serialPorts[slot])
11793 that->i_onSerialPortChange(serialPorts[slot]);
11794 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11795 if (parallelPorts[slot])
11796 that->i_onParallelPortChange(parallelPorts[slot]);
11797
11798 if (flModifications & IsModified_Storage)
11799 that->i_onStorageControllerChange();
11800
11801#if 0
11802 if (flModifications & IsModified_BandwidthControl)
11803 that->onBandwidthControlChange();
11804#endif
11805 }
11806}
11807
11808/**
11809 * Commits all the changes to machine settings.
11810 *
11811 * Note that this operation is supposed to never fail.
11812 *
11813 * @note Locks this object and children for writing.
11814 */
11815void Machine::i_commit()
11816{
11817 AutoCaller autoCaller(this);
11818 AssertComRCReturnVoid(autoCaller.rc());
11819
11820 AutoCaller peerCaller(mPeer);
11821 AssertComRCReturnVoid(peerCaller.rc());
11822
11823 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11824
11825 /*
11826 * use safe commit to ensure Snapshot machines (that share mUserData)
11827 * will still refer to a valid memory location
11828 */
11829 mUserData.commitCopy();
11830
11831 mHWData.commit();
11832
11833 if (mMediaData.isBackedUp())
11834 i_commitMedia(Global::IsOnline(mData->mMachineState));
11835
11836 mBIOSSettings->i_commit();
11837 mVRDEServer->i_commit();
11838 mAudioAdapter->i_commit();
11839 mUSBDeviceFilters->i_commit();
11840 mBandwidthControl->i_commit();
11841
11842 /* Since mNetworkAdapters is a list which might have been changed (resized)
11843 * without using the Backupable<> template we need to handle the copying
11844 * of the list entries manually, including the creation of peers for the
11845 * new objects. */
11846 bool commitNetworkAdapters = false;
11847 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11848 if (mPeer)
11849 {
11850 /* commit everything, even the ones which will go away */
11851 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11852 mNetworkAdapters[slot]->i_commit();
11853 /* copy over the new entries, creating a peer and uninit the original */
11854 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11855 for (size_t slot = 0; slot < newSize; slot++)
11856 {
11857 /* look if this adapter has a peer device */
11858 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11859 if (!peer)
11860 {
11861 /* no peer means the adapter is a newly created one;
11862 * create a peer owning data this data share it with */
11863 peer.createObject();
11864 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11865 }
11866 mPeer->mNetworkAdapters[slot] = peer;
11867 }
11868 /* uninit any no longer needed network adapters */
11869 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11870 mNetworkAdapters[slot]->uninit();
11871 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11872 {
11873 if (mPeer->mNetworkAdapters[slot])
11874 mPeer->mNetworkAdapters[slot]->uninit();
11875 }
11876 /* Keep the original network adapter count until this point, so that
11877 * discarding a chipset type change will not lose settings. */
11878 mNetworkAdapters.resize(newSize);
11879 mPeer->mNetworkAdapters.resize(newSize);
11880 }
11881 else
11882 {
11883 /* we have no peer (our parent is the newly created machine);
11884 * just commit changes to the network adapters */
11885 commitNetworkAdapters = true;
11886 }
11887 if (commitNetworkAdapters)
11888 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11889 mNetworkAdapters[slot]->i_commit();
11890
11891 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11892 mSerialPorts[slot]->i_commit();
11893 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11894 mParallelPorts[slot]->i_commit();
11895
11896 bool commitStorageControllers = false;
11897
11898 if (mStorageControllers.isBackedUp())
11899 {
11900 mStorageControllers.commit();
11901
11902 if (mPeer)
11903 {
11904 /* Commit all changes to new controllers (this will reshare data with
11905 * peers for those who have peers) */
11906 StorageControllerList *newList = new StorageControllerList();
11907 StorageControllerList::const_iterator it = mStorageControllers->begin();
11908 while (it != mStorageControllers->end())
11909 {
11910 (*it)->i_commit();
11911
11912 /* look if this controller has a peer device */
11913 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11914 if (!peer)
11915 {
11916 /* no peer means the device is a newly created one;
11917 * create a peer owning data this device share it with */
11918 peer.createObject();
11919 peer->init(mPeer, *it, true /* aReshare */);
11920 }
11921 else
11922 {
11923 /* remove peer from the old list */
11924 mPeer->mStorageControllers->remove(peer);
11925 }
11926 /* and add it to the new list */
11927 newList->push_back(peer);
11928
11929 ++it;
11930 }
11931
11932 /* uninit old peer's controllers that are left */
11933 it = mPeer->mStorageControllers->begin();
11934 while (it != mPeer->mStorageControllers->end())
11935 {
11936 (*it)->uninit();
11937 ++it;
11938 }
11939
11940 /* attach new list of controllers to our peer */
11941 mPeer->mStorageControllers.attach(newList);
11942 }
11943 else
11944 {
11945 /* we have no peer (our parent is the newly created machine);
11946 * just commit changes to devices */
11947 commitStorageControllers = true;
11948 }
11949 }
11950 else
11951 {
11952 /* the list of controllers itself is not changed,
11953 * just commit changes to controllers themselves */
11954 commitStorageControllers = true;
11955 }
11956
11957 if (commitStorageControllers)
11958 {
11959 StorageControllerList::const_iterator it = mStorageControllers->begin();
11960 while (it != mStorageControllers->end())
11961 {
11962 (*it)->i_commit();
11963 ++it;
11964 }
11965 }
11966
11967 bool commitUSBControllers = false;
11968
11969 if (mUSBControllers.isBackedUp())
11970 {
11971 mUSBControllers.commit();
11972
11973 if (mPeer)
11974 {
11975 /* Commit all changes to new controllers (this will reshare data with
11976 * peers for those who have peers) */
11977 USBControllerList *newList = new USBControllerList();
11978 USBControllerList::const_iterator it = mUSBControllers->begin();
11979 while (it != mUSBControllers->end())
11980 {
11981 (*it)->i_commit();
11982
11983 /* look if this controller has a peer device */
11984 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11985 if (!peer)
11986 {
11987 /* no peer means the device is a newly created one;
11988 * create a peer owning data this device share it with */
11989 peer.createObject();
11990 peer->init(mPeer, *it, true /* aReshare */);
11991 }
11992 else
11993 {
11994 /* remove peer from the old list */
11995 mPeer->mUSBControllers->remove(peer);
11996 }
11997 /* and add it to the new list */
11998 newList->push_back(peer);
11999
12000 ++it;
12001 }
12002
12003 /* uninit old peer's controllers that are left */
12004 it = mPeer->mUSBControllers->begin();
12005 while (it != mPeer->mUSBControllers->end())
12006 {
12007 (*it)->uninit();
12008 ++it;
12009 }
12010
12011 /* attach new list of controllers to our peer */
12012 mPeer->mUSBControllers.attach(newList);
12013 }
12014 else
12015 {
12016 /* we have no peer (our parent is the newly created machine);
12017 * just commit changes to devices */
12018 commitUSBControllers = true;
12019 }
12020 }
12021 else
12022 {
12023 /* the list of controllers itself is not changed,
12024 * just commit changes to controllers themselves */
12025 commitUSBControllers = true;
12026 }
12027
12028 if (commitUSBControllers)
12029 {
12030 USBControllerList::const_iterator it = mUSBControllers->begin();
12031 while (it != mUSBControllers->end())
12032 {
12033 (*it)->i_commit();
12034 ++it;
12035 }
12036 }
12037
12038 if (i_isSessionMachine())
12039 {
12040 /* attach new data to the primary machine and reshare it */
12041 mPeer->mUserData.attach(mUserData);
12042 mPeer->mHWData.attach(mHWData);
12043 /* mMediaData is reshared by fixupMedia */
12044 // mPeer->mMediaData.attach(mMediaData);
12045 Assert(mPeer->mMediaData.data() == mMediaData.data());
12046 }
12047}
12048
12049/**
12050 * Copies all the hardware data from the given machine.
12051 *
12052 * Currently, only called when the VM is being restored from a snapshot. In
12053 * particular, this implies that the VM is not running during this method's
12054 * call.
12055 *
12056 * @note This method must be called from under this object's lock.
12057 *
12058 * @note This method doesn't call #commit(), so all data remains backed up and
12059 * unsaved.
12060 */
12061void Machine::i_copyFrom(Machine *aThat)
12062{
12063 AssertReturnVoid(!i_isSnapshotMachine());
12064 AssertReturnVoid(aThat->i_isSnapshotMachine());
12065
12066 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12067
12068 mHWData.assignCopy(aThat->mHWData);
12069
12070 // create copies of all shared folders (mHWData after attaching a copy
12071 // contains just references to original objects)
12072 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12073 it != mHWData->mSharedFolders.end();
12074 ++it)
12075 {
12076 ComObjPtr<SharedFolder> folder;
12077 folder.createObject();
12078 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12079 AssertComRC(rc);
12080 *it = folder;
12081 }
12082
12083 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12084 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12085 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12086 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12087 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12088
12089 /* create private copies of all controllers */
12090 mStorageControllers.backup();
12091 mStorageControllers->clear();
12092 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12093 it != aThat->mStorageControllers->end();
12094 ++it)
12095 {
12096 ComObjPtr<StorageController> ctrl;
12097 ctrl.createObject();
12098 ctrl->initCopy(this, *it);
12099 mStorageControllers->push_back(ctrl);
12100 }
12101
12102 /* create private copies of all USB controllers */
12103 mUSBControllers.backup();
12104 mUSBControllers->clear();
12105 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12106 it != aThat->mUSBControllers->end();
12107 ++it)
12108 {
12109 ComObjPtr<USBController> ctrl;
12110 ctrl.createObject();
12111 ctrl->initCopy(this, *it);
12112 mUSBControllers->push_back(ctrl);
12113 }
12114
12115 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12116 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12117 {
12118 if (mNetworkAdapters[slot].isNotNull())
12119 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12120 else
12121 {
12122 unconst(mNetworkAdapters[slot]).createObject();
12123 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12124 }
12125 }
12126 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12127 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12128 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12129 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12130}
12131
12132/**
12133 * Returns whether the given storage controller is hotplug capable.
12134 *
12135 * @returns true if the controller supports hotplugging
12136 * false otherwise.
12137 * @param enmCtrlType The controller type to check for.
12138 */
12139bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12140{
12141 ComPtr<ISystemProperties> systemProperties;
12142 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12143 if (FAILED(rc))
12144 return false;
12145
12146 BOOL aHotplugCapable = FALSE;
12147 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12148
12149 return RT_BOOL(aHotplugCapable);
12150}
12151
12152#ifdef VBOX_WITH_RESOURCE_USAGE_API
12153
12154void Machine::i_getDiskList(MediaList &list)
12155{
12156 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12157 it != mMediaData->mAttachments.end();
12158 ++it)
12159 {
12160 MediumAttachment* pAttach = *it;
12161 /* just in case */
12162 AssertContinue(pAttach);
12163
12164 AutoCaller localAutoCallerA(pAttach);
12165 if (FAILED(localAutoCallerA.rc())) continue;
12166
12167 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12168
12169 if (pAttach->i_getType() == DeviceType_HardDisk)
12170 list.push_back(pAttach->i_getMedium());
12171 }
12172}
12173
12174void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12175{
12176 AssertReturnVoid(isWriteLockOnCurrentThread());
12177 AssertPtrReturnVoid(aCollector);
12178
12179 pm::CollectorHAL *hal = aCollector->getHAL();
12180 /* Create sub metrics */
12181 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12182 "Percentage of processor time spent in user mode by the VM process.");
12183 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12184 "Percentage of processor time spent in kernel mode by the VM process.");
12185 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12186 "Size of resident portion of VM process in memory.");
12187 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12188 "Actual size of all VM disks combined.");
12189 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12190 "Network receive rate.");
12191 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12192 "Network transmit rate.");
12193 /* Create and register base metrics */
12194 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12195 cpuLoadUser, cpuLoadKernel);
12196 aCollector->registerBaseMetric(cpuLoad);
12197 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12198 ramUsageUsed);
12199 aCollector->registerBaseMetric(ramUsage);
12200 MediaList disks;
12201 i_getDiskList(disks);
12202 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12203 diskUsageUsed);
12204 aCollector->registerBaseMetric(diskUsage);
12205
12206 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12207 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12208 new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12210 new pm::AggregateMin()));
12211 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12212 new pm::AggregateMax()));
12213 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12214 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12215 new pm::AggregateAvg()));
12216 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12217 new pm::AggregateMin()));
12218 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12219 new pm::AggregateMax()));
12220
12221 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12222 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12223 new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12225 new pm::AggregateMin()));
12226 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12227 new pm::AggregateMax()));
12228
12229 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12230 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12231 new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12233 new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12235 new pm::AggregateMax()));
12236
12237
12238 /* Guest metrics collector */
12239 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12240 aCollector->registerGuest(mCollectorGuest);
12241 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12242
12243 /* Create sub metrics */
12244 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12245 "Percentage of processor time spent in user mode as seen by the guest.");
12246 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12247 "Percentage of processor time spent in kernel mode as seen by the guest.");
12248 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12249 "Percentage of processor time spent idling as seen by the guest.");
12250
12251 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12252 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12253 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12254 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12255 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12256 pm::SubMetric *guestMemCache = new pm::SubMetric(
12257 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12258
12259 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12260 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12261
12262 /* Create and register base metrics */
12263 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12264 machineNetRx, machineNetTx);
12265 aCollector->registerBaseMetric(machineNetRate);
12266
12267 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12268 guestLoadUser, guestLoadKernel, guestLoadIdle);
12269 aCollector->registerBaseMetric(guestCpuLoad);
12270
12271 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12272 guestMemTotal, guestMemFree,
12273 guestMemBalloon, guestMemShared,
12274 guestMemCache, guestPagedTotal);
12275 aCollector->registerBaseMetric(guestCpuMem);
12276
12277 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12278 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12283 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12285 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12288 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12293 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12294 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12295 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12296
12297 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12298 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12306
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12311
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12316
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12321
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12326
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12331}
12332
12333void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12334{
12335 AssertReturnVoid(isWriteLockOnCurrentThread());
12336
12337 if (aCollector)
12338 {
12339 aCollector->unregisterMetricsFor(aMachine);
12340 aCollector->unregisterBaseMetricsFor(aMachine);
12341 }
12342}
12343
12344#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12345
12346
12347////////////////////////////////////////////////////////////////////////////////
12348
12349DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12350
12351HRESULT SessionMachine::FinalConstruct()
12352{
12353 LogFlowThisFunc(("\n"));
12354
12355 mClientToken = NULL;
12356
12357 return BaseFinalConstruct();
12358}
12359
12360void SessionMachine::FinalRelease()
12361{
12362 LogFlowThisFunc(("\n"));
12363
12364 Assert(!mClientToken);
12365 /* paranoia, should not hang around any more */
12366 if (mClientToken)
12367 {
12368 delete mClientToken;
12369 mClientToken = NULL;
12370 }
12371
12372 uninit(Uninit::Unexpected);
12373
12374 BaseFinalRelease();
12375}
12376
12377/**
12378 * @note Must be called only by Machine::LockMachine() from its own write lock.
12379 */
12380HRESULT SessionMachine::init(Machine *aMachine)
12381{
12382 LogFlowThisFuncEnter();
12383 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12384
12385 AssertReturn(aMachine, E_INVALIDARG);
12386
12387 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12388
12389 /* Enclose the state transition NotReady->InInit->Ready */
12390 AutoInitSpan autoInitSpan(this);
12391 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12392
12393 HRESULT rc = S_OK;
12394
12395 RT_ZERO(mAuthLibCtx);
12396
12397 /* create the machine client token */
12398 try
12399 {
12400 mClientToken = new ClientToken(aMachine, this);
12401 if (!mClientToken->isReady())
12402 {
12403 delete mClientToken;
12404 mClientToken = NULL;
12405 rc = E_FAIL;
12406 }
12407 }
12408 catch (std::bad_alloc &)
12409 {
12410 rc = E_OUTOFMEMORY;
12411 }
12412 if (FAILED(rc))
12413 return rc;
12414
12415 /* memorize the peer Machine */
12416 unconst(mPeer) = aMachine;
12417 /* share the parent pointer */
12418 unconst(mParent) = aMachine->mParent;
12419
12420 /* take the pointers to data to share */
12421 mData.share(aMachine->mData);
12422 mSSData.share(aMachine->mSSData);
12423
12424 mUserData.share(aMachine->mUserData);
12425 mHWData.share(aMachine->mHWData);
12426 mMediaData.share(aMachine->mMediaData);
12427
12428 mStorageControllers.allocate();
12429 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12430 it != aMachine->mStorageControllers->end();
12431 ++it)
12432 {
12433 ComObjPtr<StorageController> ctl;
12434 ctl.createObject();
12435 ctl->init(this, *it);
12436 mStorageControllers->push_back(ctl);
12437 }
12438
12439 mUSBControllers.allocate();
12440 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12441 it != aMachine->mUSBControllers->end();
12442 ++it)
12443 {
12444 ComObjPtr<USBController> ctl;
12445 ctl.createObject();
12446 ctl->init(this, *it);
12447 mUSBControllers->push_back(ctl);
12448 }
12449
12450 unconst(mBIOSSettings).createObject();
12451 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12452 /* create another VRDEServer object that will be mutable */
12453 unconst(mVRDEServer).createObject();
12454 mVRDEServer->init(this, aMachine->mVRDEServer);
12455 /* create another audio adapter object that will be mutable */
12456 unconst(mAudioAdapter).createObject();
12457 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12458 /* create a list of serial ports that will be mutable */
12459 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12460 {
12461 unconst(mSerialPorts[slot]).createObject();
12462 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12463 }
12464 /* create a list of parallel ports that will be mutable */
12465 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12466 {
12467 unconst(mParallelPorts[slot]).createObject();
12468 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12469 }
12470
12471 /* create another USB device filters object that will be mutable */
12472 unconst(mUSBDeviceFilters).createObject();
12473 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12474
12475 /* create a list of network adapters that will be mutable */
12476 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12477 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12478 {
12479 unconst(mNetworkAdapters[slot]).createObject();
12480 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12481 }
12482
12483 /* create another bandwidth control object that will be mutable */
12484 unconst(mBandwidthControl).createObject();
12485 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12486
12487 /* default is to delete saved state on Saved -> PoweredOff transition */
12488 mRemoveSavedState = true;
12489
12490 /* Confirm a successful initialization when it's the case */
12491 autoInitSpan.setSucceeded();
12492
12493 miNATNetworksStarted = 0;
12494
12495 LogFlowThisFuncLeave();
12496 return rc;
12497}
12498
12499/**
12500 * Uninitializes this session object. If the reason is other than
12501 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12502 * or the client watcher code.
12503 *
12504 * @param aReason uninitialization reason
12505 *
12506 * @note Locks mParent + this object for writing.
12507 */
12508void SessionMachine::uninit(Uninit::Reason aReason)
12509{
12510 LogFlowThisFuncEnter();
12511 LogFlowThisFunc(("reason=%d\n", aReason));
12512
12513 /*
12514 * Strongly reference ourselves to prevent this object deletion after
12515 * mData->mSession.mMachine.setNull() below (which can release the last
12516 * reference and call the destructor). Important: this must be done before
12517 * accessing any members (and before AutoUninitSpan that does it as well).
12518 * This self reference will be released as the very last step on return.
12519 */
12520 ComObjPtr<SessionMachine> selfRef = this;
12521
12522 /* Enclose the state transition Ready->InUninit->NotReady */
12523 AutoUninitSpan autoUninitSpan(this);
12524 if (autoUninitSpan.uninitDone())
12525 {
12526 LogFlowThisFunc(("Already uninitialized\n"));
12527 LogFlowThisFuncLeave();
12528 return;
12529 }
12530
12531 if (autoUninitSpan.initFailed())
12532 {
12533 /* We've been called by init() because it's failed. It's not really
12534 * necessary (nor it's safe) to perform the regular uninit sequence
12535 * below, the following is enough.
12536 */
12537 LogFlowThisFunc(("Initialization failed.\n"));
12538 /* destroy the machine client token */
12539 if (mClientToken)
12540 {
12541 delete mClientToken;
12542 mClientToken = NULL;
12543 }
12544 uninitDataAndChildObjects();
12545 mData.free();
12546 unconst(mParent) = NULL;
12547 unconst(mPeer) = NULL;
12548 LogFlowThisFuncLeave();
12549 return;
12550 }
12551
12552 MachineState_T lastState;
12553 {
12554 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12555 lastState = mData->mMachineState;
12556 }
12557 NOREF(lastState);
12558
12559#ifdef VBOX_WITH_USB
12560 // release all captured USB devices, but do this before requesting the locks below
12561 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12562 {
12563 /* Console::captureUSBDevices() is called in the VM process only after
12564 * setting the machine state to Starting or Restoring.
12565 * Console::detachAllUSBDevices() will be called upon successful
12566 * termination. So, we need to release USB devices only if there was
12567 * an abnormal termination of a running VM.
12568 *
12569 * This is identical to SessionMachine::DetachAllUSBDevices except
12570 * for the aAbnormal argument. */
12571 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12572 AssertComRC(rc);
12573 NOREF(rc);
12574
12575 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12576 if (service)
12577 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12578 }
12579#endif /* VBOX_WITH_USB */
12580
12581 // we need to lock this object in uninit() because the lock is shared
12582 // with mPeer (as well as data we modify below). mParent lock is needed
12583 // by several calls to it, and USB needs host lock.
12584 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12585
12586#ifdef VBOX_WITH_RESOURCE_USAGE_API
12587 /*
12588 * It is safe to call Machine::i_unregisterMetrics() here because
12589 * PerformanceCollector::samplerCallback no longer accesses guest methods
12590 * holding the lock.
12591 */
12592 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12593 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12594 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12595 if (mCollectorGuest)
12596 {
12597 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12598 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12599 mCollectorGuest = NULL;
12600 }
12601#endif
12602
12603 if (aReason == Uninit::Abnormal)
12604 {
12605 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12606
12607 /* reset the state to Aborted */
12608 if (mData->mMachineState != MachineState_Aborted)
12609 i_setMachineState(MachineState_Aborted);
12610 }
12611
12612 // any machine settings modified?
12613 if (mData->flModifications)
12614 {
12615 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12616 i_rollback(false /* aNotify */);
12617 }
12618
12619 mData->mSession.mPID = NIL_RTPROCESS;
12620
12621 if (aReason == Uninit::Unexpected)
12622 {
12623 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12624 * client watcher thread to update the set of machines that have open
12625 * sessions. */
12626 mParent->i_updateClientWatcher();
12627 }
12628
12629 /* uninitialize all remote controls */
12630 if (mData->mSession.mRemoteControls.size())
12631 {
12632 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12633 mData->mSession.mRemoteControls.size()));
12634
12635 Data::Session::RemoteControlList::iterator it =
12636 mData->mSession.mRemoteControls.begin();
12637 while (it != mData->mSession.mRemoteControls.end())
12638 {
12639 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12640 HRESULT rc = (*it)->Uninitialize();
12641 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12642 if (FAILED(rc))
12643 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12644 ++it;
12645 }
12646 mData->mSession.mRemoteControls.clear();
12647 }
12648
12649 /* Remove all references to the NAT network service. The service will stop
12650 * if all references (also from other VMs) are removed. */
12651 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12652 {
12653 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12654 {
12655 BOOL enabled;
12656 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12657 if ( FAILED(hrc)
12658 || !enabled)
12659 continue;
12660
12661 NetworkAttachmentType_T type;
12662 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12663 if ( SUCCEEDED(hrc)
12664 && type == NetworkAttachmentType_NATNetwork)
12665 {
12666 Bstr name;
12667 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12668 if (SUCCEEDED(hrc))
12669 {
12670 multilock.release();
12671 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12672 mUserData->s.strName.c_str(), name.raw()));
12673 mParent->i_natNetworkRefDec(name.raw());
12674 multilock.acquire();
12675 }
12676 }
12677 }
12678 }
12679
12680 /*
12681 * An expected uninitialization can come only from #i_checkForDeath().
12682 * Otherwise it means that something's gone really wrong (for example,
12683 * the Session implementation has released the VirtualBox reference
12684 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12685 * etc). However, it's also possible, that the client releases the IPC
12686 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12687 * but the VirtualBox release event comes first to the server process.
12688 * This case is practically possible, so we should not assert on an
12689 * unexpected uninit, just log a warning.
12690 */
12691
12692 if ((aReason == Uninit::Unexpected))
12693 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12694
12695 if (aReason != Uninit::Normal)
12696 {
12697 mData->mSession.mDirectControl.setNull();
12698 }
12699 else
12700 {
12701 /* this must be null here (see #OnSessionEnd()) */
12702 Assert(mData->mSession.mDirectControl.isNull());
12703 Assert(mData->mSession.mState == SessionState_Unlocking);
12704 Assert(!mData->mSession.mProgress.isNull());
12705 }
12706 if (mData->mSession.mProgress)
12707 {
12708 if (aReason == Uninit::Normal)
12709 mData->mSession.mProgress->i_notifyComplete(S_OK);
12710 else
12711 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12712 COM_IIDOF(ISession),
12713 getComponentName(),
12714 tr("The VM session was aborted"));
12715 mData->mSession.mProgress.setNull();
12716 }
12717
12718 if (mConsoleTaskData.mProgress)
12719 {
12720 Assert(aReason == Uninit::Abnormal);
12721 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12722 COM_IIDOF(ISession),
12723 getComponentName(),
12724 tr("The VM session was aborted"));
12725 mConsoleTaskData.mProgress.setNull();
12726 }
12727
12728 /* remove the association between the peer machine and this session machine */
12729 Assert( (SessionMachine*)mData->mSession.mMachine == this
12730 || aReason == Uninit::Unexpected);
12731
12732 /* reset the rest of session data */
12733 mData->mSession.mLockType = LockType_Null;
12734 mData->mSession.mMachine.setNull();
12735 mData->mSession.mState = SessionState_Unlocked;
12736 mData->mSession.mName.setNull();
12737
12738 /* destroy the machine client token before leaving the exclusive lock */
12739 if (mClientToken)
12740 {
12741 delete mClientToken;
12742 mClientToken = NULL;
12743 }
12744
12745 /* fire an event */
12746 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12747
12748 uninitDataAndChildObjects();
12749
12750 /* free the essential data structure last */
12751 mData.free();
12752
12753 /* release the exclusive lock before setting the below two to NULL */
12754 multilock.release();
12755
12756 unconst(mParent) = NULL;
12757 unconst(mPeer) = NULL;
12758
12759 AuthLibUnload(&mAuthLibCtx);
12760
12761 LogFlowThisFuncLeave();
12762}
12763
12764// util::Lockable interface
12765////////////////////////////////////////////////////////////////////////////////
12766
12767/**
12768 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12769 * with the primary Machine instance (mPeer).
12770 */
12771RWLockHandle *SessionMachine::lockHandle() const
12772{
12773 AssertReturn(mPeer != NULL, NULL);
12774 return mPeer->lockHandle();
12775}
12776
12777// IInternalMachineControl methods
12778////////////////////////////////////////////////////////////////////////////////
12779
12780/**
12781 * Passes collected guest statistics to performance collector object
12782 */
12783HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12784 ULONG aCpuKernel, ULONG aCpuIdle,
12785 ULONG aMemTotal, ULONG aMemFree,
12786 ULONG aMemBalloon, ULONG aMemShared,
12787 ULONG aMemCache, ULONG aPageTotal,
12788 ULONG aAllocVMM, ULONG aFreeVMM,
12789 ULONG aBalloonedVMM, ULONG aSharedVMM,
12790 ULONG aVmNetRx, ULONG aVmNetTx)
12791{
12792#ifdef VBOX_WITH_RESOURCE_USAGE_API
12793 if (mCollectorGuest)
12794 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12795 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12796 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12797 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12798
12799 return S_OK;
12800#else
12801 NOREF(aValidStats);
12802 NOREF(aCpuUser);
12803 NOREF(aCpuKernel);
12804 NOREF(aCpuIdle);
12805 NOREF(aMemTotal);
12806 NOREF(aMemFree);
12807 NOREF(aMemBalloon);
12808 NOREF(aMemShared);
12809 NOREF(aMemCache);
12810 NOREF(aPageTotal);
12811 NOREF(aAllocVMM);
12812 NOREF(aFreeVMM);
12813 NOREF(aBalloonedVMM);
12814 NOREF(aSharedVMM);
12815 NOREF(aVmNetRx);
12816 NOREF(aVmNetTx);
12817 return E_NOTIMPL;
12818#endif
12819}
12820
12821////////////////////////////////////////////////////////////////////////////////
12822//
12823// SessionMachine task records
12824//
12825////////////////////////////////////////////////////////////////////////////////
12826
12827/**
12828 * Task record for saving the machine state.
12829 */
12830struct SessionMachine::SaveStateTask
12831 : public Machine::Task
12832{
12833 SaveStateTask(SessionMachine *m,
12834 Progress *p,
12835 const Utf8Str &t,
12836 Reason_T enmReason,
12837 const Utf8Str &strStateFilePath)
12838 : Task(m, p, t),
12839 m_enmReason(enmReason),
12840 m_strStateFilePath(strStateFilePath)
12841 {}
12842
12843 void handler()
12844 {
12845 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12846 }
12847
12848 Reason_T m_enmReason;
12849 Utf8Str m_strStateFilePath;
12850};
12851
12852/**
12853 * Task thread implementation for SessionMachine::SaveState(), called from
12854 * SessionMachine::taskHandler().
12855 *
12856 * @note Locks this object for writing.
12857 *
12858 * @param task
12859 * @return
12860 */
12861void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12862{
12863 LogFlowThisFuncEnter();
12864
12865 AutoCaller autoCaller(this);
12866 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12867 if (FAILED(autoCaller.rc()))
12868 {
12869 /* we might have been uninitialized because the session was accidentally
12870 * closed by the client, so don't assert */
12871 HRESULT rc = setError(E_FAIL,
12872 tr("The session has been accidentally closed"));
12873 task.m_pProgress->i_notifyComplete(rc);
12874 LogFlowThisFuncLeave();
12875 return;
12876 }
12877
12878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12879
12880 HRESULT rc = S_OK;
12881
12882 try
12883 {
12884 ComPtr<IInternalSessionControl> directControl;
12885 if (mData->mSession.mLockType == LockType_VM)
12886 directControl = mData->mSession.mDirectControl;
12887 if (directControl.isNull())
12888 throw setError(VBOX_E_INVALID_VM_STATE,
12889 tr("Trying to save state without a running VM"));
12890 alock.release();
12891 BOOL fSuspendedBySave;
12892 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12893 Assert(!fSuspendedBySave);
12894 alock.acquire();
12895
12896 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12897 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12898 throw E_FAIL);
12899
12900 if (SUCCEEDED(rc))
12901 {
12902 mSSData->strStateFilePath = task.m_strStateFilePath;
12903
12904 /* save all VM settings */
12905 rc = i_saveSettings(NULL);
12906 // no need to check whether VirtualBox.xml needs saving also since
12907 // we can't have a name change pending at this point
12908 }
12909 else
12910 {
12911 // On failure, set the state to the state we had at the beginning.
12912 i_setMachineState(task.m_machineStateBackup);
12913 i_updateMachineStateOnClient();
12914
12915 // Delete the saved state file (might have been already created).
12916 // No need to check whether this is shared with a snapshot here
12917 // because we certainly created a fresh saved state file here.
12918 RTFileDelete(task.m_strStateFilePath.c_str());
12919 }
12920 }
12921 catch (HRESULT aRC) { rc = aRC; }
12922
12923 task.m_pProgress->i_notifyComplete(rc);
12924
12925 LogFlowThisFuncLeave();
12926}
12927
12928/**
12929 * @note Locks this object for writing.
12930 */
12931HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12932{
12933 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12934}
12935
12936HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12937{
12938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12939
12940 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12941 if (FAILED(rc)) return rc;
12942
12943 if ( mData->mMachineState != MachineState_Running
12944 && mData->mMachineState != MachineState_Paused
12945 )
12946 return setError(VBOX_E_INVALID_VM_STATE,
12947 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12948 Global::stringifyMachineState(mData->mMachineState));
12949
12950 ComObjPtr<Progress> pProgress;
12951 pProgress.createObject();
12952 rc = pProgress->init(i_getVirtualBox(),
12953 static_cast<IMachine *>(this) /* aInitiator */,
12954 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12955 FALSE /* aCancelable */);
12956 if (FAILED(rc))
12957 return rc;
12958
12959 Utf8Str strStateFilePath;
12960 i_composeSavedStateFilename(strStateFilePath);
12961
12962 /* create and start the task on a separate thread (note that it will not
12963 * start working until we release alock) */
12964 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12965 rc = pTask->createThread();
12966 if (FAILED(rc))
12967 return rc;
12968
12969 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12970 i_setMachineState(MachineState_Saving);
12971 i_updateMachineStateOnClient();
12972
12973 pProgress.queryInterfaceTo(aProgress.asOutParam());
12974
12975 return S_OK;
12976}
12977
12978/**
12979 * @note Locks this object for writing.
12980 */
12981HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12982{
12983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12984
12985 HRESULT rc = i_checkStateDependency(MutableStateDep);
12986 if (FAILED(rc)) return rc;
12987
12988 if ( mData->mMachineState != MachineState_PoweredOff
12989 && mData->mMachineState != MachineState_Teleported
12990 && mData->mMachineState != MachineState_Aborted
12991 )
12992 return setError(VBOX_E_INVALID_VM_STATE,
12993 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12994 Global::stringifyMachineState(mData->mMachineState));
12995
12996 com::Utf8Str stateFilePathFull;
12997 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12998 if (RT_FAILURE(vrc))
12999 return setError(VBOX_E_FILE_ERROR,
13000 tr("Invalid saved state file path '%s' (%Rrc)"),
13001 aSavedStateFile.c_str(),
13002 vrc);
13003
13004 mSSData->strStateFilePath = stateFilePathFull;
13005
13006 /* The below i_setMachineState() will detect the state transition and will
13007 * update the settings file */
13008
13009 return i_setMachineState(MachineState_Saved);
13010}
13011
13012/**
13013 * @note Locks this object for writing.
13014 */
13015HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13016{
13017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13018
13019 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13020 if (FAILED(rc)) return rc;
13021
13022 if (mData->mMachineState != MachineState_Saved)
13023 return setError(VBOX_E_INVALID_VM_STATE,
13024 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13025 Global::stringifyMachineState(mData->mMachineState));
13026
13027 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13028
13029 /*
13030 * Saved -> PoweredOff transition will be detected in the SessionMachine
13031 * and properly handled.
13032 */
13033 rc = i_setMachineState(MachineState_PoweredOff);
13034 return rc;
13035}
13036
13037
13038/**
13039 * @note Locks the same as #i_setMachineState() does.
13040 */
13041HRESULT SessionMachine::updateState(MachineState_T aState)
13042{
13043 return i_setMachineState(aState);
13044}
13045
13046/**
13047 * @note Locks this object for writing.
13048 */
13049HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13050{
13051 IProgress* pProgress(aProgress);
13052
13053 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13054
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 if (mData->mSession.mState != SessionState_Locked)
13058 return VBOX_E_INVALID_OBJECT_STATE;
13059
13060 if (!mData->mSession.mProgress.isNull())
13061 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13062
13063 /* If we didn't reference the NAT network service yet, add a reference to
13064 * force a start */
13065 if (miNATNetworksStarted < 1)
13066 {
13067 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13068 {
13069 BOOL enabled;
13070 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13071 if ( FAILED(hrc)
13072 || !enabled)
13073 continue;
13074
13075 NetworkAttachmentType_T type;
13076 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13077 if ( SUCCEEDED(hrc)
13078 && type == NetworkAttachmentType_NATNetwork)
13079 {
13080 Bstr name;
13081 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13082 if (SUCCEEDED(hrc))
13083 {
13084 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13085 mUserData->s.strName.c_str(), name.raw()));
13086 mPeer->lockHandle()->unlockWrite();
13087 mParent->i_natNetworkRefInc(name.raw());
13088#ifdef RT_LOCK_STRICT
13089 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13090#else
13091 mPeer->lockHandle()->lockWrite();
13092#endif
13093 }
13094 }
13095 }
13096 miNATNetworksStarted++;
13097 }
13098
13099 LogFlowThisFunc(("returns S_OK.\n"));
13100 return S_OK;
13101}
13102
13103/**
13104 * @note Locks this object for writing.
13105 */
13106HRESULT SessionMachine::endPowerUp(LONG aResult)
13107{
13108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13109
13110 if (mData->mSession.mState != SessionState_Locked)
13111 return VBOX_E_INVALID_OBJECT_STATE;
13112
13113 /* Finalize the LaunchVMProcess progress object. */
13114 if (mData->mSession.mProgress)
13115 {
13116 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13117 mData->mSession.mProgress.setNull();
13118 }
13119
13120 if (SUCCEEDED((HRESULT)aResult))
13121 {
13122#ifdef VBOX_WITH_RESOURCE_USAGE_API
13123 /* The VM has been powered up successfully, so it makes sense
13124 * now to offer the performance metrics for a running machine
13125 * object. Doing it earlier wouldn't be safe. */
13126 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13127 mData->mSession.mPID);
13128#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13129 }
13130
13131 return S_OK;
13132}
13133
13134/**
13135 * @note Locks this object for writing.
13136 */
13137HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13138{
13139 LogFlowThisFuncEnter();
13140
13141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13142
13143 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13144 E_FAIL);
13145
13146 /* create a progress object to track operation completion */
13147 ComObjPtr<Progress> pProgress;
13148 pProgress.createObject();
13149 pProgress->init(i_getVirtualBox(),
13150 static_cast<IMachine *>(this) /* aInitiator */,
13151 Bstr(tr("Stopping the virtual machine")).raw(),
13152 FALSE /* aCancelable */);
13153
13154 /* fill in the console task data */
13155 mConsoleTaskData.mLastState = mData->mMachineState;
13156 mConsoleTaskData.mProgress = pProgress;
13157
13158 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13159 i_setMachineState(MachineState_Stopping);
13160
13161 pProgress.queryInterfaceTo(aProgress.asOutParam());
13162
13163 return S_OK;
13164}
13165
13166/**
13167 * @note Locks this object for writing.
13168 */
13169HRESULT SessionMachine::endPoweringDown(LONG aResult,
13170 const com::Utf8Str &aErrMsg)
13171{
13172 LogFlowThisFuncEnter();
13173
13174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13175
13176 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13177 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13178 && mConsoleTaskData.mLastState != MachineState_Null,
13179 E_FAIL);
13180
13181 /*
13182 * On failure, set the state to the state we had when BeginPoweringDown()
13183 * was called (this is expected by Console::PowerDown() and the associated
13184 * task). On success the VM process already changed the state to
13185 * MachineState_PoweredOff, so no need to do anything.
13186 */
13187 if (FAILED(aResult))
13188 i_setMachineState(mConsoleTaskData.mLastState);
13189
13190 /* notify the progress object about operation completion */
13191 Assert(mConsoleTaskData.mProgress);
13192 if (SUCCEEDED(aResult))
13193 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13194 else
13195 {
13196 if (aErrMsg.length())
13197 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13198 COM_IIDOF(ISession),
13199 getComponentName(),
13200 aErrMsg.c_str());
13201 else
13202 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13203 }
13204
13205 /* clear out the temporary saved state data */
13206 mConsoleTaskData.mLastState = MachineState_Null;
13207 mConsoleTaskData.mProgress.setNull();
13208
13209 LogFlowThisFuncLeave();
13210 return S_OK;
13211}
13212
13213
13214/**
13215 * Goes through the USB filters of the given machine to see if the given
13216 * device matches any filter or not.
13217 *
13218 * @note Locks the same as USBController::hasMatchingFilter() does.
13219 */
13220HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13221 BOOL *aMatched,
13222 ULONG *aMaskedInterfaces)
13223{
13224 LogFlowThisFunc(("\n"));
13225
13226#ifdef VBOX_WITH_USB
13227 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13228#else
13229 NOREF(aDevice);
13230 NOREF(aMaskedInterfaces);
13231 *aMatched = FALSE;
13232#endif
13233
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks the same as Host::captureUSBDevice() does.
13239 */
13240HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13241{
13242 LogFlowThisFunc(("\n"));
13243
13244#ifdef VBOX_WITH_USB
13245 /* if captureDeviceForVM() fails, it must have set extended error info */
13246 clearError();
13247 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13248 if (FAILED(rc)) return rc;
13249
13250 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13251 AssertReturn(service, E_FAIL);
13252 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13253#else
13254 NOREF(aId);
13255 return E_NOTIMPL;
13256#endif
13257}
13258
13259/**
13260 * @note Locks the same as Host::detachUSBDevice() does.
13261 */
13262HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13263 BOOL aDone)
13264{
13265 LogFlowThisFunc(("\n"));
13266
13267#ifdef VBOX_WITH_USB
13268 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13269 AssertReturn(service, E_FAIL);
13270 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13271#else
13272 NOREF(aId);
13273 NOREF(aDone);
13274 return E_NOTIMPL;
13275#endif
13276}
13277
13278/**
13279 * Inserts all machine filters to the USB proxy service and then calls
13280 * Host::autoCaptureUSBDevices().
13281 *
13282 * Called by Console from the VM process upon VM startup.
13283 *
13284 * @note Locks what called methods lock.
13285 */
13286HRESULT SessionMachine::autoCaptureUSBDevices()
13287{
13288 LogFlowThisFunc(("\n"));
13289
13290#ifdef VBOX_WITH_USB
13291 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13292 AssertComRC(rc);
13293 NOREF(rc);
13294
13295 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13296 AssertReturn(service, E_FAIL);
13297 return service->autoCaptureDevicesForVM(this);
13298#else
13299 return S_OK;
13300#endif
13301}
13302
13303/**
13304 * Removes all machine filters from the USB proxy service and then calls
13305 * Host::detachAllUSBDevices().
13306 *
13307 * Called by Console from the VM process upon normal VM termination or by
13308 * SessionMachine::uninit() upon abnormal VM termination (from under the
13309 * Machine/SessionMachine lock).
13310 *
13311 * @note Locks what called methods lock.
13312 */
13313HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317#ifdef VBOX_WITH_USB
13318 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13319 AssertComRC(rc);
13320 NOREF(rc);
13321
13322 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13323 AssertReturn(service, E_FAIL);
13324 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13325#else
13326 NOREF(aDone);
13327 return S_OK;
13328#endif
13329}
13330
13331/**
13332 * @note Locks this object for writing.
13333 */
13334HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13335 ComPtr<IProgress> &aProgress)
13336{
13337 LogFlowThisFuncEnter();
13338
13339 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13340 /*
13341 * We don't assert below because it might happen that a non-direct session
13342 * informs us it is closed right after we've been uninitialized -- it's ok.
13343 */
13344
13345 /* get IInternalSessionControl interface */
13346 ComPtr<IInternalSessionControl> control(aSession);
13347
13348 ComAssertRet(!control.isNull(), E_INVALIDARG);
13349
13350 /* Creating a Progress object requires the VirtualBox lock, and
13351 * thus locking it here is required by the lock order rules. */
13352 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13353
13354 if (control == mData->mSession.mDirectControl)
13355 {
13356 /* The direct session is being normally closed by the client process
13357 * ----------------------------------------------------------------- */
13358
13359 /* go to the closing state (essential for all open*Session() calls and
13360 * for #i_checkForDeath()) */
13361 Assert(mData->mSession.mState == SessionState_Locked);
13362 mData->mSession.mState = SessionState_Unlocking;
13363
13364 /* set direct control to NULL to release the remote instance */
13365 mData->mSession.mDirectControl.setNull();
13366 LogFlowThisFunc(("Direct control is set to NULL\n"));
13367
13368 if (mData->mSession.mProgress)
13369 {
13370 /* finalize the progress, someone might wait if a frontend
13371 * closes the session before powering on the VM. */
13372 mData->mSession.mProgress->notifyComplete(E_FAIL,
13373 COM_IIDOF(ISession),
13374 getComponentName(),
13375 tr("The VM session was closed before any attempt to power it on"));
13376 mData->mSession.mProgress.setNull();
13377 }
13378
13379 /* Create the progress object the client will use to wait until
13380 * #i_checkForDeath() is called to uninitialize this session object after
13381 * it releases the IPC semaphore.
13382 * Note! Because we're "reusing" mProgress here, this must be a proxy
13383 * object just like for LaunchVMProcess. */
13384 Assert(mData->mSession.mProgress.isNull());
13385 ComObjPtr<ProgressProxy> progress;
13386 progress.createObject();
13387 ComPtr<IUnknown> pPeer(mPeer);
13388 progress->init(mParent, pPeer,
13389 Bstr(tr("Closing session")).raw(),
13390 FALSE /* aCancelable */);
13391 progress.queryInterfaceTo(aProgress.asOutParam());
13392 mData->mSession.mProgress = progress;
13393 }
13394 else
13395 {
13396 /* the remote session is being normally closed */
13397 Data::Session::RemoteControlList::iterator it =
13398 mData->mSession.mRemoteControls.begin();
13399 while (it != mData->mSession.mRemoteControls.end())
13400 {
13401 if (control == *it)
13402 break;
13403 ++it;
13404 }
13405 BOOL found = it != mData->mSession.mRemoteControls.end();
13406 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13407 E_INVALIDARG);
13408 // This MUST be erase(it), not remove(*it) as the latter triggers a
13409 // very nasty use after free due to the place where the value "lives".
13410 mData->mSession.mRemoteControls.erase(it);
13411 }
13412
13413 /* signal the client watcher thread, because the client is going away */
13414 mParent->i_updateClientWatcher();
13415
13416 LogFlowThisFuncLeave();
13417 return S_OK;
13418}
13419
13420HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13421 std::vector<com::Utf8Str> &aValues,
13422 std::vector<LONG64> &aTimestamps,
13423 std::vector<com::Utf8Str> &aFlags)
13424{
13425 LogFlowThisFunc(("\n"));
13426
13427#ifdef VBOX_WITH_GUEST_PROPS
13428 using namespace guestProp;
13429
13430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13431
13432 size_t cEntries = mHWData->mGuestProperties.size();
13433 aNames.resize(cEntries);
13434 aValues.resize(cEntries);
13435 aTimestamps.resize(cEntries);
13436 aFlags.resize(cEntries);
13437
13438 size_t i = 0;
13439 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13440 it != mHWData->mGuestProperties.end();
13441 ++it, ++i)
13442 {
13443 char szFlags[MAX_FLAGS_LEN + 1];
13444 aNames[i] = it->first;
13445 aValues[i] = it->second.strValue;
13446 aTimestamps[i] = it->second.mTimestamp;
13447
13448 /* If it is NULL, keep it NULL. */
13449 if (it->second.mFlags)
13450 {
13451 writeFlags(it->second.mFlags, szFlags);
13452 aFlags[i] = szFlags;
13453 }
13454 else
13455 aFlags[i] = "";
13456 }
13457 return S_OK;
13458#else
13459 ReturnComNotImplemented();
13460#endif
13461}
13462
13463HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13464 const com::Utf8Str &aValue,
13465 LONG64 aTimestamp,
13466 const com::Utf8Str &aFlags)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470#ifdef VBOX_WITH_GUEST_PROPS
13471 using namespace guestProp;
13472
13473 try
13474 {
13475 /*
13476 * Convert input up front.
13477 */
13478 uint32_t fFlags = NILFLAG;
13479 if (aFlags.length())
13480 {
13481 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13482 AssertRCReturn(vrc, E_INVALIDARG);
13483 }
13484
13485 /*
13486 * Now grab the object lock, validate the state and do the update.
13487 */
13488
13489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13490
13491 if (!Global::IsOnline(mData->mMachineState))
13492 {
13493 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13494 VBOX_E_INVALID_VM_STATE);
13495 }
13496
13497 i_setModified(IsModified_MachineData);
13498 mHWData.backup();
13499
13500 bool fDelete = !aValue.length();
13501 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13502 if (it != mHWData->mGuestProperties.end())
13503 {
13504 if (!fDelete)
13505 {
13506 it->second.strValue = aValue;
13507 it->second.mTimestamp = aTimestamp;
13508 it->second.mFlags = fFlags;
13509 }
13510 else
13511 mHWData->mGuestProperties.erase(it);
13512
13513 mData->mGuestPropertiesModified = TRUE;
13514 }
13515 else if (!fDelete)
13516 {
13517 HWData::GuestProperty prop;
13518 prop.strValue = aValue;
13519 prop.mTimestamp = aTimestamp;
13520 prop.mFlags = fFlags;
13521
13522 mHWData->mGuestProperties[aName] = prop;
13523 mData->mGuestPropertiesModified = TRUE;
13524 }
13525
13526 alock.release();
13527
13528 mParent->i_onGuestPropertyChange(mData->mUuid,
13529 Bstr(aName).raw(),
13530 Bstr(aValue).raw(),
13531 Bstr(aFlags).raw());
13532 }
13533 catch (...)
13534 {
13535 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13536 }
13537 return S_OK;
13538#else
13539 ReturnComNotImplemented();
13540#endif
13541}
13542
13543
13544HRESULT SessionMachine::lockMedia()
13545{
13546 AutoMultiWriteLock2 alock(this->lockHandle(),
13547 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13548
13549 AssertReturn( mData->mMachineState == MachineState_Starting
13550 || mData->mMachineState == MachineState_Restoring
13551 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13552
13553 clearError();
13554 alock.release();
13555 return i_lockMedia();
13556}
13557
13558HRESULT SessionMachine::unlockMedia()
13559{
13560 HRESULT hrc = i_unlockMedia();
13561 return hrc;
13562}
13563
13564HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13565 ComPtr<IMediumAttachment> &aNewAttachment)
13566{
13567 // request the host lock first, since might be calling Host methods for getting host drives;
13568 // next, protect the media tree all the while we're in here, as well as our member variables
13569 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13570 this->lockHandle(),
13571 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13572
13573 IMediumAttachment *iAttach = aAttachment;
13574 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13575
13576 Bstr ctrlName;
13577 LONG lPort;
13578 LONG lDevice;
13579 bool fTempEject;
13580 {
13581 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13582
13583 /* Need to query the details first, as the IMediumAttachment reference
13584 * might be to the original settings, which we are going to change. */
13585 ctrlName = pAttach->i_getControllerName();
13586 lPort = pAttach->i_getPort();
13587 lDevice = pAttach->i_getDevice();
13588 fTempEject = pAttach->i_getTempEject();
13589 }
13590
13591 if (!fTempEject)
13592 {
13593 /* Remember previously mounted medium. The medium before taking the
13594 * backup is not necessarily the same thing. */
13595 ComObjPtr<Medium> oldmedium;
13596 oldmedium = pAttach->i_getMedium();
13597
13598 i_setModified(IsModified_Storage);
13599 mMediaData.backup();
13600
13601 // The backup operation makes the pAttach reference point to the
13602 // old settings. Re-get the correct reference.
13603 pAttach = i_findAttachment(mMediaData->mAttachments,
13604 ctrlName.raw(),
13605 lPort,
13606 lDevice);
13607
13608 {
13609 AutoCaller autoAttachCaller(this);
13610 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13611
13612 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13613 if (!oldmedium.isNull())
13614 oldmedium->i_removeBackReference(mData->mUuid);
13615
13616 pAttach->i_updateMedium(NULL);
13617 pAttach->i_updateEjected();
13618 }
13619
13620 i_setModified(IsModified_Storage);
13621 }
13622 else
13623 {
13624 {
13625 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13626 pAttach->i_updateEjected();
13627 }
13628 }
13629
13630 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13631
13632 return S_OK;
13633}
13634
13635HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13636 com::Utf8Str &aResult)
13637{
13638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13639
13640 HRESULT hr = S_OK;
13641
13642 if (!mAuthLibCtx.hAuthLibrary)
13643 {
13644 /* Load the external authentication library. */
13645 Bstr authLibrary;
13646 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13647
13648 Utf8Str filename = authLibrary;
13649
13650 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13651 if (RT_FAILURE(rc))
13652 {
13653 hr = setError(E_FAIL,
13654 tr("Could not load the external authentication library '%s' (%Rrc)"),
13655 filename.c_str(), rc);
13656 }
13657 }
13658
13659 /* The auth library might need the machine lock. */
13660 alock.release();
13661
13662 if (FAILED(hr))
13663 return hr;
13664
13665 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13666 {
13667 enum VRDEAuthParams
13668 {
13669 parmUuid = 1,
13670 parmGuestJudgement,
13671 parmUser,
13672 parmPassword,
13673 parmDomain,
13674 parmClientId
13675 };
13676
13677 AuthResult result = AuthResultAccessDenied;
13678
13679 Guid uuid(aAuthParams[parmUuid]);
13680 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13681 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13682
13683 result = AuthLibAuthenticate(&mAuthLibCtx,
13684 uuid.raw(), guestJudgement,
13685 aAuthParams[parmUser].c_str(),
13686 aAuthParams[parmPassword].c_str(),
13687 aAuthParams[parmDomain].c_str(),
13688 u32ClientId);
13689
13690 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13691 size_t cbPassword = aAuthParams[parmPassword].length();
13692 if (cbPassword)
13693 {
13694 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13695 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13696 }
13697
13698 if (result == AuthResultAccessGranted)
13699 aResult = "granted";
13700 else
13701 aResult = "denied";
13702
13703 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13704 aAuthParams[parmUser].c_str(), aResult.c_str()));
13705 }
13706 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13707 {
13708 enum VRDEAuthDisconnectParams
13709 {
13710 parmUuid = 1,
13711 parmClientId
13712 };
13713
13714 Guid uuid(aAuthParams[parmUuid]);
13715 uint32_t u32ClientId = 0;
13716 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13717 }
13718 else
13719 {
13720 hr = E_INVALIDARG;
13721 }
13722
13723 return hr;
13724}
13725
13726// public methods only for internal purposes
13727/////////////////////////////////////////////////////////////////////////////
13728
13729#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13730/**
13731 * Called from the client watcher thread to check for expected or unexpected
13732 * death of the client process that has a direct session to this machine.
13733 *
13734 * On Win32 and on OS/2, this method is called only when we've got the
13735 * mutex (i.e. the client has either died or terminated normally) so it always
13736 * returns @c true (the client is terminated, the session machine is
13737 * uninitialized).
13738 *
13739 * On other platforms, the method returns @c true if the client process has
13740 * terminated normally or abnormally and the session machine was uninitialized,
13741 * and @c false if the client process is still alive.
13742 *
13743 * @note Locks this object for writing.
13744 */
13745bool SessionMachine::i_checkForDeath()
13746{
13747 Uninit::Reason reason;
13748 bool terminated = false;
13749
13750 /* Enclose autoCaller with a block because calling uninit() from under it
13751 * will deadlock. */
13752 {
13753 AutoCaller autoCaller(this);
13754 if (!autoCaller.isOk())
13755 {
13756 /* return true if not ready, to cause the client watcher to exclude
13757 * the corresponding session from watching */
13758 LogFlowThisFunc(("Already uninitialized!\n"));
13759 return true;
13760 }
13761
13762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13763
13764 /* Determine the reason of death: if the session state is Closing here,
13765 * everything is fine. Otherwise it means that the client did not call
13766 * OnSessionEnd() before it released the IPC semaphore. This may happen
13767 * either because the client process has abnormally terminated, or
13768 * because it simply forgot to call ISession::Close() before exiting. We
13769 * threat the latter also as an abnormal termination (see
13770 * Session::uninit() for details). */
13771 reason = mData->mSession.mState == SessionState_Unlocking ?
13772 Uninit::Normal :
13773 Uninit::Abnormal;
13774
13775 if (mClientToken)
13776 terminated = mClientToken->release();
13777 } /* AutoCaller block */
13778
13779 if (terminated)
13780 uninit(reason);
13781
13782 return terminated;
13783}
13784
13785void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13786{
13787 LogFlowThisFunc(("\n"));
13788
13789 strTokenId.setNull();
13790
13791 AutoCaller autoCaller(this);
13792 AssertComRCReturnVoid(autoCaller.rc());
13793
13794 Assert(mClientToken);
13795 if (mClientToken)
13796 mClientToken->getId(strTokenId);
13797}
13798#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13799IToken *SessionMachine::i_getToken()
13800{
13801 LogFlowThisFunc(("\n"));
13802
13803 AutoCaller autoCaller(this);
13804 AssertComRCReturn(autoCaller.rc(), NULL);
13805
13806 Assert(mClientToken);
13807 if (mClientToken)
13808 return mClientToken->getToken();
13809 else
13810 return NULL;
13811}
13812#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13813
13814Machine::ClientToken *SessionMachine::i_getClientToken()
13815{
13816 LogFlowThisFunc(("\n"));
13817
13818 AutoCaller autoCaller(this);
13819 AssertComRCReturn(autoCaller.rc(), NULL);
13820
13821 return mClientToken;
13822}
13823
13824
13825/**
13826 * @note Locks this object for reading.
13827 */
13828HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13829{
13830 LogFlowThisFunc(("\n"));
13831
13832 AutoCaller autoCaller(this);
13833 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13834
13835 ComPtr<IInternalSessionControl> directControl;
13836 {
13837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13838 if (mData->mSession.mLockType == LockType_VM)
13839 directControl = mData->mSession.mDirectControl;
13840 }
13841
13842 /* ignore notifications sent after #OnSessionEnd() is called */
13843 if (!directControl)
13844 return S_OK;
13845
13846 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13847}
13848
13849/**
13850 * @note Locks this object for reading.
13851 */
13852HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13853 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13854 IN_BSTR aGuestIp, LONG aGuestPort)
13855{
13856 LogFlowThisFunc(("\n"));
13857
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13860
13861 ComPtr<IInternalSessionControl> directControl;
13862 {
13863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13864 if (mData->mSession.mLockType == LockType_VM)
13865 directControl = mData->mSession.mDirectControl;
13866 }
13867
13868 /* ignore notifications sent after #OnSessionEnd() is called */
13869 if (!directControl)
13870 return S_OK;
13871 /*
13872 * instead acting like callback we ask IVirtualBox deliver corresponding event
13873 */
13874
13875 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13876 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13877 return S_OK;
13878}
13879
13880/**
13881 * @note Locks this object for reading.
13882 */
13883HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13884{
13885 LogFlowThisFunc(("\n"));
13886
13887 AutoCaller autoCaller(this);
13888 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13889
13890 ComPtr<IInternalSessionControl> directControl;
13891 {
13892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13893 if (mData->mSession.mLockType == LockType_VM)
13894 directControl = mData->mSession.mDirectControl;
13895 }
13896
13897 /* ignore notifications sent after #OnSessionEnd() is called */
13898 if (!directControl)
13899 return S_OK;
13900
13901 return directControl->OnSerialPortChange(serialPort);
13902}
13903
13904/**
13905 * @note Locks this object for reading.
13906 */
13907HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13908{
13909 LogFlowThisFunc(("\n"));
13910
13911 AutoCaller autoCaller(this);
13912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13913
13914 ComPtr<IInternalSessionControl> directControl;
13915 {
13916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13917 if (mData->mSession.mLockType == LockType_VM)
13918 directControl = mData->mSession.mDirectControl;
13919 }
13920
13921 /* ignore notifications sent after #OnSessionEnd() is called */
13922 if (!directControl)
13923 return S_OK;
13924
13925 return directControl->OnParallelPortChange(parallelPort);
13926}
13927
13928/**
13929 * @note Locks this object for reading.
13930 */
13931HRESULT SessionMachine::i_onStorageControllerChange()
13932{
13933 LogFlowThisFunc(("\n"));
13934
13935 AutoCaller autoCaller(this);
13936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13937
13938 ComPtr<IInternalSessionControl> directControl;
13939 {
13940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13941 if (mData->mSession.mLockType == LockType_VM)
13942 directControl = mData->mSession.mDirectControl;
13943 }
13944
13945 /* ignore notifications sent after #OnSessionEnd() is called */
13946 if (!directControl)
13947 return S_OK;
13948
13949 return directControl->OnStorageControllerChange();
13950}
13951
13952/**
13953 * @note Locks this object for reading.
13954 */
13955HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13956{
13957 LogFlowThisFunc(("\n"));
13958
13959 AutoCaller autoCaller(this);
13960 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13961
13962 ComPtr<IInternalSessionControl> directControl;
13963 {
13964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13965 if (mData->mSession.mLockType == LockType_VM)
13966 directControl = mData->mSession.mDirectControl;
13967 }
13968
13969 /* ignore notifications sent after #OnSessionEnd() is called */
13970 if (!directControl)
13971 return S_OK;
13972
13973 return directControl->OnMediumChange(aAttachment, aForce);
13974}
13975
13976/**
13977 * @note Locks this object for reading.
13978 */
13979HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13980{
13981 LogFlowThisFunc(("\n"));
13982
13983 AutoCaller autoCaller(this);
13984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13985
13986 ComPtr<IInternalSessionControl> directControl;
13987 {
13988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13989 if (mData->mSession.mLockType == LockType_VM)
13990 directControl = mData->mSession.mDirectControl;
13991 }
13992
13993 /* ignore notifications sent after #OnSessionEnd() is called */
13994 if (!directControl)
13995 return S_OK;
13996
13997 return directControl->OnCPUChange(aCPU, aRemove);
13998}
13999
14000HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14001{
14002 LogFlowThisFunc(("\n"));
14003
14004 AutoCaller autoCaller(this);
14005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14006
14007 ComPtr<IInternalSessionControl> directControl;
14008 {
14009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14010 if (mData->mSession.mLockType == LockType_VM)
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14019}
14020
14021/**
14022 * @note Locks this object for reading.
14023 */
14024HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 ComPtr<IInternalSessionControl> directControl;
14032 {
14033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14034 if (mData->mSession.mLockType == LockType_VM)
14035 directControl = mData->mSession.mDirectControl;
14036 }
14037
14038 /* ignore notifications sent after #OnSessionEnd() is called */
14039 if (!directControl)
14040 return S_OK;
14041
14042 return directControl->OnVRDEServerChange(aRestart);
14043}
14044
14045/**
14046 * @note Locks this object for reading.
14047 */
14048HRESULT SessionMachine::i_onVideoCaptureChange()
14049{
14050 LogFlowThisFunc(("\n"));
14051
14052 AutoCaller autoCaller(this);
14053 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14054
14055 ComPtr<IInternalSessionControl> directControl;
14056 {
14057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14058 if (mData->mSession.mLockType == LockType_VM)
14059 directControl = mData->mSession.mDirectControl;
14060 }
14061
14062 /* ignore notifications sent after #OnSessionEnd() is called */
14063 if (!directControl)
14064 return S_OK;
14065
14066 return directControl->OnVideoCaptureChange();
14067}
14068
14069/**
14070 * @note Locks this object for reading.
14071 */
14072HRESULT SessionMachine::i_onUSBControllerChange()
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14078
14079 ComPtr<IInternalSessionControl> directControl;
14080 {
14081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14082 if (mData->mSession.mLockType == LockType_VM)
14083 directControl = mData->mSession.mDirectControl;
14084 }
14085
14086 /* ignore notifications sent after #OnSessionEnd() is called */
14087 if (!directControl)
14088 return S_OK;
14089
14090 return directControl->OnUSBControllerChange();
14091}
14092
14093/**
14094 * @note Locks this object for reading.
14095 */
14096HRESULT SessionMachine::i_onSharedFolderChange()
14097{
14098 LogFlowThisFunc(("\n"));
14099
14100 AutoCaller autoCaller(this);
14101 AssertComRCReturnRC(autoCaller.rc());
14102
14103 ComPtr<IInternalSessionControl> directControl;
14104 {
14105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14106 if (mData->mSession.mLockType == LockType_VM)
14107 directControl = mData->mSession.mDirectControl;
14108 }
14109
14110 /* ignore notifications sent after #OnSessionEnd() is called */
14111 if (!directControl)
14112 return S_OK;
14113
14114 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14115}
14116
14117/**
14118 * @note Locks this object for reading.
14119 */
14120HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14121{
14122 LogFlowThisFunc(("\n"));
14123
14124 AutoCaller autoCaller(this);
14125 AssertComRCReturnRC(autoCaller.rc());
14126
14127 ComPtr<IInternalSessionControl> directControl;
14128 {
14129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14130 if (mData->mSession.mLockType == LockType_VM)
14131 directControl = mData->mSession.mDirectControl;
14132 }
14133
14134 /* ignore notifications sent after #OnSessionEnd() is called */
14135 if (!directControl)
14136 return S_OK;
14137
14138 return directControl->OnClipboardModeChange(aClipboardMode);
14139}
14140
14141/**
14142 * @note Locks this object for reading.
14143 */
14144HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14145{
14146 LogFlowThisFunc(("\n"));
14147
14148 AutoCaller autoCaller(this);
14149 AssertComRCReturnRC(autoCaller.rc());
14150
14151 ComPtr<IInternalSessionControl> directControl;
14152 {
14153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14154 if (mData->mSession.mLockType == LockType_VM)
14155 directControl = mData->mSession.mDirectControl;
14156 }
14157
14158 /* ignore notifications sent after #OnSessionEnd() is called */
14159 if (!directControl)
14160 return S_OK;
14161
14162 return directControl->OnDnDModeChange(aDnDMode);
14163}
14164
14165/**
14166 * @note Locks this object for reading.
14167 */
14168HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14169{
14170 LogFlowThisFunc(("\n"));
14171
14172 AutoCaller autoCaller(this);
14173 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14174
14175 ComPtr<IInternalSessionControl> directControl;
14176 {
14177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14178 if (mData->mSession.mLockType == LockType_VM)
14179 directControl = mData->mSession.mDirectControl;
14180 }
14181
14182 /* ignore notifications sent after #OnSessionEnd() is called */
14183 if (!directControl)
14184 return S_OK;
14185
14186 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14187}
14188
14189/**
14190 * @note Locks this object for reading.
14191 */
14192HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14193{
14194 LogFlowThisFunc(("\n"));
14195
14196 AutoCaller autoCaller(this);
14197 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14198
14199 ComPtr<IInternalSessionControl> directControl;
14200 {
14201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14202 if (mData->mSession.mLockType == LockType_VM)
14203 directControl = mData->mSession.mDirectControl;
14204 }
14205
14206 /* ignore notifications sent after #OnSessionEnd() is called */
14207 if (!directControl)
14208 return S_OK;
14209
14210 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14211}
14212
14213/**
14214 * Returns @c true if this machine's USB controller reports it has a matching
14215 * filter for the given USB device and @c false otherwise.
14216 *
14217 * @note locks this object for reading.
14218 */
14219bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14220{
14221 AutoCaller autoCaller(this);
14222 /* silently return if not ready -- this method may be called after the
14223 * direct machine session has been called */
14224 if (!autoCaller.isOk())
14225 return false;
14226
14227#ifdef VBOX_WITH_USB
14228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14229
14230 switch (mData->mMachineState)
14231 {
14232 case MachineState_Starting:
14233 case MachineState_Restoring:
14234 case MachineState_TeleportingIn:
14235 case MachineState_Paused:
14236 case MachineState_Running:
14237 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14238 * elsewhere... */
14239 alock.release();
14240 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14241 default: break;
14242 }
14243#else
14244 NOREF(aDevice);
14245 NOREF(aMaskedIfs);
14246#endif
14247 return false;
14248}
14249
14250/**
14251 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14252 */
14253HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14254 IVirtualBoxErrorInfo *aError,
14255 ULONG aMaskedIfs,
14256 const com::Utf8Str &aCaptureFilename)
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261
14262 /* This notification may happen after the machine object has been
14263 * uninitialized (the session was closed), so don't assert. */
14264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14265
14266 ComPtr<IInternalSessionControl> directControl;
14267 {
14268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14269 if (mData->mSession.mLockType == LockType_VM)
14270 directControl = mData->mSession.mDirectControl;
14271 }
14272
14273 /* fail on notifications sent after #OnSessionEnd() is called, it is
14274 * expected by the caller */
14275 if (!directControl)
14276 return E_FAIL;
14277
14278 /* No locks should be held at this point. */
14279 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14280 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14281
14282 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14283}
14284
14285/**
14286 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14287 */
14288HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14289 IVirtualBoxErrorInfo *aError)
14290{
14291 LogFlowThisFunc(("\n"));
14292
14293 AutoCaller autoCaller(this);
14294
14295 /* This notification may happen after the machine object has been
14296 * uninitialized (the session was closed), so don't assert. */
14297 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14298
14299 ComPtr<IInternalSessionControl> directControl;
14300 {
14301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14302 if (mData->mSession.mLockType == LockType_VM)
14303 directControl = mData->mSession.mDirectControl;
14304 }
14305
14306 /* fail on notifications sent after #OnSessionEnd() is called, it is
14307 * expected by the caller */
14308 if (!directControl)
14309 return E_FAIL;
14310
14311 /* No locks should be held at this point. */
14312 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14313 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14314
14315 return directControl->OnUSBDeviceDetach(aId, aError);
14316}
14317
14318// protected methods
14319/////////////////////////////////////////////////////////////////////////////
14320
14321/**
14322 * Deletes the given file if it is no longer in use by either the current machine state
14323 * (if the machine is "saved") or any of the machine's snapshots.
14324 *
14325 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14326 * but is different for each SnapshotMachine. When calling this, the order of calling this
14327 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14328 * is therefore critical. I know, it's all rather messy.
14329 *
14330 * @param strStateFile
14331 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14332 * the test for whether the saved state file is in use.
14333 */
14334void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14335 Snapshot *pSnapshotToIgnore)
14336{
14337 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14338 if ( (strStateFile.isNotEmpty())
14339 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14340 )
14341 // ... and it must also not be shared with other snapshots
14342 if ( !mData->mFirstSnapshot
14343 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14344 // this checks the SnapshotMachine's state file paths
14345 )
14346 RTFileDelete(strStateFile.c_str());
14347}
14348
14349/**
14350 * Locks the attached media.
14351 *
14352 * All attached hard disks are locked for writing and DVD/floppy are locked for
14353 * reading. Parents of attached hard disks (if any) are locked for reading.
14354 *
14355 * This method also performs accessibility check of all media it locks: if some
14356 * media is inaccessible, the method will return a failure and a bunch of
14357 * extended error info objects per each inaccessible medium.
14358 *
14359 * Note that this method is atomic: if it returns a success, all media are
14360 * locked as described above; on failure no media is locked at all (all
14361 * succeeded individual locks will be undone).
14362 *
14363 * The caller is responsible for doing the necessary state sanity checks.
14364 *
14365 * The locks made by this method must be undone by calling #unlockMedia() when
14366 * no more needed.
14367 */
14368HRESULT SessionMachine::i_lockMedia()
14369{
14370 AutoCaller autoCaller(this);
14371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14372
14373 AutoMultiWriteLock2 alock(this->lockHandle(),
14374 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14375
14376 /* bail out if trying to lock things with already set up locking */
14377 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14378
14379 MultiResult mrc(S_OK);
14380
14381 /* Collect locking information for all medium objects attached to the VM. */
14382 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14383 it != mMediaData->mAttachments.end();
14384 ++it)
14385 {
14386 MediumAttachment* pAtt = *it;
14387 DeviceType_T devType = pAtt->i_getType();
14388 Medium *pMedium = pAtt->i_getMedium();
14389
14390 MediumLockList *pMediumLockList(new MediumLockList());
14391 // There can be attachments without a medium (floppy/dvd), and thus
14392 // it's impossible to create a medium lock list. It still makes sense
14393 // to have the empty medium lock list in the map in case a medium is
14394 // attached later.
14395 if (pMedium != NULL)
14396 {
14397 MediumType_T mediumType = pMedium->i_getType();
14398 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14399 || mediumType == MediumType_Shareable;
14400 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14401
14402 alock.release();
14403 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14404 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14405 false /* fMediumLockWriteAll */,
14406 NULL,
14407 *pMediumLockList);
14408 alock.acquire();
14409 if (FAILED(mrc))
14410 {
14411 delete pMediumLockList;
14412 mData->mSession.mLockedMedia.Clear();
14413 break;
14414 }
14415 }
14416
14417 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14418 if (FAILED(rc))
14419 {
14420 mData->mSession.mLockedMedia.Clear();
14421 mrc = setError(rc,
14422 tr("Collecting locking information for all attached media failed"));
14423 break;
14424 }
14425 }
14426
14427 if (SUCCEEDED(mrc))
14428 {
14429 /* Now lock all media. If this fails, nothing is locked. */
14430 alock.release();
14431 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14432 alock.acquire();
14433 if (FAILED(rc))
14434 {
14435 mrc = setError(rc,
14436 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14437 }
14438 }
14439
14440 return mrc;
14441}
14442
14443/**
14444 * Undoes the locks made by by #lockMedia().
14445 */
14446HRESULT SessionMachine::i_unlockMedia()
14447{
14448 AutoCaller autoCaller(this);
14449 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14450
14451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14452
14453 /* we may be holding important error info on the current thread;
14454 * preserve it */
14455 ErrorInfoKeeper eik;
14456
14457 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14458 AssertComRC(rc);
14459 return rc;
14460}
14461
14462/**
14463 * Helper to change the machine state (reimplementation).
14464 *
14465 * @note Locks this object for writing.
14466 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14467 * it can cause crashes in random places due to unexpectedly committing
14468 * the current settings. The caller is responsible for that. The call
14469 * to saveStateSettings is fine, because this method does not commit.
14470 */
14471HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14472{
14473 LogFlowThisFuncEnter();
14474 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14475
14476 AutoCaller autoCaller(this);
14477 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14478
14479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14480
14481 MachineState_T oldMachineState = mData->mMachineState;
14482
14483 AssertMsgReturn(oldMachineState != aMachineState,
14484 ("oldMachineState=%s, aMachineState=%s\n",
14485 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14486 E_FAIL);
14487
14488 HRESULT rc = S_OK;
14489
14490 int stsFlags = 0;
14491 bool deleteSavedState = false;
14492
14493 /* detect some state transitions */
14494
14495 if ( ( oldMachineState == MachineState_Saved
14496 && aMachineState == MachineState_Restoring)
14497 || ( ( oldMachineState == MachineState_PoweredOff
14498 || oldMachineState == MachineState_Teleported
14499 || oldMachineState == MachineState_Aborted
14500 )
14501 && ( aMachineState == MachineState_TeleportingIn
14502 || aMachineState == MachineState_Starting
14503 )
14504 )
14505 )
14506 {
14507 /* The EMT thread is about to start */
14508
14509 /* Nothing to do here for now... */
14510
14511 /// @todo NEWMEDIA don't let mDVDDrive and other children
14512 /// change anything when in the Starting/Restoring state
14513 }
14514 else if ( ( oldMachineState == MachineState_Running
14515 || oldMachineState == MachineState_Paused
14516 || oldMachineState == MachineState_Teleporting
14517 || oldMachineState == MachineState_OnlineSnapshotting
14518 || oldMachineState == MachineState_LiveSnapshotting
14519 || oldMachineState == MachineState_Stuck
14520 || oldMachineState == MachineState_Starting
14521 || oldMachineState == MachineState_Stopping
14522 || oldMachineState == MachineState_Saving
14523 || oldMachineState == MachineState_Restoring
14524 || oldMachineState == MachineState_TeleportingPausedVM
14525 || oldMachineState == MachineState_TeleportingIn
14526 )
14527 && ( aMachineState == MachineState_PoweredOff
14528 || aMachineState == MachineState_Saved
14529 || aMachineState == MachineState_Teleported
14530 || aMachineState == MachineState_Aborted
14531 )
14532 )
14533 {
14534 /* The EMT thread has just stopped, unlock attached media. Note that as
14535 * opposed to locking that is done from Console, we do unlocking here
14536 * because the VM process may have aborted before having a chance to
14537 * properly unlock all media it locked. */
14538
14539 unlockMedia();
14540 }
14541
14542 if (oldMachineState == MachineState_Restoring)
14543 {
14544 if (aMachineState != MachineState_Saved)
14545 {
14546 /*
14547 * delete the saved state file once the machine has finished
14548 * restoring from it (note that Console sets the state from
14549 * Restoring to Saved if the VM couldn't restore successfully,
14550 * to give the user an ability to fix an error and retry --
14551 * we keep the saved state file in this case)
14552 */
14553 deleteSavedState = true;
14554 }
14555 }
14556 else if ( oldMachineState == MachineState_Saved
14557 && ( aMachineState == MachineState_PoweredOff
14558 || aMachineState == MachineState_Aborted
14559 || aMachineState == MachineState_Teleported
14560 )
14561 )
14562 {
14563 /*
14564 * delete the saved state after SessionMachine::ForgetSavedState() is called
14565 * or if the VM process (owning a direct VM session) crashed while the
14566 * VM was Saved
14567 */
14568
14569 /// @todo (dmik)
14570 // Not sure that deleting the saved state file just because of the
14571 // client death before it attempted to restore the VM is a good
14572 // thing. But when it crashes we need to go to the Aborted state
14573 // which cannot have the saved state file associated... The only
14574 // way to fix this is to make the Aborted condition not a VM state
14575 // but a bool flag: i.e., when a crash occurs, set it to true and
14576 // change the state to PoweredOff or Saved depending on the
14577 // saved state presence.
14578
14579 deleteSavedState = true;
14580 mData->mCurrentStateModified = TRUE;
14581 stsFlags |= SaveSTS_CurStateModified;
14582 }
14583
14584 if ( aMachineState == MachineState_Starting
14585 || aMachineState == MachineState_Restoring
14586 || aMachineState == MachineState_TeleportingIn
14587 )
14588 {
14589 /* set the current state modified flag to indicate that the current
14590 * state is no more identical to the state in the
14591 * current snapshot */
14592 if (!mData->mCurrentSnapshot.isNull())
14593 {
14594 mData->mCurrentStateModified = TRUE;
14595 stsFlags |= SaveSTS_CurStateModified;
14596 }
14597 }
14598
14599 if (deleteSavedState)
14600 {
14601 if (mRemoveSavedState)
14602 {
14603 Assert(!mSSData->strStateFilePath.isEmpty());
14604
14605 // it is safe to delete the saved state file if ...
14606 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14607 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14608 // ... none of the snapshots share the saved state file
14609 )
14610 RTFileDelete(mSSData->strStateFilePath.c_str());
14611 }
14612
14613 mSSData->strStateFilePath.setNull();
14614 stsFlags |= SaveSTS_StateFilePath;
14615 }
14616
14617 /* redirect to the underlying peer machine */
14618 mPeer->i_setMachineState(aMachineState);
14619
14620 if ( oldMachineState != MachineState_RestoringSnapshot
14621 && ( aMachineState == MachineState_PoweredOff
14622 || aMachineState == MachineState_Teleported
14623 || aMachineState == MachineState_Aborted
14624 || aMachineState == MachineState_Saved))
14625 {
14626 /* the machine has stopped execution
14627 * (or the saved state file was adopted) */
14628 stsFlags |= SaveSTS_StateTimeStamp;
14629 }
14630
14631 if ( ( oldMachineState == MachineState_PoweredOff
14632 || oldMachineState == MachineState_Aborted
14633 || oldMachineState == MachineState_Teleported
14634 )
14635 && aMachineState == MachineState_Saved)
14636 {
14637 /* the saved state file was adopted */
14638 Assert(!mSSData->strStateFilePath.isEmpty());
14639 stsFlags |= SaveSTS_StateFilePath;
14640 }
14641
14642#ifdef VBOX_WITH_GUEST_PROPS
14643 if ( aMachineState == MachineState_PoweredOff
14644 || aMachineState == MachineState_Aborted
14645 || aMachineState == MachineState_Teleported)
14646 {
14647 /* Make sure any transient guest properties get removed from the
14648 * property store on shutdown. */
14649 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14650
14651 /* remove it from the settings representation */
14652 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14653 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14654 it != llGuestProperties.end();
14655 /*nothing*/)
14656 {
14657 const settings::GuestProperty &prop = *it;
14658 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14659 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14660 {
14661 it = llGuestProperties.erase(it);
14662 fNeedsSaving = true;
14663 }
14664 else
14665 {
14666 ++it;
14667 }
14668 }
14669
14670 /* Additionally remove it from the HWData representation. Required to
14671 * keep everything in sync, as this is what the API keeps using. */
14672 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14673 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14674 it != llHWGuestProperties.end();
14675 /*nothing*/)
14676 {
14677 uint32_t fFlags = it->second.mFlags;
14678 if ( fFlags & guestProp::TRANSIENT
14679 || fFlags & guestProp::TRANSRESET)
14680 {
14681 /* iterator where we need to continue after the erase call
14682 * (C++03 is a fact still, and it doesn't return the iterator
14683 * which would allow continuing) */
14684 HWData::GuestPropertyMap::iterator it2 = it;
14685 ++it2;
14686 llHWGuestProperties.erase(it);
14687 it = it2;
14688 fNeedsSaving = true;
14689 }
14690 else
14691 {
14692 ++it;
14693 }
14694 }
14695
14696 if (fNeedsSaving)
14697 {
14698 mData->mCurrentStateModified = TRUE;
14699 stsFlags |= SaveSTS_CurStateModified;
14700 }
14701 }
14702#endif /* VBOX_WITH_GUEST_PROPS */
14703
14704 rc = i_saveStateSettings(stsFlags);
14705
14706 if ( ( oldMachineState != MachineState_PoweredOff
14707 && oldMachineState != MachineState_Aborted
14708 && oldMachineState != MachineState_Teleported
14709 )
14710 && ( aMachineState == MachineState_PoweredOff
14711 || aMachineState == MachineState_Aborted
14712 || aMachineState == MachineState_Teleported
14713 )
14714 )
14715 {
14716 /* we've been shut down for any reason */
14717 /* no special action so far */
14718 }
14719
14720 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14721 LogFlowThisFuncLeave();
14722 return rc;
14723}
14724
14725/**
14726 * Sends the current machine state value to the VM process.
14727 *
14728 * @note Locks this object for reading, then calls a client process.
14729 */
14730HRESULT SessionMachine::i_updateMachineStateOnClient()
14731{
14732 AutoCaller autoCaller(this);
14733 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14734
14735 ComPtr<IInternalSessionControl> directControl;
14736 {
14737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14738 AssertReturn(!!mData, E_FAIL);
14739 if (mData->mSession.mLockType == LockType_VM)
14740 directControl = mData->mSession.mDirectControl;
14741
14742 /* directControl may be already set to NULL here in #OnSessionEnd()
14743 * called too early by the direct session process while there is still
14744 * some operation (like deleting the snapshot) in progress. The client
14745 * process in this case is waiting inside Session::close() for the
14746 * "end session" process object to complete, while #uninit() called by
14747 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14748 * operation to complete. For now, we accept this inconsistent behavior
14749 * and simply do nothing here. */
14750
14751 if (mData->mSession.mState == SessionState_Unlocking)
14752 return S_OK;
14753 }
14754
14755 /* ignore notifications sent after #OnSessionEnd() is called */
14756 if (!directControl)
14757 return S_OK;
14758
14759 return directControl->UpdateMachineState(mData->mMachineState);
14760}
14761
14762
14763/**
14764 * Static Machine method that can get passed to RTThreadCreate to
14765 * have a thread started for a Task. See Machine::Task.
14766 */
14767/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14768{
14769 AssertReturn(pvUser, VERR_INVALID_POINTER);
14770
14771 Task *pTask = static_cast<Task *>(pvUser);
14772 pTask->handler();
14773 /** @todo r=klaus it would be safer to update the progress object here,
14774 * as it avoids possible races due to scoping issues/tricks in the handler */
14775 // it's our responsibility to delete the task
14776 delete pTask;
14777
14778 return 0;
14779}
14780
14781/*static*/
14782HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14783{
14784 va_list args;
14785 va_start(args, pcszMsg);
14786 HRESULT rc = setErrorInternal(aResultCode,
14787 getStaticClassIID(),
14788 getStaticComponentName(),
14789 Utf8Str(pcszMsg, args),
14790 false /* aWarning */,
14791 true /* aLogIt */);
14792 va_end(args);
14793 return rc;
14794}
14795
14796
14797HRESULT Machine::updateState(MachineState_T aState)
14798{
14799 NOREF(aState);
14800 ReturnComNotImplemented();
14801}
14802
14803HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14804{
14805 NOREF(aProgress);
14806 ReturnComNotImplemented();
14807}
14808
14809HRESULT Machine::endPowerUp(LONG aResult)
14810{
14811 NOREF(aResult);
14812 ReturnComNotImplemented();
14813}
14814
14815HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14816{
14817 NOREF(aProgress);
14818 ReturnComNotImplemented();
14819}
14820
14821HRESULT Machine::endPoweringDown(LONG aResult,
14822 const com::Utf8Str &aErrMsg)
14823{
14824 NOREF(aResult);
14825 NOREF(aErrMsg);
14826 ReturnComNotImplemented();
14827}
14828
14829HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14830 BOOL *aMatched,
14831 ULONG *aMaskedInterfaces)
14832{
14833 NOREF(aDevice);
14834 NOREF(aMatched);
14835 NOREF(aMaskedInterfaces);
14836 ReturnComNotImplemented();
14837
14838}
14839
14840HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14841{
14842 NOREF(aId); NOREF(aCaptureFilename);
14843 ReturnComNotImplemented();
14844}
14845
14846HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14847 BOOL aDone)
14848{
14849 NOREF(aId);
14850 NOREF(aDone);
14851 ReturnComNotImplemented();
14852}
14853
14854HRESULT Machine::autoCaptureUSBDevices()
14855{
14856 ReturnComNotImplemented();
14857}
14858
14859HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14860{
14861 NOREF(aDone);
14862 ReturnComNotImplemented();
14863}
14864
14865HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14866 ComPtr<IProgress> &aProgress)
14867{
14868 NOREF(aSession);
14869 NOREF(aProgress);
14870 ReturnComNotImplemented();
14871}
14872
14873HRESULT Machine::finishOnlineMergeMedium()
14874{
14875 ReturnComNotImplemented();
14876}
14877
14878HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14879 std::vector<com::Utf8Str> &aValues,
14880 std::vector<LONG64> &aTimestamps,
14881 std::vector<com::Utf8Str> &aFlags)
14882{
14883 NOREF(aNames);
14884 NOREF(aValues);
14885 NOREF(aTimestamps);
14886 NOREF(aFlags);
14887 ReturnComNotImplemented();
14888}
14889
14890HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14891 const com::Utf8Str &aValue,
14892 LONG64 aTimestamp,
14893 const com::Utf8Str &aFlags)
14894{
14895 NOREF(aName);
14896 NOREF(aValue);
14897 NOREF(aTimestamp);
14898 NOREF(aFlags);
14899 ReturnComNotImplemented();
14900}
14901
14902HRESULT Machine::lockMedia()
14903{
14904 ReturnComNotImplemented();
14905}
14906
14907HRESULT Machine::unlockMedia()
14908{
14909 ReturnComNotImplemented();
14910}
14911
14912HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14913 ComPtr<IMediumAttachment> &aNewAttachment)
14914{
14915 NOREF(aAttachment);
14916 NOREF(aNewAttachment);
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14921 ULONG aCpuUser,
14922 ULONG aCpuKernel,
14923 ULONG aCpuIdle,
14924 ULONG aMemTotal,
14925 ULONG aMemFree,
14926 ULONG aMemBalloon,
14927 ULONG aMemShared,
14928 ULONG aMemCache,
14929 ULONG aPagedTotal,
14930 ULONG aMemAllocTotal,
14931 ULONG aMemFreeTotal,
14932 ULONG aMemBalloonTotal,
14933 ULONG aMemSharedTotal,
14934 ULONG aVmNetRx,
14935 ULONG aVmNetTx)
14936{
14937 NOREF(aValidStats);
14938 NOREF(aCpuUser);
14939 NOREF(aCpuKernel);
14940 NOREF(aCpuIdle);
14941 NOREF(aMemTotal);
14942 NOREF(aMemFree);
14943 NOREF(aMemBalloon);
14944 NOREF(aMemShared);
14945 NOREF(aMemCache);
14946 NOREF(aPagedTotal);
14947 NOREF(aMemAllocTotal);
14948 NOREF(aMemFreeTotal);
14949 NOREF(aMemBalloonTotal);
14950 NOREF(aMemSharedTotal);
14951 NOREF(aVmNetRx);
14952 NOREF(aVmNetTx);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14957 com::Utf8Str &aResult)
14958{
14959 NOREF(aAuthParams);
14960 NOREF(aResult);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14965{
14966 NOREF(aFlags);
14967 ReturnComNotImplemented();
14968}
14969
14970/* This isn't handled entirely by the wrapper generator yet. */
14971#ifdef VBOX_WITH_XPCOM
14972NS_DECL_CLASSINFO(SessionMachine)
14973NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14974
14975NS_DECL_CLASSINFO(SnapshotMachine)
14976NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14977#endif
Note: See TracBrowser for help on using the repository browser.

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