VirtualBox

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

Last change on this file since 62514 was 62346, checked in by vboxsync, 8 years ago

Main/Machine: add lock release which was lost in the previous changeset

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