VirtualBox

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

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

bugref:8527: some coding style fixes and introduced VBOX_WITH_UNATTENDED

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 516.6 KB
Line 
1/* $Id: MachineImpl.cpp 65049 2017-01-02 09:01:46Z 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.215389.xyz. 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 default: AssertFailedBreak(); /* Shut up MSC. */
1369 }
1370 break;
1371 }
1372 }
1373
1374 Assert( *aParavirtProvider == ParavirtProvider_None
1375 || *aParavirtProvider == ParavirtProvider_Minimal
1376 || *aParavirtProvider == ParavirtProvider_HyperV
1377 || *aParavirtProvider == ParavirtProvider_KVM);
1378 return S_OK;
1379}
1380
1381HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1382{
1383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1384
1385 aHardwareVersion = mHWData->mHWVersion;
1386
1387 return S_OK;
1388}
1389
1390HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1391{
1392 /* check known version */
1393 Utf8Str hwVersion = aHardwareVersion;
1394 if ( hwVersion.compare("1") != 0
1395 && hwVersion.compare("2") != 0)
1396 return setError(E_INVALIDARG,
1397 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1398
1399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 HRESULT rc = i_checkStateDependency(MutableStateDep);
1402 if (FAILED(rc)) return rc;
1403
1404 i_setModified(IsModified_MachineData);
1405 mHWData.backup();
1406 mHWData->mHWVersion = aHardwareVersion;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 if (!mHWData->mHardwareUUID.isZero())
1416 aHardwareUUID = mHWData->mHardwareUUID;
1417 else
1418 aHardwareUUID = mData->mUuid;
1419
1420 return S_OK;
1421}
1422
1423HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1424{
1425 if (!aHardwareUUID.isValid())
1426 return E_INVALIDARG;
1427
1428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 HRESULT rc = i_checkStateDependency(MutableStateDep);
1431 if (FAILED(rc)) return rc;
1432
1433 i_setModified(IsModified_MachineData);
1434 mHWData.backup();
1435 if (aHardwareUUID == mData->mUuid)
1436 mHWData->mHardwareUUID.clear();
1437 else
1438 mHWData->mHardwareUUID = aHardwareUUID;
1439
1440 return S_OK;
1441}
1442
1443HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1444{
1445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1446
1447 *aMemorySize = mHWData->mMemorySize;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::setMemorySize(ULONG aMemorySize)
1453{
1454 /* check RAM limits */
1455 if ( aMemorySize < MM_RAM_MIN_IN_MB
1456 || aMemorySize > MM_RAM_MAX_IN_MB
1457 )
1458 return setError(E_INVALIDARG,
1459 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1460 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1461
1462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1463
1464 HRESULT rc = i_checkStateDependency(MutableStateDep);
1465 if (FAILED(rc)) return rc;
1466
1467 i_setModified(IsModified_MachineData);
1468 mHWData.backup();
1469 mHWData->mMemorySize = aMemorySize;
1470
1471 return S_OK;
1472}
1473
1474HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1475{
1476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1477
1478 *aCPUCount = mHWData->mCPUCount;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::setCPUCount(ULONG aCPUCount)
1484{
1485 /* check CPU limits */
1486 if ( aCPUCount < SchemaDefs::MinCPUCount
1487 || aCPUCount > SchemaDefs::MaxCPUCount
1488 )
1489 return setError(E_INVALIDARG,
1490 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1491 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1492
1493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1494
1495 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1496 if (mHWData->mCPUHotPlugEnabled)
1497 {
1498 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1499 {
1500 if (mHWData->mCPUAttached[idx])
1501 return setError(E_INVALIDARG,
1502 tr("There is still a CPU attached to socket %lu."
1503 "Detach the CPU before removing the socket"),
1504 aCPUCount, idx+1);
1505 }
1506 }
1507
1508 HRESULT rc = i_checkStateDependency(MutableStateDep);
1509 if (FAILED(rc)) return rc;
1510
1511 i_setModified(IsModified_MachineData);
1512 mHWData.backup();
1513 mHWData->mCPUCount = aCPUCount;
1514
1515 return S_OK;
1516}
1517
1518HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1519{
1520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1523
1524 return S_OK;
1525}
1526
1527HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1528{
1529 HRESULT rc = S_OK;
1530
1531 /* check throttle limits */
1532 if ( aCPUExecutionCap < 1
1533 || aCPUExecutionCap > 100
1534 )
1535 return setError(E_INVALIDARG,
1536 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1537 aCPUExecutionCap, 1, 100);
1538
1539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 alock.release();
1542 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1543 alock.acquire();
1544 if (FAILED(rc)) return rc;
1545
1546 i_setModified(IsModified_MachineData);
1547 mHWData.backup();
1548 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1549
1550 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1551 if (Global::IsOnline(mData->mMachineState))
1552 i_saveSettings(NULL);
1553
1554 return S_OK;
1555}
1556
1557HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1558{
1559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1560
1561 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1562
1563 return S_OK;
1564}
1565
1566HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1567{
1568 HRESULT rc = S_OK;
1569
1570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1571
1572 rc = i_checkStateDependency(MutableStateDep);
1573 if (FAILED(rc)) return rc;
1574
1575 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1576 {
1577 if (aCPUHotPlugEnabled)
1578 {
1579 i_setModified(IsModified_MachineData);
1580 mHWData.backup();
1581
1582 /* Add the amount of CPUs currently attached */
1583 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1584 mHWData->mCPUAttached[i] = true;
1585 }
1586 else
1587 {
1588 /*
1589 * We can disable hotplug only if the amount of maximum CPUs is equal
1590 * to the amount of attached CPUs
1591 */
1592 unsigned cCpusAttached = 0;
1593 unsigned iHighestId = 0;
1594
1595 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1596 {
1597 if (mHWData->mCPUAttached[i])
1598 {
1599 cCpusAttached++;
1600 iHighestId = i;
1601 }
1602 }
1603
1604 if ( (cCpusAttached != mHWData->mCPUCount)
1605 || (iHighestId >= mHWData->mCPUCount))
1606 return setError(E_INVALIDARG,
1607 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 }
1612 }
1613
1614 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1615
1616 return rc;
1617}
1618
1619HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1620{
1621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1622
1623 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1624
1625 return S_OK;
1626}
1627
1628HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1629{
1630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1631
1632 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1633 if (SUCCEEDED(hrc))
1634 {
1635 i_setModified(IsModified_MachineData);
1636 mHWData.backup();
1637 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1638 }
1639 return hrc;
1640}
1641
1642HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1643{
1644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1645 aCPUProfile = mHWData->mCpuProfile;
1646 return S_OK;
1647}
1648
1649HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1650{
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1653 if (SUCCEEDED(hrc))
1654 {
1655 i_setModified(IsModified_MachineData);
1656 mHWData.backup();
1657 /* Empty equals 'host'. */
1658 if (aCPUProfile.isNotEmpty())
1659 mHWData->mCpuProfile = aCPUProfile;
1660 else
1661 mHWData->mCpuProfile = "host";
1662 }
1663 return hrc;
1664}
1665
1666HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1667{
1668#ifdef VBOX_WITH_USB_CARDREADER
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1672
1673 return S_OK;
1674#else
1675 NOREF(aEmulatedUSBCardReaderEnabled);
1676 return E_NOTIMPL;
1677#endif
1678}
1679
1680HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1681{
1682#ifdef VBOX_WITH_USB_CARDREADER
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684
1685 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1686 if (FAILED(rc)) return rc;
1687
1688 i_setModified(IsModified_MachineData);
1689 mHWData.backup();
1690 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1691
1692 return S_OK;
1693#else
1694 NOREF(aEmulatedUSBCardReaderEnabled);
1695 return E_NOTIMPL;
1696#endif
1697}
1698
1699HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1700{
1701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 *aHPETEnabled = mHWData->mHPETEnabled;
1704
1705 return S_OK;
1706}
1707
1708HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1709{
1710 HRESULT rc = S_OK;
1711
1712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1713
1714 rc = i_checkStateDependency(MutableStateDep);
1715 if (FAILED(rc)) return rc;
1716
1717 i_setModified(IsModified_MachineData);
1718 mHWData.backup();
1719
1720 mHWData->mHPETEnabled = aHPETEnabled;
1721
1722 return rc;
1723}
1724
1725HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1726{
1727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1730 return S_OK;
1731}
1732
1733HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1734{
1735 HRESULT rc = S_OK;
1736
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 i_setModified(IsModified_MachineData);
1740 mHWData.backup();
1741 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1742
1743 alock.release();
1744 rc = i_onVideoCaptureChange();
1745 alock.acquire();
1746 if (FAILED(rc))
1747 {
1748 /*
1749 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1750 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1751 * determine if it should start or stop capturing. Therefore we need to manually
1752 * undo change.
1753 */
1754 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1755 return rc;
1756 }
1757
1758 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1759 if (Global::IsOnline(mData->mMachineState))
1760 i_saveSettings(NULL);
1761
1762 return rc;
1763}
1764
1765HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1766{
1767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1768 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1769 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1770 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1771 return S_OK;
1772}
1773
1774HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1775{
1776 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1777 bool fChanged = false;
1778
1779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1782 {
1783 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1784 {
1785 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1786 fChanged = true;
1787 }
1788 }
1789 if (fChanged)
1790 {
1791 alock.release();
1792 HRESULT rc = i_onVideoCaptureChange();
1793 alock.acquire();
1794 if (FAILED(rc)) return rc;
1795 i_setModified(IsModified_MachineData);
1796
1797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1798 if (Global::IsOnline(mData->mMachineState))
1799 i_saveSettings(NULL);
1800 }
1801
1802 return S_OK;
1803}
1804
1805HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1806{
1807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1808 if (mHWData->mVideoCaptureFile.isEmpty())
1809 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1810 else
1811 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1812 return S_OK;
1813}
1814
1815HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1816{
1817 Utf8Str strFile(aVideoCaptureFile);
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 if ( Global::IsOnline(mData->mMachineState)
1821 && mHWData->mVideoCaptureEnabled)
1822 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1823
1824 if (!RTPathStartsWithRoot(strFile.c_str()))
1825 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1826
1827 if (!strFile.isEmpty())
1828 {
1829 Utf8Str defaultFile;
1830 i_getDefaultVideoCaptureFile(defaultFile);
1831 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1832 strFile.setNull();
1833 }
1834
1835 i_setModified(IsModified_MachineData);
1836 mHWData.backup();
1837 mHWData->mVideoCaptureFile = strFile;
1838
1839 return S_OK;
1840}
1841
1842HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1843{
1844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1845 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1846 return S_OK;
1847}
1848
1849HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1850{
1851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 if ( Global::IsOnline(mData->mMachineState)
1854 && mHWData->mVideoCaptureEnabled)
1855 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1856
1857 i_setModified(IsModified_MachineData);
1858 mHWData.backup();
1859 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1860
1861 return S_OK;
1862}
1863
1864HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1865{
1866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1867 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1868 return S_OK;
1869}
1870
1871HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1872{
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 if ( Global::IsOnline(mData->mMachineState)
1876 && mHWData->mVideoCaptureEnabled)
1877 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1878
1879 i_setModified(IsModified_MachineData);
1880 mHWData.backup();
1881 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1882
1883 return S_OK;
1884}
1885
1886HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1890 return S_OK;
1891}
1892
1893HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1894{
1895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1896
1897 if ( Global::IsOnline(mData->mMachineState)
1898 && mHWData->mVideoCaptureEnabled)
1899 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1900
1901 i_setModified(IsModified_MachineData);
1902 mHWData.backup();
1903 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1904
1905 return S_OK;
1906}
1907
1908HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1909{
1910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1911 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1912 return S_OK;
1913}
1914
1915HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1916{
1917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1918
1919 if ( Global::IsOnline(mData->mMachineState)
1920 && mHWData->mVideoCaptureEnabled)
1921 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1922
1923 i_setModified(IsModified_MachineData);
1924 mHWData.backup();
1925 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1926
1927 return S_OK;
1928}
1929
1930HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1931{
1932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1933 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1934 return S_OK;
1935}
1936
1937HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1938{
1939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1940
1941 if ( Global::IsOnline(mData->mMachineState)
1942 && mHWData->mVideoCaptureEnabled)
1943 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1944
1945 i_setModified(IsModified_MachineData);
1946 mHWData.backup();
1947 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1948
1949 return S_OK;
1950}
1951
1952HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1953{
1954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1955 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1956 return S_OK;
1957}
1958
1959HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1960{
1961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1962
1963 if ( Global::IsOnline(mData->mMachineState)
1964 && mHWData->mVideoCaptureEnabled)
1965 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1966
1967 i_setModified(IsModified_MachineData);
1968 mHWData.backup();
1969 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1970
1971 return S_OK;
1972}
1973
1974HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1975{
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1979 return S_OK;
1980}
1981
1982HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1983{
1984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1985
1986 if ( Global::IsOnline(mData->mMachineState)
1987 && mHWData->mVideoCaptureEnabled)
1988 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1989
1990 i_setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1993
1994 return S_OK;
1995}
1996
1997HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1998{
1999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2002
2003 return S_OK;
2004}
2005
2006HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2007{
2008 switch (aGraphicsControllerType)
2009 {
2010 case GraphicsControllerType_Null:
2011 case GraphicsControllerType_VBoxVGA:
2012#ifdef VBOX_WITH_VMSVGA
2013 case GraphicsControllerType_VMSVGA:
2014#endif
2015 break;
2016 default:
2017 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2018 }
2019
2020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 HRESULT rc = i_checkStateDependency(MutableStateDep);
2023 if (FAILED(rc)) return rc;
2024
2025 i_setModified(IsModified_MachineData);
2026 mHWData.backup();
2027 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2028
2029 return S_OK;
2030}
2031
2032HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2033{
2034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 *aVRAMSize = mHWData->mVRAMSize;
2037
2038 return S_OK;
2039}
2040
2041HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2042{
2043 /* check VRAM limits */
2044 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2045 return setError(E_INVALIDARG,
2046 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2047 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2048
2049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 HRESULT rc = i_checkStateDependency(MutableStateDep);
2052 if (FAILED(rc)) return rc;
2053
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mVRAMSize = aVRAMSize;
2057
2058 return S_OK;
2059}
2060
2061/** @todo this method should not be public */
2062HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2063{
2064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2067
2068 return S_OK;
2069}
2070
2071/**
2072 * Set the memory balloon size.
2073 *
2074 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2075 * we have to make sure that we never call IGuest from here.
2076 */
2077HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2078{
2079 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2080#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2081 /* check limits */
2082 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2083 return setError(E_INVALIDARG,
2084 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2085 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2086
2087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2092
2093 return S_OK;
2094#else
2095 NOREF(aMemoryBalloonSize);
2096 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2097#endif
2098}
2099
2100HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2101{
2102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2103
2104 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2105 return S_OK;
2106}
2107
2108HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2109{
2110#ifdef VBOX_WITH_PAGE_SHARING
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2114 i_setModified(IsModified_MachineData);
2115 mHWData.backup();
2116 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2117 return S_OK;
2118#else
2119 NOREF(aPageFusionEnabled);
2120 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2121#endif
2122}
2123
2124HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2125{
2126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2134{
2135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 HRESULT rc = i_checkStateDependency(MutableStateDep);
2138 if (FAILED(rc)) return rc;
2139
2140 /** @todo check validity! */
2141
2142 i_setModified(IsModified_MachineData);
2143 mHWData.backup();
2144 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2145
2146 return S_OK;
2147}
2148
2149
2150HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2151{
2152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2153
2154 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2155
2156 return S_OK;
2157}
2158
2159HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2160{
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = i_checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 /** @todo check validity! */
2167 i_setModified(IsModified_MachineData);
2168 mHWData.backup();
2169 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2170
2171 return S_OK;
2172}
2173
2174HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2175{
2176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2177
2178 *aMonitorCount = mHWData->mMonitorCount;
2179
2180 return S_OK;
2181}
2182
2183HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2184{
2185 /* make sure monitor count is a sensible number */
2186 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2187 return setError(E_INVALIDARG,
2188 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2189 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2190
2191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2192
2193 HRESULT rc = i_checkStateDependency(MutableStateDep);
2194 if (FAILED(rc)) return rc;
2195
2196 i_setModified(IsModified_MachineData);
2197 mHWData.backup();
2198 mHWData->mMonitorCount = aMonitorCount;
2199
2200 return S_OK;
2201}
2202
2203HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2204{
2205 /* mBIOSSettings is constant during life time, no need to lock */
2206 aBIOSSettings = mBIOSSettings;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2212{
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 switch (aProperty)
2216 {
2217 case CPUPropertyType_PAE:
2218 *aValue = mHWData->mPAEEnabled;
2219 break;
2220
2221 case CPUPropertyType_LongMode:
2222 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2223 *aValue = TRUE;
2224 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2225 *aValue = FALSE;
2226#if HC_ARCH_BITS == 64
2227 else
2228 *aValue = TRUE;
2229#else
2230 else
2231 {
2232 *aValue = FALSE;
2233
2234 ComPtr<IGuestOSType> ptrGuestOSType;
2235 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2236 if (SUCCEEDED(hrc2))
2237 {
2238 BOOL fIs64Bit = FALSE;
2239 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2240 if (SUCCEEDED(hrc2) && fIs64Bit)
2241 {
2242 ComObjPtr<Host> ptrHost = mParent->i_host();
2243 alock.release();
2244
2245 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2246 if (FAILED(hrc2))
2247 *aValue = FALSE;
2248 }
2249 }
2250 }
2251#endif
2252 break;
2253
2254 case CPUPropertyType_TripleFaultReset:
2255 *aValue = mHWData->mTripleFaultReset;
2256 break;
2257
2258 case CPUPropertyType_APIC:
2259 *aValue = mHWData->mAPIC;
2260 break;
2261
2262 case CPUPropertyType_X2APIC:
2263 *aValue = mHWData->mX2APIC;
2264 break;
2265
2266 default:
2267 return E_INVALIDARG;
2268 }
2269 return S_OK;
2270}
2271
2272HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2273{
2274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2275
2276 HRESULT rc = i_checkStateDependency(MutableStateDep);
2277 if (FAILED(rc)) return rc;
2278
2279 switch (aProperty)
2280 {
2281 case CPUPropertyType_PAE:
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mPAEEnabled = !!aValue;
2285 break;
2286
2287 case CPUPropertyType_LongMode:
2288 i_setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2291 break;
2292
2293 case CPUPropertyType_TripleFaultReset:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mTripleFaultReset = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_APIC:
2300 if (mHWData->mX2APIC)
2301 aValue = TRUE;
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mAPIC = !!aValue;
2305 break;
2306
2307 case CPUPropertyType_X2APIC:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mX2APIC = !!aValue;
2311 if (aValue)
2312 mHWData->mAPIC = !!aValue;
2313 break;
2314
2315 default:
2316 return E_INVALIDARG;
2317 }
2318 return S_OK;
2319}
2320
2321HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2322{
2323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2324
2325 switch(aId)
2326 {
2327 case 0x0:
2328 case 0x1:
2329 case 0x2:
2330 case 0x3:
2331 case 0x4:
2332 case 0x5:
2333 case 0x6:
2334 case 0x7:
2335 case 0x8:
2336 case 0x9:
2337 case 0xA:
2338 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2339 return E_INVALIDARG;
2340
2341 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2342 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2343 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2344 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2345 break;
2346
2347 case 0x80000000:
2348 case 0x80000001:
2349 case 0x80000002:
2350 case 0x80000003:
2351 case 0x80000004:
2352 case 0x80000005:
2353 case 0x80000006:
2354 case 0x80000007:
2355 case 0x80000008:
2356 case 0x80000009:
2357 case 0x8000000A:
2358 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2359 return E_INVALIDARG;
2360
2361 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2362 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2363 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2364 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2365 break;
2366
2367 default:
2368 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2369 }
2370 return S_OK;
2371}
2372
2373
2374HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2375{
2376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2377
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 switch(aId)
2382 {
2383 case 0x0:
2384 case 0x1:
2385 case 0x2:
2386 case 0x3:
2387 case 0x4:
2388 case 0x5:
2389 case 0x6:
2390 case 0x7:
2391 case 0x8:
2392 case 0x9:
2393 case 0xA:
2394 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2395 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2396 i_setModified(IsModified_MachineData);
2397 mHWData.backup();
2398 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2399 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2400 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2401 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2402 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2403 break;
2404
2405 case 0x80000000:
2406 case 0x80000001:
2407 case 0x80000002:
2408 case 0x80000003:
2409 case 0x80000004:
2410 case 0x80000005:
2411 case 0x80000006:
2412 case 0x80000007:
2413 case 0x80000008:
2414 case 0x80000009:
2415 case 0x8000000A:
2416 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2417 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2418 i_setModified(IsModified_MachineData);
2419 mHWData.backup();
2420 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2421 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2422 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2423 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2424 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2425 break;
2426
2427 default:
2428 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2429 }
2430 return S_OK;
2431}
2432
2433HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2434{
2435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2436
2437 HRESULT rc = i_checkStateDependency(MutableStateDep);
2438 if (FAILED(rc)) return rc;
2439
2440 switch(aId)
2441 {
2442 case 0x0:
2443 case 0x1:
2444 case 0x2:
2445 case 0x3:
2446 case 0x4:
2447 case 0x5:
2448 case 0x6:
2449 case 0x7:
2450 case 0x8:
2451 case 0x9:
2452 case 0xA:
2453 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2454 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2455 i_setModified(IsModified_MachineData);
2456 mHWData.backup();
2457 /* Invalidate leaf. */
2458 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2459 break;
2460
2461 case 0x80000000:
2462 case 0x80000001:
2463 case 0x80000002:
2464 case 0x80000003:
2465 case 0x80000004:
2466 case 0x80000005:
2467 case 0x80000006:
2468 case 0x80000007:
2469 case 0x80000008:
2470 case 0x80000009:
2471 case 0x8000000A:
2472 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2473 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2474 i_setModified(IsModified_MachineData);
2475 mHWData.backup();
2476 /* Invalidate leaf. */
2477 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2478 break;
2479
2480 default:
2481 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2482 }
2483 return S_OK;
2484}
2485
2486HRESULT Machine::removeAllCPUIDLeaves()
2487{
2488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 HRESULT rc = i_checkStateDependency(MutableStateDep);
2491 if (FAILED(rc)) return rc;
2492
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495
2496 /* Invalidate all standard leafs. */
2497 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2498 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2499
2500 /* Invalidate all extended leafs. */
2501 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2502 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2503
2504 return S_OK;
2505}
2506HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2507{
2508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2509
2510 switch(aProperty)
2511 {
2512 case HWVirtExPropertyType_Enabled:
2513 *aValue = mHWData->mHWVirtExEnabled;
2514 break;
2515
2516 case HWVirtExPropertyType_VPID:
2517 *aValue = mHWData->mHWVirtExVPIDEnabled;
2518 break;
2519
2520 case HWVirtExPropertyType_NestedPaging:
2521 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2522 break;
2523
2524 case HWVirtExPropertyType_UnrestrictedExecution:
2525 *aValue = mHWData->mHWVirtExUXEnabled;
2526 break;
2527
2528 case HWVirtExPropertyType_LargePages:
2529 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2530#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2531 *aValue = FALSE;
2532#endif
2533 break;
2534
2535 case HWVirtExPropertyType_Force:
2536 *aValue = mHWData->mHWVirtExForceEnabled;
2537 break;
2538
2539 default:
2540 return E_INVALIDARG;
2541 }
2542 return S_OK;
2543}
2544
2545HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2546{
2547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 HRESULT rc = i_checkStateDependency(MutableStateDep);
2550 if (FAILED(rc)) return rc;
2551
2552 switch(aProperty)
2553 {
2554 case HWVirtExPropertyType_Enabled:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_VPID:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_NestedPaging:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_UnrestrictedExecution:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExUXEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_LargePages:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2582 break;
2583
2584 case HWVirtExPropertyType_Force:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExForceEnabled = !!aValue;
2588 break;
2589
2590 default:
2591 return E_INVALIDARG;
2592 }
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2598{
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2602
2603 return S_OK;
2604}
2605
2606HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2607{
2608 /** @todo (r=dmik):
2609 * 1. Allow to change the name of the snapshot folder containing snapshots
2610 * 2. Rename the folder on disk instead of just changing the property
2611 * value (to be smart and not to leave garbage). Note that it cannot be
2612 * done here because the change may be rolled back. Thus, the right
2613 * place is #saveSettings().
2614 */
2615
2616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 HRESULT rc = i_checkStateDependency(MutableStateDep);
2619 if (FAILED(rc)) return rc;
2620
2621 if (!mData->mCurrentSnapshot.isNull())
2622 return setError(E_FAIL,
2623 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2624
2625 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2626
2627 if (strSnapshotFolder.isEmpty())
2628 strSnapshotFolder = "Snapshots";
2629 int vrc = i_calculateFullPath(strSnapshotFolder,
2630 strSnapshotFolder);
2631 if (RT_FAILURE(vrc))
2632 return setError(E_FAIL,
2633 tr("Invalid snapshot folder '%s' (%Rrc)"),
2634 strSnapshotFolder.c_str(), vrc);
2635
2636 i_setModified(IsModified_MachineData);
2637 mUserData.backup();
2638
2639 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2640
2641 return S_OK;
2642}
2643
2644HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2645{
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 aMediumAttachments.resize(mMediaData->mAttachments.size());
2649 size_t i = 0;
2650 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2651 it != mMediaData->mAttachments.end(); ++it, ++i)
2652 aMediumAttachments[i] = *it;
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2658{
2659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 Assert(!!mVRDEServer);
2662
2663 aVRDEServer = mVRDEServer;
2664
2665 return S_OK;
2666}
2667
2668HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2669{
2670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2671
2672 aAudioAdapter = mAudioAdapter;
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2678{
2679#ifdef VBOX_WITH_VUSB
2680 clearError();
2681 MultiResult rc(S_OK);
2682
2683# ifdef VBOX_WITH_USB
2684 rc = mParent->i_host()->i_checkUSBProxyService();
2685 if (FAILED(rc)) return rc;
2686# endif
2687
2688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 USBControllerList data = *mUSBControllers.data();
2691 aUSBControllers.resize(data.size());
2692 size_t i = 0;
2693 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2694 aUSBControllers[i] = *it;
2695
2696 return S_OK;
2697#else
2698 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2699 * extended error info to indicate that USB is simply not available
2700 * (w/o treating it as a failure), for example, as in OSE */
2701 NOREF(aUSBControllers);
2702 ReturnComNotImplemented();
2703#endif /* VBOX_WITH_VUSB */
2704}
2705
2706HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2707{
2708#ifdef VBOX_WITH_VUSB
2709 clearError();
2710 MultiResult rc(S_OK);
2711
2712# ifdef VBOX_WITH_USB
2713 rc = mParent->i_host()->i_checkUSBProxyService();
2714 if (FAILED(rc)) return rc;
2715# endif
2716
2717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2718
2719 aUSBDeviceFilters = mUSBDeviceFilters;
2720 return rc;
2721#else
2722 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2723 * extended error info to indicate that USB is simply not available
2724 * (w/o treating it as a failure), for example, as in OSE */
2725 NOREF(aUSBDeviceFilters);
2726 ReturnComNotImplemented();
2727#endif /* VBOX_WITH_VUSB */
2728}
2729
2730HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 aSettingsFilePath = mData->m_strConfigFileFull;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2740{
2741 RT_NOREF(aSettingsFilePath);
2742 ReturnComNotImplemented();
2743}
2744
2745HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2746{
2747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2750 if (FAILED(rc)) return rc;
2751
2752 if (!mData->pMachineConfigFile->fileExists())
2753 // this is a new machine, and no config file exists yet:
2754 *aSettingsModified = TRUE;
2755 else
2756 *aSettingsModified = (mData->flModifications != 0);
2757
2758 return S_OK;
2759}
2760
2761HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aSessionState = mData->mSession.mState;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aSessionName = mData->mSession.mName;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 *aSessionPID = mData->mSession.mPID;
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getState(MachineState_T *aState)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 *aState = mData->mMachineState;
2793 Assert(mData->mMachineState != MachineState_Null);
2794
2795 return S_OK;
2796}
2797
2798HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2799{
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 aStateFilePath = mSSData->strStateFilePath;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2817{
2818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2819
2820 i_getLogFolder(aLogFolder);
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 aCurrentSnapshot = mData->mCurrentSnapshot;
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2839 ? 0
2840 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 /* Note: for machines with no snapshots, we always return FALSE
2850 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2851 * reasons :) */
2852
2853 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2854 ? FALSE
2855 : mData->mCurrentStateModified;
2856
2857 return S_OK;
2858}
2859
2860HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2861{
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 aSharedFolders.resize(mHWData->mSharedFolders.size());
2865 size_t i = 0;
2866 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2867 it != mHWData->mSharedFolders.end(); ++i, ++it)
2868 aSharedFolders[i] = *it;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 *aClipboardMode = mHWData->mClipboardMode;
2878
2879 return S_OK;
2880}
2881
2882HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2883{
2884 HRESULT rc = S_OK;
2885
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 alock.release();
2889 rc = i_onClipboardModeChange(aClipboardMode);
2890 alock.acquire();
2891 if (FAILED(rc)) return rc;
2892
2893 i_setModified(IsModified_MachineData);
2894 mHWData.backup();
2895 mHWData->mClipboardMode = aClipboardMode;
2896
2897 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2898 if (Global::IsOnline(mData->mMachineState))
2899 i_saveSettings(NULL);
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 *aDnDMode = mHWData->mDnDMode;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2914{
2915 HRESULT rc = S_OK;
2916
2917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 alock.release();
2920 rc = i_onDnDModeChange(aDnDMode);
2921
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mDnDMode = aDnDMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939 StorageControllerList data = *mStorageControllers.data();
2940 size_t i = 0;
2941 aStorageControllers.resize(data.size());
2942 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2943 aStorageControllers[i] = *it;
2944 return S_OK;
2945}
2946
2947HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2948{
2949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 *aEnabled = mUserData->s.fTeleporterEnabled;
2952
2953 return S_OK;
2954}
2955
2956HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2957{
2958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 /* Only allow it to be set to true when PoweredOff or Aborted.
2961 (Clearing it is always permitted.) */
2962 if ( aTeleporterEnabled
2963 && mData->mRegistered
2964 && ( !i_isSessionMachine()
2965 || ( mData->mMachineState != MachineState_PoweredOff
2966 && mData->mMachineState != MachineState_Teleported
2967 && mData->mMachineState != MachineState_Aborted
2968 )
2969 )
2970 )
2971 return setError(VBOX_E_INVALID_VM_STATE,
2972 tr("The machine is not powered off (state is %s)"),
2973 Global::stringifyMachineState(mData->mMachineState));
2974
2975 i_setModified(IsModified_MachineData);
2976 mUserData.backup();
2977 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2978
2979 return S_OK;
2980}
2981
2982HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2983{
2984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2992{
2993 if (aTeleporterPort >= _64K)
2994 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2995
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2999 if (FAILED(rc)) return rc;
3000
3001 i_setModified(IsModified_MachineData);
3002 mUserData.backup();
3003 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3004
3005 return S_OK;
3006}
3007
3008HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3009{
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3013
3014 return S_OK;
3015}
3016
3017HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3018{
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3022 if (FAILED(rc)) return rc;
3023
3024 i_setModified(IsModified_MachineData);
3025 mUserData.backup();
3026 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3027
3028 return S_OK;
3029}
3030
3031HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3032{
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3035
3036 return S_OK;
3037}
3038
3039HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3040{
3041 /*
3042 * Hash the password first.
3043 */
3044 com::Utf8Str aT = aTeleporterPassword;
3045
3046 if (!aT.isEmpty())
3047 {
3048 if (VBoxIsPasswordHashed(&aT))
3049 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3050 VBoxHashPassword(&aT);
3051 }
3052
3053 /*
3054 * Do the update.
3055 */
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3058 if (SUCCEEDED(hrc))
3059 {
3060 i_setModified(IsModified_MachineData);
3061 mUserData.backup();
3062 mUserData->s.strTeleporterPassword = aT;
3063 }
3064
3065 return hrc;
3066}
3067
3068HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3069{
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3077{
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 /** @todo deal with running state change. */
3081 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mUserData.backup();
3086 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3087 return S_OK;
3088}
3089
3090HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3091{
3092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3095 return S_OK;
3096}
3097
3098HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3099{
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 /** @todo deal with running state change. */
3103 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3104 if (FAILED(rc)) return rc;
3105
3106 i_setModified(IsModified_MachineData);
3107 mUserData.backup();
3108 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3109 return S_OK;
3110}
3111
3112HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /** @todo deal with running state change. */
3125 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mUserData.backup();
3130 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3131 return S_OK;
3132}
3133
3134HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3135{
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3139
3140 return S_OK;
3141}
3142
3143HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3144{
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 /** @todo deal with running state change. */
3148 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mUserData.backup();
3153 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3154
3155 return S_OK;
3156}
3157
3158HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3159{
3160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3163 return S_OK;
3164}
3165
3166HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3167{
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 /** @todo deal with running state change. */
3171 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3172 if (FAILED(rc)) return rc;
3173
3174 i_setModified(IsModified_MachineData);
3175 mUserData.backup();
3176 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3177 return S_OK;
3178}
3179
3180HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3185
3186 return S_OK;
3187}
3188
3189HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3190{
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 /* Only allow it to be set to true when PoweredOff or Aborted.
3194 (Clearing it is always permitted.) */
3195 if ( aRTCUseUTC
3196 && mData->mRegistered
3197 && ( !i_isSessionMachine()
3198 || ( mData->mMachineState != MachineState_PoweredOff
3199 && mData->mMachineState != MachineState_Teleported
3200 && mData->mMachineState != MachineState_Aborted
3201 )
3202 )
3203 )
3204 return setError(VBOX_E_INVALID_VM_STATE,
3205 tr("The machine is not powered off (state is %s)"),
3206 Global::stringifyMachineState(mData->mMachineState));
3207
3208 i_setModified(IsModified_MachineData);
3209 mUserData.backup();
3210 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3211
3212 return S_OK;
3213}
3214
3215HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3216{
3217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3218
3219 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3220
3221 return S_OK;
3222}
3223
3224HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3225{
3226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3227
3228 HRESULT rc = i_checkStateDependency(MutableStateDep);
3229 if (FAILED(rc)) return rc;
3230
3231 i_setModified(IsModified_MachineData);
3232 mHWData.backup();
3233 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3234
3235 return S_OK;
3236}
3237
3238HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3239{
3240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3241
3242 *aIOCacheSize = mHWData->mIOCacheSize;
3243
3244 return S_OK;
3245}
3246
3247HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3248{
3249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 HRESULT rc = i_checkStateDependency(MutableStateDep);
3252 if (FAILED(rc)) return rc;
3253
3254 i_setModified(IsModified_MachineData);
3255 mHWData.backup();
3256 mHWData->mIOCacheSize = aIOCacheSize;
3257
3258 return S_OK;
3259}
3260
3261
3262/**
3263 * @note Locks objects!
3264 */
3265HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3266 LockType_T aLockType)
3267{
3268 /* check the session state */
3269 SessionState_T state;
3270 HRESULT rc = aSession->COMGETTER(State)(&state);
3271 if (FAILED(rc)) return rc;
3272
3273 if (state != SessionState_Unlocked)
3274 return setError(VBOX_E_INVALID_OBJECT_STATE,
3275 tr("The given session is busy"));
3276
3277 // get the client's IInternalSessionControl interface
3278 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3279 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3280 E_INVALIDARG);
3281
3282 // session name (only used in some code paths)
3283 Utf8Str strSessionName;
3284
3285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3286
3287 if (!mData->mRegistered)
3288 return setError(E_UNEXPECTED,
3289 tr("The machine '%s' is not registered"),
3290 mUserData->s.strName.c_str());
3291
3292 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3293
3294 SessionState_T oldState = mData->mSession.mState;
3295 /* Hack: in case the session is closing and there is a progress object
3296 * which allows waiting for the session to be closed, take the opportunity
3297 * and do a limited wait (max. 1 second). This helps a lot when the system
3298 * is busy and thus session closing can take a little while. */
3299 if ( mData->mSession.mState == SessionState_Unlocking
3300 && mData->mSession.mProgress)
3301 {
3302 alock.release();
3303 mData->mSession.mProgress->WaitForCompletion(1000);
3304 alock.acquire();
3305 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3306 }
3307
3308 // try again now
3309 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3310 // (i.e. session machine exists)
3311 && (aLockType == LockType_Shared) // caller wants a shared link to the
3312 // existing session that holds the write lock:
3313 )
3314 {
3315 // OK, share the session... we are now dealing with three processes:
3316 // 1) VBoxSVC (where this code runs);
3317 // 2) process C: the caller's client process (who wants a shared session);
3318 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3319
3320 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3321 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3322 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3323 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3324 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3325
3326 /*
3327 * Release the lock before calling the client process. It's safe here
3328 * since the only thing to do after we get the lock again is to add
3329 * the remote control to the list (which doesn't directly influence
3330 * anything).
3331 */
3332 alock.release();
3333
3334 // get the console of the session holding the write lock (this is a remote call)
3335 ComPtr<IConsole> pConsoleW;
3336 if (mData->mSession.mLockType == LockType_VM)
3337 {
3338 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3339 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3340 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3341 if (FAILED(rc))
3342 // the failure may occur w/o any error info (from RPC), so provide one
3343 return setError(VBOX_E_VM_ERROR,
3344 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3345 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3346 }
3347
3348 // share the session machine and W's console with the caller's session
3349 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3350 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3351 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3352
3353 if (FAILED(rc))
3354 // the failure may occur w/o any error info (from RPC), so provide one
3355 return setError(VBOX_E_VM_ERROR,
3356 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3357 alock.acquire();
3358
3359 // need to revalidate the state after acquiring the lock again
3360 if (mData->mSession.mState != SessionState_Locked)
3361 {
3362 pSessionControl->Uninitialize();
3363 return setError(VBOX_E_INVALID_SESSION_STATE,
3364 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3365 mUserData->s.strName.c_str());
3366 }
3367
3368 // add the caller's session to the list
3369 mData->mSession.mRemoteControls.push_back(pSessionControl);
3370 }
3371 else if ( mData->mSession.mState == SessionState_Locked
3372 || mData->mSession.mState == SessionState_Unlocking
3373 )
3374 {
3375 // sharing not permitted, or machine still unlocking:
3376 return setError(VBOX_E_INVALID_OBJECT_STATE,
3377 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3378 mUserData->s.strName.c_str());
3379 }
3380 else
3381 {
3382 // machine is not locked: then write-lock the machine (create the session machine)
3383
3384 // must not be busy
3385 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3386
3387 // get the caller's session PID
3388 RTPROCESS pid = NIL_RTPROCESS;
3389 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3390 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3391 Assert(pid != NIL_RTPROCESS);
3392
3393 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3394
3395 if (fLaunchingVMProcess)
3396 {
3397 if (mData->mSession.mPID == NIL_RTPROCESS)
3398 {
3399 // two or more clients racing for a lock, the one which set the
3400 // session state to Spawning will win, the others will get an
3401 // error as we can't decide here if waiting a little would help
3402 // (only for shared locks this would avoid an error)
3403 return setError(VBOX_E_INVALID_OBJECT_STATE,
3404 tr("The machine '%s' already has a lock request pending"),
3405 mUserData->s.strName.c_str());
3406 }
3407
3408 // this machine is awaiting for a spawning session to be opened:
3409 // then the calling process must be the one that got started by
3410 // LaunchVMProcess()
3411
3412 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3413 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3414
3415#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3416 /* Hardened windows builds spawns three processes when a VM is
3417 launched, the 3rd one is the one that will end up here. */
3418 RTPROCESS ppid;
3419 int rc = RTProcQueryParent(pid, &ppid);
3420 if (RT_SUCCESS(rc))
3421 rc = RTProcQueryParent(ppid, &ppid);
3422 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3423 || rc == VERR_ACCESS_DENIED)
3424 {
3425 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3426 mData->mSession.mPID = pid;
3427 }
3428#endif
3429
3430 if (mData->mSession.mPID != pid)
3431 return setError(E_ACCESSDENIED,
3432 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3433 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3434 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3435 }
3436
3437 // create the mutable SessionMachine from the current machine
3438 ComObjPtr<SessionMachine> sessionMachine;
3439 sessionMachine.createObject();
3440 rc = sessionMachine->init(this);
3441 AssertComRC(rc);
3442
3443 /* NOTE: doing return from this function after this point but
3444 * before the end is forbidden since it may call SessionMachine::uninit()
3445 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3446 * lock while still holding the Machine lock in alock so that a deadlock
3447 * is possible due to the wrong lock order. */
3448
3449 if (SUCCEEDED(rc))
3450 {
3451 /*
3452 * Set the session state to Spawning to protect against subsequent
3453 * attempts to open a session and to unregister the machine after
3454 * we release the lock.
3455 */
3456 SessionState_T origState = mData->mSession.mState;
3457 mData->mSession.mState = SessionState_Spawning;
3458
3459#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3460 /* Get the client token ID to be passed to the client process */
3461 Utf8Str strTokenId;
3462 sessionMachine->i_getTokenId(strTokenId);
3463 Assert(!strTokenId.isEmpty());
3464#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3465 /* Get the client token to be passed to the client process */
3466 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3467 /* The token is now "owned" by pToken, fix refcount */
3468 if (!pToken.isNull())
3469 pToken->Release();
3470#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3471
3472 /*
3473 * Release the lock before calling the client process -- it will call
3474 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3475 * because the state is Spawning, so that LaunchVMProcess() and
3476 * LockMachine() calls will fail. This method, called before we
3477 * acquire the lock again, will fail because of the wrong PID.
3478 *
3479 * Note that mData->mSession.mRemoteControls accessed outside
3480 * the lock may not be modified when state is Spawning, so it's safe.
3481 */
3482 alock.release();
3483
3484 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3485#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3486 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3487#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3488 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3489 /* Now the token is owned by the client process. */
3490 pToken.setNull();
3491#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3492 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3493
3494 /* The failure may occur w/o any error info (from RPC), so provide one */
3495 if (FAILED(rc))
3496 setError(VBOX_E_VM_ERROR,
3497 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3498
3499 // get session name, either to remember or to compare against
3500 // the already known session name.
3501 {
3502 Bstr bstrSessionName;
3503 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3504 if (SUCCEEDED(rc2))
3505 strSessionName = bstrSessionName;
3506 }
3507
3508 if ( SUCCEEDED(rc)
3509 && fLaunchingVMProcess
3510 )
3511 {
3512 /* complete the remote session initialization */
3513
3514 /* get the console from the direct session */
3515 ComPtr<IConsole> console;
3516 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3517 ComAssertComRC(rc);
3518
3519 if (SUCCEEDED(rc) && !console)
3520 {
3521 ComAssert(!!console);
3522 rc = E_FAIL;
3523 }
3524
3525 /* assign machine & console to the remote session */
3526 if (SUCCEEDED(rc))
3527 {
3528 /*
3529 * after LaunchVMProcess(), the first and the only
3530 * entry in remoteControls is that remote session
3531 */
3532 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3533 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3534 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3535
3536 /* The failure may occur w/o any error info (from RPC), so provide one */
3537 if (FAILED(rc))
3538 setError(VBOX_E_VM_ERROR,
3539 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3540 }
3541
3542 if (FAILED(rc))
3543 pSessionControl->Uninitialize();
3544 }
3545
3546 /* acquire the lock again */
3547 alock.acquire();
3548
3549 /* Restore the session state */
3550 mData->mSession.mState = origState;
3551 }
3552
3553 // finalize spawning anyway (this is why we don't return on errors above)
3554 if (fLaunchingVMProcess)
3555 {
3556 Assert(mData->mSession.mName == strSessionName);
3557 /* Note that the progress object is finalized later */
3558 /** @todo Consider checking mData->mSession.mProgress for cancellation
3559 * around here. */
3560
3561 /* We don't reset mSession.mPID here because it is necessary for
3562 * SessionMachine::uninit() to reap the child process later. */
3563
3564 if (FAILED(rc))
3565 {
3566 /* Close the remote session, remove the remote control from the list
3567 * and reset session state to Closed (@note keep the code in sync
3568 * with the relevant part in checkForSpawnFailure()). */
3569
3570 Assert(mData->mSession.mRemoteControls.size() == 1);
3571 if (mData->mSession.mRemoteControls.size() == 1)
3572 {
3573 ErrorInfoKeeper eik;
3574 mData->mSession.mRemoteControls.front()->Uninitialize();
3575 }
3576
3577 mData->mSession.mRemoteControls.clear();
3578 mData->mSession.mState = SessionState_Unlocked;
3579 }
3580 }
3581 else
3582 {
3583 /* memorize PID of the directly opened session */
3584 if (SUCCEEDED(rc))
3585 mData->mSession.mPID = pid;
3586 }
3587
3588 if (SUCCEEDED(rc))
3589 {
3590 mData->mSession.mLockType = aLockType;
3591 /* memorize the direct session control and cache IUnknown for it */
3592 mData->mSession.mDirectControl = pSessionControl;
3593 mData->mSession.mState = SessionState_Locked;
3594 if (!fLaunchingVMProcess)
3595 mData->mSession.mName = strSessionName;
3596 /* associate the SessionMachine with this Machine */
3597 mData->mSession.mMachine = sessionMachine;
3598
3599 /* request an IUnknown pointer early from the remote party for later
3600 * identity checks (it will be internally cached within mDirectControl
3601 * at least on XPCOM) */
3602 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3603 NOREF(unk);
3604 }
3605
3606 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3607 * would break the lock order */
3608 alock.release();
3609
3610 /* uninitialize the created session machine on failure */
3611 if (FAILED(rc))
3612 sessionMachine->uninit();
3613 }
3614
3615 if (SUCCEEDED(rc))
3616 {
3617 /*
3618 * tell the client watcher thread to update the set of
3619 * machines that have open sessions
3620 */
3621 mParent->i_updateClientWatcher();
3622
3623 if (oldState != SessionState_Locked)
3624 /* fire an event */
3625 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3626 }
3627
3628 return rc;
3629}
3630
3631/**
3632 * @note Locks objects!
3633 */
3634HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3635 const com::Utf8Str &aName,
3636 const com::Utf8Str &aEnvironment,
3637 ComPtr<IProgress> &aProgress)
3638{
3639 Utf8Str strFrontend(aName);
3640 /* "emergencystop" doesn't need the session, so skip the checks/interface
3641 * retrieval. This code doesn't quite fit in here, but introducing a
3642 * special API method would be even more effort, and would require explicit
3643 * support by every API client. It's better to hide the feature a bit. */
3644 if (strFrontend != "emergencystop")
3645 CheckComArgNotNull(aSession);
3646
3647 HRESULT rc = S_OK;
3648 if (strFrontend.isEmpty())
3649 {
3650 Bstr bstrFrontend;
3651 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3652 if (FAILED(rc))
3653 return rc;
3654 strFrontend = bstrFrontend;
3655 if (strFrontend.isEmpty())
3656 {
3657 ComPtr<ISystemProperties> systemProperties;
3658 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3659 if (FAILED(rc))
3660 return rc;
3661 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3662 if (FAILED(rc))
3663 return rc;
3664 strFrontend = bstrFrontend;
3665 }
3666 /* paranoia - emergencystop is not a valid default */
3667 if (strFrontend == "emergencystop")
3668 strFrontend = Utf8Str::Empty;
3669 }
3670 /* default frontend: Qt GUI */
3671 if (strFrontend.isEmpty())
3672 strFrontend = "GUI/Qt";
3673
3674 if (strFrontend != "emergencystop")
3675 {
3676 /* check the session state */
3677 SessionState_T state;
3678 rc = aSession->COMGETTER(State)(&state);
3679 if (FAILED(rc))
3680 return rc;
3681
3682 if (state != SessionState_Unlocked)
3683 return setError(VBOX_E_INVALID_OBJECT_STATE,
3684 tr("The given session is busy"));
3685
3686 /* get the IInternalSessionControl interface */
3687 ComPtr<IInternalSessionControl> control(aSession);
3688 ComAssertMsgRet(!control.isNull(),
3689 ("No IInternalSessionControl interface"),
3690 E_INVALIDARG);
3691
3692 /* get the teleporter enable state for the progress object init. */
3693 BOOL fTeleporterEnabled;
3694 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3695 if (FAILED(rc))
3696 return rc;
3697
3698 /* create a progress object */
3699 ComObjPtr<ProgressProxy> progress;
3700 progress.createObject();
3701 rc = progress->init(mParent,
3702 static_cast<IMachine*>(this),
3703 Bstr(tr("Starting VM")).raw(),
3704 TRUE /* aCancelable */,
3705 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3706 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3707 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3708 2 /* uFirstOperationWeight */,
3709 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3710
3711 if (SUCCEEDED(rc))
3712 {
3713 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3714 if (SUCCEEDED(rc))
3715 {
3716 aProgress = progress;
3717
3718 /* signal the client watcher thread */
3719 mParent->i_updateClientWatcher();
3720
3721 /* fire an event */
3722 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3723 }
3724 }
3725 }
3726 else
3727 {
3728 /* no progress object - either instant success or failure */
3729 aProgress = NULL;
3730
3731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3732
3733 if (mData->mSession.mState != SessionState_Locked)
3734 return setError(VBOX_E_INVALID_OBJECT_STATE,
3735 tr("The machine '%s' is not locked by a session"),
3736 mUserData->s.strName.c_str());
3737
3738 /* must have a VM process associated - do not kill normal API clients
3739 * with an open session */
3740 if (!Global::IsOnline(mData->mMachineState))
3741 return setError(VBOX_E_INVALID_OBJECT_STATE,
3742 tr("The machine '%s' does not have a VM process"),
3743 mUserData->s.strName.c_str());
3744
3745 /* forcibly terminate the VM process */
3746 if (mData->mSession.mPID != NIL_RTPROCESS)
3747 RTProcTerminate(mData->mSession.mPID);
3748
3749 /* signal the client watcher thread, as most likely the client has
3750 * been terminated */
3751 mParent->i_updateClientWatcher();
3752 }
3753
3754 return rc;
3755}
3756
3757HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3758{
3759 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3760 return setError(E_INVALIDARG,
3761 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3762 aPosition, SchemaDefs::MaxBootPosition);
3763
3764 if (aDevice == DeviceType_USB)
3765 return setError(E_NOTIMPL,
3766 tr("Booting from USB device is currently not supported"));
3767
3768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3769
3770 HRESULT rc = i_checkStateDependency(MutableStateDep);
3771 if (FAILED(rc)) return rc;
3772
3773 i_setModified(IsModified_MachineData);
3774 mHWData.backup();
3775 mHWData->mBootOrder[aPosition - 1] = aDevice;
3776
3777 return S_OK;
3778}
3779
3780HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3781{
3782 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3783 return setError(E_INVALIDARG,
3784 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3785 aPosition, SchemaDefs::MaxBootPosition);
3786
3787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3788
3789 *aDevice = mHWData->mBootOrder[aPosition - 1];
3790
3791 return S_OK;
3792}
3793
3794HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3795 LONG aControllerPort,
3796 LONG aDevice,
3797 DeviceType_T aType,
3798 const ComPtr<IMedium> &aMedium)
3799{
3800 IMedium *aM = aMedium;
3801 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3802 aName.c_str(), aControllerPort, aDevice, aType, aM));
3803
3804 // request the host lock first, since might be calling Host methods for getting host drives;
3805 // next, protect the media tree all the while we're in here, as well as our member variables
3806 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3807 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3808
3809 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3810 if (FAILED(rc)) return rc;
3811
3812 /// @todo NEWMEDIA implicit machine registration
3813 if (!mData->mRegistered)
3814 return setError(VBOX_E_INVALID_OBJECT_STATE,
3815 tr("Cannot attach storage devices to an unregistered machine"));
3816
3817 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3818
3819 /* Check for an existing controller. */
3820 ComObjPtr<StorageController> ctl;
3821 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3822 if (FAILED(rc)) return rc;
3823
3824 StorageControllerType_T ctrlType;
3825 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3826 if (FAILED(rc))
3827 return setError(E_FAIL,
3828 tr("Could not get type of controller '%s'"),
3829 aName.c_str());
3830
3831 bool fSilent = false;
3832 Utf8Str strReconfig;
3833
3834 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3835 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3836 if ( mData->mMachineState == MachineState_Paused
3837 && strReconfig == "1")
3838 fSilent = true;
3839
3840 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3841 bool fHotplug = false;
3842 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3843 fHotplug = true;
3844
3845 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3846 return setError(VBOX_E_INVALID_VM_STATE,
3847 tr("Controller '%s' does not support hotplugging"),
3848 aName.c_str());
3849
3850 // check that the port and device are not out of range
3851 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3852 if (FAILED(rc)) return rc;
3853
3854 /* check if the device slot is already busy */
3855 MediumAttachment *pAttachTemp;
3856 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3857 Bstr(aName).raw(),
3858 aControllerPort,
3859 aDevice)))
3860 {
3861 Medium *pMedium = pAttachTemp->i_getMedium();
3862 if (pMedium)
3863 {
3864 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3865 return setError(VBOX_E_OBJECT_IN_USE,
3866 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3867 pMedium->i_getLocationFull().c_str(),
3868 aControllerPort,
3869 aDevice,
3870 aName.c_str());
3871 }
3872 else
3873 return setError(VBOX_E_OBJECT_IN_USE,
3874 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3875 aControllerPort, aDevice, aName.c_str());
3876 }
3877
3878 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3879 if (aMedium && medium.isNull())
3880 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3881
3882 AutoCaller mediumCaller(medium);
3883 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3884
3885 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3886
3887 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3888 && !medium.isNull()
3889 )
3890 return setError(VBOX_E_OBJECT_IN_USE,
3891 tr("Medium '%s' is already attached to this virtual machine"),
3892 medium->i_getLocationFull().c_str());
3893
3894 if (!medium.isNull())
3895 {
3896 MediumType_T mtype = medium->i_getType();
3897 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3898 // For DVDs it's not written to the config file, so needs no global config
3899 // version bump. For floppies it's a new attribute "type", which is ignored
3900 // by older VirtualBox version, so needs no global config version bump either.
3901 // For hard disks this type is not accepted.
3902 if (mtype == MediumType_MultiAttach)
3903 {
3904 // This type is new with VirtualBox 4.0 and therefore requires settings
3905 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3906 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3907 // two reasons: The medium type is a property of the media registry tree, which
3908 // can reside in the global config file (for pre-4.0 media); we would therefore
3909 // possibly need to bump the global config version. We don't want to do that though
3910 // because that might make downgrading to pre-4.0 impossible.
3911 // As a result, we can only use these two new types if the medium is NOT in the
3912 // global registry:
3913 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3914 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3915 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3916 )
3917 return setError(VBOX_E_INVALID_OBJECT_STATE,
3918 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3919 "to machines that were created with VirtualBox 4.0 or later"),
3920 medium->i_getLocationFull().c_str());
3921 }
3922 }
3923
3924 bool fIndirect = false;
3925 if (!medium.isNull())
3926 fIndirect = medium->i_isReadOnly();
3927 bool associate = true;
3928
3929 do
3930 {
3931 if ( aType == DeviceType_HardDisk
3932 && mMediaData.isBackedUp())
3933 {
3934 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3935
3936 /* check if the medium was attached to the VM before we started
3937 * changing attachments in which case the attachment just needs to
3938 * be restored */
3939 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3940 {
3941 AssertReturn(!fIndirect, E_FAIL);
3942
3943 /* see if it's the same bus/channel/device */
3944 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3945 {
3946 /* the simplest case: restore the whole attachment
3947 * and return, nothing else to do */
3948 mMediaData->mAttachments.push_back(pAttachTemp);
3949
3950 /* Reattach the medium to the VM. */
3951 if (fHotplug || fSilent)
3952 {
3953 mediumLock.release();
3954 treeLock.release();
3955 alock.release();
3956
3957 MediumLockList *pMediumLockList(new MediumLockList());
3958
3959 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3960 medium /* pToLockWrite */,
3961 false /* fMediumLockWriteAll */,
3962 NULL,
3963 *pMediumLockList);
3964 alock.acquire();
3965 if (FAILED(rc))
3966 delete pMediumLockList;
3967 else
3968 {
3969 mData->mSession.mLockedMedia.Unlock();
3970 alock.release();
3971 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3972 mData->mSession.mLockedMedia.Lock();
3973 alock.acquire();
3974 }
3975 alock.release();
3976
3977 if (SUCCEEDED(rc))
3978 {
3979 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3980 /* Remove lock list in case of error. */
3981 if (FAILED(rc))
3982 {
3983 mData->mSession.mLockedMedia.Unlock();
3984 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3985 mData->mSession.mLockedMedia.Lock();
3986 }
3987 }
3988 }
3989
3990 return S_OK;
3991 }
3992
3993 /* bus/channel/device differ; we need a new attachment object,
3994 * but don't try to associate it again */
3995 associate = false;
3996 break;
3997 }
3998 }
3999
4000 /* go further only if the attachment is to be indirect */
4001 if (!fIndirect)
4002 break;
4003
4004 /* perform the so called smart attachment logic for indirect
4005 * attachments. Note that smart attachment is only applicable to base
4006 * hard disks. */
4007
4008 if (medium->i_getParent().isNull())
4009 {
4010 /* first, investigate the backup copy of the current hard disk
4011 * attachments to make it possible to re-attach existing diffs to
4012 * another device slot w/o losing their contents */
4013 if (mMediaData.isBackedUp())
4014 {
4015 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4016
4017 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4018 uint32_t foundLevel = 0;
4019
4020 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4021 {
4022 uint32_t level = 0;
4023 MediumAttachment *pAttach = *it;
4024 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4025 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4026 if (pMedium.isNull())
4027 continue;
4028
4029 if (pMedium->i_getBase(&level) == medium)
4030 {
4031 /* skip the hard disk if its currently attached (we
4032 * cannot attach the same hard disk twice) */
4033 if (i_findAttachment(mMediaData->mAttachments,
4034 pMedium))
4035 continue;
4036
4037 /* matched device, channel and bus (i.e. attached to the
4038 * same place) will win and immediately stop the search;
4039 * otherwise the attachment that has the youngest
4040 * descendant of medium will be used
4041 */
4042 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4043 {
4044 /* the simplest case: restore the whole attachment
4045 * and return, nothing else to do */
4046 mMediaData->mAttachments.push_back(*it);
4047
4048 /* Reattach the medium to the VM. */
4049 if (fHotplug || fSilent)
4050 {
4051 mediumLock.release();
4052 treeLock.release();
4053 alock.release();
4054
4055 MediumLockList *pMediumLockList(new MediumLockList());
4056
4057 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4058 medium /* pToLockWrite */,
4059 false /* fMediumLockWriteAll */,
4060 NULL,
4061 *pMediumLockList);
4062 alock.acquire();
4063 if (FAILED(rc))
4064 delete pMediumLockList;
4065 else
4066 {
4067 mData->mSession.mLockedMedia.Unlock();
4068 alock.release();
4069 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4070 mData->mSession.mLockedMedia.Lock();
4071 alock.acquire();
4072 }
4073 alock.release();
4074
4075 if (SUCCEEDED(rc))
4076 {
4077 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4078 /* Remove lock list in case of error. */
4079 if (FAILED(rc))
4080 {
4081 mData->mSession.mLockedMedia.Unlock();
4082 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4083 mData->mSession.mLockedMedia.Lock();
4084 }
4085 }
4086 }
4087
4088 return S_OK;
4089 }
4090 else if ( foundIt == oldAtts.end()
4091 || level > foundLevel /* prefer younger */
4092 )
4093 {
4094 foundIt = it;
4095 foundLevel = level;
4096 }
4097 }
4098 }
4099
4100 if (foundIt != oldAtts.end())
4101 {
4102 /* use the previously attached hard disk */
4103 medium = (*foundIt)->i_getMedium();
4104 mediumCaller.attach(medium);
4105 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4106 mediumLock.attach(medium);
4107 /* not implicit, doesn't require association with this VM */
4108 fIndirect = false;
4109 associate = false;
4110 /* go right to the MediumAttachment creation */
4111 break;
4112 }
4113 }
4114
4115 /* must give up the medium lock and medium tree lock as below we
4116 * go over snapshots, which needs a lock with higher lock order. */
4117 mediumLock.release();
4118 treeLock.release();
4119
4120 /* then, search through snapshots for the best diff in the given
4121 * hard disk's chain to base the new diff on */
4122
4123 ComObjPtr<Medium> base;
4124 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4125 while (snap)
4126 {
4127 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4128
4129 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4130
4131 MediumAttachment *pAttachFound = NULL;
4132 uint32_t foundLevel = 0;
4133
4134 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4135 {
4136 MediumAttachment *pAttach = *it;
4137 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4138 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4139 if (pMedium.isNull())
4140 continue;
4141
4142 uint32_t level = 0;
4143 if (pMedium->i_getBase(&level) == medium)
4144 {
4145 /* matched device, channel and bus (i.e. attached to the
4146 * same place) will win and immediately stop the search;
4147 * otherwise the attachment that has the youngest
4148 * descendant of medium will be used
4149 */
4150 if ( pAttach->i_getDevice() == aDevice
4151 && pAttach->i_getPort() == aControllerPort
4152 && pAttach->i_getControllerName() == aName
4153 )
4154 {
4155 pAttachFound = pAttach;
4156 break;
4157 }
4158 else if ( !pAttachFound
4159 || level > foundLevel /* prefer younger */
4160 )
4161 {
4162 pAttachFound = pAttach;
4163 foundLevel = level;
4164 }
4165 }
4166 }
4167
4168 if (pAttachFound)
4169 {
4170 base = pAttachFound->i_getMedium();
4171 break;
4172 }
4173
4174 snap = snap->i_getParent();
4175 }
4176
4177 /* re-lock medium tree and the medium, as we need it below */
4178 treeLock.acquire();
4179 mediumLock.acquire();
4180
4181 /* found a suitable diff, use it as a base */
4182 if (!base.isNull())
4183 {
4184 medium = base;
4185 mediumCaller.attach(medium);
4186 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4187 mediumLock.attach(medium);
4188 }
4189 }
4190
4191 Utf8Str strFullSnapshotFolder;
4192 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4193
4194 ComObjPtr<Medium> diff;
4195 diff.createObject();
4196 // store this diff in the same registry as the parent
4197 Guid uuidRegistryParent;
4198 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4199 {
4200 // parent image has no registry: this can happen if we're attaching a new immutable
4201 // image that has not yet been attached (medium then points to the base and we're
4202 // creating the diff image for the immutable, and the parent is not yet registered);
4203 // put the parent in the machine registry then
4204 mediumLock.release();
4205 treeLock.release();
4206 alock.release();
4207 i_addMediumToRegistry(medium);
4208 alock.acquire();
4209 treeLock.acquire();
4210 mediumLock.acquire();
4211 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4212 }
4213 rc = diff->init(mParent,
4214 medium->i_getPreferredDiffFormat(),
4215 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4216 uuidRegistryParent,
4217 DeviceType_HardDisk);
4218 if (FAILED(rc)) return rc;
4219
4220 /* Apply the normal locking logic to the entire chain. */
4221 MediumLockList *pMediumLockList(new MediumLockList());
4222 mediumLock.release();
4223 treeLock.release();
4224 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4225 diff /* pToLockWrite */,
4226 false /* fMediumLockWriteAll */,
4227 medium,
4228 *pMediumLockList);
4229 treeLock.acquire();
4230 mediumLock.acquire();
4231 if (SUCCEEDED(rc))
4232 {
4233 mediumLock.release();
4234 treeLock.release();
4235 rc = pMediumLockList->Lock();
4236 treeLock.acquire();
4237 mediumLock.acquire();
4238 if (FAILED(rc))
4239 setError(rc,
4240 tr("Could not lock medium when creating diff '%s'"),
4241 diff->i_getLocationFull().c_str());
4242 else
4243 {
4244 /* will release the lock before the potentially lengthy
4245 * operation, so protect with the special state */
4246 MachineState_T oldState = mData->mMachineState;
4247 i_setMachineState(MachineState_SettingUp);
4248
4249 mediumLock.release();
4250 treeLock.release();
4251 alock.release();
4252
4253 rc = medium->i_createDiffStorage(diff,
4254 medium->i_getPreferredDiffVariant(),
4255 pMediumLockList,
4256 NULL /* aProgress */,
4257 true /* aWait */);
4258
4259 alock.acquire();
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262
4263 i_setMachineState(oldState);
4264 }
4265 }
4266
4267 /* Unlock the media and free the associated memory. */
4268 delete pMediumLockList;
4269
4270 if (FAILED(rc)) return rc;
4271
4272 /* use the created diff for the actual attachment */
4273 medium = diff;
4274 mediumCaller.attach(medium);
4275 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4276 mediumLock.attach(medium);
4277 }
4278 while (0);
4279
4280 ComObjPtr<MediumAttachment> attachment;
4281 attachment.createObject();
4282 rc = attachment->init(this,
4283 medium,
4284 aName,
4285 aControllerPort,
4286 aDevice,
4287 aType,
4288 fIndirect,
4289 false /* fPassthrough */,
4290 false /* fTempEject */,
4291 false /* fNonRotational */,
4292 false /* fDiscard */,
4293 fHotplug /* fHotPluggable */,
4294 Utf8Str::Empty);
4295 if (FAILED(rc)) return rc;
4296
4297 if (associate && !medium.isNull())
4298 {
4299 // as the last step, associate the medium to the VM
4300 rc = medium->i_addBackReference(mData->mUuid);
4301 // here we can fail because of Deleting, or being in process of creating a Diff
4302 if (FAILED(rc)) return rc;
4303
4304 mediumLock.release();
4305 treeLock.release();
4306 alock.release();
4307 i_addMediumToRegistry(medium);
4308 alock.acquire();
4309 treeLock.acquire();
4310 mediumLock.acquire();
4311 }
4312
4313 /* success: finally remember the attachment */
4314 i_setModified(IsModified_Storage);
4315 mMediaData.backup();
4316 mMediaData->mAttachments.push_back(attachment);
4317
4318 mediumLock.release();
4319 treeLock.release();
4320 alock.release();
4321
4322 if (fHotplug || fSilent)
4323 {
4324 if (!medium.isNull())
4325 {
4326 MediumLockList *pMediumLockList(new MediumLockList());
4327
4328 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4329 medium /* pToLockWrite */,
4330 false /* fMediumLockWriteAll */,
4331 NULL,
4332 *pMediumLockList);
4333 alock.acquire();
4334 if (FAILED(rc))
4335 delete pMediumLockList;
4336 else
4337 {
4338 mData->mSession.mLockedMedia.Unlock();
4339 alock.release();
4340 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4341 mData->mSession.mLockedMedia.Lock();
4342 alock.acquire();
4343 }
4344 alock.release();
4345 }
4346
4347 if (SUCCEEDED(rc))
4348 {
4349 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4350 /* Remove lock list in case of error. */
4351 if (FAILED(rc))
4352 {
4353 mData->mSession.mLockedMedia.Unlock();
4354 mData->mSession.mLockedMedia.Remove(attachment);
4355 mData->mSession.mLockedMedia.Lock();
4356 }
4357 }
4358 }
4359
4360 /* Save modified registries, but skip this machine as it's the caller's
4361 * job to save its settings like all other settings changes. */
4362 mParent->i_unmarkRegistryModified(i_getId());
4363 mParent->i_saveModifiedRegistries();
4364
4365 return rc;
4366}
4367
4368HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4369 LONG aDevice)
4370{
4371 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4372 aName.c_str(), aControllerPort, aDevice));
4373
4374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4375
4376 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4377 if (FAILED(rc)) return rc;
4378
4379 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4380
4381 /* Check for an existing controller. */
4382 ComObjPtr<StorageController> ctl;
4383 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4384 if (FAILED(rc)) return rc;
4385
4386 StorageControllerType_T ctrlType;
4387 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4388 if (FAILED(rc))
4389 return setError(E_FAIL,
4390 tr("Could not get type of controller '%s'"),
4391 aName.c_str());
4392
4393 bool fSilent = false;
4394 Utf8Str strReconfig;
4395
4396 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4397 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4398 if ( mData->mMachineState == MachineState_Paused
4399 && strReconfig == "1")
4400 fSilent = true;
4401
4402 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4403 bool fHotplug = false;
4404 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4405 fHotplug = true;
4406
4407 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4408 return setError(VBOX_E_INVALID_VM_STATE,
4409 tr("Controller '%s' does not support hotplugging"),
4410 aName.c_str());
4411
4412 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4413 Bstr(aName).raw(),
4414 aControllerPort,
4415 aDevice);
4416 if (!pAttach)
4417 return setError(VBOX_E_OBJECT_NOT_FOUND,
4418 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4419 aDevice, aControllerPort, aName.c_str());
4420
4421 if (fHotplug && !pAttach->i_getHotPluggable())
4422 return setError(VBOX_E_NOT_SUPPORTED,
4423 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4424 aDevice, aControllerPort, aName.c_str());
4425
4426 /*
4427 * The VM has to detach the device before we delete any implicit diffs.
4428 * If this fails we can roll back without loosing data.
4429 */
4430 if (fHotplug || fSilent)
4431 {
4432 alock.release();
4433 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4434 alock.acquire();
4435 }
4436 if (FAILED(rc)) return rc;
4437
4438 /* If we are here everything went well and we can delete the implicit now. */
4439 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4440
4441 alock.release();
4442
4443 /* Save modified registries, but skip this machine as it's the caller's
4444 * job to save its settings like all other settings changes. */
4445 mParent->i_unmarkRegistryModified(i_getId());
4446 mParent->i_saveModifiedRegistries();
4447
4448 return rc;
4449}
4450
4451HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4452 LONG aDevice, BOOL aPassthrough)
4453{
4454 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4455 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4456
4457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4458
4459 HRESULT rc = i_checkStateDependency(MutableStateDep);
4460 if (FAILED(rc)) return rc;
4461
4462 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4463
4464 if (Global::IsOnlineOrTransient(mData->mMachineState))
4465 return setError(VBOX_E_INVALID_VM_STATE,
4466 tr("Invalid machine state: %s"),
4467 Global::stringifyMachineState(mData->mMachineState));
4468
4469 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4470 Bstr(aName).raw(),
4471 aControllerPort,
4472 aDevice);
4473 if (!pAttach)
4474 return setError(VBOX_E_OBJECT_NOT_FOUND,
4475 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4476 aDevice, aControllerPort, aName.c_str());
4477
4478
4479 i_setModified(IsModified_Storage);
4480 mMediaData.backup();
4481
4482 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4483
4484 if (pAttach->i_getType() != DeviceType_DVD)
4485 return setError(E_INVALIDARG,
4486 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4487 aDevice, aControllerPort, aName.c_str());
4488 pAttach->i_updatePassthrough(!!aPassthrough);
4489
4490 return S_OK;
4491}
4492
4493HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4494 LONG aDevice, BOOL aTemporaryEject)
4495{
4496
4497 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4498 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4499
4500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4501
4502 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4503 if (FAILED(rc)) return rc;
4504
4505 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4506 Bstr(aName).raw(),
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4512 aDevice, aControllerPort, aName.c_str());
4513
4514
4515 i_setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4519
4520 if (pAttach->i_getType() != DeviceType_DVD)
4521 return setError(E_INVALIDARG,
4522 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4523 aDevice, aControllerPort, aName.c_str());
4524 pAttach->i_updateTempEject(!!aTemporaryEject);
4525
4526 return S_OK;
4527}
4528
4529HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4530 LONG aDevice, BOOL aNonRotational)
4531{
4532
4533 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4534 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4535
4536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4537
4538 HRESULT rc = i_checkStateDependency(MutableStateDep);
4539 if (FAILED(rc)) return rc;
4540
4541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4542
4543 if (Global::IsOnlineOrTransient(mData->mMachineState))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Invalid machine state: %s"),
4546 Global::stringifyMachineState(mData->mMachineState));
4547
4548 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4549 Bstr(aName).raw(),
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557
4558 i_setModified(IsModified_Storage);
4559 mMediaData.backup();
4560
4561 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4562
4563 if (pAttach->i_getType() != DeviceType_HardDisk)
4564 return setError(E_INVALIDARG,
4565 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"),
4566 aDevice, aControllerPort, aName.c_str());
4567 pAttach->i_updateNonRotational(!!aNonRotational);
4568
4569 return S_OK;
4570}
4571
4572HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4573 LONG aDevice, BOOL aDiscard)
4574{
4575
4576 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4577 aName.c_str(), aControllerPort, aDevice, aDiscard));
4578
4579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4580
4581 HRESULT rc = i_checkStateDependency(MutableStateDep);
4582 if (FAILED(rc)) return rc;
4583
4584 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4585
4586 if (Global::IsOnlineOrTransient(mData->mMachineState))
4587 return setError(VBOX_E_INVALID_VM_STATE,
4588 tr("Invalid machine state: %s"),
4589 Global::stringifyMachineState(mData->mMachineState));
4590
4591 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4592 Bstr(aName).raw(),
4593 aControllerPort,
4594 aDevice);
4595 if (!pAttach)
4596 return setError(VBOX_E_OBJECT_NOT_FOUND,
4597 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4598 aDevice, aControllerPort, aName.c_str());
4599
4600
4601 i_setModified(IsModified_Storage);
4602 mMediaData.backup();
4603
4604 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4605
4606 if (pAttach->i_getType() != DeviceType_HardDisk)
4607 return setError(E_INVALIDARG,
4608 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"),
4609 aDevice, aControllerPort, aName.c_str());
4610 pAttach->i_updateDiscard(!!aDiscard);
4611
4612 return S_OK;
4613}
4614
4615HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4616 LONG aDevice, BOOL aHotPluggable)
4617{
4618 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4619 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4620
4621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4622
4623 HRESULT rc = i_checkStateDependency(MutableStateDep);
4624 if (FAILED(rc)) return rc;
4625
4626 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4627
4628 if (Global::IsOnlineOrTransient(mData->mMachineState))
4629 return setError(VBOX_E_INVALID_VM_STATE,
4630 tr("Invalid machine state: %s"),
4631 Global::stringifyMachineState(mData->mMachineState));
4632
4633 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4634 Bstr(aName).raw(),
4635 aControllerPort,
4636 aDevice);
4637 if (!pAttach)
4638 return setError(VBOX_E_OBJECT_NOT_FOUND,
4639 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4640 aDevice, aControllerPort, aName.c_str());
4641
4642 /* Check for an existing controller. */
4643 ComObjPtr<StorageController> ctl;
4644 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4645 if (FAILED(rc)) return rc;
4646
4647 StorageControllerType_T ctrlType;
4648 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4649 if (FAILED(rc))
4650 return setError(E_FAIL,
4651 tr("Could not get type of controller '%s'"),
4652 aName.c_str());
4653
4654 if (!i_isControllerHotplugCapable(ctrlType))
4655 return setError(VBOX_E_NOT_SUPPORTED,
4656 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4657 aName.c_str());
4658
4659 i_setModified(IsModified_Storage);
4660 mMediaData.backup();
4661
4662 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4663
4664 if (pAttach->i_getType() == DeviceType_Floppy)
4665 return setError(E_INVALIDARG,
4666 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"),
4667 aDevice, aControllerPort, aName.c_str());
4668 pAttach->i_updateHotPluggable(!!aHotPluggable);
4669
4670 return S_OK;
4671}
4672
4673HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4674 LONG aDevice)
4675{
4676 int rc = S_OK;
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4678 aName.c_str(), aControllerPort, aDevice));
4679
4680 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4681
4682 return rc;
4683}
4684
4685HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4686 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4687{
4688 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4689 aName.c_str(), aControllerPort, aDevice));
4690
4691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4692
4693 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4694 if (FAILED(rc)) return rc;
4695
4696 if (Global::IsOnlineOrTransient(mData->mMachineState))
4697 return setError(VBOX_E_INVALID_VM_STATE,
4698 tr("Invalid machine state: %s"),
4699 Global::stringifyMachineState(mData->mMachineState));
4700
4701 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4702 Bstr(aName).raw(),
4703 aControllerPort,
4704 aDevice);
4705 if (!pAttach)
4706 return setError(VBOX_E_OBJECT_NOT_FOUND,
4707 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4708 aDevice, aControllerPort, aName.c_str());
4709
4710
4711 i_setModified(IsModified_Storage);
4712 mMediaData.backup();
4713
4714 IBandwidthGroup *iB = aBandwidthGroup;
4715 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4716 if (aBandwidthGroup && group.isNull())
4717 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4718
4719 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4720
4721 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4722 if (strBandwidthGroupOld.isNotEmpty())
4723 {
4724 /* Get the bandwidth group object and release it - this must not fail. */
4725 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4726 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4727 Assert(SUCCEEDED(rc));
4728
4729 pBandwidthGroupOld->i_release();
4730 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4731 }
4732
4733 if (!group.isNull())
4734 {
4735 group->i_reference();
4736 pAttach->i_updateBandwidthGroup(group->i_getName());
4737 }
4738
4739 return S_OK;
4740}
4741
4742HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4743 LONG aControllerPort,
4744 LONG aDevice,
4745 DeviceType_T aType)
4746{
4747 HRESULT rc = S_OK;
4748
4749 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4750 aName.c_str(), aControllerPort, aDevice, aType));
4751
4752 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4753
4754 return rc;
4755}
4756
4757
4758HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4759 LONG aControllerPort,
4760 LONG aDevice,
4761 BOOL aForce)
4762{
4763 int rc = S_OK;
4764 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4765 aName.c_str(), aControllerPort, aForce));
4766
4767 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4768
4769 return rc;
4770}
4771
4772HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4773 LONG aControllerPort,
4774 LONG aDevice,
4775 const ComPtr<IMedium> &aMedium,
4776 BOOL aForce)
4777{
4778 int rc = S_OK;
4779 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4780 aName.c_str(), aControllerPort, aDevice, aForce));
4781
4782 // request the host lock first, since might be calling Host methods for getting host drives;
4783 // next, protect the media tree all the while we're in here, as well as our member variables
4784 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4785 this->lockHandle(),
4786 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4787
4788 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4789 Bstr(aName).raw(),
4790 aControllerPort,
4791 aDevice);
4792 if (pAttach.isNull())
4793 return setError(VBOX_E_OBJECT_NOT_FOUND,
4794 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4795 aDevice, aControllerPort, aName.c_str());
4796
4797 /* Remember previously mounted medium. The medium before taking the
4798 * backup is not necessarily the same thing. */
4799 ComObjPtr<Medium> oldmedium;
4800 oldmedium = pAttach->i_getMedium();
4801
4802 IMedium *iM = aMedium;
4803 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4804 if (aMedium && pMedium.isNull())
4805 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4806
4807 AutoCaller mediumCaller(pMedium);
4808 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4809
4810 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4811 if (pMedium)
4812 {
4813 DeviceType_T mediumType = pAttach->i_getType();
4814 switch (mediumType)
4815 {
4816 case DeviceType_DVD:
4817 case DeviceType_Floppy:
4818 break;
4819
4820 default:
4821 return setError(VBOX_E_INVALID_OBJECT_STATE,
4822 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4823 aControllerPort,
4824 aDevice,
4825 aName.c_str());
4826 }
4827 }
4828
4829 i_setModified(IsModified_Storage);
4830 mMediaData.backup();
4831
4832 {
4833 // The backup operation makes the pAttach reference point to the
4834 // old settings. Re-get the correct reference.
4835 pAttach = i_findAttachment(mMediaData->mAttachments,
4836 Bstr(aName).raw(),
4837 aControllerPort,
4838 aDevice);
4839 if (!oldmedium.isNull())
4840 oldmedium->i_removeBackReference(mData->mUuid);
4841 if (!pMedium.isNull())
4842 {
4843 pMedium->i_addBackReference(mData->mUuid);
4844
4845 mediumLock.release();
4846 multiLock.release();
4847 i_addMediumToRegistry(pMedium);
4848 multiLock.acquire();
4849 mediumLock.acquire();
4850 }
4851
4852 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4853 pAttach->i_updateMedium(pMedium);
4854 }
4855
4856 i_setModified(IsModified_Storage);
4857
4858 mediumLock.release();
4859 multiLock.release();
4860 rc = i_onMediumChange(pAttach, aForce);
4861 multiLock.acquire();
4862 mediumLock.acquire();
4863
4864 /* On error roll back this change only. */
4865 if (FAILED(rc))
4866 {
4867 if (!pMedium.isNull())
4868 pMedium->i_removeBackReference(mData->mUuid);
4869 pAttach = i_findAttachment(mMediaData->mAttachments,
4870 Bstr(aName).raw(),
4871 aControllerPort,
4872 aDevice);
4873 /* If the attachment is gone in the meantime, bail out. */
4874 if (pAttach.isNull())
4875 return rc;
4876 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4877 if (!oldmedium.isNull())
4878 oldmedium->i_addBackReference(mData->mUuid);
4879 pAttach->i_updateMedium(oldmedium);
4880 }
4881
4882 mediumLock.release();
4883 multiLock.release();
4884
4885 /* Save modified registries, but skip this machine as it's the caller's
4886 * job to save its settings like all other settings changes. */
4887 mParent->i_unmarkRegistryModified(i_getId());
4888 mParent->i_saveModifiedRegistries();
4889
4890 return rc;
4891}
4892HRESULT Machine::getMedium(const com::Utf8Str &aName,
4893 LONG aControllerPort,
4894 LONG aDevice,
4895 ComPtr<IMedium> &aMedium)
4896{
4897 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4898 aName.c_str(), aControllerPort, aDevice));
4899
4900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4901
4902 aMedium = NULL;
4903
4904 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4905 Bstr(aName).raw(),
4906 aControllerPort,
4907 aDevice);
4908 if (pAttach.isNull())
4909 return setError(VBOX_E_OBJECT_NOT_FOUND,
4910 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4911 aDevice, aControllerPort, aName.c_str());
4912
4913 aMedium = pAttach->i_getMedium();
4914
4915 return S_OK;
4916}
4917
4918HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4919{
4920
4921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4924
4925 return S_OK;
4926}
4927
4928HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4929{
4930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4931
4932 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4933
4934 return S_OK;
4935}
4936
4937HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4938{
4939 /* Do not assert if slot is out of range, just return the advertised
4940 status. testdriver/vbox.py triggers this in logVmInfo. */
4941 if (aSlot >= mNetworkAdapters.size())
4942 return setError(E_INVALIDARG,
4943 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4944 aSlot, mNetworkAdapters.size());
4945
4946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4947
4948 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4949
4950 return S_OK;
4951}
4952
4953HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4954{
4955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4956
4957 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4958 size_t i = 0;
4959 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4960 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4961 ++it, ++i)
4962 aKeys[i] = it->first;
4963
4964 return S_OK;
4965}
4966
4967 /**
4968 * @note Locks this object for reading.
4969 */
4970HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4971 com::Utf8Str &aValue)
4972{
4973 /* start with nothing found */
4974 aValue = "";
4975
4976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4977
4978 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4979 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4980 // found:
4981 aValue = it->second; // source is a Utf8Str
4982
4983 /* return the result to caller (may be empty) */
4984 return S_OK;
4985}
4986
4987 /**
4988 * @note Locks mParent for writing + this object for writing.
4989 */
4990HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4991{
4992 Utf8Str strOldValue; // empty
4993
4994 // locking note: we only hold the read lock briefly to look up the old value,
4995 // then release it and call the onExtraCanChange callbacks. There is a small
4996 // chance of a race insofar as the callback might be called twice if two callers
4997 // change the same key at the same time, but that's a much better solution
4998 // than the deadlock we had here before. The actual changing of the extradata
4999 // is then performed under the write lock and race-free.
5000
5001 // look up the old value first; if nothing has changed then we need not do anything
5002 {
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5004
5005 // For snapshots don't even think about allowing changes, extradata
5006 // is global for a machine, so there is nothing snapshot specific.
5007 if (i_isSnapshotMachine())
5008 return setError(VBOX_E_INVALID_VM_STATE,
5009 tr("Cannot set extradata for a snapshot"));
5010
5011 // check if the right IMachine instance is used
5012 if (mData->mRegistered && !i_isSessionMachine())
5013 return setError(VBOX_E_INVALID_VM_STATE,
5014 tr("Cannot set extradata for an immutable machine"));
5015
5016 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5017 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5018 strOldValue = it->second;
5019 }
5020
5021 bool fChanged;
5022 if ((fChanged = (strOldValue != aValue)))
5023 {
5024 // ask for permission from all listeners outside the locks;
5025 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5026 // lock to copy the list of callbacks to invoke
5027 Bstr error;
5028 Bstr bstrValue(aValue);
5029
5030 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5031 {
5032 const char *sep = error.isEmpty() ? "" : ": ";
5033 CBSTR err = error.raw();
5034 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5035 return setError(E_ACCESSDENIED,
5036 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5037 aKey.c_str(),
5038 aValue.c_str(),
5039 sep,
5040 err);
5041 }
5042
5043 // data is changing and change not vetoed: then write it out under the lock
5044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5045
5046 if (aValue.isEmpty())
5047 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5048 else
5049 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5050 // creates a new key if needed
5051
5052 bool fNeedsGlobalSaveSettings = false;
5053 // This saving of settings is tricky: there is no "old state" for the
5054 // extradata items at all (unlike all other settings), so the old/new
5055 // settings comparison would give a wrong result!
5056 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5057
5058 if (fNeedsGlobalSaveSettings)
5059 {
5060 // save the global settings; for that we should hold only the VirtualBox lock
5061 alock.release();
5062 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5063 mParent->i_saveSettings();
5064 }
5065 }
5066
5067 // fire notification outside the lock
5068 if (fChanged)
5069 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5070
5071 return S_OK;
5072}
5073
5074HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5075{
5076 aProgress = NULL;
5077 NOREF(aSettingsFilePath);
5078 ReturnComNotImplemented();
5079}
5080
5081HRESULT Machine::saveSettings()
5082{
5083 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5084
5085 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5086 if (FAILED(rc)) return rc;
5087
5088 /* the settings file path may never be null */
5089 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5090
5091 /* save all VM data excluding snapshots */
5092 bool fNeedsGlobalSaveSettings = false;
5093 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5094 mlock.release();
5095
5096 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5097 {
5098 // save the global settings; for that we should hold only the VirtualBox lock
5099 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5100 rc = mParent->i_saveSettings();
5101 }
5102
5103 return rc;
5104}
5105
5106
5107HRESULT Machine::discardSettings()
5108{
5109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5110
5111 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5112 if (FAILED(rc)) return rc;
5113
5114 /*
5115 * during this rollback, the session will be notified if data has
5116 * been actually changed
5117 */
5118 i_rollback(true /* aNotify */);
5119
5120 return S_OK;
5121}
5122
5123/** @note Locks objects! */
5124HRESULT Machine::unregister(AutoCaller &autoCaller,
5125 CleanupMode_T aCleanupMode,
5126 std::vector<ComPtr<IMedium> > &aMedia)
5127{
5128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5129
5130 Guid id(i_getId());
5131
5132 if (mData->mSession.mState != SessionState_Unlocked)
5133 return setError(VBOX_E_INVALID_OBJECT_STATE,
5134 tr("Cannot unregister the machine '%s' while it is locked"),
5135 mUserData->s.strName.c_str());
5136
5137 // wait for state dependents to drop to zero
5138 i_ensureNoStateDependencies();
5139
5140 if (!mData->mAccessible)
5141 {
5142 // inaccessible maschines can only be unregistered; uninitialize ourselves
5143 // here because currently there may be no unregistered that are inaccessible
5144 // (this state combination is not supported). Note releasing the caller and
5145 // leaving the lock before calling uninit()
5146 alock.release();
5147 autoCaller.release();
5148
5149 uninit();
5150
5151 mParent->i_unregisterMachine(this, id);
5152 // calls VirtualBox::i_saveSettings()
5153
5154 return S_OK;
5155 }
5156
5157 HRESULT rc = S_OK;
5158
5159 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5160 // discard saved state
5161 if (mData->mMachineState == MachineState_Saved)
5162 {
5163 // add the saved state file to the list of files the caller should delete
5164 Assert(!mSSData->strStateFilePath.isEmpty());
5165 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5166
5167 mSSData->strStateFilePath.setNull();
5168
5169 // unconditionally set the machine state to powered off, we now
5170 // know no session has locked the machine
5171 mData->mMachineState = MachineState_PoweredOff;
5172 }
5173
5174 size_t cSnapshots = 0;
5175 if (mData->mFirstSnapshot)
5176 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5177 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5178 // fail now before we start detaching media
5179 return setError(VBOX_E_INVALID_OBJECT_STATE,
5180 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5181 mUserData->s.strName.c_str(), cSnapshots);
5182
5183 // This list collects the medium objects from all medium attachments
5184 // which we will detach from the machine and its snapshots, in a specific
5185 // order which allows for closing all media without getting "media in use"
5186 // errors, simply by going through the list from the front to the back:
5187 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5188 // and must be closed before the parent media from the snapshots, or closing the parents
5189 // will fail because they still have children);
5190 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5191 // the root ("first") snapshot of the machine.
5192 MediaList llMedia;
5193
5194 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5195 && mMediaData->mAttachments.size()
5196 )
5197 {
5198 // we have media attachments: detach them all and add the Medium objects to our list
5199 if (aCleanupMode != CleanupMode_UnregisterOnly)
5200 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5201 else
5202 return setError(VBOX_E_INVALID_OBJECT_STATE,
5203 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5204 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5205 }
5206
5207 if (cSnapshots)
5208 {
5209 // add the media from the medium attachments of the snapshots to llMedia
5210 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5211 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5212 // into the children first
5213
5214 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5215 MachineState_T oldState = mData->mMachineState;
5216 mData->mMachineState = MachineState_DeletingSnapshot;
5217
5218 // make a copy of the first snapshot so the refcount does not drop to 0
5219 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5220 // because of the AutoCaller voodoo)
5221 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5222
5223 // GO!
5224 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5225
5226 mData->mMachineState = oldState;
5227 }
5228
5229 if (FAILED(rc))
5230 {
5231 i_rollbackMedia();
5232 return rc;
5233 }
5234
5235 // commit all the media changes made above
5236 i_commitMedia();
5237
5238 mData->mRegistered = false;
5239
5240 // machine lock no longer needed
5241 alock.release();
5242
5243 // return media to caller
5244 size_t i = 0;
5245 aMedia.resize(llMedia.size());
5246 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5247 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5248
5249 mParent->i_unregisterMachine(this, id);
5250 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5251
5252 return S_OK;
5253}
5254
5255/**
5256 * Task record for deleting a machine config.
5257 */
5258class Machine::DeleteConfigTask
5259 : public Machine::Task
5260{
5261public:
5262 DeleteConfigTask(Machine *m,
5263 Progress *p,
5264 const Utf8Str &t,
5265 const RTCList<ComPtr<IMedium> > &llMediums,
5266 const StringsList &llFilesToDelete)
5267 : Task(m, p, t),
5268 m_llMediums(llMediums),
5269 m_llFilesToDelete(llFilesToDelete)
5270 {}
5271
5272private:
5273 void handler()
5274 {
5275 try
5276 {
5277 m_pMachine->i_deleteConfigHandler(*this);
5278 }
5279 catch(...)
5280 {
5281 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5282 }
5283 }
5284
5285 RTCList<ComPtr<IMedium> > m_llMediums;
5286 StringsList m_llFilesToDelete;
5287
5288 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5289};
5290
5291/**
5292 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5293 * SessionMachine::taskHandler().
5294 *
5295 * @note Locks this object for writing.
5296 *
5297 * @param task
5298 * @return
5299 */
5300void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5301{
5302 LogFlowThisFuncEnter();
5303
5304 AutoCaller autoCaller(this);
5305 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5306 if (FAILED(autoCaller.rc()))
5307 {
5308 /* we might have been uninitialized because the session was accidentally
5309 * closed by the client, so don't assert */
5310 HRESULT rc = setError(E_FAIL,
5311 tr("The session has been accidentally closed"));
5312 task.m_pProgress->i_notifyComplete(rc);
5313 LogFlowThisFuncLeave();
5314 return;
5315 }
5316
5317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5318
5319 HRESULT rc = S_OK;
5320
5321 try
5322 {
5323 ULONG uLogHistoryCount = 3;
5324 ComPtr<ISystemProperties> systemProperties;
5325 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5326 if (FAILED(rc)) throw rc;
5327
5328 if (!systemProperties.isNull())
5329 {
5330 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5331 if (FAILED(rc)) throw rc;
5332 }
5333
5334 MachineState_T oldState = mData->mMachineState;
5335 i_setMachineState(MachineState_SettingUp);
5336 alock.release();
5337 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5338 {
5339 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5340 {
5341 AutoCaller mac(pMedium);
5342 if (FAILED(mac.rc())) throw mac.rc();
5343 Utf8Str strLocation = pMedium->i_getLocationFull();
5344 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5345 if (FAILED(rc)) throw rc;
5346 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5347 }
5348 if (pMedium->i_isMediumFormatFile())
5349 {
5350 ComPtr<IProgress> pProgress2;
5351 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5352 if (FAILED(rc)) throw rc;
5353 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5354 if (FAILED(rc)) throw rc;
5355 /* Check the result of the asynchronous process. */
5356 LONG iRc;
5357 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5358 if (FAILED(rc)) throw rc;
5359 /* If the thread of the progress object has an error, then
5360 * retrieve the error info from there, or it'll be lost. */
5361 if (FAILED(iRc))
5362 throw setError(ProgressErrorInfo(pProgress2));
5363 }
5364
5365 /* Close the medium, deliberately without checking the return
5366 * code, and without leaving any trace in the error info, as
5367 * a failure here is a very minor issue, which shouldn't happen
5368 * as above we even managed to delete the medium. */
5369 {
5370 ErrorInfoKeeper eik;
5371 pMedium->Close();
5372 }
5373 }
5374 i_setMachineState(oldState);
5375 alock.acquire();
5376
5377 // delete the files pushed on the task list by Machine::Delete()
5378 // (this includes saved states of the machine and snapshots and
5379 // medium storage files from the IMedium list passed in, and the
5380 // machine XML file)
5381 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5382 while (it != task.m_llFilesToDelete.end())
5383 {
5384 const Utf8Str &strFile = *it;
5385 LogFunc(("Deleting file %s\n", strFile.c_str()));
5386 int vrc = RTFileDelete(strFile.c_str());
5387 if (RT_FAILURE(vrc))
5388 throw setError(VBOX_E_IPRT_ERROR,
5389 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5390
5391 ++it;
5392 if (it == task.m_llFilesToDelete.end())
5393 {
5394 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5395 if (FAILED(rc)) throw rc;
5396 break;
5397 }
5398
5399 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5400 if (FAILED(rc)) throw rc;
5401 }
5402
5403 /* delete the settings only when the file actually exists */
5404 if (mData->pMachineConfigFile->fileExists())
5405 {
5406 /* Delete any backup or uncommitted XML files. Ignore failures.
5407 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5408 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5409 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5410 RTFileDelete(otherXml.c_str());
5411 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5412 RTFileDelete(otherXml.c_str());
5413
5414 /* delete the Logs folder, nothing important should be left
5415 * there (we don't check for errors because the user might have
5416 * some private files there that we don't want to delete) */
5417 Utf8Str logFolder;
5418 getLogFolder(logFolder);
5419 Assert(logFolder.length());
5420 if (RTDirExists(logFolder.c_str()))
5421 {
5422 /* Delete all VBox.log[.N] files from the Logs folder
5423 * (this must be in sync with the rotation logic in
5424 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5425 * files that may have been created by the GUI. */
5426 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5427 logFolder.c_str(), RTPATH_DELIMITER);
5428 RTFileDelete(log.c_str());
5429 log = Utf8StrFmt("%s%cVBox.png",
5430 logFolder.c_str(), RTPATH_DELIMITER);
5431 RTFileDelete(log.c_str());
5432 for (int i = uLogHistoryCount; i > 0; i--)
5433 {
5434 log = Utf8StrFmt("%s%cVBox.log.%d",
5435 logFolder.c_str(), RTPATH_DELIMITER, i);
5436 RTFileDelete(log.c_str());
5437 log = Utf8StrFmt("%s%cVBox.png.%d",
5438 logFolder.c_str(), RTPATH_DELIMITER, i);
5439 RTFileDelete(log.c_str());
5440 }
5441#if defined(RT_OS_WINDOWS)
5442 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5443 RTFileDelete(log.c_str());
5444 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5445 RTFileDelete(log.c_str());
5446#endif
5447
5448 RTDirRemove(logFolder.c_str());
5449 }
5450
5451 /* delete the Snapshots folder, nothing important should be left
5452 * there (we don't check for errors because the user might have
5453 * some private files there that we don't want to delete) */
5454 Utf8Str strFullSnapshotFolder;
5455 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5456 Assert(!strFullSnapshotFolder.isEmpty());
5457 if (RTDirExists(strFullSnapshotFolder.c_str()))
5458 RTDirRemove(strFullSnapshotFolder.c_str());
5459
5460 // delete the directory that contains the settings file, but only
5461 // if it matches the VM name
5462 Utf8Str settingsDir;
5463 if (i_isInOwnDir(&settingsDir))
5464 RTDirRemove(settingsDir.c_str());
5465 }
5466
5467 alock.release();
5468
5469 mParent->i_saveModifiedRegistries();
5470 }
5471 catch (HRESULT aRC) { rc = aRC; }
5472
5473 task.m_pProgress->i_notifyComplete(rc);
5474
5475 LogFlowThisFuncLeave();
5476}
5477
5478HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5479{
5480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5481
5482 HRESULT rc = i_checkStateDependency(MutableStateDep);
5483 if (FAILED(rc)) return rc;
5484
5485 if (mData->mRegistered)
5486 return setError(VBOX_E_INVALID_VM_STATE,
5487 tr("Cannot delete settings of a registered machine"));
5488
5489 // collect files to delete
5490 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5491 if (mData->pMachineConfigFile->fileExists())
5492 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5493
5494 RTCList<ComPtr<IMedium> > llMediums;
5495 for (size_t i = 0; i < aMedia.size(); ++i)
5496 {
5497 IMedium *pIMedium(aMedia[i]);
5498 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5499 if (pMedium.isNull())
5500 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5501 SafeArray<BSTR> ids;
5502 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5503 if (FAILED(rc)) return rc;
5504 /* At this point the medium should not have any back references
5505 * anymore. If it has it is attached to another VM and *must* not
5506 * deleted. */
5507 if (ids.size() < 1)
5508 llMediums.append(pMedium);
5509 }
5510
5511 ComObjPtr<Progress> pProgress;
5512 pProgress.createObject();
5513 rc = pProgress->init(i_getVirtualBox(),
5514 static_cast<IMachine*>(this) /* aInitiator */,
5515 Bstr(tr("Deleting files")).raw(),
5516 true /* fCancellable */,
5517 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5518 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5519 if (FAILED(rc))
5520 return rc;
5521
5522 /* create and start the task on a separate thread (note that it will not
5523 * start working until we release alock) */
5524 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5525 rc = pTask->createThread();
5526 if (FAILED(rc))
5527 return rc;
5528
5529 pProgress.queryInterfaceTo(aProgress.asOutParam());
5530
5531 LogFlowFuncLeave();
5532
5533 return S_OK;
5534}
5535
5536HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5537{
5538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5539
5540 ComObjPtr<Snapshot> pSnapshot;
5541 HRESULT rc;
5542
5543 if (aNameOrId.isEmpty())
5544 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5545 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5546 else
5547 {
5548 Guid uuid(aNameOrId);
5549 if (uuid.isValid())
5550 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5551 else
5552 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5553 }
5554 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5555
5556 return rc;
5557}
5558
5559HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5560{
5561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5562
5563 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5564 if (FAILED(rc)) return rc;
5565
5566 ComObjPtr<SharedFolder> sharedFolder;
5567 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5568 if (SUCCEEDED(rc))
5569 return setError(VBOX_E_OBJECT_IN_USE,
5570 tr("Shared folder named '%s' already exists"),
5571 aName.c_str());
5572
5573 sharedFolder.createObject();
5574 rc = sharedFolder->init(i_getMachine(),
5575 aName,
5576 aHostPath,
5577 !!aWritable,
5578 !!aAutomount,
5579 true /* fFailOnError */);
5580 if (FAILED(rc)) return rc;
5581
5582 i_setModified(IsModified_SharedFolders);
5583 mHWData.backup();
5584 mHWData->mSharedFolders.push_back(sharedFolder);
5585
5586 /* inform the direct session if any */
5587 alock.release();
5588 i_onSharedFolderChange();
5589
5590 return S_OK;
5591}
5592
5593HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5594{
5595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5596
5597 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5598 if (FAILED(rc)) return rc;
5599
5600 ComObjPtr<SharedFolder> sharedFolder;
5601 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5602 if (FAILED(rc)) return rc;
5603
5604 i_setModified(IsModified_SharedFolders);
5605 mHWData.backup();
5606 mHWData->mSharedFolders.remove(sharedFolder);
5607
5608 /* inform the direct session if any */
5609 alock.release();
5610 i_onSharedFolderChange();
5611
5612 return S_OK;
5613}
5614
5615HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5616{
5617 /* start with No */
5618 *aCanShow = FALSE;
5619
5620 ComPtr<IInternalSessionControl> directControl;
5621 {
5622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5623
5624 if (mData->mSession.mState != SessionState_Locked)
5625 return setError(VBOX_E_INVALID_VM_STATE,
5626 tr("Machine is not locked for session (session state: %s)"),
5627 Global::stringifySessionState(mData->mSession.mState));
5628
5629 if (mData->mSession.mLockType == LockType_VM)
5630 directControl = mData->mSession.mDirectControl;
5631 }
5632
5633 /* ignore calls made after #OnSessionEnd() is called */
5634 if (!directControl)
5635 return S_OK;
5636
5637 LONG64 dummy;
5638 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5639}
5640
5641HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5642{
5643 ComPtr<IInternalSessionControl> directControl;
5644 {
5645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5646
5647 if (mData->mSession.mState != SessionState_Locked)
5648 return setError(E_FAIL,
5649 tr("Machine is not locked for session (session state: %s)"),
5650 Global::stringifySessionState(mData->mSession.mState));
5651
5652 if (mData->mSession.mLockType == LockType_VM)
5653 directControl = mData->mSession.mDirectControl;
5654 }
5655
5656 /* ignore calls made after #OnSessionEnd() is called */
5657 if (!directControl)
5658 return S_OK;
5659
5660 BOOL dummy;
5661 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5662}
5663
5664#ifdef VBOX_WITH_GUEST_PROPS
5665/**
5666 * Look up a guest property in VBoxSVC's internal structures.
5667 */
5668HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5669 com::Utf8Str &aValue,
5670 LONG64 *aTimestamp,
5671 com::Utf8Str &aFlags) const
5672{
5673 using namespace guestProp;
5674
5675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5676 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5677
5678 if (it != mHWData->mGuestProperties.end())
5679 {
5680 char szFlags[MAX_FLAGS_LEN + 1];
5681 aValue = it->second.strValue;
5682 *aTimestamp = it->second.mTimestamp;
5683 writeFlags(it->second.mFlags, szFlags);
5684 aFlags = Utf8Str(szFlags);
5685 }
5686
5687 return S_OK;
5688}
5689
5690/**
5691 * Query the VM that a guest property belongs to for the property.
5692 * @returns E_ACCESSDENIED if the VM process is not available or not
5693 * currently handling queries and the lookup should then be done in
5694 * VBoxSVC.
5695 */
5696HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5697 com::Utf8Str &aValue,
5698 LONG64 *aTimestamp,
5699 com::Utf8Str &aFlags) const
5700{
5701 HRESULT rc = S_OK;
5702 BSTR bValue = NULL;
5703 BSTR bFlags = NULL;
5704
5705 ComPtr<IInternalSessionControl> directControl;
5706 {
5707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5708 if (mData->mSession.mLockType == LockType_VM)
5709 directControl = mData->mSession.mDirectControl;
5710 }
5711
5712 /* ignore calls made after #OnSessionEnd() is called */
5713 if (!directControl)
5714 rc = E_ACCESSDENIED;
5715 else
5716 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5717 0 /* accessMode */,
5718 &bValue, aTimestamp, &bFlags);
5719
5720 aValue = bValue;
5721 aFlags = bFlags;
5722
5723 return rc;
5724}
5725#endif // VBOX_WITH_GUEST_PROPS
5726
5727HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5728 com::Utf8Str &aValue,
5729 LONG64 *aTimestamp,
5730 com::Utf8Str &aFlags)
5731{
5732#ifndef VBOX_WITH_GUEST_PROPS
5733 ReturnComNotImplemented();
5734#else // VBOX_WITH_GUEST_PROPS
5735
5736 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5737
5738 if (rc == E_ACCESSDENIED)
5739 /* The VM is not running or the service is not (yet) accessible */
5740 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5741 return rc;
5742#endif // VBOX_WITH_GUEST_PROPS
5743}
5744
5745HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5746{
5747 LONG64 dummyTimestamp;
5748 com::Utf8Str dummyFlags;
5749 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5750 return rc;
5751
5752}
5753HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5754{
5755 com::Utf8Str dummyFlags;
5756 com::Utf8Str dummyValue;
5757 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5758 return rc;
5759}
5760
5761#ifdef VBOX_WITH_GUEST_PROPS
5762/**
5763 * Set a guest property in VBoxSVC's internal structures.
5764 */
5765HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5766 const com::Utf8Str &aFlags, bool fDelete)
5767{
5768 using namespace guestProp;
5769
5770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5771 HRESULT rc = S_OK;
5772
5773 rc = i_checkStateDependency(MutableOrSavedStateDep);
5774 if (FAILED(rc)) return rc;
5775
5776 try
5777 {
5778 uint32_t fFlags = NILFLAG;
5779 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5780 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5781
5782 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5783 if (it == mHWData->mGuestProperties.end())
5784 {
5785 if (!fDelete)
5786 {
5787 i_setModified(IsModified_MachineData);
5788 mHWData.backupEx();
5789
5790 RTTIMESPEC time;
5791 HWData::GuestProperty prop;
5792 prop.strValue = Bstr(aValue).raw();
5793 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5794 prop.mFlags = fFlags;
5795 mHWData->mGuestProperties[aName] = prop;
5796 }
5797 }
5798 else
5799 {
5800 if (it->second.mFlags & (RDONLYHOST))
5801 {
5802 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5803 }
5804 else
5805 {
5806 i_setModified(IsModified_MachineData);
5807 mHWData.backupEx();
5808
5809 /* The backupEx() operation invalidates our iterator,
5810 * so get a new one. */
5811 it = mHWData->mGuestProperties.find(aName);
5812 Assert(it != mHWData->mGuestProperties.end());
5813
5814 if (!fDelete)
5815 {
5816 RTTIMESPEC time;
5817 it->second.strValue = aValue;
5818 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5819 it->second.mFlags = fFlags;
5820 }
5821 else
5822 mHWData->mGuestProperties.erase(it);
5823 }
5824 }
5825
5826 if (SUCCEEDED(rc))
5827 {
5828 alock.release();
5829
5830 mParent->i_onGuestPropertyChange(mData->mUuid,
5831 Bstr(aName).raw(),
5832 Bstr(aValue).raw(),
5833 Bstr(aFlags).raw());
5834 }
5835 }
5836 catch (std::bad_alloc &)
5837 {
5838 rc = E_OUTOFMEMORY;
5839 }
5840
5841 return rc;
5842}
5843
5844/**
5845 * Set a property on the VM that that property belongs to.
5846 * @returns E_ACCESSDENIED if the VM process is not available or not
5847 * currently handling queries and the setting should then be done in
5848 * VBoxSVC.
5849 */
5850HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5851 const com::Utf8Str &aFlags, bool fDelete)
5852{
5853 HRESULT rc;
5854
5855 try
5856 {
5857 ComPtr<IInternalSessionControl> directControl;
5858 {
5859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5860 if (mData->mSession.mLockType == LockType_VM)
5861 directControl = mData->mSession.mDirectControl;
5862 }
5863
5864 BSTR dummy = NULL; /* will not be changed (setter) */
5865 LONG64 dummy64;
5866 if (!directControl)
5867 rc = E_ACCESSDENIED;
5868 else
5869 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5870 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5871 fDelete? 2: 1 /* accessMode */,
5872 &dummy, &dummy64, &dummy);
5873 }
5874 catch (std::bad_alloc &)
5875 {
5876 rc = E_OUTOFMEMORY;
5877 }
5878
5879 return rc;
5880}
5881#endif // VBOX_WITH_GUEST_PROPS
5882
5883HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5884 const com::Utf8Str &aFlags)
5885{
5886#ifndef VBOX_WITH_GUEST_PROPS
5887 ReturnComNotImplemented();
5888#else // VBOX_WITH_GUEST_PROPS
5889 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5890 if (rc == E_ACCESSDENIED)
5891 /* The VM is not running or the service is not (yet) accessible */
5892 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5893 return rc;
5894#endif // VBOX_WITH_GUEST_PROPS
5895}
5896
5897HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5898{
5899 return setGuestProperty(aProperty, aValue, "");
5900}
5901
5902HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5903{
5904#ifndef VBOX_WITH_GUEST_PROPS
5905 ReturnComNotImplemented();
5906#else // VBOX_WITH_GUEST_PROPS
5907 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5908 if (rc == E_ACCESSDENIED)
5909 /* The VM is not running or the service is not (yet) accessible */
5910 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5911 return rc;
5912#endif // VBOX_WITH_GUEST_PROPS
5913}
5914
5915#ifdef VBOX_WITH_GUEST_PROPS
5916/**
5917 * Enumerate the guest properties in VBoxSVC's internal structures.
5918 */
5919HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5920 std::vector<com::Utf8Str> &aNames,
5921 std::vector<com::Utf8Str> &aValues,
5922 std::vector<LONG64> &aTimestamps,
5923 std::vector<com::Utf8Str> &aFlags)
5924{
5925 using namespace guestProp;
5926
5927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5928 Utf8Str strPatterns(aPatterns);
5929
5930 HWData::GuestPropertyMap propMap;
5931
5932 /*
5933 * Look for matching patterns and build up a list.
5934 */
5935 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5936 while (it != mHWData->mGuestProperties.end())
5937 {
5938 if ( strPatterns.isEmpty()
5939 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5940 RTSTR_MAX,
5941 it->first.c_str(),
5942 RTSTR_MAX,
5943 NULL)
5944 )
5945 propMap.insert(*it);
5946 ++it;
5947 }
5948
5949 alock.release();
5950
5951 /*
5952 * And build up the arrays for returning the property information.
5953 */
5954 size_t cEntries = propMap.size();
5955
5956 aNames.resize(cEntries);
5957 aValues.resize(cEntries);
5958 aTimestamps.resize(cEntries);
5959 aFlags.resize(cEntries);
5960
5961 char szFlags[MAX_FLAGS_LEN + 1];
5962 size_t i= 0;
5963 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5964 {
5965 aNames[i] = it->first;
5966 aValues[i] = it->second.strValue;
5967 aTimestamps[i] = it->second.mTimestamp;
5968 writeFlags(it->second.mFlags, szFlags);
5969 aFlags[i] = Utf8Str(szFlags);
5970 }
5971
5972 return S_OK;
5973}
5974
5975/**
5976 * Enumerate the properties managed by a VM.
5977 * @returns E_ACCESSDENIED if the VM process is not available or not
5978 * currently handling queries and the setting should then be done in
5979 * VBoxSVC.
5980 */
5981HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5982 std::vector<com::Utf8Str> &aNames,
5983 std::vector<com::Utf8Str> &aValues,
5984 std::vector<LONG64> &aTimestamps,
5985 std::vector<com::Utf8Str> &aFlags)
5986{
5987 HRESULT rc;
5988 ComPtr<IInternalSessionControl> directControl;
5989 {
5990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5991 if (mData->mSession.mLockType == LockType_VM)
5992 directControl = mData->mSession.mDirectControl;
5993 }
5994
5995 com::SafeArray<BSTR> bNames;
5996 com::SafeArray<BSTR> bValues;
5997 com::SafeArray<LONG64> bTimestamps;
5998 com::SafeArray<BSTR> bFlags;
5999
6000 if (!directControl)
6001 rc = E_ACCESSDENIED;
6002 else
6003 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6004 ComSafeArrayAsOutParam(bNames),
6005 ComSafeArrayAsOutParam(bValues),
6006 ComSafeArrayAsOutParam(bTimestamps),
6007 ComSafeArrayAsOutParam(bFlags));
6008 size_t i;
6009 aNames.resize(bNames.size());
6010 for (i = 0; i < bNames.size(); ++i)
6011 aNames[i] = Utf8Str(bNames[i]);
6012 aValues.resize(bValues.size());
6013 for (i = 0; i < bValues.size(); ++i)
6014 aValues[i] = Utf8Str(bValues[i]);
6015 aTimestamps.resize(bTimestamps.size());
6016 for (i = 0; i < bTimestamps.size(); ++i)
6017 aTimestamps[i] = bTimestamps[i];
6018 aFlags.resize(bFlags.size());
6019 for (i = 0; i < bFlags.size(); ++i)
6020 aFlags[i] = Utf8Str(bFlags[i]);
6021
6022 return rc;
6023}
6024#endif // VBOX_WITH_GUEST_PROPS
6025HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6026 std::vector<com::Utf8Str> &aNames,
6027 std::vector<com::Utf8Str> &aValues,
6028 std::vector<LONG64> &aTimestamps,
6029 std::vector<com::Utf8Str> &aFlags)
6030{
6031#ifndef VBOX_WITH_GUEST_PROPS
6032 ReturnComNotImplemented();
6033#else // VBOX_WITH_GUEST_PROPS
6034
6035 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6036
6037 if (rc == E_ACCESSDENIED)
6038 /* The VM is not running or the service is not (yet) accessible */
6039 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6040 return rc;
6041#endif // VBOX_WITH_GUEST_PROPS
6042}
6043
6044HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6045 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6046{
6047 MediaData::AttachmentList atts;
6048
6049 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6050 if (FAILED(rc)) return rc;
6051
6052 size_t i = 0;
6053 aMediumAttachments.resize(atts.size());
6054 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6055 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6056
6057 return S_OK;
6058}
6059
6060HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6061 LONG aControllerPort,
6062 LONG aDevice,
6063 ComPtr<IMediumAttachment> &aAttachment)
6064{
6065 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6066 aName.c_str(), aControllerPort, aDevice));
6067
6068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6069
6070 aAttachment = NULL;
6071
6072 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6073 Bstr(aName).raw(),
6074 aControllerPort,
6075 aDevice);
6076 if (pAttach.isNull())
6077 return setError(VBOX_E_OBJECT_NOT_FOUND,
6078 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6079 aDevice, aControllerPort, aName.c_str());
6080
6081 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6082
6083 return S_OK;
6084}
6085
6086
6087HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6088 StorageBus_T aConnectionType,
6089 ComPtr<IStorageController> &aController)
6090{
6091 if ( (aConnectionType <= StorageBus_Null)
6092 || (aConnectionType > StorageBus_PCIe))
6093 return setError(E_INVALIDARG,
6094 tr("Invalid connection type: %d"),
6095 aConnectionType);
6096
6097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6098
6099 HRESULT rc = i_checkStateDependency(MutableStateDep);
6100 if (FAILED(rc)) return rc;
6101
6102 /* try to find one with the name first. */
6103 ComObjPtr<StorageController> ctrl;
6104
6105 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6106 if (SUCCEEDED(rc))
6107 return setError(VBOX_E_OBJECT_IN_USE,
6108 tr("Storage controller named '%s' already exists"),
6109 aName.c_str());
6110
6111 ctrl.createObject();
6112
6113 /* get a new instance number for the storage controller */
6114 ULONG ulInstance = 0;
6115 bool fBootable = true;
6116 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6117 it != mStorageControllers->end();
6118 ++it)
6119 {
6120 if ((*it)->i_getStorageBus() == aConnectionType)
6121 {
6122 ULONG ulCurInst = (*it)->i_getInstance();
6123
6124 if (ulCurInst >= ulInstance)
6125 ulInstance = ulCurInst + 1;
6126
6127 /* Only one controller of each type can be marked as bootable. */
6128 if ((*it)->i_getBootable())
6129 fBootable = false;
6130 }
6131 }
6132
6133 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6134 if (FAILED(rc)) return rc;
6135
6136 i_setModified(IsModified_Storage);
6137 mStorageControllers.backup();
6138 mStorageControllers->push_back(ctrl);
6139
6140 ctrl.queryInterfaceTo(aController.asOutParam());
6141
6142 /* inform the direct session if any */
6143 alock.release();
6144 i_onStorageControllerChange();
6145
6146 return S_OK;
6147}
6148
6149HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6150 ComPtr<IStorageController> &aStorageController)
6151{
6152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6153
6154 ComObjPtr<StorageController> ctrl;
6155
6156 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6157 if (SUCCEEDED(rc))
6158 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6159
6160 return rc;
6161}
6162
6163HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6164 ULONG aInstance,
6165 ComPtr<IStorageController> &aStorageController)
6166{
6167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6168
6169 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6170 it != mStorageControllers->end();
6171 ++it)
6172 {
6173 if ( (*it)->i_getStorageBus() == aConnectionType
6174 && (*it)->i_getInstance() == aInstance)
6175 {
6176 (*it).queryInterfaceTo(aStorageController.asOutParam());
6177 return S_OK;
6178 }
6179 }
6180
6181 return setError(VBOX_E_OBJECT_NOT_FOUND,
6182 tr("Could not find a storage controller with instance number '%lu'"),
6183 aInstance);
6184}
6185
6186HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6187{
6188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6189
6190 HRESULT rc = i_checkStateDependency(MutableStateDep);
6191 if (FAILED(rc)) return rc;
6192
6193 ComObjPtr<StorageController> ctrl;
6194
6195 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6196 if (SUCCEEDED(rc))
6197 {
6198 /* Ensure that only one controller of each type is marked as bootable. */
6199 if (aBootable == TRUE)
6200 {
6201 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6202 it != mStorageControllers->end();
6203 ++it)
6204 {
6205 ComObjPtr<StorageController> aCtrl = (*it);
6206
6207 if ( (aCtrl->i_getName() != aName)
6208 && aCtrl->i_getBootable() == TRUE
6209 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6210 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6211 {
6212 aCtrl->i_setBootable(FALSE);
6213 break;
6214 }
6215 }
6216 }
6217
6218 if (SUCCEEDED(rc))
6219 {
6220 ctrl->i_setBootable(aBootable);
6221 i_setModified(IsModified_Storage);
6222 }
6223 }
6224
6225 if (SUCCEEDED(rc))
6226 {
6227 /* inform the direct session if any */
6228 alock.release();
6229 i_onStorageControllerChange();
6230 }
6231
6232 return rc;
6233}
6234
6235HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6236{
6237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6238
6239 HRESULT rc = i_checkStateDependency(MutableStateDep);
6240 if (FAILED(rc)) return rc;
6241
6242 ComObjPtr<StorageController> ctrl;
6243 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6244 if (FAILED(rc)) return rc;
6245
6246 {
6247 /* find all attached devices to the appropriate storage controller and detach them all */
6248 // make a temporary list because detachDevice invalidates iterators into
6249 // mMediaData->mAttachments
6250 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6251
6252 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6253 it != llAttachments2.end();
6254 ++it)
6255 {
6256 MediumAttachment *pAttachTemp = *it;
6257
6258 AutoCaller localAutoCaller(pAttachTemp);
6259 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6260
6261 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6262
6263 if (pAttachTemp->i_getControllerName() == aName)
6264 {
6265 rc = i_detachDevice(pAttachTemp, alock, NULL);
6266 if (FAILED(rc)) return rc;
6267 }
6268 }
6269 }
6270
6271 /* We can remove it now. */
6272 i_setModified(IsModified_Storage);
6273 mStorageControllers.backup();
6274
6275 ctrl->i_unshare();
6276
6277 mStorageControllers->remove(ctrl);
6278
6279 /* inform the direct session if any */
6280 alock.release();
6281 i_onStorageControllerChange();
6282
6283 return S_OK;
6284}
6285
6286HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6287 ComPtr<IUSBController> &aController)
6288{
6289 if ( (aType <= USBControllerType_Null)
6290 || (aType >= USBControllerType_Last))
6291 return setError(E_INVALIDARG,
6292 tr("Invalid USB controller type: %d"),
6293 aType);
6294
6295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6296
6297 HRESULT rc = i_checkStateDependency(MutableStateDep);
6298 if (FAILED(rc)) return rc;
6299
6300 /* try to find one with the same type first. */
6301 ComObjPtr<USBController> ctrl;
6302
6303 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6304 if (SUCCEEDED(rc))
6305 return setError(VBOX_E_OBJECT_IN_USE,
6306 tr("USB controller named '%s' already exists"),
6307 aName.c_str());
6308
6309 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6310 ULONG maxInstances;
6311 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6312 if (FAILED(rc))
6313 return rc;
6314
6315 ULONG cInstances = i_getUSBControllerCountByType(aType);
6316 if (cInstances >= maxInstances)
6317 return setError(E_INVALIDARG,
6318 tr("Too many USB controllers of this type"));
6319
6320 ctrl.createObject();
6321
6322 rc = ctrl->init(this, aName, aType);
6323 if (FAILED(rc)) return rc;
6324
6325 i_setModified(IsModified_USB);
6326 mUSBControllers.backup();
6327 mUSBControllers->push_back(ctrl);
6328
6329 ctrl.queryInterfaceTo(aController.asOutParam());
6330
6331 /* inform the direct session if any */
6332 alock.release();
6333 i_onUSBControllerChange();
6334
6335 return S_OK;
6336}
6337
6338HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6339{
6340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6341
6342 ComObjPtr<USBController> ctrl;
6343
6344 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6345 if (SUCCEEDED(rc))
6346 ctrl.queryInterfaceTo(aController.asOutParam());
6347
6348 return rc;
6349}
6350
6351HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6352 ULONG *aControllers)
6353{
6354 if ( (aType <= USBControllerType_Null)
6355 || (aType >= USBControllerType_Last))
6356 return setError(E_INVALIDARG,
6357 tr("Invalid USB controller type: %d"),
6358 aType);
6359
6360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 ComObjPtr<USBController> ctrl;
6363
6364 *aControllers = i_getUSBControllerCountByType(aType);
6365
6366 return S_OK;
6367}
6368
6369HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6370{
6371
6372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6373
6374 HRESULT rc = i_checkStateDependency(MutableStateDep);
6375 if (FAILED(rc)) return rc;
6376
6377 ComObjPtr<USBController> ctrl;
6378 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6379 if (FAILED(rc)) return rc;
6380
6381 i_setModified(IsModified_USB);
6382 mUSBControllers.backup();
6383
6384 ctrl->i_unshare();
6385
6386 mUSBControllers->remove(ctrl);
6387
6388 /* inform the direct session if any */
6389 alock.release();
6390 i_onUSBControllerChange();
6391
6392 return S_OK;
6393}
6394
6395HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6396 ULONG *aOriginX,
6397 ULONG *aOriginY,
6398 ULONG *aWidth,
6399 ULONG *aHeight,
6400 BOOL *aEnabled)
6401{
6402 uint32_t u32OriginX= 0;
6403 uint32_t u32OriginY= 0;
6404 uint32_t u32Width = 0;
6405 uint32_t u32Height = 0;
6406 uint16_t u16Flags = 0;
6407
6408 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6409 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6410 if (RT_FAILURE(vrc))
6411 {
6412#ifdef RT_OS_WINDOWS
6413 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6414 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6415 * So just assign fEnable to TRUE again.
6416 * The right fix would be to change GUI API wrappers to make sure that parameters
6417 * are changed only if API succeeds.
6418 */
6419 *aEnabled = TRUE;
6420#endif
6421 return setError(VBOX_E_IPRT_ERROR,
6422 tr("Saved guest size is not available (%Rrc)"),
6423 vrc);
6424 }
6425
6426 *aOriginX = u32OriginX;
6427 *aOriginY = u32OriginY;
6428 *aWidth = u32Width;
6429 *aHeight = u32Height;
6430 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6431
6432 return S_OK;
6433}
6434
6435HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6436 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6437{
6438 if (aScreenId != 0)
6439 return E_NOTIMPL;
6440
6441 if ( aBitmapFormat != BitmapFormat_BGR0
6442 && aBitmapFormat != BitmapFormat_BGRA
6443 && aBitmapFormat != BitmapFormat_RGBA
6444 && aBitmapFormat != BitmapFormat_PNG)
6445 return setError(E_NOTIMPL,
6446 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6447
6448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6449
6450 uint8_t *pu8Data = NULL;
6451 uint32_t cbData = 0;
6452 uint32_t u32Width = 0;
6453 uint32_t u32Height = 0;
6454
6455 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6456
6457 if (RT_FAILURE(vrc))
6458 return setError(VBOX_E_IPRT_ERROR,
6459 tr("Saved thumbnail data is not available (%Rrc)"),
6460 vrc);
6461
6462 HRESULT hr = S_OK;
6463
6464 *aWidth = u32Width;
6465 *aHeight = u32Height;
6466
6467 if (cbData > 0)
6468 {
6469 /* Convert pixels to the format expected by the API caller. */
6470 if (aBitmapFormat == BitmapFormat_BGR0)
6471 {
6472 /* [0] B, [1] G, [2] R, [3] 0. */
6473 aData.resize(cbData);
6474 memcpy(&aData.front(), pu8Data, cbData);
6475 }
6476 else if (aBitmapFormat == BitmapFormat_BGRA)
6477 {
6478 /* [0] B, [1] G, [2] R, [3] A. */
6479 aData.resize(cbData);
6480 for (uint32_t i = 0; i < cbData; i += 4)
6481 {
6482 aData[i] = pu8Data[i];
6483 aData[i + 1] = pu8Data[i + 1];
6484 aData[i + 2] = pu8Data[i + 2];
6485 aData[i + 3] = 0xff;
6486 }
6487 }
6488 else if (aBitmapFormat == BitmapFormat_RGBA)
6489 {
6490 /* [0] R, [1] G, [2] B, [3] A. */
6491 aData.resize(cbData);
6492 for (uint32_t i = 0; i < cbData; i += 4)
6493 {
6494 aData[i] = pu8Data[i + 2];
6495 aData[i + 1] = pu8Data[i + 1];
6496 aData[i + 2] = pu8Data[i];
6497 aData[i + 3] = 0xff;
6498 }
6499 }
6500 else if (aBitmapFormat == BitmapFormat_PNG)
6501 {
6502 uint8_t *pu8PNG = NULL;
6503 uint32_t cbPNG = 0;
6504 uint32_t cxPNG = 0;
6505 uint32_t cyPNG = 0;
6506
6507 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6508
6509 if (RT_SUCCESS(vrc))
6510 {
6511 aData.resize(cbPNG);
6512 if (cbPNG)
6513 memcpy(&aData.front(), pu8PNG, cbPNG);
6514 }
6515 else
6516 hr = setError(VBOX_E_IPRT_ERROR,
6517 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6518 vrc);
6519
6520 RTMemFree(pu8PNG);
6521 }
6522 }
6523
6524 freeSavedDisplayScreenshot(pu8Data);
6525
6526 return hr;
6527}
6528
6529HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6530 ULONG *aWidth,
6531 ULONG *aHeight,
6532 std::vector<BitmapFormat_T> &aBitmapFormats)
6533{
6534 if (aScreenId != 0)
6535 return E_NOTIMPL;
6536
6537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 uint8_t *pu8Data = NULL;
6540 uint32_t cbData = 0;
6541 uint32_t u32Width = 0;
6542 uint32_t u32Height = 0;
6543
6544 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6545
6546 if (RT_FAILURE(vrc))
6547 return setError(VBOX_E_IPRT_ERROR,
6548 tr("Saved screenshot data is not available (%Rrc)"),
6549 vrc);
6550
6551 *aWidth = u32Width;
6552 *aHeight = u32Height;
6553 aBitmapFormats.resize(1);
6554 aBitmapFormats[0] = BitmapFormat_PNG;
6555
6556 freeSavedDisplayScreenshot(pu8Data);
6557
6558 return S_OK;
6559}
6560
6561HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6562 BitmapFormat_T aBitmapFormat,
6563 ULONG *aWidth,
6564 ULONG *aHeight,
6565 std::vector<BYTE> &aData)
6566{
6567 if (aScreenId != 0)
6568 return E_NOTIMPL;
6569
6570 if (aBitmapFormat != BitmapFormat_PNG)
6571 return E_NOTIMPL;
6572
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 uint8_t *pu8Data = NULL;
6576 uint32_t cbData = 0;
6577 uint32_t u32Width = 0;
6578 uint32_t u32Height = 0;
6579
6580 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6581
6582 if (RT_FAILURE(vrc))
6583 return setError(VBOX_E_IPRT_ERROR,
6584 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6585 vrc);
6586
6587 *aWidth = u32Width;
6588 *aHeight = u32Height;
6589
6590 aData.resize(cbData);
6591 if (cbData)
6592 memcpy(&aData.front(), pu8Data, cbData);
6593
6594 freeSavedDisplayScreenshot(pu8Data);
6595
6596 return S_OK;
6597}
6598
6599HRESULT Machine::hotPlugCPU(ULONG aCpu)
6600{
6601 HRESULT rc = S_OK;
6602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6603
6604 if (!mHWData->mCPUHotPlugEnabled)
6605 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6606
6607 if (aCpu >= mHWData->mCPUCount)
6608 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6609
6610 if (mHWData->mCPUAttached[aCpu])
6611 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6612
6613 alock.release();
6614 rc = i_onCPUChange(aCpu, false);
6615 alock.acquire();
6616 if (FAILED(rc)) return rc;
6617
6618 i_setModified(IsModified_MachineData);
6619 mHWData.backup();
6620 mHWData->mCPUAttached[aCpu] = true;
6621
6622 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6623 if (Global::IsOnline(mData->mMachineState))
6624 i_saveSettings(NULL);
6625
6626 return S_OK;
6627}
6628
6629HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6630{
6631 HRESULT rc = S_OK;
6632
6633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 if (!mHWData->mCPUHotPlugEnabled)
6636 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6637
6638 if (aCpu >= SchemaDefs::MaxCPUCount)
6639 return setError(E_INVALIDARG,
6640 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6641 SchemaDefs::MaxCPUCount);
6642
6643 if (!mHWData->mCPUAttached[aCpu])
6644 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6645
6646 /* CPU 0 can't be detached */
6647 if (aCpu == 0)
6648 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6649
6650 alock.release();
6651 rc = i_onCPUChange(aCpu, true);
6652 alock.acquire();
6653 if (FAILED(rc)) return rc;
6654
6655 i_setModified(IsModified_MachineData);
6656 mHWData.backup();
6657 mHWData->mCPUAttached[aCpu] = false;
6658
6659 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6660 if (Global::IsOnline(mData->mMachineState))
6661 i_saveSettings(NULL);
6662
6663 return S_OK;
6664}
6665
6666HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6667{
6668 *aAttached = false;
6669
6670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6671
6672 /* If hotplug is enabled the CPU is always enabled. */
6673 if (!mHWData->mCPUHotPlugEnabled)
6674 {
6675 if (aCpu < mHWData->mCPUCount)
6676 *aAttached = true;
6677 }
6678 else
6679 {
6680 if (aCpu < SchemaDefs::MaxCPUCount)
6681 *aAttached = mHWData->mCPUAttached[aCpu];
6682 }
6683
6684 return S_OK;
6685}
6686
6687HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6688{
6689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6690
6691 Utf8Str log = i_getLogFilename(aIdx);
6692 if (!RTFileExists(log.c_str()))
6693 log.setNull();
6694 aFilename = log;
6695
6696 return S_OK;
6697}
6698
6699HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6700{
6701 if (aSize < 0)
6702 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6703
6704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 HRESULT rc = S_OK;
6707 Utf8Str log = i_getLogFilename(aIdx);
6708
6709 /* do not unnecessarily hold the lock while doing something which does
6710 * not need the lock and potentially takes a long time. */
6711 alock.release();
6712
6713 /* Limit the chunk size to 32K for now, as that gives better performance
6714 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6715 * One byte expands to approx. 25 bytes of breathtaking XML. */
6716 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6717 aData.resize(cbData);
6718
6719 RTFILE LogFile;
6720 int vrc = RTFileOpen(&LogFile, log.c_str(),
6721 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6722 if (RT_SUCCESS(vrc))
6723 {
6724 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6725 if (RT_SUCCESS(vrc))
6726 aData.resize(cbData);
6727 else
6728 rc = setError(VBOX_E_IPRT_ERROR,
6729 tr("Could not read log file '%s' (%Rrc)"),
6730 log.c_str(), vrc);
6731 RTFileClose(LogFile);
6732 }
6733 else
6734 rc = setError(VBOX_E_IPRT_ERROR,
6735 tr("Could not open log file '%s' (%Rrc)"),
6736 log.c_str(), vrc);
6737
6738 if (FAILED(rc))
6739 aData.resize(0);
6740
6741 return rc;
6742}
6743
6744
6745/**
6746 * Currently this method doesn't attach device to the running VM,
6747 * just makes sure it's plugged on next VM start.
6748 */
6749HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6750{
6751 // lock scope
6752 {
6753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 HRESULT rc = i_checkStateDependency(MutableStateDep);
6756 if (FAILED(rc)) return rc;
6757
6758 ChipsetType_T aChipset = ChipsetType_PIIX3;
6759 COMGETTER(ChipsetType)(&aChipset);
6760
6761 if (aChipset != ChipsetType_ICH9)
6762 {
6763 return setError(E_INVALIDARG,
6764 tr("Host PCI attachment only supported with ICH9 chipset"));
6765 }
6766
6767 // check if device with this host PCI address already attached
6768 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6769 it != mHWData->mPCIDeviceAssignments.end();
6770 ++it)
6771 {
6772 LONG iHostAddress = -1;
6773 ComPtr<PCIDeviceAttachment> pAttach;
6774 pAttach = *it;
6775 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6776 if (iHostAddress == aHostAddress)
6777 return setError(E_INVALIDARG,
6778 tr("Device with host PCI address already attached to this VM"));
6779 }
6780
6781 ComObjPtr<PCIDeviceAttachment> pda;
6782 char name[32];
6783
6784 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6785 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6786 Bstr bname(name);
6787 pda.createObject();
6788 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6789 i_setModified(IsModified_MachineData);
6790 mHWData.backup();
6791 mHWData->mPCIDeviceAssignments.push_back(pda);
6792 }
6793
6794 return S_OK;
6795}
6796
6797/**
6798 * Currently this method doesn't detach device from the running VM,
6799 * just makes sure it's not plugged on next VM start.
6800 */
6801HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6802{
6803 ComObjPtr<PCIDeviceAttachment> pAttach;
6804 bool fRemoved = false;
6805 HRESULT rc;
6806
6807 // lock scope
6808 {
6809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6810
6811 rc = i_checkStateDependency(MutableStateDep);
6812 if (FAILED(rc)) return rc;
6813
6814 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6815 it != mHWData->mPCIDeviceAssignments.end();
6816 ++it)
6817 {
6818 LONG iHostAddress = -1;
6819 pAttach = *it;
6820 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6821 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6822 {
6823 i_setModified(IsModified_MachineData);
6824 mHWData.backup();
6825 mHWData->mPCIDeviceAssignments.remove(pAttach);
6826 fRemoved = true;
6827 break;
6828 }
6829 }
6830 }
6831
6832
6833 /* Fire event outside of the lock */
6834 if (fRemoved)
6835 {
6836 Assert(!pAttach.isNull());
6837 ComPtr<IEventSource> es;
6838 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6839 Assert(SUCCEEDED(rc));
6840 Bstr mid;
6841 rc = this->COMGETTER(Id)(mid.asOutParam());
6842 Assert(SUCCEEDED(rc));
6843 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6844 }
6845
6846 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6847 tr("No host PCI device %08x attached"),
6848 aHostAddress
6849 );
6850}
6851
6852HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6853{
6854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6855
6856 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6857
6858 size_t i = 0;
6859 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6860 it != mHWData->mPCIDeviceAssignments.end();
6861 ++i, ++it)
6862 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6863
6864 return S_OK;
6865}
6866
6867HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6868{
6869 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6870
6871 return S_OK;
6872}
6873
6874HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6875{
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6879
6880 return S_OK;
6881}
6882
6883HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6884{
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6887 if (SUCCEEDED(hrc))
6888 {
6889 hrc = mHWData.backupEx();
6890 if (SUCCEEDED(hrc))
6891 {
6892 i_setModified(IsModified_MachineData);
6893 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6894 }
6895 }
6896 return hrc;
6897}
6898
6899HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6900{
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6903 return S_OK;
6904}
6905
6906HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6907{
6908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6909 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6910 if (SUCCEEDED(hrc))
6911 {
6912 hrc = mHWData.backupEx();
6913 if (SUCCEEDED(hrc))
6914 {
6915 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6916 if (SUCCEEDED(hrc))
6917 i_setModified(IsModified_MachineData);
6918 }
6919 }
6920 return hrc;
6921}
6922
6923HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6924{
6925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6926
6927 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6928
6929 return S_OK;
6930}
6931
6932HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6933{
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6936 if (SUCCEEDED(hrc))
6937 {
6938 hrc = mHWData.backupEx();
6939 if (SUCCEEDED(hrc))
6940 {
6941 i_setModified(IsModified_MachineData);
6942 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6943 }
6944 }
6945 return hrc;
6946}
6947
6948HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6949{
6950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6951
6952 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6953
6954 return S_OK;
6955}
6956
6957HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6958{
6959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6960
6961 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6962 if ( SUCCEEDED(hrc)
6963 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6964 {
6965 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6966 int vrc;
6967
6968 if (aAutostartEnabled)
6969 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6970 else
6971 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6972
6973 if (RT_SUCCESS(vrc))
6974 {
6975 hrc = mHWData.backupEx();
6976 if (SUCCEEDED(hrc))
6977 {
6978 i_setModified(IsModified_MachineData);
6979 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6980 }
6981 }
6982 else if (vrc == VERR_NOT_SUPPORTED)
6983 hrc = setError(VBOX_E_NOT_SUPPORTED,
6984 tr("The VM autostart feature is not supported on this platform"));
6985 else if (vrc == VERR_PATH_NOT_FOUND)
6986 hrc = setError(E_FAIL,
6987 tr("The path to the autostart database is not set"));
6988 else
6989 hrc = setError(E_UNEXPECTED,
6990 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6991 aAutostartEnabled ? "Adding" : "Removing",
6992 mUserData->s.strName.c_str(), vrc);
6993 }
6994 return hrc;
6995}
6996
6997HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6998{
6999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7000
7001 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7002
7003 return S_OK;
7004}
7005
7006HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7007{
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7010 if (SUCCEEDED(hrc))
7011 {
7012 hrc = mHWData.backupEx();
7013 if (SUCCEEDED(hrc))
7014 {
7015 i_setModified(IsModified_MachineData);
7016 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7017 }
7018 }
7019 return hrc;
7020}
7021
7022HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7023{
7024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7025
7026 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7027
7028 return S_OK;
7029}
7030
7031HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7032{
7033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7034 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7035 if ( SUCCEEDED(hrc)
7036 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7037 {
7038 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7039 int vrc;
7040
7041 if (aAutostopType != AutostopType_Disabled)
7042 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7043 else
7044 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7045
7046 if (RT_SUCCESS(vrc))
7047 {
7048 hrc = mHWData.backupEx();
7049 if (SUCCEEDED(hrc))
7050 {
7051 i_setModified(IsModified_MachineData);
7052 mHWData->mAutostart.enmAutostopType = aAutostopType;
7053 }
7054 }
7055 else if (vrc == VERR_NOT_SUPPORTED)
7056 hrc = setError(VBOX_E_NOT_SUPPORTED,
7057 tr("The VM autostop feature is not supported on this platform"));
7058 else if (vrc == VERR_PATH_NOT_FOUND)
7059 hrc = setError(E_FAIL,
7060 tr("The path to the autostart database is not set"));
7061 else
7062 hrc = setError(E_UNEXPECTED,
7063 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7064 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7065 mUserData->s.strName.c_str(), vrc);
7066 }
7067 return hrc;
7068}
7069
7070HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7071{
7072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7073
7074 aDefaultFrontend = mHWData->mDefaultFrontend;
7075
7076 return S_OK;
7077}
7078
7079HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7080{
7081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7082 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7083 if (SUCCEEDED(hrc))
7084 {
7085 hrc = mHWData.backupEx();
7086 if (SUCCEEDED(hrc))
7087 {
7088 i_setModified(IsModified_MachineData);
7089 mHWData->mDefaultFrontend = aDefaultFrontend;
7090 }
7091 }
7092 return hrc;
7093}
7094
7095HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7096{
7097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7098 size_t cbIcon = mUserData->s.ovIcon.size();
7099 aIcon.resize(cbIcon);
7100 if (cbIcon)
7101 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7102 return S_OK;
7103}
7104
7105HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7106{
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7109 if (SUCCEEDED(hrc))
7110 {
7111 i_setModified(IsModified_MachineData);
7112 mUserData.backup();
7113 size_t cbIcon = aIcon.size();
7114 mUserData->s.ovIcon.resize(cbIcon);
7115 if (cbIcon)
7116 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7117 }
7118 return hrc;
7119}
7120
7121HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7122{
7123#ifdef VBOX_WITH_USB
7124 *aUSBProxyAvailable = true;
7125#else
7126 *aUSBProxyAvailable = false;
7127#endif
7128 return S_OK;
7129}
7130
7131HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7132{
7133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 aVMProcessPriority = mUserData->s.strVMPriority;
7136
7137 return S_OK;
7138}
7139
7140HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7141{
7142 RT_NOREF(aVMProcessPriority);
7143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7144 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7145 if (SUCCEEDED(hrc))
7146 {
7147 /** @todo r=klaus: currently this is marked as not implemented, as
7148 * the code for setting the priority of the process is not there
7149 * (neither when starting the VM nor at runtime). */
7150 ReturnComNotImplemented();
7151#if 0
7152 hrc = mUserData.backupEx();
7153 if (SUCCEEDED(hrc))
7154 {
7155 i_setModified(IsModified_MachineData);
7156 mUserData->s.strVMPriority = aVMProcessPriority;
7157 }
7158#endif
7159 }
7160 return hrc;
7161}
7162
7163HRESULT Machine::getUnattended(ComPtr<IUnattended> &aUnattended)
7164{
7165#ifdef VBOX_WITH_UNATTENDED
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 aUnattended = mUnattended;
7169
7170 return S_OK;
7171#else
7172 NOREF(aUnattended);
7173 return E_NOTIMPL;
7174#endif
7175}
7176
7177HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7178 ComPtr<IProgress> &aProgress)
7179{
7180 ComObjPtr<Progress> pP;
7181 Progress *ppP = pP;
7182 IProgress *iP = static_cast<IProgress *>(ppP);
7183 IProgress **pProgress = &iP;
7184
7185 IMachine *pTarget = aTarget;
7186
7187 /* Convert the options. */
7188 RTCList<CloneOptions_T> optList;
7189 if (aOptions.size())
7190 for (size_t i = 0; i < aOptions.size(); ++i)
7191 optList.append(aOptions[i]);
7192
7193 if (optList.contains(CloneOptions_Link))
7194 {
7195 if (!i_isSnapshotMachine())
7196 return setError(E_INVALIDARG,
7197 tr("Linked clone can only be created from a snapshot"));
7198 if (aMode != CloneMode_MachineState)
7199 return setError(E_INVALIDARG,
7200 tr("Linked clone can only be created for a single machine state"));
7201 }
7202 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7203
7204 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7205
7206 HRESULT rc = pWorker->start(pProgress);
7207
7208 pP = static_cast<Progress *>(*pProgress);
7209 pP.queryInterfaceTo(aProgress.asOutParam());
7210
7211 return rc;
7212
7213}
7214
7215HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7216{
7217 NOREF(aProgress);
7218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7219
7220 // This check should always fail.
7221 HRESULT rc = i_checkStateDependency(MutableStateDep);
7222 if (FAILED(rc)) return rc;
7223
7224 AssertFailedReturn(E_NOTIMPL);
7225}
7226
7227HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7228{
7229 NOREF(aSavedStateFile);
7230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7231
7232 // This check should always fail.
7233 HRESULT rc = i_checkStateDependency(MutableStateDep);
7234 if (FAILED(rc)) return rc;
7235
7236 AssertFailedReturn(E_NOTIMPL);
7237}
7238
7239HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7240{
7241 NOREF(aFRemoveFile);
7242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7243
7244 // This check should always fail.
7245 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7246 if (FAILED(rc)) return rc;
7247
7248 AssertFailedReturn(E_NOTIMPL);
7249}
7250
7251// public methods for internal purposes
7252/////////////////////////////////////////////////////////////////////////////
7253
7254/**
7255 * Adds the given IsModified_* flag to the dirty flags of the machine.
7256 * This must be called either during i_loadSettings or under the machine write lock.
7257 * @param fl
7258 */
7259void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7260{
7261 mData->flModifications |= fl;
7262 if (fAllowStateModification && i_isStateModificationAllowed())
7263 mData->mCurrentStateModified = true;
7264}
7265
7266/**
7267 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7268 * care of the write locking.
7269 *
7270 * @param fModifications The flag to add.
7271 */
7272void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7273{
7274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7275 i_setModified(fModification, fAllowStateModification);
7276}
7277
7278/**
7279 * Saves the registry entry of this machine to the given configuration node.
7280 *
7281 * @param aEntryNode Node to save the registry entry to.
7282 *
7283 * @note locks this object for reading.
7284 */
7285HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7286{
7287 AutoLimitedCaller autoCaller(this);
7288 AssertComRCReturnRC(autoCaller.rc());
7289
7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7291
7292 data.uuid = mData->mUuid;
7293 data.strSettingsFile = mData->m_strConfigFile;
7294
7295 return S_OK;
7296}
7297
7298/**
7299 * Calculates the absolute path of the given path taking the directory of the
7300 * machine settings file as the current directory.
7301 *
7302 * @param aPath Path to calculate the absolute path for.
7303 * @param aResult Where to put the result (used only on success, can be the
7304 * same Utf8Str instance as passed in @a aPath).
7305 * @return IPRT result.
7306 *
7307 * @note Locks this object for reading.
7308 */
7309int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7310{
7311 AutoCaller autoCaller(this);
7312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7313
7314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7315
7316 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7317
7318 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7319
7320 strSettingsDir.stripFilename();
7321 char folder[RTPATH_MAX];
7322 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7323 if (RT_SUCCESS(vrc))
7324 aResult = folder;
7325
7326 return vrc;
7327}
7328
7329/**
7330 * Copies strSource to strTarget, making it relative to the machine folder
7331 * if it is a subdirectory thereof, or simply copying it otherwise.
7332 *
7333 * @param strSource Path to evaluate and copy.
7334 * @param strTarget Buffer to receive target path.
7335 *
7336 * @note Locks this object for reading.
7337 */
7338void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7339 Utf8Str &strTarget)
7340{
7341 AutoCaller autoCaller(this);
7342 AssertComRCReturn(autoCaller.rc(), (void)0);
7343
7344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7345
7346 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7347 // use strTarget as a temporary buffer to hold the machine settings dir
7348 strTarget = mData->m_strConfigFileFull;
7349 strTarget.stripFilename();
7350 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7351 {
7352 // is relative: then append what's left
7353 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7354 // for empty paths (only possible for subdirs) use "." to avoid
7355 // triggering default settings for not present config attributes.
7356 if (strTarget.isEmpty())
7357 strTarget = ".";
7358 }
7359 else
7360 // is not relative: then overwrite
7361 strTarget = strSource;
7362}
7363
7364/**
7365 * Returns the full path to the machine's log folder in the
7366 * \a aLogFolder argument.
7367 */
7368void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturnVoid(autoCaller.rc());
7372
7373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7374
7375 char szTmp[RTPATH_MAX];
7376 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7377 if (RT_SUCCESS(vrc))
7378 {
7379 if (szTmp[0] && !mUserData.isNull())
7380 {
7381 char szTmp2[RTPATH_MAX];
7382 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7383 if (RT_SUCCESS(vrc))
7384 aLogFolder = BstrFmt("%s%c%s",
7385 szTmp2,
7386 RTPATH_DELIMITER,
7387 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7388 }
7389 else
7390 vrc = VERR_PATH_IS_RELATIVE;
7391 }
7392
7393 if (RT_FAILURE(vrc))
7394 {
7395 // fallback if VBOX_USER_LOGHOME is not set or invalid
7396 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7397 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7398 aLogFolder.append(RTPATH_DELIMITER);
7399 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7400 }
7401}
7402
7403/**
7404 * Returns the full path to the machine's log file for an given index.
7405 */
7406Utf8Str Machine::i_getLogFilename(ULONG idx)
7407{
7408 Utf8Str logFolder;
7409 getLogFolder(logFolder);
7410 Assert(logFolder.length());
7411
7412 Utf8Str log;
7413 if (idx == 0)
7414 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7415#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7416 else if (idx == 1)
7417 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7418 else
7419 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7420#else
7421 else
7422 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7423#endif
7424 return log;
7425}
7426
7427/**
7428 * Returns the full path to the machine's hardened log file.
7429 */
7430Utf8Str Machine::i_getHardeningLogFilename(void)
7431{
7432 Utf8Str strFilename;
7433 getLogFolder(strFilename);
7434 Assert(strFilename.length());
7435 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7436 return strFilename;
7437}
7438
7439
7440/**
7441 * Composes a unique saved state filename based on the current system time. The filename is
7442 * granular to the second so this will work so long as no more than one snapshot is taken on
7443 * a machine per second.
7444 *
7445 * Before version 4.1, we used this formula for saved state files:
7446 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7447 * which no longer works because saved state files can now be shared between the saved state of the
7448 * "saved" machine and an online snapshot, and the following would cause problems:
7449 * 1) save machine
7450 * 2) create online snapshot from that machine state --> reusing saved state file
7451 * 3) save machine again --> filename would be reused, breaking the online snapshot
7452 *
7453 * So instead we now use a timestamp.
7454 *
7455 * @param str
7456 */
7457
7458void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7459{
7460 AutoCaller autoCaller(this);
7461 AssertComRCReturnVoid(autoCaller.rc());
7462
7463 {
7464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7465 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7466 }
7467
7468 RTTIMESPEC ts;
7469 RTTimeNow(&ts);
7470 RTTIME time;
7471 RTTimeExplode(&time, &ts);
7472
7473 strStateFilePath += RTPATH_DELIMITER;
7474 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7475 time.i32Year, time.u8Month, time.u8MonthDay,
7476 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7477}
7478
7479/**
7480 * Returns the full path to the default video capture file.
7481 */
7482void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7483{
7484 AutoCaller autoCaller(this);
7485 AssertComRCReturnVoid(autoCaller.rc());
7486
7487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7488
7489 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7490 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7491 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7492}
7493
7494/**
7495 * Returns whether at least one USB controller is present for the VM.
7496 */
7497bool Machine::i_isUSBControllerPresent()
7498{
7499 AutoCaller autoCaller(this);
7500 AssertComRCReturn(autoCaller.rc(), false);
7501
7502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7503
7504 return (mUSBControllers->size() > 0);
7505}
7506
7507/**
7508 * @note Locks this object for writing, calls the client process
7509 * (inside the lock).
7510 */
7511HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7512 const Utf8Str &strFrontend,
7513 const Utf8Str &strEnvironment,
7514 ProgressProxy *aProgress)
7515{
7516 LogFlowThisFuncEnter();
7517
7518 AssertReturn(aControl, E_FAIL);
7519 AssertReturn(aProgress, E_FAIL);
7520 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7521
7522 AutoCaller autoCaller(this);
7523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7524
7525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7526
7527 if (!mData->mRegistered)
7528 return setError(E_UNEXPECTED,
7529 tr("The machine '%s' is not registered"),
7530 mUserData->s.strName.c_str());
7531
7532 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7533
7534 /* The process started when launching a VM with separate UI/VM processes is always
7535 * the UI process, i.e. needs special handling as it won't claim the session. */
7536 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7537
7538 if (fSeparate)
7539 {
7540 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7541 return setError(VBOX_E_INVALID_OBJECT_STATE,
7542 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7543 mUserData->s.strName.c_str());
7544 }
7545 else
7546 {
7547 if ( mData->mSession.mState == SessionState_Locked
7548 || mData->mSession.mState == SessionState_Spawning
7549 || mData->mSession.mState == SessionState_Unlocking)
7550 return setError(VBOX_E_INVALID_OBJECT_STATE,
7551 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7552 mUserData->s.strName.c_str());
7553
7554 /* may not be busy */
7555 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7556 }
7557
7558 /* get the path to the executable */
7559 char szPath[RTPATH_MAX];
7560 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7561 size_t cchBufLeft = strlen(szPath);
7562 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7563 szPath[cchBufLeft] = 0;
7564 char *pszNamePart = szPath + cchBufLeft;
7565 cchBufLeft = sizeof(szPath) - cchBufLeft;
7566
7567 int vrc = VINF_SUCCESS;
7568 RTPROCESS pid = NIL_RTPROCESS;
7569
7570 RTENV env = RTENV_DEFAULT;
7571
7572 if (!strEnvironment.isEmpty())
7573 {
7574 char *newEnvStr = NULL;
7575
7576 do
7577 {
7578 /* clone the current environment */
7579 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7580 AssertRCBreakStmt(vrc2, vrc = vrc2);
7581
7582 newEnvStr = RTStrDup(strEnvironment.c_str());
7583 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7584
7585 /* put new variables to the environment
7586 * (ignore empty variable names here since RTEnv API
7587 * intentionally doesn't do that) */
7588 char *var = newEnvStr;
7589 for (char *p = newEnvStr; *p; ++p)
7590 {
7591 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7592 {
7593 *p = '\0';
7594 if (*var)
7595 {
7596 char *val = strchr(var, '=');
7597 if (val)
7598 {
7599 *val++ = '\0';
7600 vrc2 = RTEnvSetEx(env, var, val);
7601 }
7602 else
7603 vrc2 = RTEnvUnsetEx(env, var);
7604 if (RT_FAILURE(vrc2))
7605 break;
7606 }
7607 var = p + 1;
7608 }
7609 }
7610 if (RT_SUCCESS(vrc2) && *var)
7611 vrc2 = RTEnvPutEx(env, var);
7612
7613 AssertRCBreakStmt(vrc2, vrc = vrc2);
7614 }
7615 while (0);
7616
7617 if (newEnvStr != NULL)
7618 RTStrFree(newEnvStr);
7619 }
7620
7621 /* Hardening logging */
7622#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7623 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7624 {
7625 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7626 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7627 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7628 {
7629 Utf8Str strStartupLogDir = strHardeningLogFile;
7630 strStartupLogDir.stripFilename();
7631 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7632 file without stripping the file. */
7633 }
7634 strSupHardeningLogArg.append(strHardeningLogFile);
7635
7636 /* Remove legacy log filename to avoid confusion. */
7637 Utf8Str strOldStartupLogFile;
7638 getLogFolder(strOldStartupLogFile);
7639 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7640 RTFileDelete(strOldStartupLogFile.c_str());
7641 }
7642 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7643#else
7644 const char *pszSupHardeningLogArg = NULL;
7645#endif
7646
7647 Utf8Str strCanonicalName;
7648
7649#ifdef VBOX_WITH_QTGUI
7650 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7651 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7652 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7653 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7654 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7655 {
7656 strCanonicalName = "GUI/Qt";
7657# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7658 /* Modify the base path so that we don't need to use ".." below. */
7659 RTPathStripTrailingSlash(szPath);
7660 RTPathStripFilename(szPath);
7661 cchBufLeft = strlen(szPath);
7662 pszNamePart = szPath + cchBufLeft;
7663 cchBufLeft = sizeof(szPath) - cchBufLeft;
7664
7665# define OSX_APP_NAME "VirtualBoxVM"
7666# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7667
7668 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7669 if ( strAppOverride.contains(".")
7670 || strAppOverride.contains("/")
7671 || strAppOverride.contains("\\")
7672 || strAppOverride.contains(":"))
7673 strAppOverride.setNull();
7674 Utf8Str strAppPath;
7675 if (!strAppOverride.isEmpty())
7676 {
7677 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7678 Utf8Str strFullPath(szPath);
7679 strFullPath.append(strAppPath);
7680 /* there is a race, but people using this deserve the failure */
7681 if (!RTFileExists(strFullPath.c_str()))
7682 strAppOverride.setNull();
7683 }
7684 if (strAppOverride.isEmpty())
7685 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7686 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7687 strcpy(pszNamePart, strAppPath.c_str());
7688# else
7689 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7690 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7691 strcpy(pszNamePart, s_szVirtualBox_exe);
7692# endif
7693
7694 Utf8Str idStr = mData->mUuid.toString();
7695 const char *apszArgs[] =
7696 {
7697 szPath,
7698 "--comment", mUserData->s.strName.c_str(),
7699 "--startvm", idStr.c_str(),
7700 "--no-startvm-errormsgbox",
7701 NULL, /* For "--separate". */
7702 NULL, /* For "--sup-startup-log". */
7703 NULL
7704 };
7705 unsigned iArg = 6;
7706 if (fSeparate)
7707 apszArgs[iArg++] = "--separate";
7708 apszArgs[iArg++] = pszSupHardeningLogArg;
7709
7710 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7711 }
7712#else /* !VBOX_WITH_QTGUI */
7713 if (0)
7714 ;
7715#endif /* VBOX_WITH_QTGUI */
7716
7717 else
7718
7719#ifdef VBOX_WITH_VBOXSDL
7720 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7721 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7722 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7723 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7724 {
7725 strCanonicalName = "GUI/SDL";
7726 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7727 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7728 strcpy(pszNamePart, s_szVBoxSDL_exe);
7729
7730 Utf8Str idStr = mData->mUuid.toString();
7731 const char *apszArgs[] =
7732 {
7733 szPath,
7734 "--comment", mUserData->s.strName.c_str(),
7735 "--startvm", idStr.c_str(),
7736 NULL, /* For "--separate". */
7737 NULL, /* For "--sup-startup-log". */
7738 NULL
7739 };
7740 unsigned iArg = 5;
7741 if (fSeparate)
7742 apszArgs[iArg++] = "--separate";
7743 apszArgs[iArg++] = pszSupHardeningLogArg;
7744
7745 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7746 }
7747#else /* !VBOX_WITH_VBOXSDL */
7748 if (0)
7749 ;
7750#endif /* !VBOX_WITH_VBOXSDL */
7751
7752 else
7753
7754#ifdef VBOX_WITH_HEADLESS
7755 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7756 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7757 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7758 )
7759 {
7760 strCanonicalName = "headless";
7761 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7762 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7763 * and a VM works even if the server has not been installed.
7764 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7765 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7766 * differently in 4.0 and 3.x.
7767 */
7768 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7769 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7770 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7771
7772 Utf8Str idStr = mData->mUuid.toString();
7773 const char *apszArgs[] =
7774 {
7775 szPath,
7776 "--comment", mUserData->s.strName.c_str(),
7777 "--startvm", idStr.c_str(),
7778 "--vrde", "config",
7779 NULL, /* For "--capture". */
7780 NULL, /* For "--sup-startup-log". */
7781 NULL
7782 };
7783 unsigned iArg = 7;
7784 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7785 apszArgs[iArg++] = "--capture";
7786 apszArgs[iArg++] = pszSupHardeningLogArg;
7787
7788# ifdef RT_OS_WINDOWS
7789 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7790# else
7791 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7792# endif
7793 }
7794#else /* !VBOX_WITH_HEADLESS */
7795 if (0)
7796 ;
7797#endif /* !VBOX_WITH_HEADLESS */
7798 else
7799 {
7800 RTEnvDestroy(env);
7801 return setError(E_INVALIDARG,
7802 tr("Invalid frontend name: '%s'"),
7803 strFrontend.c_str());
7804 }
7805
7806 RTEnvDestroy(env);
7807
7808 if (RT_FAILURE(vrc))
7809 return setError(VBOX_E_IPRT_ERROR,
7810 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7811 mUserData->s.strName.c_str(), vrc);
7812
7813 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7814
7815 if (!fSeparate)
7816 {
7817 /*
7818 * Note that we don't release the lock here before calling the client,
7819 * because it doesn't need to call us back if called with a NULL argument.
7820 * Releasing the lock here is dangerous because we didn't prepare the
7821 * launch data yet, but the client we've just started may happen to be
7822 * too fast and call LockMachine() that will fail (because of PID, etc.),
7823 * so that the Machine will never get out of the Spawning session state.
7824 */
7825
7826 /* inform the session that it will be a remote one */
7827 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7828#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7829 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7830#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7831 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7832#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7833 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7834
7835 if (FAILED(rc))
7836 {
7837 /* restore the session state */
7838 mData->mSession.mState = SessionState_Unlocked;
7839 alock.release();
7840 mParent->i_addProcessToReap(pid);
7841 /* The failure may occur w/o any error info (from RPC), so provide one */
7842 return setError(VBOX_E_VM_ERROR,
7843 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7844 }
7845
7846 /* attach launch data to the machine */
7847 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7848 mData->mSession.mRemoteControls.push_back(aControl);
7849 mData->mSession.mProgress = aProgress;
7850 mData->mSession.mPID = pid;
7851 mData->mSession.mState = SessionState_Spawning;
7852 Assert(strCanonicalName.isNotEmpty());
7853 mData->mSession.mName = strCanonicalName;
7854 }
7855 else
7856 {
7857 /* For separate UI process we declare the launch as completed instantly, as the
7858 * actual headless VM start may or may not come. No point in remembering anything
7859 * yet, as what matters for us is when the headless VM gets started. */
7860 aProgress->i_notifyComplete(S_OK);
7861 }
7862
7863 alock.release();
7864 mParent->i_addProcessToReap(pid);
7865
7866 LogFlowThisFuncLeave();
7867 return S_OK;
7868}
7869
7870/**
7871 * Returns @c true if the given session machine instance has an open direct
7872 * session (and optionally also for direct sessions which are closing) and
7873 * returns the session control machine instance if so.
7874 *
7875 * Note that when the method returns @c false, the arguments remain unchanged.
7876 *
7877 * @param aMachine Session machine object.
7878 * @param aControl Direct session control object (optional).
7879 * @param aRequireVM If true then only allow VM sessions.
7880 * @param aAllowClosing If true then additionally a session which is currently
7881 * being closed will also be allowed.
7882 *
7883 * @note locks this object for reading.
7884 */
7885bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7886 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7887 bool aRequireVM /*= false*/,
7888 bool aAllowClosing /*= false*/)
7889{
7890 AutoLimitedCaller autoCaller(this);
7891 AssertComRCReturn(autoCaller.rc(), false);
7892
7893 /* just return false for inaccessible machines */
7894 if (getObjectState().getState() != ObjectState::Ready)
7895 return false;
7896
7897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7898
7899 if ( ( mData->mSession.mState == SessionState_Locked
7900 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7901 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7902 )
7903 {
7904 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7905
7906 aMachine = mData->mSession.mMachine;
7907
7908 if (aControl != NULL)
7909 *aControl = mData->mSession.mDirectControl;
7910
7911 return true;
7912 }
7913
7914 return false;
7915}
7916
7917/**
7918 * Returns @c true if the given machine has an spawning direct session.
7919 *
7920 * @note locks this object for reading.
7921 */
7922bool Machine::i_isSessionSpawning()
7923{
7924 AutoLimitedCaller autoCaller(this);
7925 AssertComRCReturn(autoCaller.rc(), false);
7926
7927 /* just return false for inaccessible machines */
7928 if (getObjectState().getState() != ObjectState::Ready)
7929 return false;
7930
7931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7932
7933 if (mData->mSession.mState == SessionState_Spawning)
7934 return true;
7935
7936 return false;
7937}
7938
7939/**
7940 * Called from the client watcher thread to check for unexpected client process
7941 * death during Session_Spawning state (e.g. before it successfully opened a
7942 * direct session).
7943 *
7944 * On Win32 and on OS/2, this method is called only when we've got the
7945 * direct client's process termination notification, so it always returns @c
7946 * true.
7947 *
7948 * On other platforms, this method returns @c true if the client process is
7949 * terminated and @c false if it's still alive.
7950 *
7951 * @note Locks this object for writing.
7952 */
7953bool Machine::i_checkForSpawnFailure()
7954{
7955 AutoCaller autoCaller(this);
7956 if (!autoCaller.isOk())
7957 {
7958 /* nothing to do */
7959 LogFlowThisFunc(("Already uninitialized!\n"));
7960 return true;
7961 }
7962
7963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7964
7965 if (mData->mSession.mState != SessionState_Spawning)
7966 {
7967 /* nothing to do */
7968 LogFlowThisFunc(("Not spawning any more!\n"));
7969 return true;
7970 }
7971
7972 HRESULT rc = S_OK;
7973
7974 /* PID not yet initialized, skip check. */
7975 if (mData->mSession.mPID == NIL_RTPROCESS)
7976 return false;
7977
7978 RTPROCSTATUS status;
7979 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7980
7981 if (vrc != VERR_PROCESS_RUNNING)
7982 {
7983 Utf8Str strExtraInfo;
7984
7985#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7986 /* If the startup logfile exists and is of non-zero length, tell the
7987 user to look there for more details to encourage them to attach it
7988 when reporting startup issues. */
7989 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7990 uint64_t cbStartupLogFile = 0;
7991 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7992 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7993 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7994#endif
7995
7996 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7997 rc = setError(E_FAIL,
7998 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7999 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8000 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8001 rc = setError(E_FAIL,
8002 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8003 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8004 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8005 rc = setError(E_FAIL,
8006 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8007 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8008 else
8009 rc = setError(E_FAIL,
8010 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8011 i_getName().c_str(), vrc, strExtraInfo.c_str());
8012 }
8013
8014 if (FAILED(rc))
8015 {
8016 /* Close the remote session, remove the remote control from the list
8017 * and reset session state to Closed (@note keep the code in sync with
8018 * the relevant part in LockMachine()). */
8019
8020 Assert(mData->mSession.mRemoteControls.size() == 1);
8021 if (mData->mSession.mRemoteControls.size() == 1)
8022 {
8023 ErrorInfoKeeper eik;
8024 mData->mSession.mRemoteControls.front()->Uninitialize();
8025 }
8026
8027 mData->mSession.mRemoteControls.clear();
8028 mData->mSession.mState = SessionState_Unlocked;
8029
8030 /* finalize the progress after setting the state */
8031 if (!mData->mSession.mProgress.isNull())
8032 {
8033 mData->mSession.mProgress->notifyComplete(rc);
8034 mData->mSession.mProgress.setNull();
8035 }
8036
8037 mData->mSession.mPID = NIL_RTPROCESS;
8038
8039 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8040 return true;
8041 }
8042
8043 return false;
8044}
8045
8046/**
8047 * Checks whether the machine can be registered. If so, commits and saves
8048 * all settings.
8049 *
8050 * @note Must be called from mParent's write lock. Locks this object and
8051 * children for writing.
8052 */
8053HRESULT Machine::i_prepareRegister()
8054{
8055 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8056
8057 AutoLimitedCaller autoCaller(this);
8058 AssertComRCReturnRC(autoCaller.rc());
8059
8060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8061
8062 /* wait for state dependents to drop to zero */
8063 i_ensureNoStateDependencies();
8064
8065 if (!mData->mAccessible)
8066 return setError(VBOX_E_INVALID_OBJECT_STATE,
8067 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8068 mUserData->s.strName.c_str(),
8069 mData->mUuid.toString().c_str());
8070
8071 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8072
8073 if (mData->mRegistered)
8074 return setError(VBOX_E_INVALID_OBJECT_STATE,
8075 tr("The machine '%s' with UUID {%s} is already registered"),
8076 mUserData->s.strName.c_str(),
8077 mData->mUuid.toString().c_str());
8078
8079 HRESULT rc = S_OK;
8080
8081 // Ensure the settings are saved. If we are going to be registered and
8082 // no config file exists yet, create it by calling i_saveSettings() too.
8083 if ( (mData->flModifications)
8084 || (!mData->pMachineConfigFile->fileExists())
8085 )
8086 {
8087 rc = i_saveSettings(NULL);
8088 // no need to check whether VirtualBox.xml needs saving too since
8089 // we can't have a machine XML file rename pending
8090 if (FAILED(rc)) return rc;
8091 }
8092
8093 /* more config checking goes here */
8094
8095 if (SUCCEEDED(rc))
8096 {
8097 /* we may have had implicit modifications we want to fix on success */
8098 i_commit();
8099
8100 mData->mRegistered = true;
8101 }
8102 else
8103 {
8104 /* we may have had implicit modifications we want to cancel on failure*/
8105 i_rollback(false /* aNotify */);
8106 }
8107
8108 return rc;
8109}
8110
8111/**
8112 * Increases the number of objects dependent on the machine state or on the
8113 * registered state. Guarantees that these two states will not change at least
8114 * until #releaseStateDependency() is called.
8115 *
8116 * Depending on the @a aDepType value, additional state checks may be made.
8117 * These checks will set extended error info on failure. See
8118 * #checkStateDependency() for more info.
8119 *
8120 * If this method returns a failure, the dependency is not added and the caller
8121 * is not allowed to rely on any particular machine state or registration state
8122 * value and may return the failed result code to the upper level.
8123 *
8124 * @param aDepType Dependency type to add.
8125 * @param aState Current machine state (NULL if not interested).
8126 * @param aRegistered Current registered state (NULL if not interested).
8127 *
8128 * @note Locks this object for writing.
8129 */
8130HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8131 MachineState_T *aState /* = NULL */,
8132 BOOL *aRegistered /* = NULL */)
8133{
8134 AutoCaller autoCaller(this);
8135 AssertComRCReturnRC(autoCaller.rc());
8136
8137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8138
8139 HRESULT rc = i_checkStateDependency(aDepType);
8140 if (FAILED(rc)) return rc;
8141
8142 {
8143 if (mData->mMachineStateChangePending != 0)
8144 {
8145 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8146 * drop to zero so don't add more. It may make sense to wait a bit
8147 * and retry before reporting an error (since the pending state
8148 * transition should be really quick) but let's just assert for
8149 * now to see if it ever happens on practice. */
8150
8151 AssertFailed();
8152
8153 return setError(E_ACCESSDENIED,
8154 tr("Machine state change is in progress. Please retry the operation later."));
8155 }
8156
8157 ++mData->mMachineStateDeps;
8158 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8159 }
8160
8161 if (aState)
8162 *aState = mData->mMachineState;
8163 if (aRegistered)
8164 *aRegistered = mData->mRegistered;
8165
8166 return S_OK;
8167}
8168
8169/**
8170 * Decreases the number of objects dependent on the machine state.
8171 * Must always complete the #addStateDependency() call after the state
8172 * dependency is no more necessary.
8173 */
8174void Machine::i_releaseStateDependency()
8175{
8176 AutoCaller autoCaller(this);
8177 AssertComRCReturnVoid(autoCaller.rc());
8178
8179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8180
8181 /* releaseStateDependency() w/o addStateDependency()? */
8182 AssertReturnVoid(mData->mMachineStateDeps != 0);
8183 -- mData->mMachineStateDeps;
8184
8185 if (mData->mMachineStateDeps == 0)
8186 {
8187 /* inform i_ensureNoStateDependencies() that there are no more deps */
8188 if (mData->mMachineStateChangePending != 0)
8189 {
8190 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8191 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8192 }
8193 }
8194}
8195
8196Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8197{
8198 /* start with nothing found */
8199 Utf8Str strResult("");
8200
8201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8202
8203 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8204 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8205 // found:
8206 strResult = it->second; // source is a Utf8Str
8207
8208 return strResult;
8209}
8210
8211// protected methods
8212/////////////////////////////////////////////////////////////////////////////
8213
8214/**
8215 * Performs machine state checks based on the @a aDepType value. If a check
8216 * fails, this method will set extended error info, otherwise it will return
8217 * S_OK. It is supposed, that on failure, the caller will immediately return
8218 * the return value of this method to the upper level.
8219 *
8220 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8221 *
8222 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8223 * current state of this machine object allows to change settings of the
8224 * machine (i.e. the machine is not registered, or registered but not running
8225 * and not saved). It is useful to call this method from Machine setters
8226 * before performing any change.
8227 *
8228 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8229 * as for MutableStateDep except that if the machine is saved, S_OK is also
8230 * returned. This is useful in setters which allow changing machine
8231 * properties when it is in the saved state.
8232 *
8233 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8234 * if the current state of this machine object allows to change runtime
8235 * changeable settings of the machine (i.e. the machine is not registered, or
8236 * registered but either running or not running and not saved). It is useful
8237 * to call this method from Machine setters before performing any changes to
8238 * runtime changeable settings.
8239 *
8240 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8241 * the same as for MutableOrRunningStateDep except that if the machine is
8242 * saved, S_OK is also returned. This is useful in setters which allow
8243 * changing runtime and saved state changeable machine properties.
8244 *
8245 * @param aDepType Dependency type to check.
8246 *
8247 * @note Non Machine based classes should use #addStateDependency() and
8248 * #releaseStateDependency() methods or the smart AutoStateDependency
8249 * template.
8250 *
8251 * @note This method must be called from under this object's read or write
8252 * lock.
8253 */
8254HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8255{
8256 switch (aDepType)
8257 {
8258 case AnyStateDep:
8259 {
8260 break;
8261 }
8262 case MutableStateDep:
8263 {
8264 if ( mData->mRegistered
8265 && ( !i_isSessionMachine()
8266 || ( mData->mMachineState != MachineState_Aborted
8267 && mData->mMachineState != MachineState_Teleported
8268 && mData->mMachineState != MachineState_PoweredOff
8269 )
8270 )
8271 )
8272 return setError(VBOX_E_INVALID_VM_STATE,
8273 tr("The machine is not mutable (state is %s)"),
8274 Global::stringifyMachineState(mData->mMachineState));
8275 break;
8276 }
8277 case MutableOrSavedStateDep:
8278 {
8279 if ( mData->mRegistered
8280 && ( !i_isSessionMachine()
8281 || ( mData->mMachineState != MachineState_Aborted
8282 && mData->mMachineState != MachineState_Teleported
8283 && mData->mMachineState != MachineState_Saved
8284 && mData->mMachineState != MachineState_PoweredOff
8285 )
8286 )
8287 )
8288 return setError(VBOX_E_INVALID_VM_STATE,
8289 tr("The machine is not mutable or saved (state is %s)"),
8290 Global::stringifyMachineState(mData->mMachineState));
8291 break;
8292 }
8293 case MutableOrRunningStateDep:
8294 {
8295 if ( mData->mRegistered
8296 && ( !i_isSessionMachine()
8297 || ( mData->mMachineState != MachineState_Aborted
8298 && mData->mMachineState != MachineState_Teleported
8299 && mData->mMachineState != MachineState_PoweredOff
8300 && !Global::IsOnline(mData->mMachineState)
8301 )
8302 )
8303 )
8304 return setError(VBOX_E_INVALID_VM_STATE,
8305 tr("The machine is not mutable or running (state is %s)"),
8306 Global::stringifyMachineState(mData->mMachineState));
8307 break;
8308 }
8309 case MutableOrSavedOrRunningStateDep:
8310 {
8311 if ( mData->mRegistered
8312 && ( !i_isSessionMachine()
8313 || ( mData->mMachineState != MachineState_Aborted
8314 && mData->mMachineState != MachineState_Teleported
8315 && mData->mMachineState != MachineState_Saved
8316 && mData->mMachineState != MachineState_PoweredOff
8317 && !Global::IsOnline(mData->mMachineState)
8318 )
8319 )
8320 )
8321 return setError(VBOX_E_INVALID_VM_STATE,
8322 tr("The machine is not mutable, saved or running (state is %s)"),
8323 Global::stringifyMachineState(mData->mMachineState));
8324 break;
8325 }
8326 }
8327
8328 return S_OK;
8329}
8330
8331/**
8332 * Helper to initialize all associated child objects and allocate data
8333 * structures.
8334 *
8335 * This method must be called as a part of the object's initialization procedure
8336 * (usually done in the #init() method).
8337 *
8338 * @note Must be called only from #init() or from #registeredInit().
8339 */
8340HRESULT Machine::initDataAndChildObjects()
8341{
8342 AutoCaller autoCaller(this);
8343 AssertComRCReturnRC(autoCaller.rc());
8344 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8345 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8346
8347 AssertReturn(!mData->mAccessible, E_FAIL);
8348
8349 /* allocate data structures */
8350 mSSData.allocate();
8351 mUserData.allocate();
8352 mHWData.allocate();
8353 mMediaData.allocate();
8354 mStorageControllers.allocate();
8355 mUSBControllers.allocate();
8356
8357 /* initialize mOSTypeId */
8358 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8359
8360 /* create associated BIOS settings object */
8361 unconst(mBIOSSettings).createObject();
8362 mBIOSSettings->init(this);
8363
8364 /* create an associated VRDE object (default is disabled) */
8365 unconst(mVRDEServer).createObject();
8366 mVRDEServer->init(this);
8367
8368 /* create associated serial port objects */
8369 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8370 {
8371 unconst(mSerialPorts[slot]).createObject();
8372 mSerialPorts[slot]->init(this, slot);
8373 }
8374
8375 /* create associated parallel port objects */
8376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8377 {
8378 unconst(mParallelPorts[slot]).createObject();
8379 mParallelPorts[slot]->init(this, slot);
8380 }
8381
8382 /* create the audio adapter object (always present, default is disabled) */
8383 unconst(mAudioAdapter).createObject();
8384 mAudioAdapter->init(this);
8385
8386 /* create the USB device filters object (always present) */
8387 unconst(mUSBDeviceFilters).createObject();
8388 mUSBDeviceFilters->init(this);
8389
8390 /* create associated network adapter objects */
8391 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8392 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8393 {
8394 unconst(mNetworkAdapters[slot]).createObject();
8395 mNetworkAdapters[slot]->init(this, slot);
8396 }
8397
8398 /* create the bandwidth control */
8399 unconst(mBandwidthControl).createObject();
8400 mBandwidthControl->init(this);
8401
8402#ifdef VBOX_WITH_UNATTENDED
8403 /* create the unattended object (always present) */
8404 unconst(mUnattended).createObject();
8405 mUnattended->init(this);
8406#endif
8407
8408 return S_OK;
8409}
8410
8411/**
8412 * Helper to uninitialize all associated child objects and to free all data
8413 * structures.
8414 *
8415 * This method must be called as a part of the object's uninitialization
8416 * procedure (usually done in the #uninit() method).
8417 *
8418 * @note Must be called only from #uninit() or from #registeredInit().
8419 */
8420void Machine::uninitDataAndChildObjects()
8421{
8422 AutoCaller autoCaller(this);
8423 AssertComRCReturnVoid(autoCaller.rc());
8424 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8425 || getObjectState().getState() == ObjectState::Limited);
8426
8427 /* tell all our other child objects we've been uninitialized */
8428 if (mBandwidthControl)
8429 {
8430 mBandwidthControl->uninit();
8431 unconst(mBandwidthControl).setNull();
8432 }
8433
8434 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8435 {
8436 if (mNetworkAdapters[slot])
8437 {
8438 mNetworkAdapters[slot]->uninit();
8439 unconst(mNetworkAdapters[slot]).setNull();
8440 }
8441 }
8442
8443 if (mUSBDeviceFilters)
8444 {
8445 mUSBDeviceFilters->uninit();
8446 unconst(mUSBDeviceFilters).setNull();
8447 }
8448
8449 if (mAudioAdapter)
8450 {
8451 mAudioAdapter->uninit();
8452 unconst(mAudioAdapter).setNull();
8453 }
8454
8455 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8456 {
8457 if (mParallelPorts[slot])
8458 {
8459 mParallelPorts[slot]->uninit();
8460 unconst(mParallelPorts[slot]).setNull();
8461 }
8462 }
8463
8464 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8465 {
8466 if (mSerialPorts[slot])
8467 {
8468 mSerialPorts[slot]->uninit();
8469 unconst(mSerialPorts[slot]).setNull();
8470 }
8471 }
8472
8473 if (mVRDEServer)
8474 {
8475 mVRDEServer->uninit();
8476 unconst(mVRDEServer).setNull();
8477 }
8478
8479 if (mBIOSSettings)
8480 {
8481 mBIOSSettings->uninit();
8482 unconst(mBIOSSettings).setNull();
8483 }
8484
8485#ifdef VBOX_WITH_UNATTENDED
8486 if (mUnattended)
8487 {
8488 mUnattended->uninit();
8489 unconst(mUnattended).setNull();
8490 }
8491#endif
8492
8493 /* Deassociate media (only when a real Machine or a SnapshotMachine
8494 * instance is uninitialized; SessionMachine instances refer to real
8495 * Machine media). This is necessary for a clean re-initialization of
8496 * the VM after successfully re-checking the accessibility state. Note
8497 * that in case of normal Machine or SnapshotMachine uninitialization (as
8498 * a result of unregistering or deleting the snapshot), outdated media
8499 * attachments will already be uninitialized and deleted, so this
8500 * code will not affect them. */
8501 if ( !!mMediaData
8502 && (!i_isSessionMachine())
8503 )
8504 {
8505 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8506 it != mMediaData->mAttachments.end();
8507 ++it)
8508 {
8509 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8510 if (pMedium.isNull())
8511 continue;
8512 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8513 AssertComRC(rc);
8514 }
8515 }
8516
8517 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8518 {
8519 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8520 if (mData->mFirstSnapshot)
8521 {
8522 // snapshots tree is protected by machine write lock; strictly
8523 // this isn't necessary here since we're deleting the entire
8524 // machine, but otherwise we assert in Snapshot::uninit()
8525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8526 mData->mFirstSnapshot->uninit();
8527 mData->mFirstSnapshot.setNull();
8528 }
8529
8530 mData->mCurrentSnapshot.setNull();
8531 }
8532
8533 /* free data structures (the essential mData structure is not freed here
8534 * since it may be still in use) */
8535 mMediaData.free();
8536 mStorageControllers.free();
8537 mUSBControllers.free();
8538 mHWData.free();
8539 mUserData.free();
8540 mSSData.free();
8541}
8542
8543/**
8544 * Returns a pointer to the Machine object for this machine that acts like a
8545 * parent for complex machine data objects such as shared folders, etc.
8546 *
8547 * For primary Machine objects and for SnapshotMachine objects, returns this
8548 * object's pointer itself. For SessionMachine objects, returns the peer
8549 * (primary) machine pointer.
8550 */
8551Machine* Machine::i_getMachine()
8552{
8553 if (i_isSessionMachine())
8554 return (Machine*)mPeer;
8555 return this;
8556}
8557
8558/**
8559 * Makes sure that there are no machine state dependents. If necessary, waits
8560 * for the number of dependents to drop to zero.
8561 *
8562 * Make sure this method is called from under this object's write lock to
8563 * guarantee that no new dependents may be added when this method returns
8564 * control to the caller.
8565 *
8566 * @note Locks this object for writing. The lock will be released while waiting
8567 * (if necessary).
8568 *
8569 * @warning To be used only in methods that change the machine state!
8570 */
8571void Machine::i_ensureNoStateDependencies()
8572{
8573 AssertReturnVoid(isWriteLockOnCurrentThread());
8574
8575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8576
8577 /* Wait for all state dependents if necessary */
8578 if (mData->mMachineStateDeps != 0)
8579 {
8580 /* lazy semaphore creation */
8581 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8582 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8583
8584 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8585 mData->mMachineStateDeps));
8586
8587 ++mData->mMachineStateChangePending;
8588
8589 /* reset the semaphore before waiting, the last dependent will signal
8590 * it */
8591 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8592
8593 alock.release();
8594
8595 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8596
8597 alock.acquire();
8598
8599 -- mData->mMachineStateChangePending;
8600 }
8601}
8602
8603/**
8604 * Changes the machine state and informs callbacks.
8605 *
8606 * This method is not intended to fail so it either returns S_OK or asserts (and
8607 * returns a failure).
8608 *
8609 * @note Locks this object for writing.
8610 */
8611HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8612{
8613 LogFlowThisFuncEnter();
8614 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8615 Assert(aMachineState != MachineState_Null);
8616
8617 AutoCaller autoCaller(this);
8618 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8619
8620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8621
8622 /* wait for state dependents to drop to zero */
8623 i_ensureNoStateDependencies();
8624
8625 MachineState_T const enmOldState = mData->mMachineState;
8626 if (enmOldState != aMachineState)
8627 {
8628 mData->mMachineState = aMachineState;
8629 RTTimeNow(&mData->mLastStateChange);
8630
8631#ifdef VBOX_WITH_DTRACE_R3_MAIN
8632 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8633#endif
8634 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8635 }
8636
8637 LogFlowThisFuncLeave();
8638 return S_OK;
8639}
8640
8641/**
8642 * Searches for a shared folder with the given logical name
8643 * in the collection of shared folders.
8644 *
8645 * @param aName logical name of the shared folder
8646 * @param aSharedFolder where to return the found object
8647 * @param aSetError whether to set the error info if the folder is
8648 * not found
8649 * @return
8650 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8651 *
8652 * @note
8653 * must be called from under the object's lock!
8654 */
8655HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8656 ComObjPtr<SharedFolder> &aSharedFolder,
8657 bool aSetError /* = false */)
8658{
8659 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8660 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8661 it != mHWData->mSharedFolders.end();
8662 ++it)
8663 {
8664 SharedFolder *pSF = *it;
8665 AutoCaller autoCaller(pSF);
8666 if (pSF->i_getName() == aName)
8667 {
8668 aSharedFolder = pSF;
8669 rc = S_OK;
8670 break;
8671 }
8672 }
8673
8674 if (aSetError && FAILED(rc))
8675 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8676
8677 return rc;
8678}
8679
8680/**
8681 * Initializes all machine instance data from the given settings structures
8682 * from XML. The exception is the machine UUID which needs special handling
8683 * depending on the caller's use case, so the caller needs to set that herself.
8684 *
8685 * This gets called in several contexts during machine initialization:
8686 *
8687 * -- When machine XML exists on disk already and needs to be loaded into memory,
8688 * for example, from registeredInit() to load all registered machines on
8689 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8690 * attached to the machine should be part of some media registry already.
8691 *
8692 * -- During OVF import, when a machine config has been constructed from an
8693 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8694 * ensure that the media listed as attachments in the config (which have
8695 * been imported from the OVF) receive the correct registry ID.
8696 *
8697 * -- During VM cloning.
8698 *
8699 * @param config Machine settings from XML.
8700 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8701 * for each attached medium in the config.
8702 * @return
8703 */
8704HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8705 const Guid *puuidRegistry)
8706{
8707 // copy name, description, OS type, teleporter, UTC etc.
8708 mUserData->s = config.machineUserData;
8709
8710 // look up the object by Id to check it is valid
8711 ComPtr<IGuestOSType> guestOSType;
8712 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8713 guestOSType.asOutParam());
8714 if (FAILED(rc)) return rc;
8715
8716 // stateFile (optional)
8717 if (config.strStateFile.isEmpty())
8718 mSSData->strStateFilePath.setNull();
8719 else
8720 {
8721 Utf8Str stateFilePathFull(config.strStateFile);
8722 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8723 if (RT_FAILURE(vrc))
8724 return setError(E_FAIL,
8725 tr("Invalid saved state file path '%s' (%Rrc)"),
8726 config.strStateFile.c_str(),
8727 vrc);
8728 mSSData->strStateFilePath = stateFilePathFull;
8729 }
8730
8731 // snapshot folder needs special processing so set it again
8732 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8733 if (FAILED(rc)) return rc;
8734
8735 /* Copy the extra data items (config may or may not be the same as
8736 * mData->pMachineConfigFile) if necessary. When loading the XML files
8737 * from disk they are the same, but not for OVF import. */
8738 if (mData->pMachineConfigFile != &config)
8739 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8740
8741 /* currentStateModified (optional, default is true) */
8742 mData->mCurrentStateModified = config.fCurrentStateModified;
8743
8744 mData->mLastStateChange = config.timeLastStateChange;
8745
8746 /*
8747 * note: all mUserData members must be assigned prior this point because
8748 * we need to commit changes in order to let mUserData be shared by all
8749 * snapshot machine instances.
8750 */
8751 mUserData.commitCopy();
8752
8753 // machine registry, if present (must be loaded before snapshots)
8754 if (config.canHaveOwnMediaRegistry())
8755 {
8756 // determine machine folder
8757 Utf8Str strMachineFolder = i_getSettingsFileFull();
8758 strMachineFolder.stripFilename();
8759 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8760 config.mediaRegistry,
8761 strMachineFolder);
8762 if (FAILED(rc)) return rc;
8763 }
8764
8765 /* Snapshot node (optional) */
8766 size_t cRootSnapshots;
8767 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8768 {
8769 // there must be only one root snapshot
8770 Assert(cRootSnapshots == 1);
8771
8772 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8773
8774 rc = i_loadSnapshot(snap,
8775 config.uuidCurrentSnapshot,
8776 NULL); // no parent == first snapshot
8777 if (FAILED(rc)) return rc;
8778 }
8779
8780 // hardware data
8781 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8782 if (FAILED(rc)) return rc;
8783
8784 /*
8785 * NOTE: the assignment below must be the last thing to do,
8786 * otherwise it will be not possible to change the settings
8787 * somewhere in the code above because all setters will be
8788 * blocked by i_checkStateDependency(MutableStateDep).
8789 */
8790
8791 /* set the machine state to Aborted or Saved when appropriate */
8792 if (config.fAborted)
8793 {
8794 mSSData->strStateFilePath.setNull();
8795
8796 /* no need to use i_setMachineState() during init() */
8797 mData->mMachineState = MachineState_Aborted;
8798 }
8799 else if (!mSSData->strStateFilePath.isEmpty())
8800 {
8801 /* no need to use i_setMachineState() during init() */
8802 mData->mMachineState = MachineState_Saved;
8803 }
8804
8805 // after loading settings, we are no longer different from the XML on disk
8806 mData->flModifications = 0;
8807
8808 return S_OK;
8809}
8810
8811/**
8812 * Recursively loads all snapshots starting from the given.
8813 *
8814 * @param aNode <Snapshot> node.
8815 * @param aCurSnapshotId Current snapshot ID from the settings file.
8816 * @param aParentSnapshot Parent snapshot.
8817 */
8818HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8819 const Guid &aCurSnapshotId,
8820 Snapshot *aParentSnapshot)
8821{
8822 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8823 AssertReturn(!i_isSessionMachine(), E_FAIL);
8824
8825 HRESULT rc = S_OK;
8826
8827 Utf8Str strStateFile;
8828 if (!data.strStateFile.isEmpty())
8829 {
8830 /* optional */
8831 strStateFile = data.strStateFile;
8832 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8833 if (RT_FAILURE(vrc))
8834 return setError(E_FAIL,
8835 tr("Invalid saved state file path '%s' (%Rrc)"),
8836 strStateFile.c_str(),
8837 vrc);
8838 }
8839
8840 /* create a snapshot machine object */
8841 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8842 pSnapshotMachine.createObject();
8843 rc = pSnapshotMachine->initFromSettings(this,
8844 data.hardware,
8845 &data.debugging,
8846 &data.autostart,
8847 data.uuid.ref(),
8848 strStateFile);
8849 if (FAILED(rc)) return rc;
8850
8851 /* create a snapshot object */
8852 ComObjPtr<Snapshot> pSnapshot;
8853 pSnapshot.createObject();
8854 /* initialize the snapshot */
8855 rc = pSnapshot->init(mParent, // VirtualBox object
8856 data.uuid,
8857 data.strName,
8858 data.strDescription,
8859 data.timestamp,
8860 pSnapshotMachine,
8861 aParentSnapshot);
8862 if (FAILED(rc)) return rc;
8863
8864 /* memorize the first snapshot if necessary */
8865 if (!mData->mFirstSnapshot)
8866 mData->mFirstSnapshot = pSnapshot;
8867
8868 /* memorize the current snapshot when appropriate */
8869 if ( !mData->mCurrentSnapshot
8870 && pSnapshot->i_getId() == aCurSnapshotId
8871 )
8872 mData->mCurrentSnapshot = pSnapshot;
8873
8874 // now create the children
8875 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8876 it != data.llChildSnapshots.end();
8877 ++it)
8878 {
8879 const settings::Snapshot &childData = *it;
8880 // recurse
8881 rc = i_loadSnapshot(childData,
8882 aCurSnapshotId,
8883 pSnapshot); // parent = the one we created above
8884 if (FAILED(rc)) return rc;
8885 }
8886
8887 return rc;
8888}
8889
8890/**
8891 * Loads settings into mHWData.
8892 *
8893 * @param data Reference to the hardware settings.
8894 * @param pDbg Pointer to the debugging settings.
8895 * @param pAutostart Pointer to the autostart settings.
8896 */
8897HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8898 const Guid *puuidSnapshot,
8899 const settings::Hardware &data,
8900 const settings::Debugging *pDbg,
8901 const settings::Autostart *pAutostart)
8902{
8903 AssertReturn(!i_isSessionMachine(), E_FAIL);
8904
8905 HRESULT rc = S_OK;
8906
8907 try
8908 {
8909 /* The hardware version attribute (optional). */
8910 mHWData->mHWVersion = data.strVersion;
8911 mHWData->mHardwareUUID = data.uuid;
8912
8913 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8914 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8915 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8916 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8917 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8918 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8919 mHWData->mPAEEnabled = data.fPAE;
8920 mHWData->mLongMode = data.enmLongMode;
8921 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8922 mHWData->mAPIC = data.fAPIC;
8923 mHWData->mX2APIC = data.fX2APIC;
8924 mHWData->mCPUCount = data.cCPUs;
8925 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8926 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8927 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8928 mHWData->mCpuProfile = data.strCpuProfile;
8929
8930 // cpu
8931 if (mHWData->mCPUHotPlugEnabled)
8932 {
8933 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8934 it != data.llCpus.end();
8935 ++it)
8936 {
8937 const settings::Cpu &cpu = *it;
8938
8939 mHWData->mCPUAttached[cpu.ulId] = true;
8940 }
8941 }
8942
8943 // cpuid leafs
8944 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8945 it != data.llCpuIdLeafs.end();
8946 ++it)
8947 {
8948 const settings::CpuIdLeaf &leaf = *it;
8949
8950 switch (leaf.ulId)
8951 {
8952 case 0x0:
8953 case 0x1:
8954 case 0x2:
8955 case 0x3:
8956 case 0x4:
8957 case 0x5:
8958 case 0x6:
8959 case 0x7:
8960 case 0x8:
8961 case 0x9:
8962 case 0xA:
8963 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8964 break;
8965
8966 case 0x80000000:
8967 case 0x80000001:
8968 case 0x80000002:
8969 case 0x80000003:
8970 case 0x80000004:
8971 case 0x80000005:
8972 case 0x80000006:
8973 case 0x80000007:
8974 case 0x80000008:
8975 case 0x80000009:
8976 case 0x8000000A:
8977 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8978 break;
8979
8980 default:
8981 /* just ignore */
8982 break;
8983 }
8984 }
8985
8986 mHWData->mMemorySize = data.ulMemorySizeMB;
8987 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8988
8989 // boot order
8990 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8991 {
8992 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8993 if (it == data.mapBootOrder.end())
8994 mHWData->mBootOrder[i] = DeviceType_Null;
8995 else
8996 mHWData->mBootOrder[i] = it->second;
8997 }
8998
8999 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9000 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9001 mHWData->mMonitorCount = data.cMonitors;
9002 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9003 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9004 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9005 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9006 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9007 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9008 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9009 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9010 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9011 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9012 if (!data.strVideoCaptureFile.isEmpty())
9013 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9014 else
9015 mHWData->mVideoCaptureFile.setNull();
9016 mHWData->mFirmwareType = data.firmwareType;
9017 mHWData->mPointingHIDType = data.pointingHIDType;
9018 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9019 mHWData->mChipsetType = data.chipsetType;
9020 mHWData->mParavirtProvider = data.paravirtProvider;
9021 mHWData->mParavirtDebug = data.strParavirtDebug;
9022 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9023 mHWData->mHPETEnabled = data.fHPETEnabled;
9024
9025 /* VRDEServer */
9026 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9027 if (FAILED(rc)) return rc;
9028
9029 /* BIOS */
9030 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9031 if (FAILED(rc)) return rc;
9032
9033 // Bandwidth control (must come before network adapters)
9034 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9035 if (FAILED(rc)) return rc;
9036
9037 /* Shared folders */
9038 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9039 it != data.usbSettings.llUSBControllers.end();
9040 ++it)
9041 {
9042 const settings::USBController &settingsCtrl = *it;
9043 ComObjPtr<USBController> newCtrl;
9044
9045 newCtrl.createObject();
9046 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9047 mUSBControllers->push_back(newCtrl);
9048 }
9049
9050 /* USB device filters */
9051 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9052 if (FAILED(rc)) return rc;
9053
9054 // network adapters
9055 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9056 size_t oldCount = mNetworkAdapters.size();
9057 if (newCount > oldCount)
9058 {
9059 mNetworkAdapters.resize(newCount);
9060 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9061 {
9062 unconst(mNetworkAdapters[slot]).createObject();
9063 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9064 }
9065 }
9066 else if (newCount < oldCount)
9067 mNetworkAdapters.resize(newCount);
9068 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9069 it != data.llNetworkAdapters.end();
9070 ++it)
9071 {
9072 const settings::NetworkAdapter &nic = *it;
9073
9074 /* slot unicity is guaranteed by XML Schema */
9075 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9076 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9077 if (FAILED(rc)) return rc;
9078 }
9079
9080 // serial ports
9081 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9082 it != data.llSerialPorts.end();
9083 ++it)
9084 {
9085 const settings::SerialPort &s = *it;
9086
9087 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9088 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9089 if (FAILED(rc)) return rc;
9090 }
9091
9092 // parallel ports (optional)
9093 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9094 it != data.llParallelPorts.end();
9095 ++it)
9096 {
9097 const settings::ParallelPort &p = *it;
9098
9099 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9100 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9101 if (FAILED(rc)) return rc;
9102 }
9103
9104 /* AudioAdapter */
9105 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9106 if (FAILED(rc)) return rc;
9107
9108 /* storage controllers */
9109 rc = i_loadStorageControllers(data.storage,
9110 puuidRegistry,
9111 puuidSnapshot);
9112 if (FAILED(rc)) return rc;
9113
9114 /* Shared folders */
9115 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9116 it != data.llSharedFolders.end();
9117 ++it)
9118 {
9119 const settings::SharedFolder &sf = *it;
9120
9121 ComObjPtr<SharedFolder> sharedFolder;
9122 /* Check for double entries. Not allowed! */
9123 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9124 if (SUCCEEDED(rc))
9125 return setError(VBOX_E_OBJECT_IN_USE,
9126 tr("Shared folder named '%s' already exists"),
9127 sf.strName.c_str());
9128
9129 /* Create the new shared folder. Don't break on error. This will be
9130 * reported when the machine starts. */
9131 sharedFolder.createObject();
9132 rc = sharedFolder->init(i_getMachine(),
9133 sf.strName,
9134 sf.strHostPath,
9135 RT_BOOL(sf.fWritable),
9136 RT_BOOL(sf.fAutoMount),
9137 false /* fFailOnError */);
9138 if (FAILED(rc)) return rc;
9139 mHWData->mSharedFolders.push_back(sharedFolder);
9140 }
9141
9142 // Clipboard
9143 mHWData->mClipboardMode = data.clipboardMode;
9144
9145 // drag'n'drop
9146 mHWData->mDnDMode = data.dndMode;
9147
9148 // guest settings
9149 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9150
9151 // IO settings
9152 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9153 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9154
9155 // Host PCI devices
9156 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9157 it != data.pciAttachments.end();
9158 ++it)
9159 {
9160 const settings::HostPCIDeviceAttachment &hpda = *it;
9161 ComObjPtr<PCIDeviceAttachment> pda;
9162
9163 pda.createObject();
9164 pda->i_loadSettings(this, hpda);
9165 mHWData->mPCIDeviceAssignments.push_back(pda);
9166 }
9167
9168 /*
9169 * (The following isn't really real hardware, but it lives in HWData
9170 * for reasons of convenience.)
9171 */
9172
9173#ifdef VBOX_WITH_GUEST_PROPS
9174 /* Guest properties (optional) */
9175
9176 /* Only load transient guest properties for configs which have saved
9177 * state, because there shouldn't be any for powered off VMs. The same
9178 * logic applies for snapshots, as offline snapshots shouldn't have
9179 * any such properties. They confuse the code in various places.
9180 * Note: can't rely on the machine state, as it isn't set yet. */
9181 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9182 /* apologies for the hacky unconst() usage, but this needs hacking
9183 * actually inconsistent settings into consistency, otherwise there
9184 * will be some corner cases where the inconsistency survives
9185 * surprisingly long without getting fixed, especially for snapshots
9186 * as there are no config changes. */
9187 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9188 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9189 it != llGuestProperties.end();
9190 /*nothing*/)
9191 {
9192 const settings::GuestProperty &prop = *it;
9193 uint32_t fFlags = guestProp::NILFLAG;
9194 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9195 if ( fSkipTransientGuestProperties
9196 && ( fFlags & guestProp::TRANSIENT
9197 || fFlags & guestProp::TRANSRESET))
9198 {
9199 it = llGuestProperties.erase(it);
9200 continue;
9201 }
9202 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9203 mHWData->mGuestProperties[prop.strName] = property;
9204 ++it;
9205 }
9206#endif /* VBOX_WITH_GUEST_PROPS defined */
9207
9208 rc = i_loadDebugging(pDbg);
9209 if (FAILED(rc))
9210 return rc;
9211
9212 mHWData->mAutostart = *pAutostart;
9213
9214 /* default frontend */
9215 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9216 }
9217 catch(std::bad_alloc &)
9218 {
9219 return E_OUTOFMEMORY;
9220 }
9221
9222 AssertComRC(rc);
9223 return rc;
9224}
9225
9226/**
9227 * Called from Machine::loadHardware() to load the debugging settings of the
9228 * machine.
9229 *
9230 * @param pDbg Pointer to the settings.
9231 */
9232HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9233{
9234 mHWData->mDebugging = *pDbg;
9235 /* no more processing currently required, this will probably change. */
9236 return S_OK;
9237}
9238
9239/**
9240 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9241 *
9242 * @param data
9243 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9244 * @param puuidSnapshot
9245 * @return
9246 */
9247HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9248 const Guid *puuidRegistry,
9249 const Guid *puuidSnapshot)
9250{
9251 AssertReturn(!i_isSessionMachine(), E_FAIL);
9252
9253 HRESULT rc = S_OK;
9254
9255 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9256 it != data.llStorageControllers.end();
9257 ++it)
9258 {
9259 const settings::StorageController &ctlData = *it;
9260
9261 ComObjPtr<StorageController> pCtl;
9262 /* Try to find one with the name first. */
9263 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9264 if (SUCCEEDED(rc))
9265 return setError(VBOX_E_OBJECT_IN_USE,
9266 tr("Storage controller named '%s' already exists"),
9267 ctlData.strName.c_str());
9268
9269 pCtl.createObject();
9270 rc = pCtl->init(this,
9271 ctlData.strName,
9272 ctlData.storageBus,
9273 ctlData.ulInstance,
9274 ctlData.fBootable);
9275 if (FAILED(rc)) return rc;
9276
9277 mStorageControllers->push_back(pCtl);
9278
9279 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9280 if (FAILED(rc)) return rc;
9281
9282 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9283 if (FAILED(rc)) return rc;
9284
9285 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9286 if (FAILED(rc)) return rc;
9287
9288 /* Load the attached devices now. */
9289 rc = i_loadStorageDevices(pCtl,
9290 ctlData,
9291 puuidRegistry,
9292 puuidSnapshot);
9293 if (FAILED(rc)) return rc;
9294 }
9295
9296 return S_OK;
9297}
9298
9299/**
9300 * Called from i_loadStorageControllers for a controller's devices.
9301 *
9302 * @param aStorageController
9303 * @param data
9304 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9305 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9306 * @return
9307 */
9308HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9309 const settings::StorageController &data,
9310 const Guid *puuidRegistry,
9311 const Guid *puuidSnapshot)
9312{
9313 HRESULT rc = S_OK;
9314
9315 /* paranoia: detect duplicate attachments */
9316 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9317 it != data.llAttachedDevices.end();
9318 ++it)
9319 {
9320 const settings::AttachedDevice &ad = *it;
9321
9322 for (settings::AttachedDevicesList::const_iterator it2 = it;
9323 it2 != data.llAttachedDevices.end();
9324 ++it2)
9325 {
9326 if (it == it2)
9327 continue;
9328
9329 const settings::AttachedDevice &ad2 = *it2;
9330
9331 if ( ad.lPort == ad2.lPort
9332 && ad.lDevice == ad2.lDevice)
9333 {
9334 return setError(E_FAIL,
9335 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9336 aStorageController->i_getName().c_str(),
9337 ad.lPort,
9338 ad.lDevice,
9339 mUserData->s.strName.c_str());
9340 }
9341 }
9342 }
9343
9344 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9345 it != data.llAttachedDevices.end();
9346 ++it)
9347 {
9348 const settings::AttachedDevice &dev = *it;
9349 ComObjPtr<Medium> medium;
9350
9351 switch (dev.deviceType)
9352 {
9353 case DeviceType_Floppy:
9354 case DeviceType_DVD:
9355 if (dev.strHostDriveSrc.isNotEmpty())
9356 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9357 false /* fRefresh */, medium);
9358 else
9359 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9360 dev.uuid,
9361 false /* fRefresh */,
9362 false /* aSetError */,
9363 medium);
9364 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9365 // This is not an error. The host drive or UUID might have vanished, so just go
9366 // ahead without this removeable medium attachment
9367 rc = S_OK;
9368 break;
9369
9370 case DeviceType_HardDisk:
9371 {
9372 /* find a hard disk by UUID */
9373 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9374 if (FAILED(rc))
9375 {
9376 if (i_isSnapshotMachine())
9377 {
9378 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9379 // so the user knows that the bad disk is in a snapshot somewhere
9380 com::ErrorInfo info;
9381 return setError(E_FAIL,
9382 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9383 puuidSnapshot->raw(),
9384 info.getText().raw());
9385 }
9386 else
9387 return rc;
9388 }
9389
9390 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9391
9392 if (medium->i_getType() == MediumType_Immutable)
9393 {
9394 if (i_isSnapshotMachine())
9395 return setError(E_FAIL,
9396 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9397 "of the virtual machine '%s' ('%s')"),
9398 medium->i_getLocationFull().c_str(),
9399 dev.uuid.raw(),
9400 puuidSnapshot->raw(),
9401 mUserData->s.strName.c_str(),
9402 mData->m_strConfigFileFull.c_str());
9403
9404 return setError(E_FAIL,
9405 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9406 medium->i_getLocationFull().c_str(),
9407 dev.uuid.raw(),
9408 mUserData->s.strName.c_str(),
9409 mData->m_strConfigFileFull.c_str());
9410 }
9411
9412 if (medium->i_getType() == MediumType_MultiAttach)
9413 {
9414 if (i_isSnapshotMachine())
9415 return setError(E_FAIL,
9416 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9417 "of the virtual machine '%s' ('%s')"),
9418 medium->i_getLocationFull().c_str(),
9419 dev.uuid.raw(),
9420 puuidSnapshot->raw(),
9421 mUserData->s.strName.c_str(),
9422 mData->m_strConfigFileFull.c_str());
9423
9424 return setError(E_FAIL,
9425 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9426 medium->i_getLocationFull().c_str(),
9427 dev.uuid.raw(),
9428 mUserData->s.strName.c_str(),
9429 mData->m_strConfigFileFull.c_str());
9430 }
9431
9432 if ( !i_isSnapshotMachine()
9433 && medium->i_getChildren().size() != 0
9434 )
9435 return setError(E_FAIL,
9436 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9437 "because it has %d differencing child hard disks"),
9438 medium->i_getLocationFull().c_str(),
9439 dev.uuid.raw(),
9440 mUserData->s.strName.c_str(),
9441 mData->m_strConfigFileFull.c_str(),
9442 medium->i_getChildren().size());
9443
9444 if (i_findAttachment(mMediaData->mAttachments,
9445 medium))
9446 return setError(E_FAIL,
9447 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9448 medium->i_getLocationFull().c_str(),
9449 dev.uuid.raw(),
9450 mUserData->s.strName.c_str(),
9451 mData->m_strConfigFileFull.c_str());
9452
9453 break;
9454 }
9455
9456 default:
9457 return setError(E_FAIL,
9458 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9459 medium->i_getLocationFull().c_str(),
9460 mUserData->s.strName.c_str(),
9461 mData->m_strConfigFileFull.c_str());
9462 }
9463
9464 if (FAILED(rc))
9465 break;
9466
9467 /* Bandwidth groups are loaded at this point. */
9468 ComObjPtr<BandwidthGroup> pBwGroup;
9469
9470 if (!dev.strBwGroup.isEmpty())
9471 {
9472 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9473 if (FAILED(rc))
9474 return setError(E_FAIL,
9475 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9476 medium->i_getLocationFull().c_str(),
9477 dev.strBwGroup.c_str(),
9478 mUserData->s.strName.c_str(),
9479 mData->m_strConfigFileFull.c_str());
9480 pBwGroup->i_reference();
9481 }
9482
9483 const Bstr controllerName = aStorageController->i_getName();
9484 ComObjPtr<MediumAttachment> pAttachment;
9485 pAttachment.createObject();
9486 rc = pAttachment->init(this,
9487 medium,
9488 controllerName,
9489 dev.lPort,
9490 dev.lDevice,
9491 dev.deviceType,
9492 false,
9493 dev.fPassThrough,
9494 dev.fTempEject,
9495 dev.fNonRotational,
9496 dev.fDiscard,
9497 dev.fHotPluggable,
9498 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9499 if (FAILED(rc)) break;
9500
9501 /* associate the medium with this machine and snapshot */
9502 if (!medium.isNull())
9503 {
9504 AutoCaller medCaller(medium);
9505 if (FAILED(medCaller.rc())) return medCaller.rc();
9506 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9507
9508 if (i_isSnapshotMachine())
9509 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9510 else
9511 rc = medium->i_addBackReference(mData->mUuid);
9512 /* If the medium->addBackReference fails it sets an appropriate
9513 * error message, so no need to do any guesswork here. */
9514
9515 if (puuidRegistry)
9516 // caller wants registry ID to be set on all attached media (OVF import case)
9517 medium->i_addRegistry(*puuidRegistry);
9518 }
9519
9520 if (FAILED(rc))
9521 break;
9522
9523 /* back up mMediaData to let registeredInit() properly rollback on failure
9524 * (= limited accessibility) */
9525 i_setModified(IsModified_Storage);
9526 mMediaData.backup();
9527 mMediaData->mAttachments.push_back(pAttachment);
9528 }
9529
9530 return rc;
9531}
9532
9533/**
9534 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9535 *
9536 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9537 * @param aSnapshot where to return the found snapshot
9538 * @param aSetError true to set extended error info on failure
9539 */
9540HRESULT Machine::i_findSnapshotById(const Guid &aId,
9541 ComObjPtr<Snapshot> &aSnapshot,
9542 bool aSetError /* = false */)
9543{
9544 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9545
9546 if (!mData->mFirstSnapshot)
9547 {
9548 if (aSetError)
9549 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9550 return E_FAIL;
9551 }
9552
9553 if (aId.isZero())
9554 aSnapshot = mData->mFirstSnapshot;
9555 else
9556 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9557
9558 if (!aSnapshot)
9559 {
9560 if (aSetError)
9561 return setError(E_FAIL,
9562 tr("Could not find a snapshot with UUID {%s}"),
9563 aId.toString().c_str());
9564 return E_FAIL;
9565 }
9566
9567 return S_OK;
9568}
9569
9570/**
9571 * Returns the snapshot with the given name or fails of no such snapshot.
9572 *
9573 * @param aName snapshot name to find
9574 * @param aSnapshot where to return the found snapshot
9575 * @param aSetError true to set extended error info on failure
9576 */
9577HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9578 ComObjPtr<Snapshot> &aSnapshot,
9579 bool aSetError /* = false */)
9580{
9581 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9582
9583 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9584
9585 if (!mData->mFirstSnapshot)
9586 {
9587 if (aSetError)
9588 return setError(VBOX_E_OBJECT_NOT_FOUND,
9589 tr("This machine does not have any snapshots"));
9590 return VBOX_E_OBJECT_NOT_FOUND;
9591 }
9592
9593 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9594
9595 if (!aSnapshot)
9596 {
9597 if (aSetError)
9598 return setError(VBOX_E_OBJECT_NOT_FOUND,
9599 tr("Could not find a snapshot named '%s'"), strName.c_str());
9600 return VBOX_E_OBJECT_NOT_FOUND;
9601 }
9602
9603 return S_OK;
9604}
9605
9606/**
9607 * Returns a storage controller object with the given name.
9608 *
9609 * @param aName storage controller name to find
9610 * @param aStorageController where to return the found storage controller
9611 * @param aSetError true to set extended error info on failure
9612 */
9613HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9614 ComObjPtr<StorageController> &aStorageController,
9615 bool aSetError /* = false */)
9616{
9617 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9618
9619 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9620 it != mStorageControllers->end();
9621 ++it)
9622 {
9623 if ((*it)->i_getName() == aName)
9624 {
9625 aStorageController = (*it);
9626 return S_OK;
9627 }
9628 }
9629
9630 if (aSetError)
9631 return setError(VBOX_E_OBJECT_NOT_FOUND,
9632 tr("Could not find a storage controller named '%s'"),
9633 aName.c_str());
9634 return VBOX_E_OBJECT_NOT_FOUND;
9635}
9636
9637/**
9638 * Returns a USB controller object with the given name.
9639 *
9640 * @param aName USB controller name to find
9641 * @param aUSBController where to return the found USB controller
9642 * @param aSetError true to set extended error info on failure
9643 */
9644HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9645 ComObjPtr<USBController> &aUSBController,
9646 bool aSetError /* = false */)
9647{
9648 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9649
9650 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9651 it != mUSBControllers->end();
9652 ++it)
9653 {
9654 if ((*it)->i_getName() == aName)
9655 {
9656 aUSBController = (*it);
9657 return S_OK;
9658 }
9659 }
9660
9661 if (aSetError)
9662 return setError(VBOX_E_OBJECT_NOT_FOUND,
9663 tr("Could not find a storage controller named '%s'"),
9664 aName.c_str());
9665 return VBOX_E_OBJECT_NOT_FOUND;
9666}
9667
9668/**
9669 * Returns the number of USB controller instance of the given type.
9670 *
9671 * @param enmType USB controller type.
9672 */
9673ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9674{
9675 ULONG cCtrls = 0;
9676
9677 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9678 it != mUSBControllers->end();
9679 ++it)
9680 {
9681 if ((*it)->i_getControllerType() == enmType)
9682 cCtrls++;
9683 }
9684
9685 return cCtrls;
9686}
9687
9688HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9689 MediaData::AttachmentList &atts)
9690{
9691 AutoCaller autoCaller(this);
9692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9693
9694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9695
9696 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9697 it != mMediaData->mAttachments.end();
9698 ++it)
9699 {
9700 const ComObjPtr<MediumAttachment> &pAtt = *it;
9701 // should never happen, but deal with NULL pointers in the list.
9702 AssertContinue(!pAtt.isNull());
9703
9704 // getControllerName() needs caller+read lock
9705 AutoCaller autoAttCaller(pAtt);
9706 if (FAILED(autoAttCaller.rc()))
9707 {
9708 atts.clear();
9709 return autoAttCaller.rc();
9710 }
9711 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9712
9713 if (pAtt->i_getControllerName() == aName)
9714 atts.push_back(pAtt);
9715 }
9716
9717 return S_OK;
9718}
9719
9720
9721/**
9722 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9723 * file if the machine name was changed and about creating a new settings file
9724 * if this is a new machine.
9725 *
9726 * @note Must be never called directly but only from #saveSettings().
9727 */
9728HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9729{
9730 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9731
9732 HRESULT rc = S_OK;
9733
9734 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9735
9736 /// @todo need to handle primary group change, too
9737
9738 /* attempt to rename the settings file if machine name is changed */
9739 if ( mUserData->s.fNameSync
9740 && mUserData.isBackedUp()
9741 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9742 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9743 )
9744 {
9745 bool dirRenamed = false;
9746 bool fileRenamed = false;
9747
9748 Utf8Str configFile, newConfigFile;
9749 Utf8Str configFilePrev, newConfigFilePrev;
9750 Utf8Str configDir, newConfigDir;
9751
9752 do
9753 {
9754 int vrc = VINF_SUCCESS;
9755
9756 Utf8Str name = mUserData.backedUpData()->s.strName;
9757 Utf8Str newName = mUserData->s.strName;
9758 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9759 if (group == "/")
9760 group.setNull();
9761 Utf8Str newGroup = mUserData->s.llGroups.front();
9762 if (newGroup == "/")
9763 newGroup.setNull();
9764
9765 configFile = mData->m_strConfigFileFull;
9766
9767 /* first, rename the directory if it matches the group and machine name */
9768 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9769 group.c_str(), RTPATH_DELIMITER, name.c_str());
9770 /** @todo hack, make somehow use of ComposeMachineFilename */
9771 if (mUserData->s.fDirectoryIncludesUUID)
9772 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9773 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9774 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9775 /** @todo hack, make somehow use of ComposeMachineFilename */
9776 if (mUserData->s.fDirectoryIncludesUUID)
9777 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9778 configDir = configFile;
9779 configDir.stripFilename();
9780 newConfigDir = configDir;
9781 if ( configDir.length() >= groupPlusName.length()
9782 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9783 groupPlusName.c_str()))
9784 {
9785 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9786 Utf8Str newConfigBaseDir(newConfigDir);
9787 newConfigDir.append(newGroupPlusName);
9788 /* consistency: use \ if appropriate on the platform */
9789 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9790 /* new dir and old dir cannot be equal here because of 'if'
9791 * above and because name != newName */
9792 Assert(configDir != newConfigDir);
9793 if (!fSettingsFileIsNew)
9794 {
9795 /* perform real rename only if the machine is not new */
9796 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9797 if ( vrc == VERR_FILE_NOT_FOUND
9798 || vrc == VERR_PATH_NOT_FOUND)
9799 {
9800 /* create the parent directory, then retry renaming */
9801 Utf8Str parent(newConfigDir);
9802 parent.stripFilename();
9803 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9804 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9805 }
9806 if (RT_FAILURE(vrc))
9807 {
9808 rc = setError(E_FAIL,
9809 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9810 configDir.c_str(),
9811 newConfigDir.c_str(),
9812 vrc);
9813 break;
9814 }
9815 /* delete subdirectories which are no longer needed */
9816 Utf8Str dir(configDir);
9817 dir.stripFilename();
9818 while (dir != newConfigBaseDir && dir != ".")
9819 {
9820 vrc = RTDirRemove(dir.c_str());
9821 if (RT_FAILURE(vrc))
9822 break;
9823 dir.stripFilename();
9824 }
9825 dirRenamed = true;
9826 }
9827 }
9828
9829 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9830 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9831
9832 /* then try to rename the settings file itself */
9833 if (newConfigFile != configFile)
9834 {
9835 /* get the path to old settings file in renamed directory */
9836 configFile = Utf8StrFmt("%s%c%s",
9837 newConfigDir.c_str(),
9838 RTPATH_DELIMITER,
9839 RTPathFilename(configFile.c_str()));
9840 if (!fSettingsFileIsNew)
9841 {
9842 /* perform real rename only if the machine is not new */
9843 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9844 if (RT_FAILURE(vrc))
9845 {
9846 rc = setError(E_FAIL,
9847 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9848 configFile.c_str(),
9849 newConfigFile.c_str(),
9850 vrc);
9851 break;
9852 }
9853 fileRenamed = true;
9854 configFilePrev = configFile;
9855 configFilePrev += "-prev";
9856 newConfigFilePrev = newConfigFile;
9857 newConfigFilePrev += "-prev";
9858 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9859 }
9860 }
9861
9862 // update m_strConfigFileFull amd mConfigFile
9863 mData->m_strConfigFileFull = newConfigFile;
9864 // compute the relative path too
9865 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9866
9867 // store the old and new so that VirtualBox::i_saveSettings() can update
9868 // the media registry
9869 if ( mData->mRegistered
9870 && (configDir != newConfigDir || configFile != newConfigFile))
9871 {
9872 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9873
9874 if (pfNeedsGlobalSaveSettings)
9875 *pfNeedsGlobalSaveSettings = true;
9876 }
9877
9878 // in the saved state file path, replace the old directory with the new directory
9879 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9880 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9881
9882 // and do the same thing for the saved state file paths of all the online snapshots
9883 if (mData->mFirstSnapshot)
9884 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9885 newConfigDir.c_str());
9886 }
9887 while (0);
9888
9889 if (FAILED(rc))
9890 {
9891 /* silently try to rename everything back */
9892 if (fileRenamed)
9893 {
9894 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9895 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9896 }
9897 if (dirRenamed)
9898 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9899 }
9900
9901 if (FAILED(rc)) return rc;
9902 }
9903
9904 if (fSettingsFileIsNew)
9905 {
9906 /* create a virgin config file */
9907 int vrc = VINF_SUCCESS;
9908
9909 /* ensure the settings directory exists */
9910 Utf8Str path(mData->m_strConfigFileFull);
9911 path.stripFilename();
9912 if (!RTDirExists(path.c_str()))
9913 {
9914 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9915 if (RT_FAILURE(vrc))
9916 {
9917 return setError(E_FAIL,
9918 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9919 path.c_str(),
9920 vrc);
9921 }
9922 }
9923
9924 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9925 path = Utf8Str(mData->m_strConfigFileFull);
9926 RTFILE f = NIL_RTFILE;
9927 vrc = RTFileOpen(&f, path.c_str(),
9928 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9929 if (RT_FAILURE(vrc))
9930 return setError(E_FAIL,
9931 tr("Could not create the settings file '%s' (%Rrc)"),
9932 path.c_str(),
9933 vrc);
9934 RTFileClose(f);
9935 }
9936
9937 return rc;
9938}
9939
9940/**
9941 * Saves and commits machine data, user data and hardware data.
9942 *
9943 * Note that on failure, the data remains uncommitted.
9944 *
9945 * @a aFlags may combine the following flags:
9946 *
9947 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9948 * Used when saving settings after an operation that makes them 100%
9949 * correspond to the settings from the current snapshot.
9950 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9951 * #isReallyModified() returns false. This is necessary for cases when we
9952 * change machine data directly, not through the backup()/commit() mechanism.
9953 * - SaveS_Force: settings will be saved without doing a deep compare of the
9954 * settings structures. This is used when this is called because snapshots
9955 * have changed to avoid the overhead of the deep compare.
9956 *
9957 * @note Must be called from under this object's write lock. Locks children for
9958 * writing.
9959 *
9960 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9961 * initialized to false and that will be set to true by this function if
9962 * the caller must invoke VirtualBox::i_saveSettings() because the global
9963 * settings have changed. This will happen if a machine rename has been
9964 * saved and the global machine and media registries will therefore need
9965 * updating.
9966 */
9967HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9968 int aFlags /*= 0*/)
9969{
9970 LogFlowThisFuncEnter();
9971
9972 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9973
9974 /* make sure child objects are unable to modify the settings while we are
9975 * saving them */
9976 i_ensureNoStateDependencies();
9977
9978 AssertReturn(!i_isSnapshotMachine(),
9979 E_FAIL);
9980
9981 HRESULT rc = S_OK;
9982 bool fNeedsWrite = false;
9983
9984 /* First, prepare to save settings. It will care about renaming the
9985 * settings directory and file if the machine name was changed and about
9986 * creating a new settings file if this is a new machine. */
9987 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9988 if (FAILED(rc)) return rc;
9989
9990 // keep a pointer to the current settings structures
9991 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9992 settings::MachineConfigFile *pNewConfig = NULL;
9993
9994 try
9995 {
9996 // make a fresh one to have everyone write stuff into
9997 pNewConfig = new settings::MachineConfigFile(NULL);
9998 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9999
10000 // now go and copy all the settings data from COM to the settings structures
10001 // (this calls i_saveSettings() on all the COM objects in the machine)
10002 i_copyMachineDataToSettings(*pNewConfig);
10003
10004 if (aFlags & SaveS_ResetCurStateModified)
10005 {
10006 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10007 mData->mCurrentStateModified = FALSE;
10008 fNeedsWrite = true; // always, no need to compare
10009 }
10010 else if (aFlags & SaveS_Force)
10011 {
10012 fNeedsWrite = true; // always, no need to compare
10013 }
10014 else
10015 {
10016 if (!mData->mCurrentStateModified)
10017 {
10018 // do a deep compare of the settings that we just saved with the settings
10019 // previously stored in the config file; this invokes MachineConfigFile::operator==
10020 // which does a deep compare of all the settings, which is expensive but less expensive
10021 // than writing out XML in vain
10022 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10023
10024 // could still be modified if any settings changed
10025 mData->mCurrentStateModified = fAnySettingsChanged;
10026
10027 fNeedsWrite = fAnySettingsChanged;
10028 }
10029 else
10030 fNeedsWrite = true;
10031 }
10032
10033 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10034
10035 if (fNeedsWrite)
10036 // now spit it all out!
10037 pNewConfig->write(mData->m_strConfigFileFull);
10038
10039 mData->pMachineConfigFile = pNewConfig;
10040 delete pOldConfig;
10041 i_commit();
10042
10043 // after saving settings, we are no longer different from the XML on disk
10044 mData->flModifications = 0;
10045 }
10046 catch (HRESULT err)
10047 {
10048 // we assume that error info is set by the thrower
10049 rc = err;
10050
10051 // restore old config
10052 delete pNewConfig;
10053 mData->pMachineConfigFile = pOldConfig;
10054 }
10055 catch (...)
10056 {
10057 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10058 }
10059
10060 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10061 {
10062 /* Fire the data change event, even on failure (since we've already
10063 * committed all data). This is done only for SessionMachines because
10064 * mutable Machine instances are always not registered (i.e. private
10065 * to the client process that creates them) and thus don't need to
10066 * inform callbacks. */
10067 if (i_isSessionMachine())
10068 mParent->i_onMachineDataChange(mData->mUuid);
10069 }
10070
10071 LogFlowThisFunc(("rc=%08X\n", rc));
10072 LogFlowThisFuncLeave();
10073 return rc;
10074}
10075
10076/**
10077 * Implementation for saving the machine settings into the given
10078 * settings::MachineConfigFile instance. This copies machine extradata
10079 * from the previous machine config file in the instance data, if any.
10080 *
10081 * This gets called from two locations:
10082 *
10083 * -- Machine::i_saveSettings(), during the regular XML writing;
10084 *
10085 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10086 * exported to OVF and we write the VirtualBox proprietary XML
10087 * into a <vbox:Machine> tag.
10088 *
10089 * This routine fills all the fields in there, including snapshots, *except*
10090 * for the following:
10091 *
10092 * -- fCurrentStateModified. There is some special logic associated with that.
10093 *
10094 * The caller can then call MachineConfigFile::write() or do something else
10095 * with it.
10096 *
10097 * Caller must hold the machine lock!
10098 *
10099 * This throws XML errors and HRESULT, so the caller must have a catch block!
10100 */
10101void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10102{
10103 // deep copy extradata, being extra careful with self assignment (the STL
10104 // map assignment on Mac OS X clang based Xcode isn't checking)
10105 if (&config != mData->pMachineConfigFile)
10106 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10107
10108 config.uuid = mData->mUuid;
10109
10110 // copy name, description, OS type, teleport, UTC etc.
10111 config.machineUserData = mUserData->s;
10112
10113 if ( mData->mMachineState == MachineState_Saved
10114 || mData->mMachineState == MachineState_Restoring
10115 // when doing certain snapshot operations we may or may not have
10116 // a saved state in the current state, so keep everything as is
10117 || ( ( mData->mMachineState == MachineState_Snapshotting
10118 || mData->mMachineState == MachineState_DeletingSnapshot
10119 || mData->mMachineState == MachineState_RestoringSnapshot)
10120 && (!mSSData->strStateFilePath.isEmpty())
10121 )
10122 )
10123 {
10124 Assert(!mSSData->strStateFilePath.isEmpty());
10125 /* try to make the file name relative to the settings file dir */
10126 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10127 }
10128 else
10129 {
10130 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10131 config.strStateFile.setNull();
10132 }
10133
10134 if (mData->mCurrentSnapshot)
10135 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10136 else
10137 config.uuidCurrentSnapshot.clear();
10138
10139 config.timeLastStateChange = mData->mLastStateChange;
10140 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10141 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10142
10143 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10144 if (FAILED(rc)) throw rc;
10145
10146 // save machine's media registry if this is VirtualBox 4.0 or later
10147 if (config.canHaveOwnMediaRegistry())
10148 {
10149 // determine machine folder
10150 Utf8Str strMachineFolder = i_getSettingsFileFull();
10151 strMachineFolder.stripFilename();
10152 mParent->i_saveMediaRegistry(config.mediaRegistry,
10153 i_getId(), // only media with registry ID == machine UUID
10154 strMachineFolder);
10155 // this throws HRESULT
10156 }
10157
10158 // save snapshots
10159 rc = i_saveAllSnapshots(config);
10160 if (FAILED(rc)) throw rc;
10161}
10162
10163/**
10164 * Saves all snapshots of the machine into the given machine config file. Called
10165 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10166 * @param config
10167 * @return
10168 */
10169HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10170{
10171 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10172
10173 HRESULT rc = S_OK;
10174
10175 try
10176 {
10177 config.llFirstSnapshot.clear();
10178
10179 if (mData->mFirstSnapshot)
10180 {
10181 // the settings use a list for "the first snapshot"
10182 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10183
10184 // get reference to the snapshot on the list and work on that
10185 // element straight in the list to avoid excessive copying later
10186 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10187 if (FAILED(rc)) throw rc;
10188 }
10189
10190// if (mType == IsSessionMachine)
10191// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10192
10193 }
10194 catch (HRESULT err)
10195 {
10196 /* we assume that error info is set by the thrower */
10197 rc = err;
10198 }
10199 catch (...)
10200 {
10201 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10202 }
10203
10204 return rc;
10205}
10206
10207/**
10208 * Saves the VM hardware configuration. It is assumed that the
10209 * given node is empty.
10210 *
10211 * @param data Reference to the settings object for the hardware config.
10212 * @param pDbg Pointer to the settings object for the debugging config
10213 * which happens to live in mHWData.
10214 * @param pAutostart Pointer to the settings object for the autostart config
10215 * which happens to live in mHWData.
10216 */
10217HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10218 settings::Autostart *pAutostart)
10219{
10220 HRESULT rc = S_OK;
10221
10222 try
10223 {
10224 /* The hardware version attribute (optional).
10225 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10226 if ( mHWData->mHWVersion == "1"
10227 && mSSData->strStateFilePath.isEmpty()
10228 )
10229 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10230 other point needs to be found where this can be done. */
10231
10232 data.strVersion = mHWData->mHWVersion;
10233 data.uuid = mHWData->mHardwareUUID;
10234
10235 // CPU
10236 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10237 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10238 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10239 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10240 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10241 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10242 data.fPAE = !!mHWData->mPAEEnabled;
10243 data.enmLongMode = mHWData->mLongMode;
10244 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10245 data.fAPIC = !!mHWData->mAPIC;
10246 data.fX2APIC = !!mHWData->mX2APIC;
10247 data.cCPUs = mHWData->mCPUCount;
10248 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10249 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10250 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10251 data.strCpuProfile = mHWData->mCpuProfile;
10252
10253 data.llCpus.clear();
10254 if (data.fCpuHotPlug)
10255 {
10256 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10257 {
10258 if (mHWData->mCPUAttached[idx])
10259 {
10260 settings::Cpu cpu;
10261 cpu.ulId = idx;
10262 data.llCpus.push_back(cpu);
10263 }
10264 }
10265 }
10266
10267 /* Standard and Extended CPUID leafs. */
10268 data.llCpuIdLeafs.clear();
10269 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10270 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10271 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10272 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10273 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10274 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10275
10276 // memory
10277 data.ulMemorySizeMB = mHWData->mMemorySize;
10278 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10279
10280 // firmware
10281 data.firmwareType = mHWData->mFirmwareType;
10282
10283 // HID
10284 data.pointingHIDType = mHWData->mPointingHIDType;
10285 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10286
10287 // chipset
10288 data.chipsetType = mHWData->mChipsetType;
10289
10290 // paravirt
10291 data.paravirtProvider = mHWData->mParavirtProvider;
10292 data.strParavirtDebug = mHWData->mParavirtDebug;
10293
10294 // emulated USB card reader
10295 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10296
10297 // HPET
10298 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10299
10300 // boot order
10301 data.mapBootOrder.clear();
10302 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10303 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10304
10305 // display
10306 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10307 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10308 data.cMonitors = mHWData->mMonitorCount;
10309 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10310 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10311 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10312 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10313 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10314 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10315 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10316 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10317 {
10318 if (mHWData->maVideoCaptureScreens[i])
10319 ASMBitSet(&data.u64VideoCaptureScreens, i);
10320 else
10321 ASMBitClear(&data.u64VideoCaptureScreens, i);
10322 }
10323 /* store relative video capture file if possible */
10324 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10325
10326 /* VRDEServer settings (optional) */
10327 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10328 if (FAILED(rc)) throw rc;
10329
10330 /* BIOS (required) */
10331 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10332 if (FAILED(rc)) throw rc;
10333
10334 /* USB Controller (required) */
10335 data.usbSettings.llUSBControllers.clear();
10336 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10337 {
10338 ComObjPtr<USBController> ctrl = *it;
10339 settings::USBController settingsCtrl;
10340
10341 settingsCtrl.strName = ctrl->i_getName();
10342 settingsCtrl.enmType = ctrl->i_getControllerType();
10343
10344 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10345 }
10346
10347 /* USB device filters (required) */
10348 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10349 if (FAILED(rc)) throw rc;
10350
10351 /* Network adapters (required) */
10352 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10353 data.llNetworkAdapters.clear();
10354 /* Write out only the nominal number of network adapters for this
10355 * chipset type. Since Machine::commit() hasn't been called there
10356 * may be extra NIC settings in the vector. */
10357 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10358 {
10359 settings::NetworkAdapter nic;
10360 nic.ulSlot = (uint32_t)slot;
10361 /* paranoia check... must not be NULL, but must not crash either. */
10362 if (mNetworkAdapters[slot])
10363 {
10364 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10365 if (FAILED(rc)) throw rc;
10366
10367 data.llNetworkAdapters.push_back(nic);
10368 }
10369 }
10370
10371 /* Serial ports */
10372 data.llSerialPorts.clear();
10373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10374 {
10375 if (mSerialPorts[slot]->i_hasDefaults())
10376 continue;
10377
10378 settings::SerialPort s;
10379 s.ulSlot = slot;
10380 rc = mSerialPorts[slot]->i_saveSettings(s);
10381 if (FAILED(rc)) return rc;
10382
10383 data.llSerialPorts.push_back(s);
10384 }
10385
10386 /* Parallel ports */
10387 data.llParallelPorts.clear();
10388 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10389 {
10390 if (mParallelPorts[slot]->i_hasDefaults())
10391 continue;
10392
10393 settings::ParallelPort p;
10394 p.ulSlot = slot;
10395 rc = mParallelPorts[slot]->i_saveSettings(p);
10396 if (FAILED(rc)) return rc;
10397
10398 data.llParallelPorts.push_back(p);
10399 }
10400
10401 /* Audio adapter */
10402 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10403 if (FAILED(rc)) return rc;
10404
10405 rc = i_saveStorageControllers(data.storage);
10406 if (FAILED(rc)) return rc;
10407
10408 /* Shared folders */
10409 data.llSharedFolders.clear();
10410 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10411 it != mHWData->mSharedFolders.end();
10412 ++it)
10413 {
10414 SharedFolder *pSF = *it;
10415 AutoCaller sfCaller(pSF);
10416 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10417 settings::SharedFolder sf;
10418 sf.strName = pSF->i_getName();
10419 sf.strHostPath = pSF->i_getHostPath();
10420 sf.fWritable = !!pSF->i_isWritable();
10421 sf.fAutoMount = !!pSF->i_isAutoMounted();
10422
10423 data.llSharedFolders.push_back(sf);
10424 }
10425
10426 // clipboard
10427 data.clipboardMode = mHWData->mClipboardMode;
10428
10429 // drag'n'drop
10430 data.dndMode = mHWData->mDnDMode;
10431
10432 /* Guest */
10433 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10434
10435 // IO settings
10436 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10437 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10438
10439 /* BandwidthControl (required) */
10440 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10441 if (FAILED(rc)) throw rc;
10442
10443 /* Host PCI devices */
10444 data.pciAttachments.clear();
10445 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10446 it != mHWData->mPCIDeviceAssignments.end();
10447 ++it)
10448 {
10449 ComObjPtr<PCIDeviceAttachment> pda = *it;
10450 settings::HostPCIDeviceAttachment hpda;
10451
10452 rc = pda->i_saveSettings(hpda);
10453 if (FAILED(rc)) throw rc;
10454
10455 data.pciAttachments.push_back(hpda);
10456 }
10457
10458 // guest properties
10459 data.llGuestProperties.clear();
10460#ifdef VBOX_WITH_GUEST_PROPS
10461 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10462 it != mHWData->mGuestProperties.end();
10463 ++it)
10464 {
10465 HWData::GuestProperty property = it->second;
10466
10467 /* Remove transient guest properties at shutdown unless we
10468 * are saving state. Note that restoring snapshot intentionally
10469 * keeps them, they will be removed if appropriate once the final
10470 * machine state is set (as crashes etc. need to work). */
10471 if ( ( mData->mMachineState == MachineState_PoweredOff
10472 || mData->mMachineState == MachineState_Aborted
10473 || mData->mMachineState == MachineState_Teleported)
10474 && ( property.mFlags & guestProp::TRANSIENT
10475 || property.mFlags & guestProp::TRANSRESET))
10476 continue;
10477 settings::GuestProperty prop;
10478 prop.strName = it->first;
10479 prop.strValue = property.strValue;
10480 prop.timestamp = property.mTimestamp;
10481 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10482 guestProp::writeFlags(property.mFlags, szFlags);
10483 prop.strFlags = szFlags;
10484
10485 data.llGuestProperties.push_back(prop);
10486 }
10487
10488 /* I presume this doesn't require a backup(). */
10489 mData->mGuestPropertiesModified = FALSE;
10490#endif /* VBOX_WITH_GUEST_PROPS defined */
10491
10492 *pDbg = mHWData->mDebugging;
10493 *pAutostart = mHWData->mAutostart;
10494
10495 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10496 }
10497 catch(std::bad_alloc &)
10498 {
10499 return E_OUTOFMEMORY;
10500 }
10501
10502 AssertComRC(rc);
10503 return rc;
10504}
10505
10506/**
10507 * Saves the storage controller configuration.
10508 *
10509 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10510 */
10511HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10512{
10513 data.llStorageControllers.clear();
10514
10515 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10516 it != mStorageControllers->end();
10517 ++it)
10518 {
10519 HRESULT rc;
10520 ComObjPtr<StorageController> pCtl = *it;
10521
10522 settings::StorageController ctl;
10523 ctl.strName = pCtl->i_getName();
10524 ctl.controllerType = pCtl->i_getControllerType();
10525 ctl.storageBus = pCtl->i_getStorageBus();
10526 ctl.ulInstance = pCtl->i_getInstance();
10527 ctl.fBootable = pCtl->i_getBootable();
10528
10529 /* Save the port count. */
10530 ULONG portCount;
10531 rc = pCtl->COMGETTER(PortCount)(&portCount);
10532 ComAssertComRCRet(rc, rc);
10533 ctl.ulPortCount = portCount;
10534
10535 /* Save fUseHostIOCache */
10536 BOOL fUseHostIOCache;
10537 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10538 ComAssertComRCRet(rc, rc);
10539 ctl.fUseHostIOCache = !!fUseHostIOCache;
10540
10541 /* save the devices now. */
10542 rc = i_saveStorageDevices(pCtl, ctl);
10543 ComAssertComRCRet(rc, rc);
10544
10545 data.llStorageControllers.push_back(ctl);
10546 }
10547
10548 return S_OK;
10549}
10550
10551/**
10552 * Saves the hard disk configuration.
10553 */
10554HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10555 settings::StorageController &data)
10556{
10557 MediaData::AttachmentList atts;
10558
10559 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10560 if (FAILED(rc)) return rc;
10561
10562 data.llAttachedDevices.clear();
10563 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10564 it != atts.end();
10565 ++it)
10566 {
10567 settings::AttachedDevice dev;
10568 IMediumAttachment *iA = *it;
10569 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10570 Medium *pMedium = pAttach->i_getMedium();
10571
10572 dev.deviceType = pAttach->i_getType();
10573 dev.lPort = pAttach->i_getPort();
10574 dev.lDevice = pAttach->i_getDevice();
10575 dev.fPassThrough = pAttach->i_getPassthrough();
10576 dev.fHotPluggable = pAttach->i_getHotPluggable();
10577 if (pMedium)
10578 {
10579 if (pMedium->i_isHostDrive())
10580 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10581 else
10582 dev.uuid = pMedium->i_getId();
10583 dev.fTempEject = pAttach->i_getTempEject();
10584 dev.fNonRotational = pAttach->i_getNonRotational();
10585 dev.fDiscard = pAttach->i_getDiscard();
10586 }
10587
10588 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10589
10590 data.llAttachedDevices.push_back(dev);
10591 }
10592
10593 return S_OK;
10594}
10595
10596/**
10597 * Saves machine state settings as defined by aFlags
10598 * (SaveSTS_* values).
10599 *
10600 * @param aFlags Combination of SaveSTS_* flags.
10601 *
10602 * @note Locks objects for writing.
10603 */
10604HRESULT Machine::i_saveStateSettings(int aFlags)
10605{
10606 if (aFlags == 0)
10607 return S_OK;
10608
10609 AutoCaller autoCaller(this);
10610 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10611
10612 /* This object's write lock is also necessary to serialize file access
10613 * (prevent concurrent reads and writes) */
10614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10615
10616 HRESULT rc = S_OK;
10617
10618 Assert(mData->pMachineConfigFile);
10619
10620 try
10621 {
10622 if (aFlags & SaveSTS_CurStateModified)
10623 mData->pMachineConfigFile->fCurrentStateModified = true;
10624
10625 if (aFlags & SaveSTS_StateFilePath)
10626 {
10627 if (!mSSData->strStateFilePath.isEmpty())
10628 /* try to make the file name relative to the settings file dir */
10629 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10630 else
10631 mData->pMachineConfigFile->strStateFile.setNull();
10632 }
10633
10634 if (aFlags & SaveSTS_StateTimeStamp)
10635 {
10636 Assert( mData->mMachineState != MachineState_Aborted
10637 || mSSData->strStateFilePath.isEmpty());
10638
10639 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10640
10641 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10642/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10643 }
10644
10645 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10646 }
10647 catch (...)
10648 {
10649 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10650 }
10651
10652 return rc;
10653}
10654
10655/**
10656 * Ensures that the given medium is added to a media registry. If this machine
10657 * was created with 4.0 or later, then the machine registry is used. Otherwise
10658 * the global VirtualBox media registry is used.
10659 *
10660 * Caller must NOT hold machine lock, media tree or any medium locks!
10661 *
10662 * @param pMedium
10663 */
10664void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10665{
10666 /* Paranoia checks: do not hold machine or media tree locks. */
10667 AssertReturnVoid(!isWriteLockOnCurrentThread());
10668 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10669
10670 ComObjPtr<Medium> pBase;
10671 {
10672 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10673 pBase = pMedium->i_getBase();
10674 }
10675
10676 /* Paranoia checks: do not hold medium locks. */
10677 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10678 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10679
10680 // decide which medium registry to use now that the medium is attached:
10681 Guid uuid;
10682 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10683 // machine XML is VirtualBox 4.0 or higher:
10684 uuid = i_getId(); // machine UUID
10685 else
10686 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10687
10688 if (pMedium->i_addRegistry(uuid))
10689 mParent->i_markRegistryModified(uuid);
10690
10691 /* For more complex hard disk structures it can happen that the base
10692 * medium isn't yet associated with any medium registry. Do that now. */
10693 if (pMedium != pBase)
10694 {
10695 /* Tree lock needed by Medium::addRegistry when recursing. */
10696 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10697 if (pBase->i_addRegistryRecursive(uuid))
10698 {
10699 treeLock.release();
10700 mParent->i_markRegistryModified(uuid);
10701 }
10702 }
10703}
10704
10705/**
10706 * Creates differencing hard disks for all normal hard disks attached to this
10707 * machine and a new set of attachments to refer to created disks.
10708 *
10709 * Used when taking a snapshot or when deleting the current state. Gets called
10710 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10711 *
10712 * This method assumes that mMediaData contains the original hard disk attachments
10713 * it needs to create diffs for. On success, these attachments will be replaced
10714 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10715 * called to delete created diffs which will also rollback mMediaData and restore
10716 * whatever was backed up before calling this method.
10717 *
10718 * Attachments with non-normal hard disks are left as is.
10719 *
10720 * If @a aOnline is @c false then the original hard disks that require implicit
10721 * diffs will be locked for reading. Otherwise it is assumed that they are
10722 * already locked for writing (when the VM was started). Note that in the latter
10723 * case it is responsibility of the caller to lock the newly created diffs for
10724 * writing if this method succeeds.
10725 *
10726 * @param aProgress Progress object to run (must contain at least as
10727 * many operations left as the number of hard disks
10728 * attached).
10729 * @param aOnline Whether the VM was online prior to this operation.
10730 *
10731 * @note The progress object is not marked as completed, neither on success nor
10732 * on failure. This is a responsibility of the caller.
10733 *
10734 * @note Locks this object and the media tree for writing.
10735 */
10736HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10737 ULONG aWeight,
10738 bool aOnline)
10739{
10740 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10741
10742 AutoCaller autoCaller(this);
10743 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10744
10745 AutoMultiWriteLock2 alock(this->lockHandle(),
10746 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10747
10748 /* must be in a protective state because we release the lock below */
10749 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10750 || mData->mMachineState == MachineState_OnlineSnapshotting
10751 || mData->mMachineState == MachineState_LiveSnapshotting
10752 || mData->mMachineState == MachineState_RestoringSnapshot
10753 || mData->mMachineState == MachineState_DeletingSnapshot
10754 , E_FAIL);
10755
10756 HRESULT rc = S_OK;
10757
10758 // use appropriate locked media map (online or offline)
10759 MediumLockListMap lockedMediaOffline;
10760 MediumLockListMap *lockedMediaMap;
10761 if (aOnline)
10762 lockedMediaMap = &mData->mSession.mLockedMedia;
10763 else
10764 lockedMediaMap = &lockedMediaOffline;
10765
10766 try
10767 {
10768 if (!aOnline)
10769 {
10770 /* lock all attached hard disks early to detect "in use"
10771 * situations before creating actual diffs */
10772 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10773 it != mMediaData->mAttachments.end();
10774 ++it)
10775 {
10776 MediumAttachment* pAtt = *it;
10777 if (pAtt->i_getType() == DeviceType_HardDisk)
10778 {
10779 Medium* pMedium = pAtt->i_getMedium();
10780 Assert(pMedium);
10781
10782 MediumLockList *pMediumLockList(new MediumLockList());
10783 alock.release();
10784 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10785 NULL /* pToLockWrite */,
10786 false /* fMediumLockWriteAll */,
10787 NULL,
10788 *pMediumLockList);
10789 alock.acquire();
10790 if (FAILED(rc))
10791 {
10792 delete pMediumLockList;
10793 throw rc;
10794 }
10795 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10796 if (FAILED(rc))
10797 {
10798 throw setError(rc,
10799 tr("Collecting locking information for all attached media failed"));
10800 }
10801 }
10802 }
10803
10804 /* Now lock all media. If this fails, nothing is locked. */
10805 alock.release();
10806 rc = lockedMediaMap->Lock();
10807 alock.acquire();
10808 if (FAILED(rc))
10809 {
10810 throw setError(rc,
10811 tr("Locking of attached media failed"));
10812 }
10813 }
10814
10815 /* remember the current list (note that we don't use backup() since
10816 * mMediaData may be already backed up) */
10817 MediaData::AttachmentList atts = mMediaData->mAttachments;
10818
10819 /* start from scratch */
10820 mMediaData->mAttachments.clear();
10821
10822 /* go through remembered attachments and create diffs for normal hard
10823 * disks and attach them */
10824 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10825 it != atts.end();
10826 ++it)
10827 {
10828 MediumAttachment* pAtt = *it;
10829
10830 DeviceType_T devType = pAtt->i_getType();
10831 Medium* pMedium = pAtt->i_getMedium();
10832
10833 if ( devType != DeviceType_HardDisk
10834 || pMedium == NULL
10835 || pMedium->i_getType() != MediumType_Normal)
10836 {
10837 /* copy the attachment as is */
10838
10839 /** @todo the progress object created in SessionMachine::TakeSnaphot
10840 * only expects operations for hard disks. Later other
10841 * device types need to show up in the progress as well. */
10842 if (devType == DeviceType_HardDisk)
10843 {
10844 if (pMedium == NULL)
10845 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10846 aWeight); // weight
10847 else
10848 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10849 pMedium->i_getBase()->i_getName().c_str()).raw(),
10850 aWeight); // weight
10851 }
10852
10853 mMediaData->mAttachments.push_back(pAtt);
10854 continue;
10855 }
10856
10857 /* need a diff */
10858 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10859 pMedium->i_getBase()->i_getName().c_str()).raw(),
10860 aWeight); // weight
10861
10862 Utf8Str strFullSnapshotFolder;
10863 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10864
10865 ComObjPtr<Medium> diff;
10866 diff.createObject();
10867 // store the diff in the same registry as the parent
10868 // (this cannot fail here because we can't create implicit diffs for
10869 // unregistered images)
10870 Guid uuidRegistryParent;
10871 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10872 Assert(fInRegistry); NOREF(fInRegistry);
10873 rc = diff->init(mParent,
10874 pMedium->i_getPreferredDiffFormat(),
10875 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10876 uuidRegistryParent,
10877 DeviceType_HardDisk);
10878 if (FAILED(rc)) throw rc;
10879
10880 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10881 * the push_back? Looks like we're going to release medium with the
10882 * wrong kind of lock (general issue with if we fail anywhere at all)
10883 * and an orphaned VDI in the snapshots folder. */
10884
10885 /* update the appropriate lock list */
10886 MediumLockList *pMediumLockList;
10887 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10888 AssertComRCThrowRC(rc);
10889 if (aOnline)
10890 {
10891 alock.release();
10892 /* The currently attached medium will be read-only, change
10893 * the lock type to read. */
10894 rc = pMediumLockList->Update(pMedium, false);
10895 alock.acquire();
10896 AssertComRCThrowRC(rc);
10897 }
10898
10899 /* release the locks before the potentially lengthy operation */
10900 alock.release();
10901 rc = pMedium->i_createDiffStorage(diff,
10902 pMedium->i_getPreferredDiffVariant(),
10903 pMediumLockList,
10904 NULL /* aProgress */,
10905 true /* aWait */);
10906 alock.acquire();
10907 if (FAILED(rc)) throw rc;
10908
10909 /* actual lock list update is done in Machine::i_commitMedia */
10910
10911 rc = diff->i_addBackReference(mData->mUuid);
10912 AssertComRCThrowRC(rc);
10913
10914 /* add a new attachment */
10915 ComObjPtr<MediumAttachment> attachment;
10916 attachment.createObject();
10917 rc = attachment->init(this,
10918 diff,
10919 pAtt->i_getControllerName(),
10920 pAtt->i_getPort(),
10921 pAtt->i_getDevice(),
10922 DeviceType_HardDisk,
10923 true /* aImplicit */,
10924 false /* aPassthrough */,
10925 false /* aTempEject */,
10926 pAtt->i_getNonRotational(),
10927 pAtt->i_getDiscard(),
10928 pAtt->i_getHotPluggable(),
10929 pAtt->i_getBandwidthGroup());
10930 if (FAILED(rc)) throw rc;
10931
10932 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10933 AssertComRCThrowRC(rc);
10934 mMediaData->mAttachments.push_back(attachment);
10935 }
10936 }
10937 catch (HRESULT aRC) { rc = aRC; }
10938
10939 /* unlock all hard disks we locked when there is no VM */
10940 if (!aOnline)
10941 {
10942 ErrorInfoKeeper eik;
10943
10944 HRESULT rc1 = lockedMediaMap->Clear();
10945 AssertComRC(rc1);
10946 }
10947
10948 return rc;
10949}
10950
10951/**
10952 * Deletes implicit differencing hard disks created either by
10953 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10954 *
10955 * Note that to delete hard disks created by #AttachDevice() this method is
10956 * called from #fixupMedia() when the changes are rolled back.
10957 *
10958 * @note Locks this object and the media tree for writing.
10959 */
10960HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10961{
10962 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10963
10964 AutoCaller autoCaller(this);
10965 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10966
10967 AutoMultiWriteLock2 alock(this->lockHandle(),
10968 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10969
10970 /* We absolutely must have backed up state. */
10971 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10972
10973 /* Check if there are any implicitly created diff images. */
10974 bool fImplicitDiffs = false;
10975 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10976 it != mMediaData->mAttachments.end();
10977 ++it)
10978 {
10979 const ComObjPtr<MediumAttachment> &pAtt = *it;
10980 if (pAtt->i_isImplicit())
10981 {
10982 fImplicitDiffs = true;
10983 break;
10984 }
10985 }
10986 /* If there is nothing to do, leave early. This saves lots of image locking
10987 * effort. It also avoids a MachineStateChanged event without real reason.
10988 * This is important e.g. when loading a VM config, because there should be
10989 * no events. Otherwise API clients can become thoroughly confused for
10990 * inaccessible VMs (the code for loading VM configs uses this method for
10991 * cleanup if the config makes no sense), as they take such events as an
10992 * indication that the VM is alive, and they would force the VM config to
10993 * be reread, leading to an endless loop. */
10994 if (!fImplicitDiffs)
10995 return S_OK;
10996
10997 HRESULT rc = S_OK;
10998 MachineState_T oldState = mData->mMachineState;
10999
11000 /* will release the lock before the potentially lengthy operation,
11001 * so protect with the special state (unless already protected) */
11002 if ( oldState != MachineState_Snapshotting
11003 && oldState != MachineState_OnlineSnapshotting
11004 && oldState != MachineState_LiveSnapshotting
11005 && oldState != MachineState_RestoringSnapshot
11006 && oldState != MachineState_DeletingSnapshot
11007 && oldState != MachineState_DeletingSnapshotOnline
11008 && oldState != MachineState_DeletingSnapshotPaused
11009 )
11010 i_setMachineState(MachineState_SettingUp);
11011
11012 // use appropriate locked media map (online or offline)
11013 MediumLockListMap lockedMediaOffline;
11014 MediumLockListMap *lockedMediaMap;
11015 if (aOnline)
11016 lockedMediaMap = &mData->mSession.mLockedMedia;
11017 else
11018 lockedMediaMap = &lockedMediaOffline;
11019
11020 try
11021 {
11022 if (!aOnline)
11023 {
11024 /* lock all attached hard disks early to detect "in use"
11025 * situations before deleting actual diffs */
11026 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11027 it != mMediaData->mAttachments.end();
11028 ++it)
11029 {
11030 MediumAttachment* pAtt = *it;
11031 if (pAtt->i_getType() == DeviceType_HardDisk)
11032 {
11033 Medium* pMedium = pAtt->i_getMedium();
11034 Assert(pMedium);
11035
11036 MediumLockList *pMediumLockList(new MediumLockList());
11037 alock.release();
11038 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11039 NULL /* pToLockWrite */,
11040 false /* fMediumLockWriteAll */,
11041 NULL,
11042 *pMediumLockList);
11043 alock.acquire();
11044
11045 if (FAILED(rc))
11046 {
11047 delete pMediumLockList;
11048 throw rc;
11049 }
11050
11051 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11052 if (FAILED(rc))
11053 throw rc;
11054 }
11055 }
11056
11057 if (FAILED(rc))
11058 throw rc;
11059 } // end of offline
11060
11061 /* Lock lists are now up to date and include implicitly created media */
11062
11063 /* Go through remembered attachments and delete all implicitly created
11064 * diffs and fix up the attachment information */
11065 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11066 MediaData::AttachmentList implicitAtts;
11067 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11068 it != mMediaData->mAttachments.end();
11069 ++it)
11070 {
11071 ComObjPtr<MediumAttachment> pAtt = *it;
11072 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11073 if (pMedium.isNull())
11074 continue;
11075
11076 // Implicit attachments go on the list for deletion and back references are removed.
11077 if (pAtt->i_isImplicit())
11078 {
11079 /* Deassociate and mark for deletion */
11080 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11081 rc = pMedium->i_removeBackReference(mData->mUuid);
11082 if (FAILED(rc))
11083 throw rc;
11084 implicitAtts.push_back(pAtt);
11085 continue;
11086 }
11087
11088 /* Was this medium attached before? */
11089 if (!i_findAttachment(oldAtts, pMedium))
11090 {
11091 /* no: de-associate */
11092 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11093 rc = pMedium->i_removeBackReference(mData->mUuid);
11094 if (FAILED(rc))
11095 throw rc;
11096 continue;
11097 }
11098 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11099 }
11100
11101 /* If there are implicit attachments to delete, throw away the lock
11102 * map contents (which will unlock all media) since the medium
11103 * attachments will be rolled back. Below we need to completely
11104 * recreate the lock map anyway since it is infinitely complex to
11105 * do this incrementally (would need reconstructing each attachment
11106 * change, which would be extremely hairy). */
11107 if (implicitAtts.size() != 0)
11108 {
11109 ErrorInfoKeeper eik;
11110
11111 HRESULT rc1 = lockedMediaMap->Clear();
11112 AssertComRC(rc1);
11113 }
11114
11115 /* rollback hard disk changes */
11116 mMediaData.rollback();
11117
11118 MultiResult mrc(S_OK);
11119
11120 // Delete unused implicit diffs.
11121 if (implicitAtts.size() != 0)
11122 {
11123 alock.release();
11124
11125 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11126 {
11127 // Remove medium associated with this attachment.
11128 ComObjPtr<MediumAttachment> pAtt = *it;
11129 Assert(pAtt);
11130 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11131 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11132 Assert(pMedium);
11133
11134 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11135 // continue on delete failure, just collect error messages
11136 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11137 pMedium->i_getLocationFull().c_str() ));
11138 mrc = rc;
11139 }
11140 // Clear the list of deleted implicit attachments now, while not
11141 // holding the lock, as it will ultimately trigger Medium::uninit()
11142 // calls which assume that the media tree lock isn't held.
11143 implicitAtts.clear();
11144
11145 alock.acquire();
11146
11147 /* if there is a VM recreate media lock map as mentioned above,
11148 * otherwise it is a waste of time and we leave things unlocked */
11149 if (aOnline)
11150 {
11151 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11152 /* must never be NULL, but better safe than sorry */
11153 if (!pMachine.isNull())
11154 {
11155 alock.release();
11156 rc = mData->mSession.mMachine->i_lockMedia();
11157 alock.acquire();
11158 if (FAILED(rc))
11159 throw rc;
11160 }
11161 }
11162 }
11163 }
11164 catch (HRESULT aRC) {rc = aRC;}
11165
11166 if (mData->mMachineState == MachineState_SettingUp)
11167 i_setMachineState(oldState);
11168
11169 /* unlock all hard disks we locked when there is no VM */
11170 if (!aOnline)
11171 {
11172 ErrorInfoKeeper eik;
11173
11174 HRESULT rc1 = lockedMediaMap->Clear();
11175 AssertComRC(rc1);
11176 }
11177
11178 return rc;
11179}
11180
11181
11182/**
11183 * Looks through the given list of media attachments for one with the given parameters
11184 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11185 * can be searched as well if needed.
11186 *
11187 * @param list
11188 * @param aControllerName
11189 * @param aControllerPort
11190 * @param aDevice
11191 * @return
11192 */
11193MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11194 const Utf8Str &aControllerName,
11195 LONG aControllerPort,
11196 LONG aDevice)
11197{
11198 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11199 {
11200 MediumAttachment *pAttach = *it;
11201 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11202 return pAttach;
11203 }
11204
11205 return NULL;
11206}
11207
11208/**
11209 * Looks through the given list of media attachments for one with the given parameters
11210 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11211 * can be searched as well if needed.
11212 *
11213 * @param list
11214 * @param aControllerName
11215 * @param aControllerPort
11216 * @param aDevice
11217 * @return
11218 */
11219MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11220 ComObjPtr<Medium> pMedium)
11221{
11222 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11223 {
11224 MediumAttachment *pAttach = *it;
11225 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11226 if (pMediumThis == pMedium)
11227 return pAttach;
11228 }
11229
11230 return NULL;
11231}
11232
11233/**
11234 * Looks through the given list of media attachments for one with the given parameters
11235 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11236 * can be searched as well if needed.
11237 *
11238 * @param list
11239 * @param aControllerName
11240 * @param aControllerPort
11241 * @param aDevice
11242 * @return
11243 */
11244MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11245 Guid &id)
11246{
11247 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11248 {
11249 MediumAttachment *pAttach = *it;
11250 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11251 if (pMediumThis->i_getId() == id)
11252 return pAttach;
11253 }
11254
11255 return NULL;
11256}
11257
11258/**
11259 * Main implementation for Machine::DetachDevice. This also gets called
11260 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11261 *
11262 * @param pAttach Medium attachment to detach.
11263 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11264 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11265 * SnapshotMachine, and this must be its snapshot.
11266 * @return
11267 */
11268HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11269 AutoWriteLock &writeLock,
11270 Snapshot *pSnapshot)
11271{
11272 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11273 DeviceType_T mediumType = pAttach->i_getType();
11274
11275 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11276
11277 if (pAttach->i_isImplicit())
11278 {
11279 /* attempt to implicitly delete the implicitly created diff */
11280
11281 /// @todo move the implicit flag from MediumAttachment to Medium
11282 /// and forbid any hard disk operation when it is implicit. Or maybe
11283 /// a special media state for it to make it even more simple.
11284
11285 Assert(mMediaData.isBackedUp());
11286
11287 /* will release the lock before the potentially lengthy operation, so
11288 * protect with the special state */
11289 MachineState_T oldState = mData->mMachineState;
11290 i_setMachineState(MachineState_SettingUp);
11291
11292 writeLock.release();
11293
11294 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11295 true /*aWait*/);
11296
11297 writeLock.acquire();
11298
11299 i_setMachineState(oldState);
11300
11301 if (FAILED(rc)) return rc;
11302 }
11303
11304 i_setModified(IsModified_Storage);
11305 mMediaData.backup();
11306 mMediaData->mAttachments.remove(pAttach);
11307
11308 if (!oldmedium.isNull())
11309 {
11310 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11311 if (pSnapshot)
11312 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11313 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11314 else if (mediumType != DeviceType_HardDisk)
11315 oldmedium->i_removeBackReference(mData->mUuid);
11316 }
11317
11318 return S_OK;
11319}
11320
11321/**
11322 * Goes thru all media of the given list and
11323 *
11324 * 1) calls i_detachDevice() on each of them for this machine and
11325 * 2) adds all Medium objects found in the process to the given list,
11326 * depending on cleanupMode.
11327 *
11328 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11329 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11330 * media to the list.
11331 *
11332 * This gets called from Machine::Unregister, both for the actual Machine and
11333 * the SnapshotMachine objects that might be found in the snapshots.
11334 *
11335 * Requires caller and locking. The machine lock must be passed in because it
11336 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11337 *
11338 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11339 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11340 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11341 * Full, then all media get added;
11342 * otherwise no media get added.
11343 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11344 * @return
11345 */
11346HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11347 Snapshot *pSnapshot,
11348 CleanupMode_T cleanupMode,
11349 MediaList &llMedia)
11350{
11351 Assert(isWriteLockOnCurrentThread());
11352
11353 HRESULT rc;
11354
11355 // make a temporary list because i_detachDevice invalidates iterators into
11356 // mMediaData->mAttachments
11357 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11358
11359 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11360 {
11361 ComObjPtr<MediumAttachment> &pAttach = *it;
11362 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11363
11364 if (!pMedium.isNull())
11365 {
11366 AutoCaller mac(pMedium);
11367 if (FAILED(mac.rc())) return mac.rc();
11368 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11369 DeviceType_T devType = pMedium->i_getDeviceType();
11370 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11371 && devType == DeviceType_HardDisk)
11372 || (cleanupMode == CleanupMode_Full)
11373 )
11374 {
11375 llMedia.push_back(pMedium);
11376 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11377 /* Not allowed to keep this lock as below we need the parent
11378 * medium lock, and the lock order is parent to child. */
11379 lock.release();
11380 /*
11381 * Search for medias which are not attached to any machine, but
11382 * in the chain to an attached disk. Mediums are only consided
11383 * if they are:
11384 * - have only one child
11385 * - no references to any machines
11386 * - are of normal medium type
11387 */
11388 while (!pParent.isNull())
11389 {
11390 AutoCaller mac1(pParent);
11391 if (FAILED(mac1.rc())) return mac1.rc();
11392 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11393 if (pParent->i_getChildren().size() == 1)
11394 {
11395 if ( pParent->i_getMachineBackRefCount() == 0
11396 && pParent->i_getType() == MediumType_Normal
11397 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11398 llMedia.push_back(pParent);
11399 }
11400 else
11401 break;
11402 pParent = pParent->i_getParent();
11403 }
11404 }
11405 }
11406
11407 // real machine: then we need to use the proper method
11408 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11409
11410 if (FAILED(rc))
11411 return rc;
11412 }
11413
11414 return S_OK;
11415}
11416
11417/**
11418 * Perform deferred hard disk detachments.
11419 *
11420 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11421 * backed up).
11422 *
11423 * If @a aOnline is @c true then this method will also unlock the old hard disks
11424 * for which the new implicit diffs were created and will lock these new diffs for
11425 * writing.
11426 *
11427 * @param aOnline Whether the VM was online prior to this operation.
11428 *
11429 * @note Locks this object for writing!
11430 */
11431void Machine::i_commitMedia(bool aOnline /*= false*/)
11432{
11433 AutoCaller autoCaller(this);
11434 AssertComRCReturnVoid(autoCaller.rc());
11435
11436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11437
11438 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11439
11440 HRESULT rc = S_OK;
11441
11442 /* no attach/detach operations -- nothing to do */
11443 if (!mMediaData.isBackedUp())
11444 return;
11445
11446 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11447 bool fMediaNeedsLocking = false;
11448
11449 /* enumerate new attachments */
11450 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11451 it != mMediaData->mAttachments.end();
11452 ++it)
11453 {
11454 MediumAttachment *pAttach = *it;
11455
11456 pAttach->i_commit();
11457
11458 Medium* pMedium = pAttach->i_getMedium();
11459 bool fImplicit = pAttach->i_isImplicit();
11460
11461 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11462 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11463 fImplicit));
11464
11465 /** @todo convert all this Machine-based voodoo to MediumAttachment
11466 * based commit logic. */
11467 if (fImplicit)
11468 {
11469 /* convert implicit attachment to normal */
11470 pAttach->i_setImplicit(false);
11471
11472 if ( aOnline
11473 && pMedium
11474 && pAttach->i_getType() == DeviceType_HardDisk
11475 )
11476 {
11477 /* update the appropriate lock list */
11478 MediumLockList *pMediumLockList;
11479 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11480 AssertComRC(rc);
11481 if (pMediumLockList)
11482 {
11483 /* unlock if there's a need to change the locking */
11484 if (!fMediaNeedsLocking)
11485 {
11486 rc = mData->mSession.mLockedMedia.Unlock();
11487 AssertComRC(rc);
11488 fMediaNeedsLocking = true;
11489 }
11490 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11491 AssertComRC(rc);
11492 rc = pMediumLockList->Append(pMedium, true);
11493 AssertComRC(rc);
11494 }
11495 }
11496
11497 continue;
11498 }
11499
11500 if (pMedium)
11501 {
11502 /* was this medium attached before? */
11503 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11504 {
11505 MediumAttachment *pOldAttach = *oldIt;
11506 if (pOldAttach->i_getMedium() == pMedium)
11507 {
11508 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11509
11510 /* yes: remove from old to avoid de-association */
11511 oldAtts.erase(oldIt);
11512 break;
11513 }
11514 }
11515 }
11516 }
11517
11518 /* enumerate remaining old attachments and de-associate from the
11519 * current machine state */
11520 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11521 {
11522 MediumAttachment *pAttach = *it;
11523 Medium* pMedium = pAttach->i_getMedium();
11524
11525 /* Detach only hard disks, since DVD/floppy media is detached
11526 * instantly in MountMedium. */
11527 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11528 {
11529 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11530
11531 /* now de-associate from the current machine state */
11532 rc = pMedium->i_removeBackReference(mData->mUuid);
11533 AssertComRC(rc);
11534
11535 if (aOnline)
11536 {
11537 /* unlock since medium is not used anymore */
11538 MediumLockList *pMediumLockList;
11539 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11540 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11541 {
11542 /* this happens for online snapshots, there the attachment
11543 * is changing, but only to a diff image created under
11544 * the old one, so there is no separate lock list */
11545 Assert(!pMediumLockList);
11546 }
11547 else
11548 {
11549 AssertComRC(rc);
11550 if (pMediumLockList)
11551 {
11552 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11553 AssertComRC(rc);
11554 }
11555 }
11556 }
11557 }
11558 }
11559
11560 /* take media locks again so that the locking state is consistent */
11561 if (fMediaNeedsLocking)
11562 {
11563 Assert(aOnline);
11564 rc = mData->mSession.mLockedMedia.Lock();
11565 AssertComRC(rc);
11566 }
11567
11568 /* commit the hard disk changes */
11569 mMediaData.commit();
11570
11571 if (i_isSessionMachine())
11572 {
11573 /*
11574 * Update the parent machine to point to the new owner.
11575 * This is necessary because the stored parent will point to the
11576 * session machine otherwise and cause crashes or errors later
11577 * when the session machine gets invalid.
11578 */
11579 /** @todo Change the MediumAttachment class to behave like any other
11580 * class in this regard by creating peer MediumAttachment
11581 * objects for session machines and share the data with the peer
11582 * machine.
11583 */
11584 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11585 it != mMediaData->mAttachments.end();
11586 ++it)
11587 (*it)->i_updateParentMachine(mPeer);
11588
11589 /* attach new data to the primary machine and reshare it */
11590 mPeer->mMediaData.attach(mMediaData);
11591 }
11592
11593 return;
11594}
11595
11596/**
11597 * Perform deferred deletion of implicitly created diffs.
11598 *
11599 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11600 * backed up).
11601 *
11602 * @note Locks this object for writing!
11603 */
11604void Machine::i_rollbackMedia()
11605{
11606 AutoCaller autoCaller(this);
11607 AssertComRCReturnVoid(autoCaller.rc());
11608
11609 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11610 LogFlowThisFunc(("Entering rollbackMedia\n"));
11611
11612 HRESULT rc = S_OK;
11613
11614 /* no attach/detach operations -- nothing to do */
11615 if (!mMediaData.isBackedUp())
11616 return;
11617
11618 /* enumerate new attachments */
11619 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11620 it != mMediaData->mAttachments.end();
11621 ++it)
11622 {
11623 MediumAttachment *pAttach = *it;
11624 /* Fix up the backrefs for DVD/floppy media. */
11625 if (pAttach->i_getType() != DeviceType_HardDisk)
11626 {
11627 Medium* pMedium = pAttach->i_getMedium();
11628 if (pMedium)
11629 {
11630 rc = pMedium->i_removeBackReference(mData->mUuid);
11631 AssertComRC(rc);
11632 }
11633 }
11634
11635 (*it)->i_rollback();
11636
11637 pAttach = *it;
11638 /* Fix up the backrefs for DVD/floppy media. */
11639 if (pAttach->i_getType() != DeviceType_HardDisk)
11640 {
11641 Medium* pMedium = pAttach->i_getMedium();
11642 if (pMedium)
11643 {
11644 rc = pMedium->i_addBackReference(mData->mUuid);
11645 AssertComRC(rc);
11646 }
11647 }
11648 }
11649
11650 /** @todo convert all this Machine-based voodoo to MediumAttachment
11651 * based rollback logic. */
11652 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11653
11654 return;
11655}
11656
11657/**
11658 * Returns true if the settings file is located in the directory named exactly
11659 * as the machine; this means, among other things, that the machine directory
11660 * should be auto-renamed.
11661 *
11662 * @param aSettingsDir if not NULL, the full machine settings file directory
11663 * name will be assigned there.
11664 *
11665 * @note Doesn't lock anything.
11666 * @note Not thread safe (must be called from this object's lock).
11667 */
11668bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11669{
11670 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11671 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11672 if (aSettingsDir)
11673 *aSettingsDir = strMachineDirName;
11674 strMachineDirName.stripPath(); // vmname
11675 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11676 strConfigFileOnly.stripPath() // vmname.vbox
11677 .stripSuffix(); // vmname
11678 /** @todo hack, make somehow use of ComposeMachineFilename */
11679 if (mUserData->s.fDirectoryIncludesUUID)
11680 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11681
11682 AssertReturn(!strMachineDirName.isEmpty(), false);
11683 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11684
11685 return strMachineDirName == strConfigFileOnly;
11686}
11687
11688/**
11689 * Discards all changes to machine settings.
11690 *
11691 * @param aNotify Whether to notify the direct session about changes or not.
11692 *
11693 * @note Locks objects for writing!
11694 */
11695void Machine::i_rollback(bool aNotify)
11696{
11697 AutoCaller autoCaller(this);
11698 AssertComRCReturn(autoCaller.rc(), (void)0);
11699
11700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11701
11702 if (!mStorageControllers.isNull())
11703 {
11704 if (mStorageControllers.isBackedUp())
11705 {
11706 /* unitialize all new devices (absent in the backed up list). */
11707 StorageControllerList::const_iterator it = mStorageControllers->begin();
11708 StorageControllerList *backedList = mStorageControllers.backedUpData();
11709 while (it != mStorageControllers->end())
11710 {
11711 if ( std::find(backedList->begin(), backedList->end(), *it)
11712 == backedList->end()
11713 )
11714 {
11715 (*it)->uninit();
11716 }
11717 ++it;
11718 }
11719
11720 /* restore the list */
11721 mStorageControllers.rollback();
11722 }
11723
11724 /* rollback any changes to devices after restoring the list */
11725 if (mData->flModifications & IsModified_Storage)
11726 {
11727 StorageControllerList::const_iterator it = mStorageControllers->begin();
11728 while (it != mStorageControllers->end())
11729 {
11730 (*it)->i_rollback();
11731 ++it;
11732 }
11733 }
11734 }
11735
11736 if (!mUSBControllers.isNull())
11737 {
11738 if (mUSBControllers.isBackedUp())
11739 {
11740 /* unitialize all new devices (absent in the backed up list). */
11741 USBControllerList::const_iterator it = mUSBControllers->begin();
11742 USBControllerList *backedList = mUSBControllers.backedUpData();
11743 while (it != mUSBControllers->end())
11744 {
11745 if ( std::find(backedList->begin(), backedList->end(), *it)
11746 == backedList->end()
11747 )
11748 {
11749 (*it)->uninit();
11750 }
11751 ++it;
11752 }
11753
11754 /* restore the list */
11755 mUSBControllers.rollback();
11756 }
11757
11758 /* rollback any changes to devices after restoring the list */
11759 if (mData->flModifications & IsModified_USB)
11760 {
11761 USBControllerList::const_iterator it = mUSBControllers->begin();
11762 while (it != mUSBControllers->end())
11763 {
11764 (*it)->i_rollback();
11765 ++it;
11766 }
11767 }
11768 }
11769
11770 mUserData.rollback();
11771
11772 mHWData.rollback();
11773
11774 if (mData->flModifications & IsModified_Storage)
11775 i_rollbackMedia();
11776
11777 if (mBIOSSettings)
11778 mBIOSSettings->i_rollback();
11779
11780 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11781 mVRDEServer->i_rollback();
11782
11783 if (mAudioAdapter)
11784 mAudioAdapter->i_rollback();
11785
11786 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11787 mUSBDeviceFilters->i_rollback();
11788
11789 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11790 mBandwidthControl->i_rollback();
11791
11792 if (!mHWData.isNull())
11793 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11794 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11795 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11796 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11797
11798 if (mData->flModifications & IsModified_NetworkAdapters)
11799 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11800 if ( mNetworkAdapters[slot]
11801 && mNetworkAdapters[slot]->i_isModified())
11802 {
11803 mNetworkAdapters[slot]->i_rollback();
11804 networkAdapters[slot] = mNetworkAdapters[slot];
11805 }
11806
11807 if (mData->flModifications & IsModified_SerialPorts)
11808 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11809 if ( mSerialPorts[slot]
11810 && mSerialPorts[slot]->i_isModified())
11811 {
11812 mSerialPorts[slot]->i_rollback();
11813 serialPorts[slot] = mSerialPorts[slot];
11814 }
11815
11816 if (mData->flModifications & IsModified_ParallelPorts)
11817 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11818 if ( mParallelPorts[slot]
11819 && mParallelPorts[slot]->i_isModified())
11820 {
11821 mParallelPorts[slot]->i_rollback();
11822 parallelPorts[slot] = mParallelPorts[slot];
11823 }
11824
11825 if (aNotify)
11826 {
11827 /* inform the direct session about changes */
11828
11829 ComObjPtr<Machine> that = this;
11830 uint32_t flModifications = mData->flModifications;
11831 alock.release();
11832
11833 if (flModifications & IsModified_SharedFolders)
11834 that->i_onSharedFolderChange();
11835
11836 if (flModifications & IsModified_VRDEServer)
11837 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11838 if (flModifications & IsModified_USB)
11839 that->i_onUSBControllerChange();
11840
11841 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11842 if (networkAdapters[slot])
11843 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11844 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11845 if (serialPorts[slot])
11846 that->i_onSerialPortChange(serialPorts[slot]);
11847 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11848 if (parallelPorts[slot])
11849 that->i_onParallelPortChange(parallelPorts[slot]);
11850
11851 if (flModifications & IsModified_Storage)
11852 that->i_onStorageControllerChange();
11853
11854#if 0
11855 if (flModifications & IsModified_BandwidthControl)
11856 that->onBandwidthControlChange();
11857#endif
11858 }
11859}
11860
11861/**
11862 * Commits all the changes to machine settings.
11863 *
11864 * Note that this operation is supposed to never fail.
11865 *
11866 * @note Locks this object and children for writing.
11867 */
11868void Machine::i_commit()
11869{
11870 AutoCaller autoCaller(this);
11871 AssertComRCReturnVoid(autoCaller.rc());
11872
11873 AutoCaller peerCaller(mPeer);
11874 AssertComRCReturnVoid(peerCaller.rc());
11875
11876 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11877
11878 /*
11879 * use safe commit to ensure Snapshot machines (that share mUserData)
11880 * will still refer to a valid memory location
11881 */
11882 mUserData.commitCopy();
11883
11884 mHWData.commit();
11885
11886 if (mMediaData.isBackedUp())
11887 i_commitMedia(Global::IsOnline(mData->mMachineState));
11888
11889 mBIOSSettings->i_commit();
11890 mVRDEServer->i_commit();
11891 mAudioAdapter->i_commit();
11892 mUSBDeviceFilters->i_commit();
11893 mBandwidthControl->i_commit();
11894
11895 /* Since mNetworkAdapters is a list which might have been changed (resized)
11896 * without using the Backupable<> template we need to handle the copying
11897 * of the list entries manually, including the creation of peers for the
11898 * new objects. */
11899 bool commitNetworkAdapters = false;
11900 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11901 if (mPeer)
11902 {
11903 /* commit everything, even the ones which will go away */
11904 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11905 mNetworkAdapters[slot]->i_commit();
11906 /* copy over the new entries, creating a peer and uninit the original */
11907 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11908 for (size_t slot = 0; slot < newSize; slot++)
11909 {
11910 /* look if this adapter has a peer device */
11911 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11912 if (!peer)
11913 {
11914 /* no peer means the adapter is a newly created one;
11915 * create a peer owning data this data share it with */
11916 peer.createObject();
11917 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11918 }
11919 mPeer->mNetworkAdapters[slot] = peer;
11920 }
11921 /* uninit any no longer needed network adapters */
11922 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11923 mNetworkAdapters[slot]->uninit();
11924 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11925 {
11926 if (mPeer->mNetworkAdapters[slot])
11927 mPeer->mNetworkAdapters[slot]->uninit();
11928 }
11929 /* Keep the original network adapter count until this point, so that
11930 * discarding a chipset type change will not lose settings. */
11931 mNetworkAdapters.resize(newSize);
11932 mPeer->mNetworkAdapters.resize(newSize);
11933 }
11934 else
11935 {
11936 /* we have no peer (our parent is the newly created machine);
11937 * just commit changes to the network adapters */
11938 commitNetworkAdapters = true;
11939 }
11940 if (commitNetworkAdapters)
11941 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11942 mNetworkAdapters[slot]->i_commit();
11943
11944 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11945 mSerialPorts[slot]->i_commit();
11946 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11947 mParallelPorts[slot]->i_commit();
11948
11949 bool commitStorageControllers = false;
11950
11951 if (mStorageControllers.isBackedUp())
11952 {
11953 mStorageControllers.commit();
11954
11955 if (mPeer)
11956 {
11957 /* Commit all changes to new controllers (this will reshare data with
11958 * peers for those who have peers) */
11959 StorageControllerList *newList = new StorageControllerList();
11960 StorageControllerList::const_iterator it = mStorageControllers->begin();
11961 while (it != mStorageControllers->end())
11962 {
11963 (*it)->i_commit();
11964
11965 /* look if this controller has a peer device */
11966 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11967 if (!peer)
11968 {
11969 /* no peer means the device is a newly created one;
11970 * create a peer owning data this device share it with */
11971 peer.createObject();
11972 peer->init(mPeer, *it, true /* aReshare */);
11973 }
11974 else
11975 {
11976 /* remove peer from the old list */
11977 mPeer->mStorageControllers->remove(peer);
11978 }
11979 /* and add it to the new list */
11980 newList->push_back(peer);
11981
11982 ++it;
11983 }
11984
11985 /* uninit old peer's controllers that are left */
11986 it = mPeer->mStorageControllers->begin();
11987 while (it != mPeer->mStorageControllers->end())
11988 {
11989 (*it)->uninit();
11990 ++it;
11991 }
11992
11993 /* attach new list of controllers to our peer */
11994 mPeer->mStorageControllers.attach(newList);
11995 }
11996 else
11997 {
11998 /* we have no peer (our parent is the newly created machine);
11999 * just commit changes to devices */
12000 commitStorageControllers = true;
12001 }
12002 }
12003 else
12004 {
12005 /* the list of controllers itself is not changed,
12006 * just commit changes to controllers themselves */
12007 commitStorageControllers = true;
12008 }
12009
12010 if (commitStorageControllers)
12011 {
12012 StorageControllerList::const_iterator it = mStorageControllers->begin();
12013 while (it != mStorageControllers->end())
12014 {
12015 (*it)->i_commit();
12016 ++it;
12017 }
12018 }
12019
12020 bool commitUSBControllers = false;
12021
12022 if (mUSBControllers.isBackedUp())
12023 {
12024 mUSBControllers.commit();
12025
12026 if (mPeer)
12027 {
12028 /* Commit all changes to new controllers (this will reshare data with
12029 * peers for those who have peers) */
12030 USBControllerList *newList = new USBControllerList();
12031 USBControllerList::const_iterator it = mUSBControllers->begin();
12032 while (it != mUSBControllers->end())
12033 {
12034 (*it)->i_commit();
12035
12036 /* look if this controller has a peer device */
12037 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12038 if (!peer)
12039 {
12040 /* no peer means the device is a newly created one;
12041 * create a peer owning data this device share it with */
12042 peer.createObject();
12043 peer->init(mPeer, *it, true /* aReshare */);
12044 }
12045 else
12046 {
12047 /* remove peer from the old list */
12048 mPeer->mUSBControllers->remove(peer);
12049 }
12050 /* and add it to the new list */
12051 newList->push_back(peer);
12052
12053 ++it;
12054 }
12055
12056 /* uninit old peer's controllers that are left */
12057 it = mPeer->mUSBControllers->begin();
12058 while (it != mPeer->mUSBControllers->end())
12059 {
12060 (*it)->uninit();
12061 ++it;
12062 }
12063
12064 /* attach new list of controllers to our peer */
12065 mPeer->mUSBControllers.attach(newList);
12066 }
12067 else
12068 {
12069 /* we have no peer (our parent is the newly created machine);
12070 * just commit changes to devices */
12071 commitUSBControllers = true;
12072 }
12073 }
12074 else
12075 {
12076 /* the list of controllers itself is not changed,
12077 * just commit changes to controllers themselves */
12078 commitUSBControllers = true;
12079 }
12080
12081 if (commitUSBControllers)
12082 {
12083 USBControllerList::const_iterator it = mUSBControllers->begin();
12084 while (it != mUSBControllers->end())
12085 {
12086 (*it)->i_commit();
12087 ++it;
12088 }
12089 }
12090
12091 if (i_isSessionMachine())
12092 {
12093 /* attach new data to the primary machine and reshare it */
12094 mPeer->mUserData.attach(mUserData);
12095 mPeer->mHWData.attach(mHWData);
12096 /* mMediaData is reshared by fixupMedia */
12097 // mPeer->mMediaData.attach(mMediaData);
12098 Assert(mPeer->mMediaData.data() == mMediaData.data());
12099 }
12100}
12101
12102/**
12103 * Copies all the hardware data from the given machine.
12104 *
12105 * Currently, only called when the VM is being restored from a snapshot. In
12106 * particular, this implies that the VM is not running during this method's
12107 * call.
12108 *
12109 * @note This method must be called from under this object's lock.
12110 *
12111 * @note This method doesn't call #commit(), so all data remains backed up and
12112 * unsaved.
12113 */
12114void Machine::i_copyFrom(Machine *aThat)
12115{
12116 AssertReturnVoid(!i_isSnapshotMachine());
12117 AssertReturnVoid(aThat->i_isSnapshotMachine());
12118
12119 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12120
12121 mHWData.assignCopy(aThat->mHWData);
12122
12123 // create copies of all shared folders (mHWData after attaching a copy
12124 // contains just references to original objects)
12125 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12126 it != mHWData->mSharedFolders.end();
12127 ++it)
12128 {
12129 ComObjPtr<SharedFolder> folder;
12130 folder.createObject();
12131 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12132 AssertComRC(rc);
12133 *it = folder;
12134 }
12135
12136 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12137 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12138 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12139 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12140 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12141
12142 /* create private copies of all controllers */
12143 mStorageControllers.backup();
12144 mStorageControllers->clear();
12145 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12146 it != aThat->mStorageControllers->end();
12147 ++it)
12148 {
12149 ComObjPtr<StorageController> ctrl;
12150 ctrl.createObject();
12151 ctrl->initCopy(this, *it);
12152 mStorageControllers->push_back(ctrl);
12153 }
12154
12155 /* create private copies of all USB controllers */
12156 mUSBControllers.backup();
12157 mUSBControllers->clear();
12158 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12159 it != aThat->mUSBControllers->end();
12160 ++it)
12161 {
12162 ComObjPtr<USBController> ctrl;
12163 ctrl.createObject();
12164 ctrl->initCopy(this, *it);
12165 mUSBControllers->push_back(ctrl);
12166 }
12167
12168 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12170 {
12171 if (mNetworkAdapters[slot].isNotNull())
12172 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12173 else
12174 {
12175 unconst(mNetworkAdapters[slot]).createObject();
12176 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12177 }
12178 }
12179 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12180 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12181 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12182 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12183}
12184
12185/**
12186 * Returns whether the given storage controller is hotplug capable.
12187 *
12188 * @returns true if the controller supports hotplugging
12189 * false otherwise.
12190 * @param enmCtrlType The controller type to check for.
12191 */
12192bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12193{
12194 ComPtr<ISystemProperties> systemProperties;
12195 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12196 if (FAILED(rc))
12197 return false;
12198
12199 BOOL aHotplugCapable = FALSE;
12200 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12201
12202 return RT_BOOL(aHotplugCapable);
12203}
12204
12205#ifdef VBOX_WITH_RESOURCE_USAGE_API
12206
12207void Machine::i_getDiskList(MediaList &list)
12208{
12209 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12210 it != mMediaData->mAttachments.end();
12211 ++it)
12212 {
12213 MediumAttachment* pAttach = *it;
12214 /* just in case */
12215 AssertContinue(pAttach);
12216
12217 AutoCaller localAutoCallerA(pAttach);
12218 if (FAILED(localAutoCallerA.rc())) continue;
12219
12220 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12221
12222 if (pAttach->i_getType() == DeviceType_HardDisk)
12223 list.push_back(pAttach->i_getMedium());
12224 }
12225}
12226
12227void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12228{
12229 AssertReturnVoid(isWriteLockOnCurrentThread());
12230 AssertPtrReturnVoid(aCollector);
12231
12232 pm::CollectorHAL *hal = aCollector->getHAL();
12233 /* Create sub metrics */
12234 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12235 "Percentage of processor time spent in user mode by the VM process.");
12236 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12237 "Percentage of processor time spent in kernel mode by the VM process.");
12238 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12239 "Size of resident portion of VM process in memory.");
12240 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12241 "Actual size of all VM disks combined.");
12242 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12243 "Network receive rate.");
12244 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12245 "Network transmit rate.");
12246 /* Create and register base metrics */
12247 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12248 cpuLoadUser, cpuLoadKernel);
12249 aCollector->registerBaseMetric(cpuLoad);
12250 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12251 ramUsageUsed);
12252 aCollector->registerBaseMetric(ramUsage);
12253 MediaList disks;
12254 i_getDiskList(disks);
12255 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12256 diskUsageUsed);
12257 aCollector->registerBaseMetric(diskUsage);
12258
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12260 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12261 new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12263 new pm::AggregateMin()));
12264 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12265 new pm::AggregateMax()));
12266 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12267 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12268 new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12270 new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12272 new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12275 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12276 new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12278 new pm::AggregateMin()));
12279 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12280 new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12283 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12284 new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12286 new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12288 new pm::AggregateMax()));
12289
12290
12291 /* Guest metrics collector */
12292 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12293 aCollector->registerGuest(mCollectorGuest);
12294 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12295
12296 /* Create sub metrics */
12297 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12298 "Percentage of processor time spent in user mode as seen by the guest.");
12299 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12300 "Percentage of processor time spent in kernel mode as seen by the guest.");
12301 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12302 "Percentage of processor time spent idling as seen by the guest.");
12303
12304 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12305 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12306 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12307 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12308 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12309 pm::SubMetric *guestMemCache = new pm::SubMetric(
12310 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12311
12312 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12313 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12314
12315 /* Create and register base metrics */
12316 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12317 machineNetRx, machineNetTx);
12318 aCollector->registerBaseMetric(machineNetRate);
12319
12320 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12321 guestLoadUser, guestLoadKernel, guestLoadIdle);
12322 aCollector->registerBaseMetric(guestCpuLoad);
12323
12324 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12325 guestMemTotal, guestMemFree,
12326 guestMemBalloon, guestMemShared,
12327 guestMemCache, guestPagedTotal);
12328 aCollector->registerBaseMetric(guestCpuMem);
12329
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12331 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12336 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12384}
12385
12386void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12387{
12388 AssertReturnVoid(isWriteLockOnCurrentThread());
12389
12390 if (aCollector)
12391 {
12392 aCollector->unregisterMetricsFor(aMachine);
12393 aCollector->unregisterBaseMetricsFor(aMachine);
12394 }
12395}
12396
12397#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12398
12399
12400////////////////////////////////////////////////////////////////////////////////
12401
12402DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12403
12404HRESULT SessionMachine::FinalConstruct()
12405{
12406 LogFlowThisFunc(("\n"));
12407
12408 mClientToken = NULL;
12409
12410 return BaseFinalConstruct();
12411}
12412
12413void SessionMachine::FinalRelease()
12414{
12415 LogFlowThisFunc(("\n"));
12416
12417 Assert(!mClientToken);
12418 /* paranoia, should not hang around any more */
12419 if (mClientToken)
12420 {
12421 delete mClientToken;
12422 mClientToken = NULL;
12423 }
12424
12425 uninit(Uninit::Unexpected);
12426
12427 BaseFinalRelease();
12428}
12429
12430/**
12431 * @note Must be called only by Machine::LockMachine() from its own write lock.
12432 */
12433HRESULT SessionMachine::init(Machine *aMachine)
12434{
12435 LogFlowThisFuncEnter();
12436 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12437
12438 AssertReturn(aMachine, E_INVALIDARG);
12439
12440 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12441
12442 /* Enclose the state transition NotReady->InInit->Ready */
12443 AutoInitSpan autoInitSpan(this);
12444 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12445
12446 HRESULT rc = S_OK;
12447
12448 RT_ZERO(mAuthLibCtx);
12449
12450 /* create the machine client token */
12451 try
12452 {
12453 mClientToken = new ClientToken(aMachine, this);
12454 if (!mClientToken->isReady())
12455 {
12456 delete mClientToken;
12457 mClientToken = NULL;
12458 rc = E_FAIL;
12459 }
12460 }
12461 catch (std::bad_alloc &)
12462 {
12463 rc = E_OUTOFMEMORY;
12464 }
12465 if (FAILED(rc))
12466 return rc;
12467
12468 /* memorize the peer Machine */
12469 unconst(mPeer) = aMachine;
12470 /* share the parent pointer */
12471 unconst(mParent) = aMachine->mParent;
12472
12473 /* take the pointers to data to share */
12474 mData.share(aMachine->mData);
12475 mSSData.share(aMachine->mSSData);
12476
12477 mUserData.share(aMachine->mUserData);
12478 mHWData.share(aMachine->mHWData);
12479 mMediaData.share(aMachine->mMediaData);
12480
12481 mStorageControllers.allocate();
12482 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12483 it != aMachine->mStorageControllers->end();
12484 ++it)
12485 {
12486 ComObjPtr<StorageController> ctl;
12487 ctl.createObject();
12488 ctl->init(this, *it);
12489 mStorageControllers->push_back(ctl);
12490 }
12491
12492 mUSBControllers.allocate();
12493 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12494 it != aMachine->mUSBControllers->end();
12495 ++it)
12496 {
12497 ComObjPtr<USBController> ctl;
12498 ctl.createObject();
12499 ctl->init(this, *it);
12500 mUSBControllers->push_back(ctl);
12501 }
12502
12503 unconst(mBIOSSettings).createObject();
12504 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12505 /* create another VRDEServer object that will be mutable */
12506 unconst(mVRDEServer).createObject();
12507 mVRDEServer->init(this, aMachine->mVRDEServer);
12508 /* create another audio adapter object that will be mutable */
12509 unconst(mAudioAdapter).createObject();
12510 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12511 /* create a list of serial ports that will be mutable */
12512 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12513 {
12514 unconst(mSerialPorts[slot]).createObject();
12515 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12516 }
12517 /* create a list of parallel ports that will be mutable */
12518 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12519 {
12520 unconst(mParallelPorts[slot]).createObject();
12521 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12522 }
12523
12524 /* create another USB device filters object that will be mutable */
12525 unconst(mUSBDeviceFilters).createObject();
12526 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12527
12528 /* create a list of network adapters that will be mutable */
12529 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12530 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12531 {
12532 unconst(mNetworkAdapters[slot]).createObject();
12533 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12534 }
12535
12536 /* create another bandwidth control object that will be mutable */
12537 unconst(mBandwidthControl).createObject();
12538 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12539
12540#ifdef VBOX_WITH_UNATTENDED
12541 /* create another unattended object that will be mutable */
12542 unconst(mUnattended).createObject();
12543 mUnattended->init(this, aMachine->mUnattended);
12544#endif
12545
12546 /* default is to delete saved state on Saved -> PoweredOff transition */
12547 mRemoveSavedState = true;
12548
12549 /* Confirm a successful initialization when it's the case */
12550 autoInitSpan.setSucceeded();
12551
12552 miNATNetworksStarted = 0;
12553
12554 LogFlowThisFuncLeave();
12555 return rc;
12556}
12557
12558/**
12559 * Uninitializes this session object. If the reason is other than
12560 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12561 * or the client watcher code.
12562 *
12563 * @param aReason uninitialization reason
12564 *
12565 * @note Locks mParent + this object for writing.
12566 */
12567void SessionMachine::uninit(Uninit::Reason aReason)
12568{
12569 LogFlowThisFuncEnter();
12570 LogFlowThisFunc(("reason=%d\n", aReason));
12571
12572 /*
12573 * Strongly reference ourselves to prevent this object deletion after
12574 * mData->mSession.mMachine.setNull() below (which can release the last
12575 * reference and call the destructor). Important: this must be done before
12576 * accessing any members (and before AutoUninitSpan that does it as well).
12577 * This self reference will be released as the very last step on return.
12578 */
12579 ComObjPtr<SessionMachine> selfRef = this;
12580
12581 /* Enclose the state transition Ready->InUninit->NotReady */
12582 AutoUninitSpan autoUninitSpan(this);
12583 if (autoUninitSpan.uninitDone())
12584 {
12585 LogFlowThisFunc(("Already uninitialized\n"));
12586 LogFlowThisFuncLeave();
12587 return;
12588 }
12589
12590 if (autoUninitSpan.initFailed())
12591 {
12592 /* We've been called by init() because it's failed. It's not really
12593 * necessary (nor it's safe) to perform the regular uninit sequence
12594 * below, the following is enough.
12595 */
12596 LogFlowThisFunc(("Initialization failed.\n"));
12597 /* destroy the machine client token */
12598 if (mClientToken)
12599 {
12600 delete mClientToken;
12601 mClientToken = NULL;
12602 }
12603 uninitDataAndChildObjects();
12604 mData.free();
12605 unconst(mParent) = NULL;
12606 unconst(mPeer) = NULL;
12607 LogFlowThisFuncLeave();
12608 return;
12609 }
12610
12611 MachineState_T lastState;
12612 {
12613 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12614 lastState = mData->mMachineState;
12615 }
12616 NOREF(lastState);
12617
12618#ifdef VBOX_WITH_USB
12619 // release all captured USB devices, but do this before requesting the locks below
12620 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12621 {
12622 /* Console::captureUSBDevices() is called in the VM process only after
12623 * setting the machine state to Starting or Restoring.
12624 * Console::detachAllUSBDevices() will be called upon successful
12625 * termination. So, we need to release USB devices only if there was
12626 * an abnormal termination of a running VM.
12627 *
12628 * This is identical to SessionMachine::DetachAllUSBDevices except
12629 * for the aAbnormal argument. */
12630 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12631 AssertComRC(rc);
12632 NOREF(rc);
12633
12634 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12635 if (service)
12636 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12637 }
12638#endif /* VBOX_WITH_USB */
12639
12640 // we need to lock this object in uninit() because the lock is shared
12641 // with mPeer (as well as data we modify below). mParent lock is needed
12642 // by several calls to it.
12643 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12644
12645#ifdef VBOX_WITH_RESOURCE_USAGE_API
12646 /*
12647 * It is safe to call Machine::i_unregisterMetrics() here because
12648 * PerformanceCollector::samplerCallback no longer accesses guest methods
12649 * holding the lock.
12650 */
12651 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12652 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12653 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12654 if (mCollectorGuest)
12655 {
12656 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12657 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12658 mCollectorGuest = NULL;
12659 }
12660#endif
12661
12662 if (aReason == Uninit::Abnormal)
12663 {
12664 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12665
12666 /* reset the state to Aborted */
12667 if (mData->mMachineState != MachineState_Aborted)
12668 i_setMachineState(MachineState_Aborted);
12669 }
12670
12671 // any machine settings modified?
12672 if (mData->flModifications)
12673 {
12674 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12675 i_rollback(false /* aNotify */);
12676 }
12677
12678 mData->mSession.mPID = NIL_RTPROCESS;
12679
12680 if (aReason == Uninit::Unexpected)
12681 {
12682 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12683 * client watcher thread to update the set of machines that have open
12684 * sessions. */
12685 mParent->i_updateClientWatcher();
12686 }
12687
12688 /* uninitialize all remote controls */
12689 if (mData->mSession.mRemoteControls.size())
12690 {
12691 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12692 mData->mSession.mRemoteControls.size()));
12693
12694 Data::Session::RemoteControlList::iterator it =
12695 mData->mSession.mRemoteControls.begin();
12696 while (it != mData->mSession.mRemoteControls.end())
12697 {
12698 ComPtr<IInternalSessionControl> pControl = *it;
12699 mData->mSession.mRemoteControls.erase(it);
12700 multilock.release();
12701 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12702 HRESULT rc = pControl->Uninitialize();
12703 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12704 if (FAILED(rc))
12705 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12706 multilock.acquire();
12707 it = mData->mSession.mRemoteControls.begin();
12708 }
12709 mData->mSession.mRemoteControls.clear();
12710 }
12711
12712 /* Remove all references to the NAT network service. The service will stop
12713 * if all references (also from other VMs) are removed. */
12714 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12715 {
12716 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12717 {
12718 BOOL enabled;
12719 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12720 if ( FAILED(hrc)
12721 || !enabled)
12722 continue;
12723
12724 NetworkAttachmentType_T type;
12725 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12726 if ( SUCCEEDED(hrc)
12727 && type == NetworkAttachmentType_NATNetwork)
12728 {
12729 Bstr name;
12730 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12731 if (SUCCEEDED(hrc))
12732 {
12733 multilock.release();
12734 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12735 mUserData->s.strName.c_str(), name.raw()));
12736 mParent->i_natNetworkRefDec(name.raw());
12737 multilock.acquire();
12738 }
12739 }
12740 }
12741 }
12742
12743 /*
12744 * An expected uninitialization can come only from #i_checkForDeath().
12745 * Otherwise it means that something's gone really wrong (for example,
12746 * the Session implementation has released the VirtualBox reference
12747 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12748 * etc). However, it's also possible, that the client releases the IPC
12749 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12750 * but the VirtualBox release event comes first to the server process.
12751 * This case is practically possible, so we should not assert on an
12752 * unexpected uninit, just log a warning.
12753 */
12754
12755 if (aReason == Uninit::Unexpected)
12756 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12757
12758 if (aReason != Uninit::Normal)
12759 {
12760 mData->mSession.mDirectControl.setNull();
12761 }
12762 else
12763 {
12764 /* this must be null here (see #OnSessionEnd()) */
12765 Assert(mData->mSession.mDirectControl.isNull());
12766 Assert(mData->mSession.mState == SessionState_Unlocking);
12767 Assert(!mData->mSession.mProgress.isNull());
12768 }
12769 if (mData->mSession.mProgress)
12770 {
12771 if (aReason == Uninit::Normal)
12772 mData->mSession.mProgress->i_notifyComplete(S_OK);
12773 else
12774 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12775 COM_IIDOF(ISession),
12776 getComponentName(),
12777 tr("The VM session was aborted"));
12778 mData->mSession.mProgress.setNull();
12779 }
12780
12781 if (mConsoleTaskData.mProgress)
12782 {
12783 Assert(aReason == Uninit::Abnormal);
12784 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12785 COM_IIDOF(ISession),
12786 getComponentName(),
12787 tr("The VM session was aborted"));
12788 mConsoleTaskData.mProgress.setNull();
12789 }
12790
12791 /* remove the association between the peer machine and this session machine */
12792 Assert( (SessionMachine*)mData->mSession.mMachine == this
12793 || aReason == Uninit::Unexpected);
12794
12795 /* reset the rest of session data */
12796 mData->mSession.mLockType = LockType_Null;
12797 mData->mSession.mMachine.setNull();
12798 mData->mSession.mState = SessionState_Unlocked;
12799 mData->mSession.mName.setNull();
12800
12801 /* destroy the machine client token before leaving the exclusive lock */
12802 if (mClientToken)
12803 {
12804 delete mClientToken;
12805 mClientToken = NULL;
12806 }
12807
12808 /* fire an event */
12809 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12810
12811 uninitDataAndChildObjects();
12812
12813 /* free the essential data structure last */
12814 mData.free();
12815
12816 /* release the exclusive lock before setting the below two to NULL */
12817 multilock.release();
12818
12819 unconst(mParent) = NULL;
12820 unconst(mPeer) = NULL;
12821
12822 AuthLibUnload(&mAuthLibCtx);
12823
12824 LogFlowThisFuncLeave();
12825}
12826
12827// util::Lockable interface
12828////////////////////////////////////////////////////////////////////////////////
12829
12830/**
12831 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12832 * with the primary Machine instance (mPeer).
12833 */
12834RWLockHandle *SessionMachine::lockHandle() const
12835{
12836 AssertReturn(mPeer != NULL, NULL);
12837 return mPeer->lockHandle();
12838}
12839
12840// IInternalMachineControl methods
12841////////////////////////////////////////////////////////////////////////////////
12842
12843/**
12844 * Passes collected guest statistics to performance collector object
12845 */
12846HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12847 ULONG aCpuKernel, ULONG aCpuIdle,
12848 ULONG aMemTotal, ULONG aMemFree,
12849 ULONG aMemBalloon, ULONG aMemShared,
12850 ULONG aMemCache, ULONG aPageTotal,
12851 ULONG aAllocVMM, ULONG aFreeVMM,
12852 ULONG aBalloonedVMM, ULONG aSharedVMM,
12853 ULONG aVmNetRx, ULONG aVmNetTx)
12854{
12855#ifdef VBOX_WITH_RESOURCE_USAGE_API
12856 if (mCollectorGuest)
12857 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12858 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12859 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12860 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12861
12862 return S_OK;
12863#else
12864 NOREF(aValidStats);
12865 NOREF(aCpuUser);
12866 NOREF(aCpuKernel);
12867 NOREF(aCpuIdle);
12868 NOREF(aMemTotal);
12869 NOREF(aMemFree);
12870 NOREF(aMemBalloon);
12871 NOREF(aMemShared);
12872 NOREF(aMemCache);
12873 NOREF(aPageTotal);
12874 NOREF(aAllocVMM);
12875 NOREF(aFreeVMM);
12876 NOREF(aBalloonedVMM);
12877 NOREF(aSharedVMM);
12878 NOREF(aVmNetRx);
12879 NOREF(aVmNetTx);
12880 return E_NOTIMPL;
12881#endif
12882}
12883
12884////////////////////////////////////////////////////////////////////////////////
12885//
12886// SessionMachine task records
12887//
12888////////////////////////////////////////////////////////////////////////////////
12889
12890/**
12891 * Task record for saving the machine state.
12892 */
12893class SessionMachine::SaveStateTask
12894 : public Machine::Task
12895{
12896public:
12897 SaveStateTask(SessionMachine *m,
12898 Progress *p,
12899 const Utf8Str &t,
12900 Reason_T enmReason,
12901 const Utf8Str &strStateFilePath)
12902 : Task(m, p, t),
12903 m_enmReason(enmReason),
12904 m_strStateFilePath(strStateFilePath)
12905 {}
12906
12907private:
12908 void handler()
12909 {
12910 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12911 }
12912
12913 Reason_T m_enmReason;
12914 Utf8Str m_strStateFilePath;
12915
12916 friend class SessionMachine;
12917};
12918
12919/**
12920 * Task thread implementation for SessionMachine::SaveState(), called from
12921 * SessionMachine::taskHandler().
12922 *
12923 * @note Locks this object for writing.
12924 *
12925 * @param task
12926 * @return
12927 */
12928void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12929{
12930 LogFlowThisFuncEnter();
12931
12932 AutoCaller autoCaller(this);
12933 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12934 if (FAILED(autoCaller.rc()))
12935 {
12936 /* we might have been uninitialized because the session was accidentally
12937 * closed by the client, so don't assert */
12938 HRESULT rc = setError(E_FAIL,
12939 tr("The session has been accidentally closed"));
12940 task.m_pProgress->i_notifyComplete(rc);
12941 LogFlowThisFuncLeave();
12942 return;
12943 }
12944
12945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12946
12947 HRESULT rc = S_OK;
12948
12949 try
12950 {
12951 ComPtr<IInternalSessionControl> directControl;
12952 if (mData->mSession.mLockType == LockType_VM)
12953 directControl = mData->mSession.mDirectControl;
12954 if (directControl.isNull())
12955 throw setError(VBOX_E_INVALID_VM_STATE,
12956 tr("Trying to save state without a running VM"));
12957 alock.release();
12958 BOOL fSuspendedBySave;
12959 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12960 Assert(!fSuspendedBySave);
12961 alock.acquire();
12962
12963 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12964 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12965 throw E_FAIL);
12966
12967 if (SUCCEEDED(rc))
12968 {
12969 mSSData->strStateFilePath = task.m_strStateFilePath;
12970
12971 /* save all VM settings */
12972 rc = i_saveSettings(NULL);
12973 // no need to check whether VirtualBox.xml needs saving also since
12974 // we can't have a name change pending at this point
12975 }
12976 else
12977 {
12978 // On failure, set the state to the state we had at the beginning.
12979 i_setMachineState(task.m_machineStateBackup);
12980 i_updateMachineStateOnClient();
12981
12982 // Delete the saved state file (might have been already created).
12983 // No need to check whether this is shared with a snapshot here
12984 // because we certainly created a fresh saved state file here.
12985 RTFileDelete(task.m_strStateFilePath.c_str());
12986 }
12987 }
12988 catch (HRESULT aRC) { rc = aRC; }
12989
12990 task.m_pProgress->i_notifyComplete(rc);
12991
12992 LogFlowThisFuncLeave();
12993}
12994
12995/**
12996 * @note Locks this object for writing.
12997 */
12998HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12999{
13000 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13001}
13002
13003HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13004{
13005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13006
13007 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13008 if (FAILED(rc)) return rc;
13009
13010 if ( mData->mMachineState != MachineState_Running
13011 && mData->mMachineState != MachineState_Paused
13012 )
13013 return setError(VBOX_E_INVALID_VM_STATE,
13014 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13015 Global::stringifyMachineState(mData->mMachineState));
13016
13017 ComObjPtr<Progress> pProgress;
13018 pProgress.createObject();
13019 rc = pProgress->init(i_getVirtualBox(),
13020 static_cast<IMachine *>(this) /* aInitiator */,
13021 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13022 FALSE /* aCancelable */);
13023 if (FAILED(rc))
13024 return rc;
13025
13026 Utf8Str strStateFilePath;
13027 i_composeSavedStateFilename(strStateFilePath);
13028
13029 /* create and start the task on a separate thread (note that it will not
13030 * start working until we release alock) */
13031 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13032 rc = pTask->createThread();
13033 if (FAILED(rc))
13034 return rc;
13035
13036 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13037 i_setMachineState(MachineState_Saving);
13038 i_updateMachineStateOnClient();
13039
13040 pProgress.queryInterfaceTo(aProgress.asOutParam());
13041
13042 return S_OK;
13043}
13044
13045/**
13046 * @note Locks this object for writing.
13047 */
13048HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13049{
13050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13051
13052 HRESULT rc = i_checkStateDependency(MutableStateDep);
13053 if (FAILED(rc)) return rc;
13054
13055 if ( mData->mMachineState != MachineState_PoweredOff
13056 && mData->mMachineState != MachineState_Teleported
13057 && mData->mMachineState != MachineState_Aborted
13058 )
13059 return setError(VBOX_E_INVALID_VM_STATE,
13060 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13061 Global::stringifyMachineState(mData->mMachineState));
13062
13063 com::Utf8Str stateFilePathFull;
13064 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13065 if (RT_FAILURE(vrc))
13066 return setError(VBOX_E_FILE_ERROR,
13067 tr("Invalid saved state file path '%s' (%Rrc)"),
13068 aSavedStateFile.c_str(),
13069 vrc);
13070
13071 mSSData->strStateFilePath = stateFilePathFull;
13072
13073 /* The below i_setMachineState() will detect the state transition and will
13074 * update the settings file */
13075
13076 return i_setMachineState(MachineState_Saved);
13077}
13078
13079/**
13080 * @note Locks this object for writing.
13081 */
13082HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13083{
13084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13085
13086 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13087 if (FAILED(rc)) return rc;
13088
13089 if (mData->mMachineState != MachineState_Saved)
13090 return setError(VBOX_E_INVALID_VM_STATE,
13091 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13092 Global::stringifyMachineState(mData->mMachineState));
13093
13094 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13095
13096 /*
13097 * Saved -> PoweredOff transition will be detected in the SessionMachine
13098 * and properly handled.
13099 */
13100 rc = i_setMachineState(MachineState_PoweredOff);
13101 return rc;
13102}
13103
13104
13105/**
13106 * @note Locks the same as #i_setMachineState() does.
13107 */
13108HRESULT SessionMachine::updateState(MachineState_T aState)
13109{
13110 return i_setMachineState(aState);
13111}
13112
13113/**
13114 * @note Locks this object for writing.
13115 */
13116HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13117{
13118 IProgress* pProgress(aProgress);
13119
13120 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13121
13122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13123
13124 if (mData->mSession.mState != SessionState_Locked)
13125 return VBOX_E_INVALID_OBJECT_STATE;
13126
13127 if (!mData->mSession.mProgress.isNull())
13128 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13129
13130 /* If we didn't reference the NAT network service yet, add a reference to
13131 * force a start */
13132 if (miNATNetworksStarted < 1)
13133 {
13134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13135 {
13136 BOOL enabled;
13137 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13138 if ( FAILED(hrc)
13139 || !enabled)
13140 continue;
13141
13142 NetworkAttachmentType_T type;
13143 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13144 if ( SUCCEEDED(hrc)
13145 && type == NetworkAttachmentType_NATNetwork)
13146 {
13147 Bstr name;
13148 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13149 if (SUCCEEDED(hrc))
13150 {
13151 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13152 mUserData->s.strName.c_str(), name.raw()));
13153 mPeer->lockHandle()->unlockWrite();
13154 mParent->i_natNetworkRefInc(name.raw());
13155#ifdef RT_LOCK_STRICT
13156 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13157#else
13158 mPeer->lockHandle()->lockWrite();
13159#endif
13160 }
13161 }
13162 }
13163 miNATNetworksStarted++;
13164 }
13165
13166 LogFlowThisFunc(("returns S_OK.\n"));
13167 return S_OK;
13168}
13169
13170/**
13171 * @note Locks this object for writing.
13172 */
13173HRESULT SessionMachine::endPowerUp(LONG aResult)
13174{
13175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13176
13177 if (mData->mSession.mState != SessionState_Locked)
13178 return VBOX_E_INVALID_OBJECT_STATE;
13179
13180 /* Finalize the LaunchVMProcess progress object. */
13181 if (mData->mSession.mProgress)
13182 {
13183 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13184 mData->mSession.mProgress.setNull();
13185 }
13186
13187 if (SUCCEEDED((HRESULT)aResult))
13188 {
13189#ifdef VBOX_WITH_RESOURCE_USAGE_API
13190 /* The VM has been powered up successfully, so it makes sense
13191 * now to offer the performance metrics for a running machine
13192 * object. Doing it earlier wouldn't be safe. */
13193 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13194 mData->mSession.mPID);
13195#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13196 }
13197
13198 return S_OK;
13199}
13200
13201/**
13202 * @note Locks this object for writing.
13203 */
13204HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13205{
13206 LogFlowThisFuncEnter();
13207
13208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13209
13210 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13211 E_FAIL);
13212
13213 /* create a progress object to track operation completion */
13214 ComObjPtr<Progress> pProgress;
13215 pProgress.createObject();
13216 pProgress->init(i_getVirtualBox(),
13217 static_cast<IMachine *>(this) /* aInitiator */,
13218 Bstr(tr("Stopping the virtual machine")).raw(),
13219 FALSE /* aCancelable */);
13220
13221 /* fill in the console task data */
13222 mConsoleTaskData.mLastState = mData->mMachineState;
13223 mConsoleTaskData.mProgress = pProgress;
13224
13225 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13226 i_setMachineState(MachineState_Stopping);
13227
13228 pProgress.queryInterfaceTo(aProgress.asOutParam());
13229
13230 return S_OK;
13231}
13232
13233/**
13234 * @note Locks this object for writing.
13235 */
13236HRESULT SessionMachine::endPoweringDown(LONG aResult,
13237 const com::Utf8Str &aErrMsg)
13238{
13239 LogFlowThisFuncEnter();
13240
13241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13242
13243 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13244 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13245 && mConsoleTaskData.mLastState != MachineState_Null,
13246 E_FAIL);
13247
13248 /*
13249 * On failure, set the state to the state we had when BeginPoweringDown()
13250 * was called (this is expected by Console::PowerDown() and the associated
13251 * task). On success the VM process already changed the state to
13252 * MachineState_PoweredOff, so no need to do anything.
13253 */
13254 if (FAILED(aResult))
13255 i_setMachineState(mConsoleTaskData.mLastState);
13256
13257 /* notify the progress object about operation completion */
13258 Assert(mConsoleTaskData.mProgress);
13259 if (SUCCEEDED(aResult))
13260 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13261 else
13262 {
13263 if (aErrMsg.length())
13264 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13265 COM_IIDOF(ISession),
13266 getComponentName(),
13267 aErrMsg.c_str());
13268 else
13269 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13270 }
13271
13272 /* clear out the temporary saved state data */
13273 mConsoleTaskData.mLastState = MachineState_Null;
13274 mConsoleTaskData.mProgress.setNull();
13275
13276 LogFlowThisFuncLeave();
13277 return S_OK;
13278}
13279
13280
13281/**
13282 * Goes through the USB filters of the given machine to see if the given
13283 * device matches any filter or not.
13284 *
13285 * @note Locks the same as USBController::hasMatchingFilter() does.
13286 */
13287HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13288 BOOL *aMatched,
13289 ULONG *aMaskedInterfaces)
13290{
13291 LogFlowThisFunc(("\n"));
13292
13293#ifdef VBOX_WITH_USB
13294 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13295#else
13296 NOREF(aDevice);
13297 NOREF(aMaskedInterfaces);
13298 *aMatched = FALSE;
13299#endif
13300
13301 return S_OK;
13302}
13303
13304/**
13305 * @note Locks the same as Host::captureUSBDevice() does.
13306 */
13307HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13308{
13309 LogFlowThisFunc(("\n"));
13310
13311#ifdef VBOX_WITH_USB
13312 /* if captureDeviceForVM() fails, it must have set extended error info */
13313 clearError();
13314 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13315 if (FAILED(rc)) return rc;
13316
13317 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13318 AssertReturn(service, E_FAIL);
13319 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13320#else
13321 NOREF(aId);
13322 return E_NOTIMPL;
13323#endif
13324}
13325
13326/**
13327 * @note Locks the same as Host::detachUSBDevice() does.
13328 */
13329HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13330 BOOL aDone)
13331{
13332 LogFlowThisFunc(("\n"));
13333
13334#ifdef VBOX_WITH_USB
13335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13336 AssertReturn(service, E_FAIL);
13337 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13338#else
13339 NOREF(aId);
13340 NOREF(aDone);
13341 return E_NOTIMPL;
13342#endif
13343}
13344
13345/**
13346 * Inserts all machine filters to the USB proxy service and then calls
13347 * Host::autoCaptureUSBDevices().
13348 *
13349 * Called by Console from the VM process upon VM startup.
13350 *
13351 * @note Locks what called methods lock.
13352 */
13353HRESULT SessionMachine::autoCaptureUSBDevices()
13354{
13355 LogFlowThisFunc(("\n"));
13356
13357#ifdef VBOX_WITH_USB
13358 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13359 AssertComRC(rc);
13360 NOREF(rc);
13361
13362 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13363 AssertReturn(service, E_FAIL);
13364 return service->autoCaptureDevicesForVM(this);
13365#else
13366 return S_OK;
13367#endif
13368}
13369
13370/**
13371 * Removes all machine filters from the USB proxy service and then calls
13372 * Host::detachAllUSBDevices().
13373 *
13374 * Called by Console from the VM process upon normal VM termination or by
13375 * SessionMachine::uninit() upon abnormal VM termination (from under the
13376 * Machine/SessionMachine lock).
13377 *
13378 * @note Locks what called methods lock.
13379 */
13380HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384#ifdef VBOX_WITH_USB
13385 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13386 AssertComRC(rc);
13387 NOREF(rc);
13388
13389 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13390 AssertReturn(service, E_FAIL);
13391 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13392#else
13393 NOREF(aDone);
13394 return S_OK;
13395#endif
13396}
13397
13398/**
13399 * @note Locks this object for writing.
13400 */
13401HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13402 ComPtr<IProgress> &aProgress)
13403{
13404 LogFlowThisFuncEnter();
13405
13406 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13407 /*
13408 * We don't assert below because it might happen that a non-direct session
13409 * informs us it is closed right after we've been uninitialized -- it's ok.
13410 */
13411
13412 /* get IInternalSessionControl interface */
13413 ComPtr<IInternalSessionControl> control(aSession);
13414
13415 ComAssertRet(!control.isNull(), E_INVALIDARG);
13416
13417 /* Creating a Progress object requires the VirtualBox lock, and
13418 * thus locking it here is required by the lock order rules. */
13419 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13420
13421 if (control == mData->mSession.mDirectControl)
13422 {
13423 /* The direct session is being normally closed by the client process
13424 * ----------------------------------------------------------------- */
13425
13426 /* go to the closing state (essential for all open*Session() calls and
13427 * for #i_checkForDeath()) */
13428 Assert(mData->mSession.mState == SessionState_Locked);
13429 mData->mSession.mState = SessionState_Unlocking;
13430
13431 /* set direct control to NULL to release the remote instance */
13432 mData->mSession.mDirectControl.setNull();
13433 LogFlowThisFunc(("Direct control is set to NULL\n"));
13434
13435 if (mData->mSession.mProgress)
13436 {
13437 /* finalize the progress, someone might wait if a frontend
13438 * closes the session before powering on the VM. */
13439 mData->mSession.mProgress->notifyComplete(E_FAIL,
13440 COM_IIDOF(ISession),
13441 getComponentName(),
13442 tr("The VM session was closed before any attempt to power it on"));
13443 mData->mSession.mProgress.setNull();
13444 }
13445
13446 /* Create the progress object the client will use to wait until
13447 * #i_checkForDeath() is called to uninitialize this session object after
13448 * it releases the IPC semaphore.
13449 * Note! Because we're "reusing" mProgress here, this must be a proxy
13450 * object just like for LaunchVMProcess. */
13451 Assert(mData->mSession.mProgress.isNull());
13452 ComObjPtr<ProgressProxy> progress;
13453 progress.createObject();
13454 ComPtr<IUnknown> pPeer(mPeer);
13455 progress->init(mParent, pPeer,
13456 Bstr(tr("Closing session")).raw(),
13457 FALSE /* aCancelable */);
13458 progress.queryInterfaceTo(aProgress.asOutParam());
13459 mData->mSession.mProgress = progress;
13460 }
13461 else
13462 {
13463 /* the remote session is being normally closed */
13464 Data::Session::RemoteControlList::iterator it =
13465 mData->mSession.mRemoteControls.begin();
13466 while (it != mData->mSession.mRemoteControls.end())
13467 {
13468 if (control == *it)
13469 break;
13470 ++it;
13471 }
13472 BOOL found = it != mData->mSession.mRemoteControls.end();
13473 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13474 E_INVALIDARG);
13475 // This MUST be erase(it), not remove(*it) as the latter triggers a
13476 // very nasty use after free due to the place where the value "lives".
13477 mData->mSession.mRemoteControls.erase(it);
13478 }
13479
13480 /* signal the client watcher thread, because the client is going away */
13481 mParent->i_updateClientWatcher();
13482
13483 LogFlowThisFuncLeave();
13484 return S_OK;
13485}
13486
13487HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13488 std::vector<com::Utf8Str> &aValues,
13489 std::vector<LONG64> &aTimestamps,
13490 std::vector<com::Utf8Str> &aFlags)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494#ifdef VBOX_WITH_GUEST_PROPS
13495 using namespace guestProp;
13496
13497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13498
13499 size_t cEntries = mHWData->mGuestProperties.size();
13500 aNames.resize(cEntries);
13501 aValues.resize(cEntries);
13502 aTimestamps.resize(cEntries);
13503 aFlags.resize(cEntries);
13504
13505 size_t i = 0;
13506 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13507 it != mHWData->mGuestProperties.end();
13508 ++it, ++i)
13509 {
13510 char szFlags[MAX_FLAGS_LEN + 1];
13511 aNames[i] = it->first;
13512 aValues[i] = it->second.strValue;
13513 aTimestamps[i] = it->second.mTimestamp;
13514
13515 /* If it is NULL, keep it NULL. */
13516 if (it->second.mFlags)
13517 {
13518 writeFlags(it->second.mFlags, szFlags);
13519 aFlags[i] = szFlags;
13520 }
13521 else
13522 aFlags[i] = "";
13523 }
13524 return S_OK;
13525#else
13526 ReturnComNotImplemented();
13527#endif
13528}
13529
13530HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13531 const com::Utf8Str &aValue,
13532 LONG64 aTimestamp,
13533 const com::Utf8Str &aFlags)
13534{
13535 LogFlowThisFunc(("\n"));
13536
13537#ifdef VBOX_WITH_GUEST_PROPS
13538 using namespace guestProp;
13539
13540 try
13541 {
13542 /*
13543 * Convert input up front.
13544 */
13545 uint32_t fFlags = NILFLAG;
13546 if (aFlags.length())
13547 {
13548 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13549 AssertRCReturn(vrc, E_INVALIDARG);
13550 }
13551
13552 /*
13553 * Now grab the object lock, validate the state and do the update.
13554 */
13555
13556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13557
13558 if (!Global::IsOnline(mData->mMachineState))
13559 {
13560 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13561 VBOX_E_INVALID_VM_STATE);
13562 }
13563
13564 i_setModified(IsModified_MachineData);
13565 mHWData.backup();
13566
13567 bool fDelete = !aValue.length();
13568 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13569 if (it != mHWData->mGuestProperties.end())
13570 {
13571 if (!fDelete)
13572 {
13573 it->second.strValue = aValue;
13574 it->second.mTimestamp = aTimestamp;
13575 it->second.mFlags = fFlags;
13576 }
13577 else
13578 mHWData->mGuestProperties.erase(it);
13579
13580 mData->mGuestPropertiesModified = TRUE;
13581 }
13582 else if (!fDelete)
13583 {
13584 HWData::GuestProperty prop;
13585 prop.strValue = aValue;
13586 prop.mTimestamp = aTimestamp;
13587 prop.mFlags = fFlags;
13588
13589 mHWData->mGuestProperties[aName] = prop;
13590 mData->mGuestPropertiesModified = TRUE;
13591 }
13592
13593 alock.release();
13594
13595 mParent->i_onGuestPropertyChange(mData->mUuid,
13596 Bstr(aName).raw(),
13597 Bstr(aValue).raw(),
13598 Bstr(aFlags).raw());
13599 }
13600 catch (...)
13601 {
13602 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13603 }
13604 return S_OK;
13605#else
13606 ReturnComNotImplemented();
13607#endif
13608}
13609
13610
13611HRESULT SessionMachine::lockMedia()
13612{
13613 AutoMultiWriteLock2 alock(this->lockHandle(),
13614 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13615
13616 AssertReturn( mData->mMachineState == MachineState_Starting
13617 || mData->mMachineState == MachineState_Restoring
13618 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13619
13620 clearError();
13621 alock.release();
13622 return i_lockMedia();
13623}
13624
13625HRESULT SessionMachine::unlockMedia()
13626{
13627 HRESULT hrc = i_unlockMedia();
13628 return hrc;
13629}
13630
13631HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13632 ComPtr<IMediumAttachment> &aNewAttachment)
13633{
13634 // request the host lock first, since might be calling Host methods for getting host drives;
13635 // next, protect the media tree all the while we're in here, as well as our member variables
13636 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13637 this->lockHandle(),
13638 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13639
13640 IMediumAttachment *iAttach = aAttachment;
13641 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13642
13643 Bstr ctrlName;
13644 LONG lPort;
13645 LONG lDevice;
13646 bool fTempEject;
13647 {
13648 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13649
13650 /* Need to query the details first, as the IMediumAttachment reference
13651 * might be to the original settings, which we are going to change. */
13652 ctrlName = pAttach->i_getControllerName();
13653 lPort = pAttach->i_getPort();
13654 lDevice = pAttach->i_getDevice();
13655 fTempEject = pAttach->i_getTempEject();
13656 }
13657
13658 if (!fTempEject)
13659 {
13660 /* Remember previously mounted medium. The medium before taking the
13661 * backup is not necessarily the same thing. */
13662 ComObjPtr<Medium> oldmedium;
13663 oldmedium = pAttach->i_getMedium();
13664
13665 i_setModified(IsModified_Storage);
13666 mMediaData.backup();
13667
13668 // The backup operation makes the pAttach reference point to the
13669 // old settings. Re-get the correct reference.
13670 pAttach = i_findAttachment(mMediaData->mAttachments,
13671 ctrlName.raw(),
13672 lPort,
13673 lDevice);
13674
13675 {
13676 AutoCaller autoAttachCaller(this);
13677 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13678
13679 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13680 if (!oldmedium.isNull())
13681 oldmedium->i_removeBackReference(mData->mUuid);
13682
13683 pAttach->i_updateMedium(NULL);
13684 pAttach->i_updateEjected();
13685 }
13686
13687 i_setModified(IsModified_Storage);
13688 }
13689 else
13690 {
13691 {
13692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13693 pAttach->i_updateEjected();
13694 }
13695 }
13696
13697 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13698
13699 return S_OK;
13700}
13701
13702HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13703 com::Utf8Str &aResult)
13704{
13705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13706
13707 HRESULT hr = S_OK;
13708
13709 if (!mAuthLibCtx.hAuthLibrary)
13710 {
13711 /* Load the external authentication library. */
13712 Bstr authLibrary;
13713 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13714
13715 Utf8Str filename = authLibrary;
13716
13717 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13718 if (RT_FAILURE(rc))
13719 {
13720 hr = setError(E_FAIL,
13721 tr("Could not load the external authentication library '%s' (%Rrc)"),
13722 filename.c_str(), rc);
13723 }
13724 }
13725
13726 /* The auth library might need the machine lock. */
13727 alock.release();
13728
13729 if (FAILED(hr))
13730 return hr;
13731
13732 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13733 {
13734 enum VRDEAuthParams
13735 {
13736 parmUuid = 1,
13737 parmGuestJudgement,
13738 parmUser,
13739 parmPassword,
13740 parmDomain,
13741 parmClientId
13742 };
13743
13744 AuthResult result = AuthResultAccessDenied;
13745
13746 Guid uuid(aAuthParams[parmUuid]);
13747 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13748 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13749
13750 result = AuthLibAuthenticate(&mAuthLibCtx,
13751 uuid.raw(), guestJudgement,
13752 aAuthParams[parmUser].c_str(),
13753 aAuthParams[parmPassword].c_str(),
13754 aAuthParams[parmDomain].c_str(),
13755 u32ClientId);
13756
13757 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13758 size_t cbPassword = aAuthParams[parmPassword].length();
13759 if (cbPassword)
13760 {
13761 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13762 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13763 }
13764
13765 if (result == AuthResultAccessGranted)
13766 aResult = "granted";
13767 else
13768 aResult = "denied";
13769
13770 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13771 aAuthParams[parmUser].c_str(), aResult.c_str()));
13772 }
13773 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13774 {
13775 enum VRDEAuthDisconnectParams
13776 {
13777 parmUuid = 1,
13778 parmClientId
13779 };
13780
13781 Guid uuid(aAuthParams[parmUuid]);
13782 uint32_t u32ClientId = 0;
13783 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13784 }
13785 else
13786 {
13787 hr = E_INVALIDARG;
13788 }
13789
13790 return hr;
13791}
13792
13793// public methods only for internal purposes
13794/////////////////////////////////////////////////////////////////////////////
13795
13796#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13797/**
13798 * Called from the client watcher thread to check for expected or unexpected
13799 * death of the client process that has a direct session to this machine.
13800 *
13801 * On Win32 and on OS/2, this method is called only when we've got the
13802 * mutex (i.e. the client has either died or terminated normally) so it always
13803 * returns @c true (the client is terminated, the session machine is
13804 * uninitialized).
13805 *
13806 * On other platforms, the method returns @c true if the client process has
13807 * terminated normally or abnormally and the session machine was uninitialized,
13808 * and @c false if the client process is still alive.
13809 *
13810 * @note Locks this object for writing.
13811 */
13812bool SessionMachine::i_checkForDeath()
13813{
13814 Uninit::Reason reason;
13815 bool terminated = false;
13816
13817 /* Enclose autoCaller with a block because calling uninit() from under it
13818 * will deadlock. */
13819 {
13820 AutoCaller autoCaller(this);
13821 if (!autoCaller.isOk())
13822 {
13823 /* return true if not ready, to cause the client watcher to exclude
13824 * the corresponding session from watching */
13825 LogFlowThisFunc(("Already uninitialized!\n"));
13826 return true;
13827 }
13828
13829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13830
13831 /* Determine the reason of death: if the session state is Closing here,
13832 * everything is fine. Otherwise it means that the client did not call
13833 * OnSessionEnd() before it released the IPC semaphore. This may happen
13834 * either because the client process has abnormally terminated, or
13835 * because it simply forgot to call ISession::Close() before exiting. We
13836 * threat the latter also as an abnormal termination (see
13837 * Session::uninit() for details). */
13838 reason = mData->mSession.mState == SessionState_Unlocking ?
13839 Uninit::Normal :
13840 Uninit::Abnormal;
13841
13842 if (mClientToken)
13843 terminated = mClientToken->release();
13844 } /* AutoCaller block */
13845
13846 if (terminated)
13847 uninit(reason);
13848
13849 return terminated;
13850}
13851
13852void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13853{
13854 LogFlowThisFunc(("\n"));
13855
13856 strTokenId.setNull();
13857
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturnVoid(autoCaller.rc());
13860
13861 Assert(mClientToken);
13862 if (mClientToken)
13863 mClientToken->getId(strTokenId);
13864}
13865#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13866IToken *SessionMachine::i_getToken()
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), NULL);
13872
13873 Assert(mClientToken);
13874 if (mClientToken)
13875 return mClientToken->getToken();
13876 else
13877 return NULL;
13878}
13879#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13880
13881Machine::ClientToken *SessionMachine::i_getClientToken()
13882{
13883 LogFlowThisFunc(("\n"));
13884
13885 AutoCaller autoCaller(this);
13886 AssertComRCReturn(autoCaller.rc(), NULL);
13887
13888 return mClientToken;
13889}
13890
13891
13892/**
13893 * @note Locks this object for reading.
13894 */
13895HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
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->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13914}
13915
13916/**
13917 * @note Locks this object for reading.
13918 */
13919HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13920 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13921 IN_BSTR aGuestIp, LONG aGuestPort)
13922{
13923 LogFlowThisFunc(("\n"));
13924
13925 AutoCaller autoCaller(this);
13926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13927
13928 ComPtr<IInternalSessionControl> directControl;
13929 {
13930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13931 if (mData->mSession.mLockType == LockType_VM)
13932 directControl = mData->mSession.mDirectControl;
13933 }
13934
13935 /* ignore notifications sent after #OnSessionEnd() is called */
13936 if (!directControl)
13937 return S_OK;
13938 /*
13939 * instead acting like callback we ask IVirtualBox deliver corresponding event
13940 */
13941
13942 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13943 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13944 return S_OK;
13945}
13946
13947/**
13948 * @note Locks this object for reading.
13949 */
13950HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13951{
13952 LogFlowThisFunc(("\n"));
13953
13954 AutoCaller autoCaller(this);
13955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13956
13957 ComPtr<IInternalSessionControl> directControl;
13958 {
13959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13960 if (mData->mSession.mLockType == LockType_VM)
13961 directControl = mData->mSession.mDirectControl;
13962 }
13963
13964 /* ignore notifications sent after #OnSessionEnd() is called */
13965 if (!directControl)
13966 return S_OK;
13967
13968 return directControl->OnSerialPortChange(serialPort);
13969}
13970
13971/**
13972 * @note Locks this object for reading.
13973 */
13974HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13975{
13976 LogFlowThisFunc(("\n"));
13977
13978 AutoCaller autoCaller(this);
13979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13980
13981 ComPtr<IInternalSessionControl> directControl;
13982 {
13983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13984 if (mData->mSession.mLockType == LockType_VM)
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnParallelPortChange(parallelPort);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onStorageControllerChange()
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnStorageControllerChange();
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 /* ignore notifications sent after #OnSessionEnd() is called */
14037 if (!directControl)
14038 return S_OK;
14039
14040 return directControl->OnMediumChange(aAttachment, aForce);
14041}
14042
14043/**
14044 * @note Locks this object for reading.
14045 */
14046HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14047{
14048 LogFlowThisFunc(("\n"));
14049
14050 AutoCaller autoCaller(this);
14051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14052
14053 ComPtr<IInternalSessionControl> directControl;
14054 {
14055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14056 if (mData->mSession.mLockType == LockType_VM)
14057 directControl = mData->mSession.mDirectControl;
14058 }
14059
14060 /* ignore notifications sent after #OnSessionEnd() is called */
14061 if (!directControl)
14062 return S_OK;
14063
14064 return directControl->OnCPUChange(aCPU, aRemove);
14065}
14066
14067HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 /* ignore notifications sent after #OnSessionEnd() is called */
14082 if (!directControl)
14083 return S_OK;
14084
14085 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14086}
14087
14088/**
14089 * @note Locks this object for reading.
14090 */
14091HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14097
14098 ComPtr<IInternalSessionControl> directControl;
14099 {
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101 if (mData->mSession.mLockType == LockType_VM)
14102 directControl = mData->mSession.mDirectControl;
14103 }
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnVRDEServerChange(aRestart);
14110}
14111
14112/**
14113 * @note Locks this object for reading.
14114 */
14115HRESULT SessionMachine::i_onVideoCaptureChange()
14116{
14117 LogFlowThisFunc(("\n"));
14118
14119 AutoCaller autoCaller(this);
14120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14121
14122 ComPtr<IInternalSessionControl> directControl;
14123 {
14124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14125 if (mData->mSession.mLockType == LockType_VM)
14126 directControl = mData->mSession.mDirectControl;
14127 }
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnVideoCaptureChange();
14134}
14135
14136/**
14137 * @note Locks this object for reading.
14138 */
14139HRESULT SessionMachine::i_onUSBControllerChange()
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnUSBControllerChange();
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::i_onSharedFolderChange()
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturnRC(autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 if (mData->mSession.mLockType == LockType_VM)
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturnRC(autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 if (mData->mSession.mLockType == LockType_VM)
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 /* ignore notifications sent after #OnSessionEnd() is called */
14202 if (!directControl)
14203 return S_OK;
14204
14205 return directControl->OnClipboardModeChange(aClipboardMode);
14206}
14207
14208/**
14209 * @note Locks this object for reading.
14210 */
14211HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14212{
14213 LogFlowThisFunc(("\n"));
14214
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturnRC(autoCaller.rc());
14217
14218 ComPtr<IInternalSessionControl> directControl;
14219 {
14220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14221 if (mData->mSession.mLockType == LockType_VM)
14222 directControl = mData->mSession.mDirectControl;
14223 }
14224
14225 /* ignore notifications sent after #OnSessionEnd() is called */
14226 if (!directControl)
14227 return S_OK;
14228
14229 return directControl->OnDnDModeChange(aDnDMode);
14230}
14231
14232/**
14233 * @note Locks this object for reading.
14234 */
14235HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14236{
14237 LogFlowThisFunc(("\n"));
14238
14239 AutoCaller autoCaller(this);
14240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14241
14242 ComPtr<IInternalSessionControl> directControl;
14243 {
14244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14245 if (mData->mSession.mLockType == LockType_VM)
14246 directControl = mData->mSession.mDirectControl;
14247 }
14248
14249 /* ignore notifications sent after #OnSessionEnd() is called */
14250 if (!directControl)
14251 return S_OK;
14252
14253 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14254}
14255
14256/**
14257 * @note Locks this object for reading.
14258 */
14259HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14260{
14261 LogFlowThisFunc(("\n"));
14262
14263 AutoCaller autoCaller(this);
14264 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14265
14266 ComPtr<IInternalSessionControl> directControl;
14267 {
14268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14269 if (mData->mSession.mLockType == LockType_VM)
14270 directControl = mData->mSession.mDirectControl;
14271 }
14272
14273 /* ignore notifications sent after #OnSessionEnd() is called */
14274 if (!directControl)
14275 return S_OK;
14276
14277 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14278}
14279
14280/**
14281 * Returns @c true if this machine's USB controller reports it has a matching
14282 * filter for the given USB device and @c false otherwise.
14283 *
14284 * @note locks this object for reading.
14285 */
14286bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14287{
14288 AutoCaller autoCaller(this);
14289 /* silently return if not ready -- this method may be called after the
14290 * direct machine session has been called */
14291 if (!autoCaller.isOk())
14292 return false;
14293
14294#ifdef VBOX_WITH_USB
14295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14296
14297 switch (mData->mMachineState)
14298 {
14299 case MachineState_Starting:
14300 case MachineState_Restoring:
14301 case MachineState_TeleportingIn:
14302 case MachineState_Paused:
14303 case MachineState_Running:
14304 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14305 * elsewhere... */
14306 alock.release();
14307 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14308 default: break;
14309 }
14310#else
14311 NOREF(aDevice);
14312 NOREF(aMaskedIfs);
14313#endif
14314 return false;
14315}
14316
14317/**
14318 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14319 */
14320HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14321 IVirtualBoxErrorInfo *aError,
14322 ULONG aMaskedIfs,
14323 const com::Utf8Str &aCaptureFilename)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328
14329 /* This notification may happen after the machine object has been
14330 * uninitialized (the session was closed), so don't assert. */
14331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14332
14333 ComPtr<IInternalSessionControl> directControl;
14334 {
14335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14336 if (mData->mSession.mLockType == LockType_VM)
14337 directControl = mData->mSession.mDirectControl;
14338 }
14339
14340 /* fail on notifications sent after #OnSessionEnd() is called, it is
14341 * expected by the caller */
14342 if (!directControl)
14343 return E_FAIL;
14344
14345 /* No locks should be held at this point. */
14346 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14347 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14348
14349 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14350}
14351
14352/**
14353 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14354 */
14355HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14356 IVirtualBoxErrorInfo *aError)
14357{
14358 LogFlowThisFunc(("\n"));
14359
14360 AutoCaller autoCaller(this);
14361
14362 /* This notification may happen after the machine object has been
14363 * uninitialized (the session was closed), so don't assert. */
14364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14365
14366 ComPtr<IInternalSessionControl> directControl;
14367 {
14368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14369 if (mData->mSession.mLockType == LockType_VM)
14370 directControl = mData->mSession.mDirectControl;
14371 }
14372
14373 /* fail on notifications sent after #OnSessionEnd() is called, it is
14374 * expected by the caller */
14375 if (!directControl)
14376 return E_FAIL;
14377
14378 /* No locks should be held at this point. */
14379 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14380 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14381
14382 return directControl->OnUSBDeviceDetach(aId, aError);
14383}
14384
14385// protected methods
14386/////////////////////////////////////////////////////////////////////////////
14387
14388/**
14389 * Deletes the given file if it is no longer in use by either the current machine state
14390 * (if the machine is "saved") or any of the machine's snapshots.
14391 *
14392 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14393 * but is different for each SnapshotMachine. When calling this, the order of calling this
14394 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14395 * is therefore critical. I know, it's all rather messy.
14396 *
14397 * @param strStateFile
14398 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14399 * the test for whether the saved state file is in use.
14400 */
14401void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14402 Snapshot *pSnapshotToIgnore)
14403{
14404 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14405 if ( (strStateFile.isNotEmpty())
14406 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14407 )
14408 // ... and it must also not be shared with other snapshots
14409 if ( !mData->mFirstSnapshot
14410 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14411 // this checks the SnapshotMachine's state file paths
14412 )
14413 RTFileDelete(strStateFile.c_str());
14414}
14415
14416/**
14417 * Locks the attached media.
14418 *
14419 * All attached hard disks are locked for writing and DVD/floppy are locked for
14420 * reading. Parents of attached hard disks (if any) are locked for reading.
14421 *
14422 * This method also performs accessibility check of all media it locks: if some
14423 * media is inaccessible, the method will return a failure and a bunch of
14424 * extended error info objects per each inaccessible medium.
14425 *
14426 * Note that this method is atomic: if it returns a success, all media are
14427 * locked as described above; on failure no media is locked at all (all
14428 * succeeded individual locks will be undone).
14429 *
14430 * The caller is responsible for doing the necessary state sanity checks.
14431 *
14432 * The locks made by this method must be undone by calling #unlockMedia() when
14433 * no more needed.
14434 */
14435HRESULT SessionMachine::i_lockMedia()
14436{
14437 AutoCaller autoCaller(this);
14438 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14439
14440 AutoMultiWriteLock2 alock(this->lockHandle(),
14441 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14442
14443 /* bail out if trying to lock things with already set up locking */
14444 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14445
14446 MultiResult mrc(S_OK);
14447
14448 /* Collect locking information for all medium objects attached to the VM. */
14449 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14450 it != mMediaData->mAttachments.end();
14451 ++it)
14452 {
14453 MediumAttachment* pAtt = *it;
14454 DeviceType_T devType = pAtt->i_getType();
14455 Medium *pMedium = pAtt->i_getMedium();
14456
14457 MediumLockList *pMediumLockList(new MediumLockList());
14458 // There can be attachments without a medium (floppy/dvd), and thus
14459 // it's impossible to create a medium lock list. It still makes sense
14460 // to have the empty medium lock list in the map in case a medium is
14461 // attached later.
14462 if (pMedium != NULL)
14463 {
14464 MediumType_T mediumType = pMedium->i_getType();
14465 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14466 || mediumType == MediumType_Shareable;
14467 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14468
14469 alock.release();
14470 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14471 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14472 false /* fMediumLockWriteAll */,
14473 NULL,
14474 *pMediumLockList);
14475 alock.acquire();
14476 if (FAILED(mrc))
14477 {
14478 delete pMediumLockList;
14479 mData->mSession.mLockedMedia.Clear();
14480 break;
14481 }
14482 }
14483
14484 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14485 if (FAILED(rc))
14486 {
14487 mData->mSession.mLockedMedia.Clear();
14488 mrc = setError(rc,
14489 tr("Collecting locking information for all attached media failed"));
14490 break;
14491 }
14492 }
14493
14494 if (SUCCEEDED(mrc))
14495 {
14496 /* Now lock all media. If this fails, nothing is locked. */
14497 alock.release();
14498 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14499 alock.acquire();
14500 if (FAILED(rc))
14501 {
14502 mrc = setError(rc,
14503 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14504 }
14505 }
14506
14507 return mrc;
14508}
14509
14510/**
14511 * Undoes the locks made by by #lockMedia().
14512 */
14513HRESULT SessionMachine::i_unlockMedia()
14514{
14515 AutoCaller autoCaller(this);
14516 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14517
14518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14519
14520 /* we may be holding important error info on the current thread;
14521 * preserve it */
14522 ErrorInfoKeeper eik;
14523
14524 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14525 AssertComRC(rc);
14526 return rc;
14527}
14528
14529/**
14530 * Helper to change the machine state (reimplementation).
14531 *
14532 * @note Locks this object for writing.
14533 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14534 * it can cause crashes in random places due to unexpectedly committing
14535 * the current settings. The caller is responsible for that. The call
14536 * to saveStateSettings is fine, because this method does not commit.
14537 */
14538HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14539{
14540 LogFlowThisFuncEnter();
14541 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14542
14543 AutoCaller autoCaller(this);
14544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14545
14546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14547
14548 MachineState_T oldMachineState = mData->mMachineState;
14549
14550 AssertMsgReturn(oldMachineState != aMachineState,
14551 ("oldMachineState=%s, aMachineState=%s\n",
14552 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14553 E_FAIL);
14554
14555 HRESULT rc = S_OK;
14556
14557 int stsFlags = 0;
14558 bool deleteSavedState = false;
14559
14560 /* detect some state transitions */
14561
14562 if ( ( oldMachineState == MachineState_Saved
14563 && aMachineState == MachineState_Restoring)
14564 || ( ( oldMachineState == MachineState_PoweredOff
14565 || oldMachineState == MachineState_Teleported
14566 || oldMachineState == MachineState_Aborted
14567 )
14568 && ( aMachineState == MachineState_TeleportingIn
14569 || aMachineState == MachineState_Starting
14570 )
14571 )
14572 )
14573 {
14574 /* The EMT thread is about to start */
14575
14576 /* Nothing to do here for now... */
14577
14578 /// @todo NEWMEDIA don't let mDVDDrive and other children
14579 /// change anything when in the Starting/Restoring state
14580 }
14581 else if ( ( oldMachineState == MachineState_Running
14582 || oldMachineState == MachineState_Paused
14583 || oldMachineState == MachineState_Teleporting
14584 || oldMachineState == MachineState_OnlineSnapshotting
14585 || oldMachineState == MachineState_LiveSnapshotting
14586 || oldMachineState == MachineState_Stuck
14587 || oldMachineState == MachineState_Starting
14588 || oldMachineState == MachineState_Stopping
14589 || oldMachineState == MachineState_Saving
14590 || oldMachineState == MachineState_Restoring
14591 || oldMachineState == MachineState_TeleportingPausedVM
14592 || oldMachineState == MachineState_TeleportingIn
14593 )
14594 && ( aMachineState == MachineState_PoweredOff
14595 || aMachineState == MachineState_Saved
14596 || aMachineState == MachineState_Teleported
14597 || aMachineState == MachineState_Aborted
14598 )
14599 )
14600 {
14601 /* The EMT thread has just stopped, unlock attached media. Note that as
14602 * opposed to locking that is done from Console, we do unlocking here
14603 * because the VM process may have aborted before having a chance to
14604 * properly unlock all media it locked. */
14605
14606 unlockMedia();
14607 }
14608
14609 if (oldMachineState == MachineState_Restoring)
14610 {
14611 if (aMachineState != MachineState_Saved)
14612 {
14613 /*
14614 * delete the saved state file once the machine has finished
14615 * restoring from it (note that Console sets the state from
14616 * Restoring to Saved if the VM couldn't restore successfully,
14617 * to give the user an ability to fix an error and retry --
14618 * we keep the saved state file in this case)
14619 */
14620 deleteSavedState = true;
14621 }
14622 }
14623 else if ( oldMachineState == MachineState_Saved
14624 && ( aMachineState == MachineState_PoweredOff
14625 || aMachineState == MachineState_Aborted
14626 || aMachineState == MachineState_Teleported
14627 )
14628 )
14629 {
14630 /*
14631 * delete the saved state after SessionMachine::ForgetSavedState() is called
14632 * or if the VM process (owning a direct VM session) crashed while the
14633 * VM was Saved
14634 */
14635
14636 /// @todo (dmik)
14637 // Not sure that deleting the saved state file just because of the
14638 // client death before it attempted to restore the VM is a good
14639 // thing. But when it crashes we need to go to the Aborted state
14640 // which cannot have the saved state file associated... The only
14641 // way to fix this is to make the Aborted condition not a VM state
14642 // but a bool flag: i.e., when a crash occurs, set it to true and
14643 // change the state to PoweredOff or Saved depending on the
14644 // saved state presence.
14645
14646 deleteSavedState = true;
14647 mData->mCurrentStateModified = TRUE;
14648 stsFlags |= SaveSTS_CurStateModified;
14649 }
14650
14651 if ( aMachineState == MachineState_Starting
14652 || aMachineState == MachineState_Restoring
14653 || aMachineState == MachineState_TeleportingIn
14654 )
14655 {
14656 /* set the current state modified flag to indicate that the current
14657 * state is no more identical to the state in the
14658 * current snapshot */
14659 if (!mData->mCurrentSnapshot.isNull())
14660 {
14661 mData->mCurrentStateModified = TRUE;
14662 stsFlags |= SaveSTS_CurStateModified;
14663 }
14664 }
14665
14666 if (deleteSavedState)
14667 {
14668 if (mRemoveSavedState)
14669 {
14670 Assert(!mSSData->strStateFilePath.isEmpty());
14671
14672 // it is safe to delete the saved state file if ...
14673 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14674 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14675 // ... none of the snapshots share the saved state file
14676 )
14677 RTFileDelete(mSSData->strStateFilePath.c_str());
14678 }
14679
14680 mSSData->strStateFilePath.setNull();
14681 stsFlags |= SaveSTS_StateFilePath;
14682 }
14683
14684 /* redirect to the underlying peer machine */
14685 mPeer->i_setMachineState(aMachineState);
14686
14687 if ( oldMachineState != MachineState_RestoringSnapshot
14688 && ( aMachineState == MachineState_PoweredOff
14689 || aMachineState == MachineState_Teleported
14690 || aMachineState == MachineState_Aborted
14691 || aMachineState == MachineState_Saved))
14692 {
14693 /* the machine has stopped execution
14694 * (or the saved state file was adopted) */
14695 stsFlags |= SaveSTS_StateTimeStamp;
14696 }
14697
14698 if ( ( oldMachineState == MachineState_PoweredOff
14699 || oldMachineState == MachineState_Aborted
14700 || oldMachineState == MachineState_Teleported
14701 )
14702 && aMachineState == MachineState_Saved)
14703 {
14704 /* the saved state file was adopted */
14705 Assert(!mSSData->strStateFilePath.isEmpty());
14706 stsFlags |= SaveSTS_StateFilePath;
14707 }
14708
14709#ifdef VBOX_WITH_GUEST_PROPS
14710 if ( aMachineState == MachineState_PoweredOff
14711 || aMachineState == MachineState_Aborted
14712 || aMachineState == MachineState_Teleported)
14713 {
14714 /* Make sure any transient guest properties get removed from the
14715 * property store on shutdown. */
14716 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14717
14718 /* remove it from the settings representation */
14719 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14720 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14721 it != llGuestProperties.end();
14722 /*nothing*/)
14723 {
14724 const settings::GuestProperty &prop = *it;
14725 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14726 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14727 {
14728 it = llGuestProperties.erase(it);
14729 fNeedsSaving = true;
14730 }
14731 else
14732 {
14733 ++it;
14734 }
14735 }
14736
14737 /* Additionally remove it from the HWData representation. Required to
14738 * keep everything in sync, as this is what the API keeps using. */
14739 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14740 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14741 it != llHWGuestProperties.end();
14742 /*nothing*/)
14743 {
14744 uint32_t fFlags = it->second.mFlags;
14745 if ( fFlags & guestProp::TRANSIENT
14746 || fFlags & guestProp::TRANSRESET)
14747 {
14748 /* iterator where we need to continue after the erase call
14749 * (C++03 is a fact still, and it doesn't return the iterator
14750 * which would allow continuing) */
14751 HWData::GuestPropertyMap::iterator it2 = it;
14752 ++it2;
14753 llHWGuestProperties.erase(it);
14754 it = it2;
14755 fNeedsSaving = true;
14756 }
14757 else
14758 {
14759 ++it;
14760 }
14761 }
14762
14763 if (fNeedsSaving)
14764 {
14765 mData->mCurrentStateModified = TRUE;
14766 stsFlags |= SaveSTS_CurStateModified;
14767 }
14768 }
14769#endif /* VBOX_WITH_GUEST_PROPS */
14770
14771 rc = i_saveStateSettings(stsFlags);
14772
14773 if ( ( oldMachineState != MachineState_PoweredOff
14774 && oldMachineState != MachineState_Aborted
14775 && oldMachineState != MachineState_Teleported
14776 )
14777 && ( aMachineState == MachineState_PoweredOff
14778 || aMachineState == MachineState_Aborted
14779 || aMachineState == MachineState_Teleported
14780 )
14781 )
14782 {
14783 /* we've been shut down for any reason */
14784 /* no special action so far */
14785 }
14786
14787 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14788 LogFlowThisFuncLeave();
14789 return rc;
14790}
14791
14792/**
14793 * Sends the current machine state value to the VM process.
14794 *
14795 * @note Locks this object for reading, then calls a client process.
14796 */
14797HRESULT SessionMachine::i_updateMachineStateOnClient()
14798{
14799 AutoCaller autoCaller(this);
14800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14801
14802 ComPtr<IInternalSessionControl> directControl;
14803 {
14804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14805 AssertReturn(!!mData, E_FAIL);
14806 if (mData->mSession.mLockType == LockType_VM)
14807 directControl = mData->mSession.mDirectControl;
14808
14809 /* directControl may be already set to NULL here in #OnSessionEnd()
14810 * called too early by the direct session process while there is still
14811 * some operation (like deleting the snapshot) in progress. The client
14812 * process in this case is waiting inside Session::close() for the
14813 * "end session" process object to complete, while #uninit() called by
14814 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14815 * operation to complete. For now, we accept this inconsistent behavior
14816 * and simply do nothing here. */
14817
14818 if (mData->mSession.mState == SessionState_Unlocking)
14819 return S_OK;
14820 }
14821
14822 /* ignore notifications sent after #OnSessionEnd() is called */
14823 if (!directControl)
14824 return S_OK;
14825
14826 return directControl->UpdateMachineState(mData->mMachineState);
14827}
14828
14829
14830/*static*/
14831HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14832{
14833 va_list args;
14834 va_start(args, pcszMsg);
14835 HRESULT rc = setErrorInternal(aResultCode,
14836 getStaticClassIID(),
14837 getStaticComponentName(),
14838 Utf8Str(pcszMsg, args),
14839 false /* aWarning */,
14840 true /* aLogIt */);
14841 va_end(args);
14842 return rc;
14843}
14844
14845
14846HRESULT Machine::updateState(MachineState_T aState)
14847{
14848 NOREF(aState);
14849 ReturnComNotImplemented();
14850}
14851
14852HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14853{
14854 NOREF(aProgress);
14855 ReturnComNotImplemented();
14856}
14857
14858HRESULT Machine::endPowerUp(LONG aResult)
14859{
14860 NOREF(aResult);
14861 ReturnComNotImplemented();
14862}
14863
14864HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14865{
14866 NOREF(aProgress);
14867 ReturnComNotImplemented();
14868}
14869
14870HRESULT Machine::endPoweringDown(LONG aResult,
14871 const com::Utf8Str &aErrMsg)
14872{
14873 NOREF(aResult);
14874 NOREF(aErrMsg);
14875 ReturnComNotImplemented();
14876}
14877
14878HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14879 BOOL *aMatched,
14880 ULONG *aMaskedInterfaces)
14881{
14882 NOREF(aDevice);
14883 NOREF(aMatched);
14884 NOREF(aMaskedInterfaces);
14885 ReturnComNotImplemented();
14886
14887}
14888
14889HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14890{
14891 NOREF(aId); NOREF(aCaptureFilename);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14896 BOOL aDone)
14897{
14898 NOREF(aId);
14899 NOREF(aDone);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::autoCaptureUSBDevices()
14904{
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14909{
14910 NOREF(aDone);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14915 ComPtr<IProgress> &aProgress)
14916{
14917 NOREF(aSession);
14918 NOREF(aProgress);
14919 ReturnComNotImplemented();
14920}
14921
14922HRESULT Machine::finishOnlineMergeMedium()
14923{
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14928 std::vector<com::Utf8Str> &aValues,
14929 std::vector<LONG64> &aTimestamps,
14930 std::vector<com::Utf8Str> &aFlags)
14931{
14932 NOREF(aNames);
14933 NOREF(aValues);
14934 NOREF(aTimestamps);
14935 NOREF(aFlags);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14940 const com::Utf8Str &aValue,
14941 LONG64 aTimestamp,
14942 const com::Utf8Str &aFlags)
14943{
14944 NOREF(aName);
14945 NOREF(aValue);
14946 NOREF(aTimestamp);
14947 NOREF(aFlags);
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::lockMedia()
14952{
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::unlockMedia()
14957{
14958 ReturnComNotImplemented();
14959}
14960
14961HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14962 ComPtr<IMediumAttachment> &aNewAttachment)
14963{
14964 NOREF(aAttachment);
14965 NOREF(aNewAttachment);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14970 ULONG aCpuUser,
14971 ULONG aCpuKernel,
14972 ULONG aCpuIdle,
14973 ULONG aMemTotal,
14974 ULONG aMemFree,
14975 ULONG aMemBalloon,
14976 ULONG aMemShared,
14977 ULONG aMemCache,
14978 ULONG aPagedTotal,
14979 ULONG aMemAllocTotal,
14980 ULONG aMemFreeTotal,
14981 ULONG aMemBalloonTotal,
14982 ULONG aMemSharedTotal,
14983 ULONG aVmNetRx,
14984 ULONG aVmNetTx)
14985{
14986 NOREF(aValidStats);
14987 NOREF(aCpuUser);
14988 NOREF(aCpuKernel);
14989 NOREF(aCpuIdle);
14990 NOREF(aMemTotal);
14991 NOREF(aMemFree);
14992 NOREF(aMemBalloon);
14993 NOREF(aMemShared);
14994 NOREF(aMemCache);
14995 NOREF(aPagedTotal);
14996 NOREF(aMemAllocTotal);
14997 NOREF(aMemFreeTotal);
14998 NOREF(aMemBalloonTotal);
14999 NOREF(aMemSharedTotal);
15000 NOREF(aVmNetRx);
15001 NOREF(aVmNetTx);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15006 com::Utf8Str &aResult)
15007{
15008 NOREF(aAuthParams);
15009 NOREF(aResult);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15014{
15015 NOREF(aFlags);
15016 ReturnComNotImplemented();
15017}
15018
15019/* This isn't handled entirely by the wrapper generator yet. */
15020#ifdef VBOX_WITH_XPCOM
15021NS_DECL_CLASSINFO(SessionMachine)
15022NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15023
15024NS_DECL_CLASSINFO(SnapshotMachine)
15025NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15026#endif
Note: See TracBrowser for help on using the repository browser.

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