VirtualBox

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

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

Main/Machine+Medium: must not touch the refcount of the current object in the uninit method if it was called from the FinalRelease method

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 517.1 KB
Line 
1/* $Id: MachineImpl.cpp 65390 2017-01-20 15:03:00Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
289 * scheme (includes the UUID).
290 *
291 * @return Success indicator. if not S_OK, the machine object is invalid
292 */
293HRESULT Machine::init(VirtualBox *aParent,
294 const Utf8Str &strConfigFile,
295 const Utf8Str &strName,
296 const StringsList &llGroups,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID)
301{
302 LogFlowThisFuncEnter();
303 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
304
305 /* Enclose the state transition NotReady->InInit->Ready */
306 AutoInitSpan autoInitSpan(this);
307 AssertReturn(autoInitSpan.isOk(), E_FAIL);
308
309 HRESULT rc = initImpl(aParent, strConfigFile);
310 if (FAILED(rc)) return rc;
311
312 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
313 if (FAILED(rc)) return rc;
314
315 if (SUCCEEDED(rc))
316 {
317 // create an empty machine config
318 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
319
320 rc = initDataAndChildObjects();
321 }
322
323 if (SUCCEEDED(rc))
324 {
325 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
326 mData->mAccessible = TRUE;
327
328 unconst(mData->mUuid) = aId;
329
330 mUserData->s.strName = strName;
331
332 mUserData->s.llGroups = llGroups;
333
334 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
335 // the "name sync" flag determines whether the machine directory gets renamed along
336 // with the machine file; say so if the settings file name is the same as the
337 // settings file parent directory (machine directory)
338 mUserData->s.fNameSync = i_isInOwnDir();
339
340 // initialize the default snapshots folder
341 rc = COMSETTER(SnapshotFolder)(NULL);
342 AssertComRC(rc);
343
344 if (aOsType)
345 {
346 /* Store OS type */
347 mUserData->s.strOsType = aOsType->i_id();
348
349 /* Apply BIOS defaults */
350 mBIOSSettings->i_applyDefaults(aOsType);
351
352 /* Apply network adapters defaults */
353 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
354 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
355
356 /* Apply serial port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
358 mSerialPorts[slot]->i_applyDefaults(aOsType);
359
360 /* Let the OS type select 64-bit ness. */
361 mHWData->mLongMode = aOsType->i_is64Bit()
362 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363
364 /* Let the OS type enable the X2APIC */
365 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
366 }
367
368 /* Apply parallel port defaults */
369 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
370 mParallelPorts[slot]->i_applyDefaults();
371
372 /* At this point the changing of the current state modification
373 * flag is allowed. */
374 i_allowStateModification();
375
376 /* commit all changes made during the initialization */
377 i_commit();
378 }
379
380 /* Confirm a successful initialization when it's the case */
381 if (SUCCEEDED(rc))
382 {
383 if (mData->mAccessible)
384 autoInitSpan.setSucceeded();
385 else
386 autoInitSpan.setLimited();
387 }
388
389 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
390 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
391 mData->mRegistered,
392 mData->mAccessible,
393 rc));
394
395 LogFlowThisFuncLeave();
396
397 return rc;
398}
399
400/**
401 * Initializes a new instance with data from machine XML (formerly Init_Registered).
402 * Gets called in two modes:
403 *
404 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
405 * UUID is specified and we mark the machine as "registered";
406 *
407 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
408 * and the machine remains unregistered until RegisterMachine() is called.
409 *
410 * @param aParent Associated parent object
411 * @param strConfigFile Local file system path to the VM settings file (can
412 * be relative to the VirtualBox config directory).
413 * @param aId UUID of the machine or NULL (see above).
414 *
415 * @return Success indicator. if not S_OK, the machine object is invalid
416 */
417HRESULT Machine::initFromSettings(VirtualBox *aParent,
418 const Utf8Str &strConfigFile,
419 const Guid *aId)
420{
421 LogFlowThisFuncEnter();
422 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
423
424 /* Enclose the state transition NotReady->InInit->Ready */
425 AutoInitSpan autoInitSpan(this);
426 AssertReturn(autoInitSpan.isOk(), E_FAIL);
427
428 HRESULT rc = initImpl(aParent, strConfigFile);
429 if (FAILED(rc)) return rc;
430
431 if (aId)
432 {
433 // loading a registered VM:
434 unconst(mData->mUuid) = *aId;
435 mData->mRegistered = TRUE;
436 // now load the settings from XML:
437 rc = i_registeredInit();
438 // this calls initDataAndChildObjects() and loadSettings()
439 }
440 else
441 {
442 // opening an unregistered VM (VirtualBox::OpenMachine()):
443 rc = initDataAndChildObjects();
444
445 if (SUCCEEDED(rc))
446 {
447 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
448 mData->mAccessible = TRUE;
449
450 try
451 {
452 // load and parse machine XML; this will throw on XML or logic errors
453 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
454
455 // reject VM UUID duplicates, they can happen if someone
456 // tries to register an already known VM config again
457 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
458 true /* fPermitInaccessible */,
459 false /* aDoSetError */,
460 NULL) != VBOX_E_OBJECT_NOT_FOUND)
461 {
462 throw setError(E_FAIL,
463 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
464 mData->m_strConfigFile.c_str());
465 }
466
467 // use UUID from machine config
468 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
469
470 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
471 NULL /* puuidRegistry */);
472 if (FAILED(rc)) throw rc;
473
474 /* At this point the changing of the current state modification
475 * flag is allowed. */
476 i_allowStateModification();
477
478 i_commit();
479 }
480 catch (HRESULT err)
481 {
482 /* we assume that error info is set by the thrower */
483 rc = err;
484 }
485 catch (...)
486 {
487 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
488 }
489 }
490 }
491
492 /* Confirm a successful initialization when it's the case */
493 if (SUCCEEDED(rc))
494 {
495 if (mData->mAccessible)
496 autoInitSpan.setSucceeded();
497 else
498 {
499 autoInitSpan.setLimited();
500
501 // uninit media from this machine's media registry, or else
502 // reloading the settings will fail
503 mParent->i_unregisterMachineMedia(i_getId());
504 }
505 }
506
507 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
508 "rc=%08X\n",
509 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
510 mData->mRegistered, mData->mAccessible, rc));
511
512 LogFlowThisFuncLeave();
513
514 return rc;
515}
516
517/**
518 * Initializes a new instance from a machine config that is already in memory
519 * (import OVF case). Since we are importing, the UUID in the machine
520 * config is ignored and we always generate a fresh one.
521 *
522 * @param aParent Associated parent object.
523 * @param strName Name for the new machine; this overrides what is specified in config and is used
524 * for the settings file as well.
525 * @param config Machine configuration loaded and parsed from XML.
526 *
527 * @return Success indicator. if not S_OK, the machine object is invalid
528 */
529HRESULT Machine::init(VirtualBox *aParent,
530 const Utf8Str &strName,
531 const settings::MachineConfigFile &config)
532{
533 LogFlowThisFuncEnter();
534
535 /* Enclose the state transition NotReady->InInit->Ready */
536 AutoInitSpan autoInitSpan(this);
537 AssertReturn(autoInitSpan.isOk(), E_FAIL);
538
539 Utf8Str strConfigFile;
540 aParent->i_getDefaultMachineFolder(strConfigFile);
541 strConfigFile.append(RTPATH_DELIMITER);
542 strConfigFile.append(strName);
543 strConfigFile.append(RTPATH_DELIMITER);
544 strConfigFile.append(strName);
545 strConfigFile.append(".vbox");
546
547 HRESULT rc = initImpl(aParent, strConfigFile);
548 if (FAILED(rc)) return rc;
549
550 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
551 if (FAILED(rc)) return rc;
552
553 rc = initDataAndChildObjects();
554
555 if (SUCCEEDED(rc))
556 {
557 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
558 mData->mAccessible = TRUE;
559
560 // create empty machine config for instance data
561 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
562
563 // generate fresh UUID, ignore machine config
564 unconst(mData->mUuid).create();
565
566 rc = i_loadMachineDataFromSettings(config,
567 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
568
569 // override VM name as well, it may be different
570 mUserData->s.strName = strName;
571
572 if (SUCCEEDED(rc))
573 {
574 /* At this point the changing of the current state modification
575 * flag is allowed. */
576 i_allowStateModification();
577
578 /* commit all changes made during the initialization */
579 i_commit();
580 }
581 }
582
583 /* Confirm a successful initialization when it's the case */
584 if (SUCCEEDED(rc))
585 {
586 if (mData->mAccessible)
587 autoInitSpan.setSucceeded();
588 else
589 {
590 /* Ignore all errors from unregistering, they would destroy
591- * the more interesting error information we already have,
592- * pinpointing the issue with the VM config. */
593 ErrorInfoKeeper eik;
594
595 autoInitSpan.setLimited();
596
597 // uninit media from this machine's media registry, or else
598 // reloading the settings will fail
599 mParent->i_unregisterMachineMedia(i_getId());
600 }
601 }
602
603 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
604 "rc=%08X\n",
605 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
606 mData->mRegistered, mData->mAccessible, rc));
607
608 LogFlowThisFuncLeave();
609
610 return rc;
611}
612
613/**
614 * Shared code between the various init() implementations.
615 * @param aParent The VirtualBox object.
616 * @param strConfigFile Settings file.
617 * @return
618 */
619HRESULT Machine::initImpl(VirtualBox *aParent,
620 const Utf8Str &strConfigFile)
621{
622 LogFlowThisFuncEnter();
623
624 AssertReturn(aParent, E_INVALIDARG);
625 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
626
627 HRESULT rc = S_OK;
628
629 /* share the parent weakly */
630 unconst(mParent) = aParent;
631
632 /* allocate the essential machine data structure (the rest will be
633 * allocated later by initDataAndChildObjects() */
634 mData.allocate();
635
636 /* memorize the config file name (as provided) */
637 mData->m_strConfigFile = strConfigFile;
638
639 /* get the full file name */
640 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
641 if (RT_FAILURE(vrc1))
642 return setError(VBOX_E_FILE_ERROR,
643 tr("Invalid machine settings file name '%s' (%Rrc)"),
644 strConfigFile.c_str(),
645 vrc1);
646
647 LogFlowThisFuncLeave();
648
649 return rc;
650}
651
652/**
653 * Tries to create a machine settings file in the path stored in the machine
654 * instance data. Used when a new machine is created to fail gracefully if
655 * the settings file could not be written (e.g. because machine dir is read-only).
656 * @return
657 */
658HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
659{
660 HRESULT rc = S_OK;
661
662 // when we create a new machine, we must be able to create the settings file
663 RTFILE f = NIL_RTFILE;
664 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
665 if ( RT_SUCCESS(vrc)
666 || vrc == VERR_SHARING_VIOLATION
667 )
668 {
669 if (RT_SUCCESS(vrc))
670 RTFileClose(f);
671 if (!fForceOverwrite)
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Machine settings file '%s' already exists"),
674 mData->m_strConfigFileFull.c_str());
675 else
676 {
677 /* try to delete the config file, as otherwise the creation
678 * of a new settings file will fail. */
679 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
680 if (RT_FAILURE(vrc2))
681 rc = setError(VBOX_E_FILE_ERROR,
682 tr("Could not delete the existing settings file '%s' (%Rrc)"),
683 mData->m_strConfigFileFull.c_str(), vrc2);
684 }
685 }
686 else if ( vrc != VERR_FILE_NOT_FOUND
687 && vrc != VERR_PATH_NOT_FOUND
688 )
689 rc = setError(VBOX_E_FILE_ERROR,
690 tr("Invalid machine settings file name '%s' (%Rrc)"),
691 mData->m_strConfigFileFull.c_str(),
692 vrc);
693 return rc;
694}
695
696/**
697 * Initializes the registered machine by loading the settings file.
698 * This method is separated from #init() in order to make it possible to
699 * retry the operation after VirtualBox startup instead of refusing to
700 * startup the whole VirtualBox server in case if the settings file of some
701 * registered VM is invalid or inaccessible.
702 *
703 * @note Must be always called from this object's write lock
704 * (unless called from #init() that doesn't need any locking).
705 * @note Locks the mUSBController method for writing.
706 * @note Subclasses must not call this method.
707 */
708HRESULT Machine::i_registeredInit()
709{
710 AssertReturn(!i_isSessionMachine(), E_FAIL);
711 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
712 AssertReturn(mData->mUuid.isValid(), E_FAIL);
713 AssertReturn(!mData->mAccessible, E_FAIL);
714
715 HRESULT rc = initDataAndChildObjects();
716
717 if (SUCCEEDED(rc))
718 {
719 /* Temporarily reset the registered flag in order to let setters
720 * potentially called from loadSettings() succeed (isMutable() used in
721 * all setters will return FALSE for a Machine instance if mRegistered
722 * is TRUE). */
723 mData->mRegistered = FALSE;
724
725 try
726 {
727 // load and parse machine XML; this will throw on XML or logic errors
728 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
729
730 if (mData->mUuid != mData->pMachineConfigFile->uuid)
731 throw setError(E_FAIL,
732 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
733 mData->pMachineConfigFile->uuid.raw(),
734 mData->m_strConfigFileFull.c_str(),
735 mData->mUuid.toString().c_str(),
736 mParent->i_settingsFilePath().c_str());
737
738 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
739 NULL /* const Guid *puuidRegistry */);
740 if (FAILED(rc)) throw rc;
741 }
742 catch (HRESULT err)
743 {
744 /* we assume that error info is set by the thrower */
745 rc = err;
746 }
747 catch (...)
748 {
749 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
750 }
751
752 /* Restore the registered flag (even on failure) */
753 mData->mRegistered = TRUE;
754 }
755
756 if (SUCCEEDED(rc))
757 {
758 /* Set mAccessible to TRUE only if we successfully locked and loaded
759 * the settings file */
760 mData->mAccessible = TRUE;
761
762 /* commit all changes made during loading the settings file */
763 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
764 /// @todo r=klaus for some reason the settings loading logic backs up
765 // the settings, and therefore a commit is needed. Should probably be changed.
766 }
767 else
768 {
769 /* If the machine is registered, then, instead of returning a
770 * failure, we mark it as inaccessible and set the result to
771 * success to give it a try later */
772
773 /* fetch the current error info */
774 mData->mAccessError = com::ErrorInfo();
775 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
776
777 /* rollback all changes */
778 i_rollback(false /* aNotify */);
779
780 // uninit media from this machine's media registry, or else
781 // reloading the settings will fail
782 mParent->i_unregisterMachineMedia(i_getId());
783
784 /* uninitialize the common part to make sure all data is reset to
785 * default (null) values */
786 uninitDataAndChildObjects();
787
788 rc = S_OK;
789 }
790
791 return rc;
792}
793
794/**
795 * Uninitializes the instance.
796 * Called either from FinalRelease() or by the parent when it gets destroyed.
797 *
798 * @note The caller of this method must make sure that this object
799 * a) doesn't have active callers on the current thread and b) is not locked
800 * by the current thread; otherwise uninit() will hang either a) due to
801 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
802 * a dead-lock caused by this thread waiting for all callers on the other
803 * threads are done but preventing them from doing so by holding a lock.
804 */
805void Machine::uninit()
806{
807 LogFlowThisFuncEnter();
808
809 Assert(!isWriteLockOnCurrentThread());
810
811 Assert(!uRegistryNeedsSaving);
812 if (uRegistryNeedsSaving)
813 {
814 AutoCaller autoCaller(this);
815 if (SUCCEEDED(autoCaller.rc()))
816 {
817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
818 i_saveSettings(NULL, Machine::SaveS_Force);
819 }
820 }
821
822 /* Enclose the state transition Ready->InUninit->NotReady */
823 AutoUninitSpan autoUninitSpan(this);
824 if (autoUninitSpan.uninitDone())
825 return;
826
827 Assert(!i_isSnapshotMachine());
828 Assert(!i_isSessionMachine());
829 Assert(!!mData);
830
831 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
832 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
833
834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
835
836 if (!mData->mSession.mMachine.isNull())
837 {
838 /* Theoretically, this can only happen if the VirtualBox server has been
839 * terminated while there were clients running that owned open direct
840 * sessions. Since in this case we are definitely called by
841 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
842 * won't happen on the client watcher thread (because it has a
843 * VirtualBox caller for the duration of the
844 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
845 * cannot happen until the VirtualBox caller is released). This is
846 * important, because SessionMachine::uninit() cannot correctly operate
847 * after we return from this method (it expects the Machine instance is
848 * still valid). We'll call it ourselves below.
849 */
850 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
851 (SessionMachine*)mData->mSession.mMachine));
852
853 if (Global::IsOnlineOrTransient(mData->mMachineState))
854 {
855 Log1WarningThisFunc(("Setting state to Aborted!\n"));
856 /* set machine state using SessionMachine reimplementation */
857 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
858 }
859
860 /*
861 * Uninitialize SessionMachine using public uninit() to indicate
862 * an unexpected uninitialization.
863 */
864 mData->mSession.mMachine->uninit();
865 /* SessionMachine::uninit() must set mSession.mMachine to null */
866 Assert(mData->mSession.mMachine.isNull());
867 }
868
869 // uninit media from this machine's media registry, if they're still there
870 Guid uuidMachine(i_getId());
871
872 /* the lock is no more necessary (SessionMachine is uninitialized) */
873 alock.release();
874
875 /* XXX This will fail with
876 * "cannot be closed because it is still attached to 1 virtual machines"
877 * because at this point we did not call uninitDataAndChildObjects() yet
878 * and therefore also removeBackReference() for all these mediums was not called! */
879
880 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
881 mParent->i_unregisterMachineMedia(uuidMachine);
882
883 // has machine been modified?
884 if (mData->flModifications)
885 {
886 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
887 i_rollback(false /* aNotify */);
888 }
889
890 if (mData->mAccessible)
891 uninitDataAndChildObjects();
892
893 /* free the essential data structure last */
894 mData.free();
895
896 LogFlowThisFuncLeave();
897}
898
899// Wrapped IMachine properties
900/////////////////////////////////////////////////////////////////////////////
901HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
902{
903 /* mParent is constant during life time, no need to lock */
904 ComObjPtr<VirtualBox> pVirtualBox(mParent);
905 aParent = pVirtualBox;
906
907 return S_OK;
908}
909
910
911HRESULT Machine::getAccessible(BOOL *aAccessible)
912{
913 /* In some cases (medium registry related), it is necessary to be able to
914 * go through the list of all machines. Happens when an inaccessible VM
915 * has a sensible medium registry. */
916 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
918
919 HRESULT rc = S_OK;
920
921 if (!mData->mAccessible)
922 {
923 /* try to initialize the VM once more if not accessible */
924
925 AutoReinitSpan autoReinitSpan(this);
926 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
927
928#ifdef DEBUG
929 LogFlowThisFunc(("Dumping media backreferences\n"));
930 mParent->i_dumpAllBackRefs();
931#endif
932
933 if (mData->pMachineConfigFile)
934 {
935 // reset the XML file to force loadSettings() (called from i_registeredInit())
936 // to parse it again; the file might have changed
937 delete mData->pMachineConfigFile;
938 mData->pMachineConfigFile = NULL;
939 }
940
941 rc = i_registeredInit();
942
943 if (SUCCEEDED(rc) && mData->mAccessible)
944 {
945 autoReinitSpan.setSucceeded();
946
947 /* make sure interesting parties will notice the accessibility
948 * state change */
949 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
950 mParent->i_onMachineDataChange(mData->mUuid);
951 }
952 }
953
954 if (SUCCEEDED(rc))
955 *aAccessible = mData->mAccessible;
956
957 LogFlowThisFuncLeave();
958
959 return rc;
960}
961
962HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
963{
964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
965
966 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
967 {
968 /* return shortly */
969 aAccessError = NULL;
970 return S_OK;
971 }
972
973 HRESULT rc = S_OK;
974
975 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
976 rc = errorInfo.createObject();
977 if (SUCCEEDED(rc))
978 {
979 errorInfo->init(mData->mAccessError.getResultCode(),
980 mData->mAccessError.getInterfaceID().ref(),
981 Utf8Str(mData->mAccessError.getComponent()).c_str(),
982 Utf8Str(mData->mAccessError.getText()));
983 aAccessError = errorInfo;
984 }
985
986 return rc;
987}
988
989HRESULT Machine::getName(com::Utf8Str &aName)
990{
991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
992
993 aName = mUserData->s.strName;
994
995 return S_OK;
996}
997
998HRESULT Machine::setName(const com::Utf8Str &aName)
999{
1000 // prohibit setting a UUID only as the machine name, or else it can
1001 // never be found by findMachine()
1002 Guid test(aName);
1003
1004 if (test.isValid())
1005 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1006
1007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 HRESULT rc = i_checkStateDependency(MutableStateDep);
1010 if (FAILED(rc)) return rc;
1011
1012 i_setModified(IsModified_MachineData);
1013 mUserData.backup();
1014 mUserData->s.strName = aName;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1020{
1021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 aDescription = mUserData->s.strDescription;
1024
1025 return S_OK;
1026}
1027
1028HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1029{
1030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1031
1032 // this can be done in principle in any state as it doesn't affect the VM
1033 // significantly, but play safe by not messing around while complex
1034 // activities are going on
1035 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1036 if (FAILED(rc)) return rc;
1037
1038 i_setModified(IsModified_MachineData);
1039 mUserData.backup();
1040 mUserData->s.strDescription = aDescription;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getId(com::Guid &aId)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048
1049 aId = mData->mUuid;
1050
1051 return S_OK;
1052}
1053
1054HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1055{
1056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1057 aGroups.resize(mUserData->s.llGroups.size());
1058 size_t i = 0;
1059 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1060 it != mUserData->s.llGroups.end(); ++it, ++i)
1061 aGroups[i] = (*it);
1062
1063 return S_OK;
1064}
1065
1066HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1067{
1068 StringsList llGroups;
1069 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1070 if (FAILED(rc))
1071 return rc;
1072
1073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 rc = i_checkStateDependency(MutableOrSavedStateDep);
1076 if (FAILED(rc)) return rc;
1077
1078 i_setModified(IsModified_MachineData);
1079 mUserData.backup();
1080 mUserData->s.llGroups = llGroups;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1086{
1087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1088
1089 aOSTypeId = mUserData->s.strOsType;
1090
1091 return S_OK;
1092}
1093
1094HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1095{
1096 /* look up the object by Id to check it is valid */
1097 ComPtr<IGuestOSType> guestOSType;
1098 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1099 if (FAILED(rc)) return rc;
1100
1101 /* when setting, always use the "etalon" value for consistency -- lookup
1102 * by ID is case-insensitive and the input value may have different case */
1103 Bstr osTypeId;
1104 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1105 if (FAILED(rc)) return rc;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComPtr<IGuestOSType> ptrGuestOSType;
1298 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1299 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1300
1301 Bstr guestTypeFamilyId;
1302 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1303 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1304 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1305
1306 switch (mHWData->mParavirtProvider)
1307 {
1308 case ParavirtProvider_Legacy:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else
1313 *aParavirtProvider = ParavirtProvider_None;
1314 break;
1315 }
1316
1317 case ParavirtProvider_Default:
1318 {
1319 if (fOsXGuest)
1320 *aParavirtProvider = ParavirtProvider_Minimal;
1321 else if ( mUserData->s.strOsType == "Windows10"
1322 || mUserData->s.strOsType == "Windows10_64"
1323 || mUserData->s.strOsType == "Windows81"
1324 || mUserData->s.strOsType == "Windows81_64"
1325 || mUserData->s.strOsType == "Windows8"
1326 || mUserData->s.strOsType == "Windows8_64"
1327 || mUserData->s.strOsType == "Windows7"
1328 || mUserData->s.strOsType == "Windows7_64"
1329 || mUserData->s.strOsType == "WindowsVista"
1330 || mUserData->s.strOsType == "WindowsVista_64"
1331 || mUserData->s.strOsType == "Windows2012"
1332 || mUserData->s.strOsType == "Windows2012_64"
1333 || mUserData->s.strOsType == "Windows2008"
1334 || mUserData->s.strOsType == "Windows2008_64")
1335 {
1336 *aParavirtProvider = ParavirtProvider_HyperV;
1337 }
1338 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1339 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1340 || mUserData->s.strOsType == "Linux"
1341 || mUserData->s.strOsType == "Linux_64"
1342 || mUserData->s.strOsType == "ArchLinux"
1343 || mUserData->s.strOsType == "ArchLinux_64"
1344 || mUserData->s.strOsType == "Debian"
1345 || mUserData->s.strOsType == "Debian_64"
1346 || mUserData->s.strOsType == "Fedora"
1347 || mUserData->s.strOsType == "Fedora_64"
1348 || mUserData->s.strOsType == "Gentoo"
1349 || mUserData->s.strOsType == "Gentoo_64"
1350 || mUserData->s.strOsType == "Mandriva"
1351 || mUserData->s.strOsType == "Mandriva_64"
1352 || mUserData->s.strOsType == "OpenSUSE"
1353 || mUserData->s.strOsType == "OpenSUSE_64"
1354 || mUserData->s.strOsType == "Oracle"
1355 || mUserData->s.strOsType == "Oracle_64"
1356 || mUserData->s.strOsType == "RedHat"
1357 || mUserData->s.strOsType == "RedHat_64"
1358 || mUserData->s.strOsType == "Turbolinux"
1359 || mUserData->s.strOsType == "Turbolinux_64"
1360 || mUserData->s.strOsType == "Ubuntu"
1361 || mUserData->s.strOsType == "Ubuntu_64"
1362 || mUserData->s.strOsType == "Xandros"
1363 || mUserData->s.strOsType == "Xandros_64")
1364 {
1365 *aParavirtProvider = ParavirtProvider_KVM;
1366 }
1367 else
1368 *aParavirtProvider = ParavirtProvider_None;
1369 break;
1370 }
1371
1372 default: AssertFailedBreak(); /* Shut up MSC. */
1373 }
1374 break;
1375 }
1376 }
1377
1378 Assert( *aParavirtProvider == ParavirtProvider_None
1379 || *aParavirtProvider == ParavirtProvider_Minimal
1380 || *aParavirtProvider == ParavirtProvider_HyperV
1381 || *aParavirtProvider == ParavirtProvider_KVM);
1382 return S_OK;
1383}
1384
1385HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1386{
1387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 aHardwareVersion = mHWData->mHWVersion;
1390
1391 return S_OK;
1392}
1393
1394HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1395{
1396 /* check known version */
1397 Utf8Str hwVersion = aHardwareVersion;
1398 if ( hwVersion.compare("1") != 0
1399 && hwVersion.compare("2") != 0)
1400 return setError(E_INVALIDARG,
1401 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1402
1403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1404
1405 HRESULT rc = i_checkStateDependency(MutableStateDep);
1406 if (FAILED(rc)) return rc;
1407
1408 i_setModified(IsModified_MachineData);
1409 mHWData.backup();
1410 mHWData->mHWVersion = aHardwareVersion;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1416{
1417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1418
1419 if (!mHWData->mHardwareUUID.isZero())
1420 aHardwareUUID = mHWData->mHardwareUUID;
1421 else
1422 aHardwareUUID = mData->mUuid;
1423
1424 return S_OK;
1425}
1426
1427HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1428{
1429 if (!aHardwareUUID.isValid())
1430 return E_INVALIDARG;
1431
1432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 HRESULT rc = i_checkStateDependency(MutableStateDep);
1435 if (FAILED(rc)) return rc;
1436
1437 i_setModified(IsModified_MachineData);
1438 mHWData.backup();
1439 if (aHardwareUUID == mData->mUuid)
1440 mHWData->mHardwareUUID.clear();
1441 else
1442 mHWData->mHardwareUUID = aHardwareUUID;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1448{
1449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 *aMemorySize = mHWData->mMemorySize;
1452
1453 return S_OK;
1454}
1455
1456HRESULT Machine::setMemorySize(ULONG aMemorySize)
1457{
1458 /* check RAM limits */
1459 if ( aMemorySize < MM_RAM_MIN_IN_MB
1460 || aMemorySize > MM_RAM_MAX_IN_MB
1461 )
1462 return setError(E_INVALIDARG,
1463 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1464 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1465
1466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1467
1468 HRESULT rc = i_checkStateDependency(MutableStateDep);
1469 if (FAILED(rc)) return rc;
1470
1471 i_setModified(IsModified_MachineData);
1472 mHWData.backup();
1473 mHWData->mMemorySize = aMemorySize;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1479{
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 *aCPUCount = mHWData->mCPUCount;
1483
1484 return S_OK;
1485}
1486
1487HRESULT Machine::setCPUCount(ULONG aCPUCount)
1488{
1489 /* check CPU limits */
1490 if ( aCPUCount < SchemaDefs::MinCPUCount
1491 || aCPUCount > SchemaDefs::MaxCPUCount
1492 )
1493 return setError(E_INVALIDARG,
1494 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1495 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1496
1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1500 if (mHWData->mCPUHotPlugEnabled)
1501 {
1502 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1503 {
1504 if (mHWData->mCPUAttached[idx])
1505 return setError(E_INVALIDARG,
1506 tr("There is still a CPU attached to socket %lu."
1507 "Detach the CPU before removing the socket"),
1508 aCPUCount, idx+1);
1509 }
1510 }
1511
1512 HRESULT rc = i_checkStateDependency(MutableStateDep);
1513 if (FAILED(rc)) return rc;
1514
1515 i_setModified(IsModified_MachineData);
1516 mHWData.backup();
1517 mHWData->mCPUCount = aCPUCount;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1523{
1524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1525
1526 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1527
1528 return S_OK;
1529}
1530
1531HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1532{
1533 HRESULT rc = S_OK;
1534
1535 /* check throttle limits */
1536 if ( aCPUExecutionCap < 1
1537 || aCPUExecutionCap > 100
1538 )
1539 return setError(E_INVALIDARG,
1540 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1541 aCPUExecutionCap, 1, 100);
1542
1543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1544
1545 alock.release();
1546 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1547 alock.acquire();
1548 if (FAILED(rc)) return rc;
1549
1550 i_setModified(IsModified_MachineData);
1551 mHWData.backup();
1552 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1553
1554 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1555 if (Global::IsOnline(mData->mMachineState))
1556 i_saveSettings(NULL);
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1562{
1563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1566
1567 return S_OK;
1568}
1569
1570HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1571{
1572 HRESULT rc = S_OK;
1573
1574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 rc = i_checkStateDependency(MutableStateDep);
1577 if (FAILED(rc)) return rc;
1578
1579 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1580 {
1581 if (aCPUHotPlugEnabled)
1582 {
1583 i_setModified(IsModified_MachineData);
1584 mHWData.backup();
1585
1586 /* Add the amount of CPUs currently attached */
1587 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1588 mHWData->mCPUAttached[i] = true;
1589 }
1590 else
1591 {
1592 /*
1593 * We can disable hotplug only if the amount of maximum CPUs is equal
1594 * to the amount of attached CPUs
1595 */
1596 unsigned cCpusAttached = 0;
1597 unsigned iHighestId = 0;
1598
1599 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1600 {
1601 if (mHWData->mCPUAttached[i])
1602 {
1603 cCpusAttached++;
1604 iHighestId = i;
1605 }
1606 }
1607
1608 if ( (cCpusAttached != mHWData->mCPUCount)
1609 || (iHighestId >= mHWData->mCPUCount))
1610 return setError(E_INVALIDARG,
1611 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1612
1613 i_setModified(IsModified_MachineData);
1614 mHWData.backup();
1615 }
1616 }
1617
1618 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1619
1620 return rc;
1621}
1622
1623HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1624{
1625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1628
1629 return S_OK;
1630}
1631
1632HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1633{
1634 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1635
1636 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1637 if (SUCCEEDED(hrc))
1638 {
1639 i_setModified(IsModified_MachineData);
1640 mHWData.backup();
1641 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1642 }
1643 return hrc;
1644}
1645
1646HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649 aCPUProfile = mHWData->mCpuProfile;
1650 return S_OK;
1651}
1652
1653HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1654{
1655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1656 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1657 if (SUCCEEDED(hrc))
1658 {
1659 i_setModified(IsModified_MachineData);
1660 mHWData.backup();
1661 /* Empty equals 'host'. */
1662 if (aCPUProfile.isNotEmpty())
1663 mHWData->mCpuProfile = aCPUProfile;
1664 else
1665 mHWData->mCpuProfile = "host";
1666 }
1667 return hrc;
1668}
1669
1670HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1671{
1672#ifdef VBOX_WITH_USB_CARDREADER
1673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1674
1675 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1676
1677 return S_OK;
1678#else
1679 NOREF(aEmulatedUSBCardReaderEnabled);
1680 return E_NOTIMPL;
1681#endif
1682}
1683
1684HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1685{
1686#ifdef VBOX_WITH_USB_CARDREADER
1687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1688
1689 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1690 if (FAILED(rc)) return rc;
1691
1692 i_setModified(IsModified_MachineData);
1693 mHWData.backup();
1694 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1695
1696 return S_OK;
1697#else
1698 NOREF(aEmulatedUSBCardReaderEnabled);
1699 return E_NOTIMPL;
1700#endif
1701}
1702
1703HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1704{
1705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 *aHPETEnabled = mHWData->mHPETEnabled;
1708
1709 return S_OK;
1710}
1711
1712HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1713{
1714 HRESULT rc = S_OK;
1715
1716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 rc = i_checkStateDependency(MutableStateDep);
1719 if (FAILED(rc)) return rc;
1720
1721 i_setModified(IsModified_MachineData);
1722 mHWData.backup();
1723
1724 mHWData->mHPETEnabled = aHPETEnabled;
1725
1726 return rc;
1727}
1728
1729HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1730{
1731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1734 return S_OK;
1735}
1736
1737HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1738{
1739 HRESULT rc = S_OK;
1740
1741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1742
1743 i_setModified(IsModified_MachineData);
1744 mHWData.backup();
1745 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1746
1747 alock.release();
1748 rc = i_onVideoCaptureChange();
1749 alock.acquire();
1750 if (FAILED(rc))
1751 {
1752 /*
1753 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1754 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1755 * determine if it should start or stop capturing. Therefore we need to manually
1756 * undo change.
1757 */
1758 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1759 return rc;
1760 }
1761
1762 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1763 if (Global::IsOnline(mData->mMachineState))
1764 i_saveSettings(NULL);
1765
1766 return rc;
1767}
1768
1769HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1772 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1773 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1774 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1775 return S_OK;
1776}
1777
1778HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1779{
1780 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1781 bool fChanged = false;
1782
1783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1784
1785 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1786 {
1787 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1788 {
1789 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1790 fChanged = true;
1791 }
1792 }
1793 if (fChanged)
1794 {
1795 alock.release();
1796 HRESULT rc = i_onVideoCaptureChange();
1797 alock.acquire();
1798 if (FAILED(rc)) return rc;
1799 i_setModified(IsModified_MachineData);
1800
1801 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1802 if (Global::IsOnline(mData->mMachineState))
1803 i_saveSettings(NULL);
1804 }
1805
1806 return S_OK;
1807}
1808
1809HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1810{
1811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1812 if (mHWData->mVideoCaptureFile.isEmpty())
1813 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1814 else
1815 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1816 return S_OK;
1817}
1818
1819HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1820{
1821 Utf8Str strFile(aVideoCaptureFile);
1822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 if ( Global::IsOnline(mData->mMachineState)
1825 && mHWData->mVideoCaptureEnabled)
1826 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1827
1828 if (!RTPathStartsWithRoot(strFile.c_str()))
1829 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1830
1831 if (!strFile.isEmpty())
1832 {
1833 Utf8Str defaultFile;
1834 i_getDefaultVideoCaptureFile(defaultFile);
1835 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1836 strFile.setNull();
1837 }
1838
1839 i_setModified(IsModified_MachineData);
1840 mHWData.backup();
1841 mHWData->mVideoCaptureFile = strFile;
1842
1843 return S_OK;
1844}
1845
1846HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1847{
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1850 return S_OK;
1851}
1852
1853HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1854{
1855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1856
1857 if ( Global::IsOnline(mData->mMachineState)
1858 && mHWData->mVideoCaptureEnabled)
1859 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1860
1861 i_setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1864
1865 return S_OK;
1866}
1867
1868HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1869{
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1872 return S_OK;
1873}
1874
1875HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1876{
1877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 if ( Global::IsOnline(mData->mMachineState)
1880 && mHWData->mVideoCaptureEnabled)
1881 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1882
1883 i_setModified(IsModified_MachineData);
1884 mHWData.backup();
1885 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1886
1887 return S_OK;
1888}
1889
1890HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1891{
1892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1893 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1894 return S_OK;
1895}
1896
1897HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1898{
1899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 if ( Global::IsOnline(mData->mMachineState)
1902 && mHWData->mVideoCaptureEnabled)
1903 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1904
1905 i_setModified(IsModified_MachineData);
1906 mHWData.backup();
1907 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1908
1909 return S_OK;
1910}
1911
1912HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1913{
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1916 return S_OK;
1917}
1918
1919HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1920{
1921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1922
1923 if ( Global::IsOnline(mData->mMachineState)
1924 && mHWData->mVideoCaptureEnabled)
1925 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1926
1927 i_setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1930
1931 return S_OK;
1932}
1933
1934HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1935{
1936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1937 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1938 return S_OK;
1939}
1940
1941HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1942{
1943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 if ( Global::IsOnline(mData->mMachineState)
1946 && mHWData->mVideoCaptureEnabled)
1947 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1948
1949 i_setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1952
1953 return S_OK;
1954}
1955
1956HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1957{
1958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1959 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1960 return S_OK;
1961}
1962
1963HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1964{
1965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 if ( Global::IsOnline(mData->mMachineState)
1968 && mHWData->mVideoCaptureEnabled)
1969 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1970
1971 i_setModified(IsModified_MachineData);
1972 mHWData.backup();
1973 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1974
1975 return S_OK;
1976}
1977
1978HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1979{
1980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1981
1982 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1983 return S_OK;
1984}
1985
1986HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1987{
1988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1989
1990 if ( Global::IsOnline(mData->mMachineState)
1991 && mHWData->mVideoCaptureEnabled)
1992 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1993
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2002{
2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2006
2007 return S_OK;
2008}
2009
2010HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2011{
2012 switch (aGraphicsControllerType)
2013 {
2014 case GraphicsControllerType_Null:
2015 case GraphicsControllerType_VBoxVGA:
2016#ifdef VBOX_WITH_VMSVGA
2017 case GraphicsControllerType_VMSVGA:
2018#endif
2019 break;
2020 default:
2021 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2022 }
2023
2024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 HRESULT rc = i_checkStateDependency(MutableStateDep);
2027 if (FAILED(rc)) return rc;
2028
2029 i_setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2037{
2038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2039
2040 *aVRAMSize = mHWData->mVRAMSize;
2041
2042 return S_OK;
2043}
2044
2045HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2046{
2047 /* check VRAM limits */
2048 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2049 return setError(E_INVALIDARG,
2050 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2051 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2052
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 HRESULT rc = i_checkStateDependency(MutableStateDep);
2056 if (FAILED(rc)) return rc;
2057
2058 i_setModified(IsModified_MachineData);
2059 mHWData.backup();
2060 mHWData->mVRAMSize = aVRAMSize;
2061
2062 return S_OK;
2063}
2064
2065/** @todo this method should not be public */
2066HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2067{
2068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2071
2072 return S_OK;
2073}
2074
2075/**
2076 * Set the memory balloon size.
2077 *
2078 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2079 * we have to make sure that we never call IGuest from here.
2080 */
2081HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2082{
2083 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2084#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2085 /* check limits */
2086 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2087 return setError(E_INVALIDARG,
2088 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2089 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2090
2091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2092
2093 i_setModified(IsModified_MachineData);
2094 mHWData.backup();
2095 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2096
2097 return S_OK;
2098#else
2099 NOREF(aMemoryBalloonSize);
2100 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2101#endif
2102}
2103
2104HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2105{
2106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2109 return S_OK;
2110}
2111
2112HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2113{
2114#ifdef VBOX_WITH_PAGE_SHARING
2115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2116
2117 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2121 return S_OK;
2122#else
2123 NOREF(aPageFusionEnabled);
2124 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2125#endif
2126}
2127
2128HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2129{
2130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2133
2134 return S_OK;
2135}
2136
2137HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2138{
2139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 HRESULT rc = i_checkStateDependency(MutableStateDep);
2142 if (FAILED(rc)) return rc;
2143
2144 /** @todo check validity! */
2145
2146 i_setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2149
2150 return S_OK;
2151}
2152
2153
2154HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2155{
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2159
2160 return S_OK;
2161}
2162
2163HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2164{
2165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 HRESULT rc = i_checkStateDependency(MutableStateDep);
2168 if (FAILED(rc)) return rc;
2169
2170 /** @todo check validity! */
2171 i_setModified(IsModified_MachineData);
2172 mHWData.backup();
2173 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2179{
2180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2181
2182 *aMonitorCount = mHWData->mMonitorCount;
2183
2184 return S_OK;
2185}
2186
2187HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2188{
2189 /* make sure monitor count is a sensible number */
2190 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2191 return setError(E_INVALIDARG,
2192 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2193 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2194
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = i_checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mMonitorCount = aMonitorCount;
2203
2204 return S_OK;
2205}
2206
2207HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2208{
2209 /* mBIOSSettings is constant during life time, no need to lock */
2210 aBIOSSettings = mBIOSSettings;
2211
2212 return S_OK;
2213}
2214
2215HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2216{
2217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 switch (aProperty)
2220 {
2221 case CPUPropertyType_PAE:
2222 *aValue = mHWData->mPAEEnabled;
2223 break;
2224
2225 case CPUPropertyType_LongMode:
2226 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2227 *aValue = TRUE;
2228 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2229 *aValue = FALSE;
2230#if HC_ARCH_BITS == 64
2231 else
2232 *aValue = TRUE;
2233#else
2234 else
2235 {
2236 *aValue = FALSE;
2237
2238 ComPtr<IGuestOSType> ptrGuestOSType;
2239 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2240 if (SUCCEEDED(hrc2))
2241 {
2242 BOOL fIs64Bit = FALSE;
2243 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2244 if (SUCCEEDED(hrc2) && fIs64Bit)
2245 {
2246 ComObjPtr<Host> ptrHost = mParent->i_host();
2247 alock.release();
2248
2249 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2250 if (FAILED(hrc2))
2251 *aValue = FALSE;
2252 }
2253 }
2254 }
2255#endif
2256 break;
2257
2258 case CPUPropertyType_TripleFaultReset:
2259 *aValue = mHWData->mTripleFaultReset;
2260 break;
2261
2262 case CPUPropertyType_APIC:
2263 *aValue = mHWData->mAPIC;
2264 break;
2265
2266 case CPUPropertyType_X2APIC:
2267 *aValue = mHWData->mX2APIC;
2268 break;
2269
2270 default:
2271 return E_INVALIDARG;
2272 }
2273 return S_OK;
2274}
2275
2276HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2277{
2278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2279
2280 HRESULT rc = i_checkStateDependency(MutableStateDep);
2281 if (FAILED(rc)) return rc;
2282
2283 switch (aProperty)
2284 {
2285 case CPUPropertyType_PAE:
2286 i_setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mPAEEnabled = !!aValue;
2289 break;
2290
2291 case CPUPropertyType_LongMode:
2292 i_setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2295 break;
2296
2297 case CPUPropertyType_TripleFaultReset:
2298 i_setModified(IsModified_MachineData);
2299 mHWData.backup();
2300 mHWData->mTripleFaultReset = !!aValue;
2301 break;
2302
2303 case CPUPropertyType_APIC:
2304 if (mHWData->mX2APIC)
2305 aValue = TRUE;
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mAPIC = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_X2APIC:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mX2APIC = !!aValue;
2315 if (aValue)
2316 mHWData->mAPIC = !!aValue;
2317 break;
2318
2319 default:
2320 return E_INVALIDARG;
2321 }
2322 return S_OK;
2323}
2324
2325HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2326{
2327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 switch(aId)
2330 {
2331 case 0x0:
2332 case 0x1:
2333 case 0x2:
2334 case 0x3:
2335 case 0x4:
2336 case 0x5:
2337 case 0x6:
2338 case 0x7:
2339 case 0x8:
2340 case 0x9:
2341 case 0xA:
2342 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2343 return E_INVALIDARG;
2344
2345 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2346 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2347 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2348 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2349 break;
2350
2351 case 0x80000000:
2352 case 0x80000001:
2353 case 0x80000002:
2354 case 0x80000003:
2355 case 0x80000004:
2356 case 0x80000005:
2357 case 0x80000006:
2358 case 0x80000007:
2359 case 0x80000008:
2360 case 0x80000009:
2361 case 0x8000000A:
2362 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2363 return E_INVALIDARG;
2364
2365 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2366 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2367 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2368 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2369 break;
2370
2371 default:
2372 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2373 }
2374 return S_OK;
2375}
2376
2377
2378HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2379{
2380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2381
2382 HRESULT rc = i_checkStateDependency(MutableStateDep);
2383 if (FAILED(rc)) return rc;
2384
2385 switch(aId)
2386 {
2387 case 0x0:
2388 case 0x1:
2389 case 0x2:
2390 case 0x3:
2391 case 0x4:
2392 case 0x5:
2393 case 0x6:
2394 case 0x7:
2395 case 0x8:
2396 case 0x9:
2397 case 0xA:
2398 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2399 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2400 i_setModified(IsModified_MachineData);
2401 mHWData.backup();
2402 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2403 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2404 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2405 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2406 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2407 break;
2408
2409 case 0x80000000:
2410 case 0x80000001:
2411 case 0x80000002:
2412 case 0x80000003:
2413 case 0x80000004:
2414 case 0x80000005:
2415 case 0x80000006:
2416 case 0x80000007:
2417 case 0x80000008:
2418 case 0x80000009:
2419 case 0x8000000A:
2420 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2421 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2422 i_setModified(IsModified_MachineData);
2423 mHWData.backup();
2424 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2425 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2426 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2427 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2428 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2429 break;
2430
2431 default:
2432 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2433 }
2434 return S_OK;
2435}
2436
2437HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2438{
2439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2440
2441 HRESULT rc = i_checkStateDependency(MutableStateDep);
2442 if (FAILED(rc)) return rc;
2443
2444 switch(aId)
2445 {
2446 case 0x0:
2447 case 0x1:
2448 case 0x2:
2449 case 0x3:
2450 case 0x4:
2451 case 0x5:
2452 case 0x6:
2453 case 0x7:
2454 case 0x8:
2455 case 0x9:
2456 case 0xA:
2457 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2458 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2459 i_setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 /* Invalidate leaf. */
2462 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2463 break;
2464
2465 case 0x80000000:
2466 case 0x80000001:
2467 case 0x80000002:
2468 case 0x80000003:
2469 case 0x80000004:
2470 case 0x80000005:
2471 case 0x80000006:
2472 case 0x80000007:
2473 case 0x80000008:
2474 case 0x80000009:
2475 case 0x8000000A:
2476 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2477 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2478 i_setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 /* Invalidate leaf. */
2481 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2482 break;
2483
2484 default:
2485 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2486 }
2487 return S_OK;
2488}
2489
2490HRESULT Machine::removeAllCPUIDLeaves()
2491{
2492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 HRESULT rc = i_checkStateDependency(MutableStateDep);
2495 if (FAILED(rc)) return rc;
2496
2497 i_setModified(IsModified_MachineData);
2498 mHWData.backup();
2499
2500 /* Invalidate all standard leafs. */
2501 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2502 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2503
2504 /* Invalidate all extended leafs. */
2505 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2506 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2507
2508 return S_OK;
2509}
2510HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 switch(aProperty)
2515 {
2516 case HWVirtExPropertyType_Enabled:
2517 *aValue = mHWData->mHWVirtExEnabled;
2518 break;
2519
2520 case HWVirtExPropertyType_VPID:
2521 *aValue = mHWData->mHWVirtExVPIDEnabled;
2522 break;
2523
2524 case HWVirtExPropertyType_NestedPaging:
2525 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2526 break;
2527
2528 case HWVirtExPropertyType_UnrestrictedExecution:
2529 *aValue = mHWData->mHWVirtExUXEnabled;
2530 break;
2531
2532 case HWVirtExPropertyType_LargePages:
2533 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2534#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2535 *aValue = FALSE;
2536#endif
2537 break;
2538
2539 case HWVirtExPropertyType_Force:
2540 *aValue = mHWData->mHWVirtExForceEnabled;
2541 break;
2542
2543 default:
2544 return E_INVALIDARG;
2545 }
2546 return S_OK;
2547}
2548
2549HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2550{
2551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 HRESULT rc = i_checkStateDependency(MutableStateDep);
2554 if (FAILED(rc)) return rc;
2555
2556 switch(aProperty)
2557 {
2558 case HWVirtExPropertyType_Enabled:
2559 i_setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExEnabled = !!aValue;
2562 break;
2563
2564 case HWVirtExPropertyType_VPID:
2565 i_setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2568 break;
2569
2570 case HWVirtExPropertyType_NestedPaging:
2571 i_setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2574 break;
2575
2576 case HWVirtExPropertyType_UnrestrictedExecution:
2577 i_setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExUXEnabled = !!aValue;
2580 break;
2581
2582 case HWVirtExPropertyType_LargePages:
2583 i_setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2586 break;
2587
2588 case HWVirtExPropertyType_Force:
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExForceEnabled = !!aValue;
2592 break;
2593
2594 default:
2595 return E_INVALIDARG;
2596 }
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2602{
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2611{
2612 /** @todo (r=dmik):
2613 * 1. Allow to change the name of the snapshot folder containing snapshots
2614 * 2. Rename the folder on disk instead of just changing the property
2615 * value (to be smart and not to leave garbage). Note that it cannot be
2616 * done here because the change may be rolled back. Thus, the right
2617 * place is #saveSettings().
2618 */
2619
2620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 HRESULT rc = i_checkStateDependency(MutableStateDep);
2623 if (FAILED(rc)) return rc;
2624
2625 if (!mData->mCurrentSnapshot.isNull())
2626 return setError(E_FAIL,
2627 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2628
2629 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2630
2631 if (strSnapshotFolder.isEmpty())
2632 strSnapshotFolder = "Snapshots";
2633 int vrc = i_calculateFullPath(strSnapshotFolder,
2634 strSnapshotFolder);
2635 if (RT_FAILURE(vrc))
2636 return setError(E_FAIL,
2637 tr("Invalid snapshot folder '%s' (%Rrc)"),
2638 strSnapshotFolder.c_str(), vrc);
2639
2640 i_setModified(IsModified_MachineData);
2641 mUserData.backup();
2642
2643 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 aMediumAttachments.resize(mMediaData->mAttachments.size());
2653 size_t i = 0;
2654 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2655 it != mMediaData->mAttachments.end(); ++it, ++i)
2656 aMediumAttachments[i] = *it;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2662{
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 Assert(!!mVRDEServer);
2666
2667 aVRDEServer = mVRDEServer;
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2673{
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 aAudioAdapter = mAudioAdapter;
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2682{
2683#ifdef VBOX_WITH_VUSB
2684 clearError();
2685 MultiResult rc(S_OK);
2686
2687# ifdef VBOX_WITH_USB
2688 rc = mParent->i_host()->i_checkUSBProxyService();
2689 if (FAILED(rc)) return rc;
2690# endif
2691
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 USBControllerList data = *mUSBControllers.data();
2695 aUSBControllers.resize(data.size());
2696 size_t i = 0;
2697 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2698 aUSBControllers[i] = *it;
2699
2700 return S_OK;
2701#else
2702 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2703 * extended error info to indicate that USB is simply not available
2704 * (w/o treating it as a failure), for example, as in OSE */
2705 NOREF(aUSBControllers);
2706 ReturnComNotImplemented();
2707#endif /* VBOX_WITH_VUSB */
2708}
2709
2710HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2711{
2712#ifdef VBOX_WITH_VUSB
2713 clearError();
2714 MultiResult rc(S_OK);
2715
2716# ifdef VBOX_WITH_USB
2717 rc = mParent->i_host()->i_checkUSBProxyService();
2718 if (FAILED(rc)) return rc;
2719# endif
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 aUSBDeviceFilters = mUSBDeviceFilters;
2724 return rc;
2725#else
2726 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2727 * extended error info to indicate that USB is simply not available
2728 * (w/o treating it as a failure), for example, as in OSE */
2729 NOREF(aUSBDeviceFilters);
2730 ReturnComNotImplemented();
2731#endif /* VBOX_WITH_VUSB */
2732}
2733
2734HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2735{
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 aSettingsFilePath = mData->m_strConfigFileFull;
2739
2740 return S_OK;
2741}
2742
2743HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2744{
2745 RT_NOREF(aSettingsFilePath);
2746 ReturnComNotImplemented();
2747}
2748
2749HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2754 if (FAILED(rc)) return rc;
2755
2756 if (!mData->pMachineConfigFile->fileExists())
2757 // this is a new machine, and no config file exists yet:
2758 *aSettingsModified = TRUE;
2759 else
2760 *aSettingsModified = (mData->flModifications != 0);
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 *aSessionState = mData->mSession.mState;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 aSessionName = mData->mSession.mName;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aSessionPID = mData->mSession.mPID;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::getState(MachineState_T *aState)
2793{
2794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 *aState = mData->mMachineState;
2797 Assert(mData->mMachineState != MachineState_Null);
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 aStateFilePath = mSSData->strStateFilePath;
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2821{
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 i_getLogFolder(aLogFolder);
2825
2826 return S_OK;
2827}
2828
2829HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2830{
2831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 aCurrentSnapshot = mData->mCurrentSnapshot;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2843 ? 0
2844 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 /* Note: for machines with no snapshots, we always return FALSE
2854 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2855 * reasons :) */
2856
2857 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2858 ? FALSE
2859 : mData->mCurrentStateModified;
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 aSharedFolders.resize(mHWData->mSharedFolders.size());
2869 size_t i = 0;
2870 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2871 it != mHWData->mSharedFolders.end(); ++i, ++it)
2872 aSharedFolders[i] = *it;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2878{
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 *aClipboardMode = mHWData->mClipboardMode;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2887{
2888 HRESULT rc = S_OK;
2889
2890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2891
2892 alock.release();
2893 rc = i_onClipboardModeChange(aClipboardMode);
2894 alock.acquire();
2895 if (FAILED(rc)) return rc;
2896
2897 i_setModified(IsModified_MachineData);
2898 mHWData.backup();
2899 mHWData->mClipboardMode = aClipboardMode;
2900
2901 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2902 if (Global::IsOnline(mData->mMachineState))
2903 i_saveSettings(NULL);
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2909{
2910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 *aDnDMode = mHWData->mDnDMode;
2913
2914 return S_OK;
2915}
2916
2917HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2918{
2919 HRESULT rc = S_OK;
2920
2921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 alock.release();
2924 rc = i_onDnDModeChange(aDnDMode);
2925
2926 alock.acquire();
2927 if (FAILED(rc)) return rc;
2928
2929 i_setModified(IsModified_MachineData);
2930 mHWData.backup();
2931 mHWData->mDnDMode = aDnDMode;
2932
2933 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2934 if (Global::IsOnline(mData->mMachineState))
2935 i_saveSettings(NULL);
2936
2937 return S_OK;
2938}
2939
2940HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2941{
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943 StorageControllerList data = *mStorageControllers.data();
2944 size_t i = 0;
2945 aStorageControllers.resize(data.size());
2946 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2947 aStorageControllers[i] = *it;
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aEnabled = mUserData->s.fTeleporterEnabled;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2961{
2962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 /* Only allow it to be set to true when PoweredOff or Aborted.
2965 (Clearing it is always permitted.) */
2966 if ( aTeleporterEnabled
2967 && mData->mRegistered
2968 && ( !i_isSessionMachine()
2969 || ( mData->mMachineState != MachineState_PoweredOff
2970 && mData->mMachineState != MachineState_Teleported
2971 && mData->mMachineState != MachineState_Aborted
2972 )
2973 )
2974 )
2975 return setError(VBOX_E_INVALID_VM_STATE,
2976 tr("The machine is not powered off (state is %s)"),
2977 Global::stringifyMachineState(mData->mMachineState));
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2991
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2996{
2997 if (aTeleporterPort >= _64K)
2998 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2999
3000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3003 if (FAILED(rc)) return rc;
3004
3005 i_setModified(IsModified_MachineData);
3006 mUserData.backup();
3007 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3008
3009 return S_OK;
3010}
3011
3012HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3013{
3014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3017
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3022{
3023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3026 if (FAILED(rc)) return rc;
3027
3028 i_setModified(IsModified_MachineData);
3029 mUserData.backup();
3030 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3031
3032 return S_OK;
3033}
3034
3035HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3036{
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3044{
3045 /*
3046 * Hash the password first.
3047 */
3048 com::Utf8Str aT = aTeleporterPassword;
3049
3050 if (!aT.isEmpty())
3051 {
3052 if (VBoxIsPasswordHashed(&aT))
3053 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3054 VBoxHashPassword(&aT);
3055 }
3056
3057 /*
3058 * Do the update.
3059 */
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3062 if (SUCCEEDED(hrc))
3063 {
3064 i_setModified(IsModified_MachineData);
3065 mUserData.backup();
3066 mUserData->s.strTeleporterPassword = aT;
3067 }
3068
3069 return hrc;
3070}
3071
3072HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3073{
3074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3077 return S_OK;
3078}
3079
3080HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3081{
3082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 /** @todo deal with running state change. */
3085 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3086 if (FAILED(rc)) return rc;
3087
3088 i_setModified(IsModified_MachineData);
3089 mUserData.backup();
3090 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3091 return S_OK;
3092}
3093
3094HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3095{
3096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3097
3098 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3099 return S_OK;
3100}
3101
3102HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3103{
3104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3105
3106 /** @todo deal with running state change. */
3107 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3108 if (FAILED(rc)) return rc;
3109
3110 i_setModified(IsModified_MachineData);
3111 mUserData.backup();
3112 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3113 return S_OK;
3114}
3115
3116HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3117{
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3121 return S_OK;
3122}
3123
3124HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3125{
3126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 /** @todo deal with running state change. */
3129 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3130 if (FAILED(rc)) return rc;
3131
3132 i_setModified(IsModified_MachineData);
3133 mUserData.backup();
3134 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3135 return S_OK;
3136}
3137
3138HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3139{
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3148{
3149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 /** @todo deal with running state change. */
3152 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3153 if (FAILED(rc)) return rc;
3154
3155 i_setModified(IsModified_MachineData);
3156 mUserData.backup();
3157 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3163{
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3167 return S_OK;
3168}
3169
3170HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3171{
3172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 /** @todo deal with running state change. */
3175 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3176 if (FAILED(rc)) return rc;
3177
3178 i_setModified(IsModified_MachineData);
3179 mUserData.backup();
3180 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3181 return S_OK;
3182}
3183
3184HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3185{
3186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3189
3190 return S_OK;
3191}
3192
3193HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3194{
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 /* Only allow it to be set to true when PoweredOff or Aborted.
3198 (Clearing it is always permitted.) */
3199 if ( aRTCUseUTC
3200 && mData->mRegistered
3201 && ( !i_isSessionMachine()
3202 || ( mData->mMachineState != MachineState_PoweredOff
3203 && mData->mMachineState != MachineState_Teleported
3204 && mData->mMachineState != MachineState_Aborted
3205 )
3206 )
3207 )
3208 return setError(VBOX_E_INVALID_VM_STATE,
3209 tr("The machine is not powered off (state is %s)"),
3210 Global::stringifyMachineState(mData->mMachineState));
3211
3212 i_setModified(IsModified_MachineData);
3213 mUserData.backup();
3214 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3215
3216 return S_OK;
3217}
3218
3219HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3220{
3221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3222
3223 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3224
3225 return S_OK;
3226}
3227
3228HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3229{
3230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 HRESULT rc = i_checkStateDependency(MutableStateDep);
3233 if (FAILED(rc)) return rc;
3234
3235 i_setModified(IsModified_MachineData);
3236 mHWData.backup();
3237 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3238
3239 return S_OK;
3240}
3241
3242HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3243{
3244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3245
3246 *aIOCacheSize = mHWData->mIOCacheSize;
3247
3248 return S_OK;
3249}
3250
3251HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3252{
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 HRESULT rc = i_checkStateDependency(MutableStateDep);
3256 if (FAILED(rc)) return rc;
3257
3258 i_setModified(IsModified_MachineData);
3259 mHWData.backup();
3260 mHWData->mIOCacheSize = aIOCacheSize;
3261
3262 return S_OK;
3263}
3264
3265
3266/**
3267 * @note Locks objects!
3268 */
3269HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3270 LockType_T aLockType)
3271{
3272 /* check the session state */
3273 SessionState_T state;
3274 HRESULT rc = aSession->COMGETTER(State)(&state);
3275 if (FAILED(rc)) return rc;
3276
3277 if (state != SessionState_Unlocked)
3278 return setError(VBOX_E_INVALID_OBJECT_STATE,
3279 tr("The given session is busy"));
3280
3281 // get the client's IInternalSessionControl interface
3282 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3283 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3284 E_INVALIDARG);
3285
3286 // session name (only used in some code paths)
3287 Utf8Str strSessionName;
3288
3289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3290
3291 if (!mData->mRegistered)
3292 return setError(E_UNEXPECTED,
3293 tr("The machine '%s' is not registered"),
3294 mUserData->s.strName.c_str());
3295
3296 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3297
3298 SessionState_T oldState = mData->mSession.mState;
3299 /* Hack: in case the session is closing and there is a progress object
3300 * which allows waiting for the session to be closed, take the opportunity
3301 * and do a limited wait (max. 1 second). This helps a lot when the system
3302 * is busy and thus session closing can take a little while. */
3303 if ( mData->mSession.mState == SessionState_Unlocking
3304 && mData->mSession.mProgress)
3305 {
3306 alock.release();
3307 mData->mSession.mProgress->WaitForCompletion(1000);
3308 alock.acquire();
3309 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3310 }
3311
3312 // try again now
3313 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3314 // (i.e. session machine exists)
3315 && (aLockType == LockType_Shared) // caller wants a shared link to the
3316 // existing session that holds the write lock:
3317 )
3318 {
3319 // OK, share the session... we are now dealing with three processes:
3320 // 1) VBoxSVC (where this code runs);
3321 // 2) process C: the caller's client process (who wants a shared session);
3322 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3323
3324 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3325 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3326 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3327 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3328 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3329
3330 /*
3331 * Release the lock before calling the client process. It's safe here
3332 * since the only thing to do after we get the lock again is to add
3333 * the remote control to the list (which doesn't directly influence
3334 * anything).
3335 */
3336 alock.release();
3337
3338 // get the console of the session holding the write lock (this is a remote call)
3339 ComPtr<IConsole> pConsoleW;
3340 if (mData->mSession.mLockType == LockType_VM)
3341 {
3342 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3343 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3344 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3345 if (FAILED(rc))
3346 // the failure may occur w/o any error info (from RPC), so provide one
3347 return setError(VBOX_E_VM_ERROR,
3348 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3349 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3350 }
3351
3352 // share the session machine and W's console with the caller's session
3353 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3354 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3355 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3356
3357 if (FAILED(rc))
3358 // the failure may occur w/o any error info (from RPC), so provide one
3359 return setError(VBOX_E_VM_ERROR,
3360 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3361 alock.acquire();
3362
3363 // need to revalidate the state after acquiring the lock again
3364 if (mData->mSession.mState != SessionState_Locked)
3365 {
3366 pSessionControl->Uninitialize();
3367 return setError(VBOX_E_INVALID_SESSION_STATE,
3368 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3369 mUserData->s.strName.c_str());
3370 }
3371
3372 // add the caller's session to the list
3373 mData->mSession.mRemoteControls.push_back(pSessionControl);
3374 }
3375 else if ( mData->mSession.mState == SessionState_Locked
3376 || mData->mSession.mState == SessionState_Unlocking
3377 )
3378 {
3379 // sharing not permitted, or machine still unlocking:
3380 return setError(VBOX_E_INVALID_OBJECT_STATE,
3381 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3382 mUserData->s.strName.c_str());
3383 }
3384 else
3385 {
3386 // machine is not locked: then write-lock the machine (create the session machine)
3387
3388 // must not be busy
3389 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3390
3391 // get the caller's session PID
3392 RTPROCESS pid = NIL_RTPROCESS;
3393 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3394 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3395 Assert(pid != NIL_RTPROCESS);
3396
3397 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3398
3399 if (fLaunchingVMProcess)
3400 {
3401 if (mData->mSession.mPID == NIL_RTPROCESS)
3402 {
3403 // two or more clients racing for a lock, the one which set the
3404 // session state to Spawning will win, the others will get an
3405 // error as we can't decide here if waiting a little would help
3406 // (only for shared locks this would avoid an error)
3407 return setError(VBOX_E_INVALID_OBJECT_STATE,
3408 tr("The machine '%s' already has a lock request pending"),
3409 mUserData->s.strName.c_str());
3410 }
3411
3412 // this machine is awaiting for a spawning session to be opened:
3413 // then the calling process must be the one that got started by
3414 // LaunchVMProcess()
3415
3416 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3417 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3418
3419#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3420 /* Hardened windows builds spawns three processes when a VM is
3421 launched, the 3rd one is the one that will end up here. */
3422 RTPROCESS ppid;
3423 int rc = RTProcQueryParent(pid, &ppid);
3424 if (RT_SUCCESS(rc))
3425 rc = RTProcQueryParent(ppid, &ppid);
3426 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3427 || rc == VERR_ACCESS_DENIED)
3428 {
3429 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3430 mData->mSession.mPID = pid;
3431 }
3432#endif
3433
3434 if (mData->mSession.mPID != pid)
3435 return setError(E_ACCESSDENIED,
3436 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3437 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3438 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3439 }
3440
3441 // create the mutable SessionMachine from the current machine
3442 ComObjPtr<SessionMachine> sessionMachine;
3443 sessionMachine.createObject();
3444 rc = sessionMachine->init(this);
3445 AssertComRC(rc);
3446
3447 /* NOTE: doing return from this function after this point but
3448 * before the end is forbidden since it may call SessionMachine::uninit()
3449 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3450 * lock while still holding the Machine lock in alock so that a deadlock
3451 * is possible due to the wrong lock order. */
3452
3453 if (SUCCEEDED(rc))
3454 {
3455 /*
3456 * Set the session state to Spawning to protect against subsequent
3457 * attempts to open a session and to unregister the machine after
3458 * we release the lock.
3459 */
3460 SessionState_T origState = mData->mSession.mState;
3461 mData->mSession.mState = SessionState_Spawning;
3462
3463#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3464 /* Get the client token ID to be passed to the client process */
3465 Utf8Str strTokenId;
3466 sessionMachine->i_getTokenId(strTokenId);
3467 Assert(!strTokenId.isEmpty());
3468#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3469 /* Get the client token to be passed to the client process */
3470 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3471 /* The token is now "owned" by pToken, fix refcount */
3472 if (!pToken.isNull())
3473 pToken->Release();
3474#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3475
3476 /*
3477 * Release the lock before calling the client process -- it will call
3478 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3479 * because the state is Spawning, so that LaunchVMProcess() and
3480 * LockMachine() calls will fail. This method, called before we
3481 * acquire the lock again, will fail because of the wrong PID.
3482 *
3483 * Note that mData->mSession.mRemoteControls accessed outside
3484 * the lock may not be modified when state is Spawning, so it's safe.
3485 */
3486 alock.release();
3487
3488 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3489#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3490 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3491#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3492 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3493 /* Now the token is owned by the client process. */
3494 pToken.setNull();
3495#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3496 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3497
3498 /* The failure may occur w/o any error info (from RPC), so provide one */
3499 if (FAILED(rc))
3500 setError(VBOX_E_VM_ERROR,
3501 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3502
3503 // get session name, either to remember or to compare against
3504 // the already known session name.
3505 {
3506 Bstr bstrSessionName;
3507 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3508 if (SUCCEEDED(rc2))
3509 strSessionName = bstrSessionName;
3510 }
3511
3512 if ( SUCCEEDED(rc)
3513 && fLaunchingVMProcess
3514 )
3515 {
3516 /* complete the remote session initialization */
3517
3518 /* get the console from the direct session */
3519 ComPtr<IConsole> console;
3520 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3521 ComAssertComRC(rc);
3522
3523 if (SUCCEEDED(rc) && !console)
3524 {
3525 ComAssert(!!console);
3526 rc = E_FAIL;
3527 }
3528
3529 /* assign machine & console to the remote session */
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * after LaunchVMProcess(), the first and the only
3534 * entry in remoteControls is that remote session
3535 */
3536 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3537 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3538 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3539
3540 /* The failure may occur w/o any error info (from RPC), so provide one */
3541 if (FAILED(rc))
3542 setError(VBOX_E_VM_ERROR,
3543 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3544 }
3545
3546 if (FAILED(rc))
3547 pSessionControl->Uninitialize();
3548 }
3549
3550 /* acquire the lock again */
3551 alock.acquire();
3552
3553 /* Restore the session state */
3554 mData->mSession.mState = origState;
3555 }
3556
3557 // finalize spawning anyway (this is why we don't return on errors above)
3558 if (fLaunchingVMProcess)
3559 {
3560 Assert(mData->mSession.mName == strSessionName);
3561 /* Note that the progress object is finalized later */
3562 /** @todo Consider checking mData->mSession.mProgress for cancellation
3563 * around here. */
3564
3565 /* We don't reset mSession.mPID here because it is necessary for
3566 * SessionMachine::uninit() to reap the child process later. */
3567
3568 if (FAILED(rc))
3569 {
3570 /* Close the remote session, remove the remote control from the list
3571 * and reset session state to Closed (@note keep the code in sync
3572 * with the relevant part in checkForSpawnFailure()). */
3573
3574 Assert(mData->mSession.mRemoteControls.size() == 1);
3575 if (mData->mSession.mRemoteControls.size() == 1)
3576 {
3577 ErrorInfoKeeper eik;
3578 mData->mSession.mRemoteControls.front()->Uninitialize();
3579 }
3580
3581 mData->mSession.mRemoteControls.clear();
3582 mData->mSession.mState = SessionState_Unlocked;
3583 }
3584 }
3585 else
3586 {
3587 /* memorize PID of the directly opened session */
3588 if (SUCCEEDED(rc))
3589 mData->mSession.mPID = pid;
3590 }
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 mData->mSession.mLockType = aLockType;
3595 /* memorize the direct session control and cache IUnknown for it */
3596 mData->mSession.mDirectControl = pSessionControl;
3597 mData->mSession.mState = SessionState_Locked;
3598 if (!fLaunchingVMProcess)
3599 mData->mSession.mName = strSessionName;
3600 /* associate the SessionMachine with this Machine */
3601 mData->mSession.mMachine = sessionMachine;
3602
3603 /* request an IUnknown pointer early from the remote party for later
3604 * identity checks (it will be internally cached within mDirectControl
3605 * at least on XPCOM) */
3606 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3607 NOREF(unk);
3608 }
3609
3610 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3611 * would break the lock order */
3612 alock.release();
3613
3614 /* uninitialize the created session machine on failure */
3615 if (FAILED(rc))
3616 sessionMachine->uninit();
3617 }
3618
3619 if (SUCCEEDED(rc))
3620 {
3621 /*
3622 * tell the client watcher thread to update the set of
3623 * machines that have open sessions
3624 */
3625 mParent->i_updateClientWatcher();
3626
3627 if (oldState != SessionState_Locked)
3628 /* fire an event */
3629 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3630 }
3631
3632 return rc;
3633}
3634
3635/**
3636 * @note Locks objects!
3637 */
3638HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3639 const com::Utf8Str &aName,
3640 const com::Utf8Str &aEnvironment,
3641 ComPtr<IProgress> &aProgress)
3642{
3643 Utf8Str strFrontend(aName);
3644 /* "emergencystop" doesn't need the session, so skip the checks/interface
3645 * retrieval. This code doesn't quite fit in here, but introducing a
3646 * special API method would be even more effort, and would require explicit
3647 * support by every API client. It's better to hide the feature a bit. */
3648 if (strFrontend != "emergencystop")
3649 CheckComArgNotNull(aSession);
3650
3651 HRESULT rc = S_OK;
3652 if (strFrontend.isEmpty())
3653 {
3654 Bstr bstrFrontend;
3655 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3656 if (FAILED(rc))
3657 return rc;
3658 strFrontend = bstrFrontend;
3659 if (strFrontend.isEmpty())
3660 {
3661 ComPtr<ISystemProperties> systemProperties;
3662 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3663 if (FAILED(rc))
3664 return rc;
3665 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3666 if (FAILED(rc))
3667 return rc;
3668 strFrontend = bstrFrontend;
3669 }
3670 /* paranoia - emergencystop is not a valid default */
3671 if (strFrontend == "emergencystop")
3672 strFrontend = Utf8Str::Empty;
3673 }
3674 /* default frontend: Qt GUI */
3675 if (strFrontend.isEmpty())
3676 strFrontend = "GUI/Qt";
3677
3678 if (strFrontend != "emergencystop")
3679 {
3680 /* check the session state */
3681 SessionState_T state;
3682 rc = aSession->COMGETTER(State)(&state);
3683 if (FAILED(rc))
3684 return rc;
3685
3686 if (state != SessionState_Unlocked)
3687 return setError(VBOX_E_INVALID_OBJECT_STATE,
3688 tr("The given session is busy"));
3689
3690 /* get the IInternalSessionControl interface */
3691 ComPtr<IInternalSessionControl> control(aSession);
3692 ComAssertMsgRet(!control.isNull(),
3693 ("No IInternalSessionControl interface"),
3694 E_INVALIDARG);
3695
3696 /* get the teleporter enable state for the progress object init. */
3697 BOOL fTeleporterEnabled;
3698 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3699 if (FAILED(rc))
3700 return rc;
3701
3702 /* create a progress object */
3703 ComObjPtr<ProgressProxy> progress;
3704 progress.createObject();
3705 rc = progress->init(mParent,
3706 static_cast<IMachine*>(this),
3707 Bstr(tr("Starting VM")).raw(),
3708 TRUE /* aCancelable */,
3709 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3710 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3711 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3712 2 /* uFirstOperationWeight */,
3713 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3714
3715 if (SUCCEEDED(rc))
3716 {
3717 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3718 if (SUCCEEDED(rc))
3719 {
3720 aProgress = progress;
3721
3722 /* signal the client watcher thread */
3723 mParent->i_updateClientWatcher();
3724
3725 /* fire an event */
3726 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3727 }
3728 }
3729 }
3730 else
3731 {
3732 /* no progress object - either instant success or failure */
3733 aProgress = NULL;
3734
3735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3736
3737 if (mData->mSession.mState != SessionState_Locked)
3738 return setError(VBOX_E_INVALID_OBJECT_STATE,
3739 tr("The machine '%s' is not locked by a session"),
3740 mUserData->s.strName.c_str());
3741
3742 /* must have a VM process associated - do not kill normal API clients
3743 * with an open session */
3744 if (!Global::IsOnline(mData->mMachineState))
3745 return setError(VBOX_E_INVALID_OBJECT_STATE,
3746 tr("The machine '%s' does not have a VM process"),
3747 mUserData->s.strName.c_str());
3748
3749 /* forcibly terminate the VM process */
3750 if (mData->mSession.mPID != NIL_RTPROCESS)
3751 RTProcTerminate(mData->mSession.mPID);
3752
3753 /* signal the client watcher thread, as most likely the client has
3754 * been terminated */
3755 mParent->i_updateClientWatcher();
3756 }
3757
3758 return rc;
3759}
3760
3761HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3762{
3763 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3764 return setError(E_INVALIDARG,
3765 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3766 aPosition, SchemaDefs::MaxBootPosition);
3767
3768 if (aDevice == DeviceType_USB)
3769 return setError(E_NOTIMPL,
3770 tr("Booting from USB device is currently not supported"));
3771
3772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3773
3774 HRESULT rc = i_checkStateDependency(MutableStateDep);
3775 if (FAILED(rc)) return rc;
3776
3777 i_setModified(IsModified_MachineData);
3778 mHWData.backup();
3779 mHWData->mBootOrder[aPosition - 1] = aDevice;
3780
3781 return S_OK;
3782}
3783
3784HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3785{
3786 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3787 return setError(E_INVALIDARG,
3788 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3789 aPosition, SchemaDefs::MaxBootPosition);
3790
3791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3792
3793 *aDevice = mHWData->mBootOrder[aPosition - 1];
3794
3795 return S_OK;
3796}
3797
3798HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3799 LONG aControllerPort,
3800 LONG aDevice,
3801 DeviceType_T aType,
3802 const ComPtr<IMedium> &aMedium)
3803{
3804 IMedium *aM = aMedium;
3805 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3806 aName.c_str(), aControllerPort, aDevice, aType, aM));
3807
3808 // request the host lock first, since might be calling Host methods for getting host drives;
3809 // next, protect the media tree all the while we're in here, as well as our member variables
3810 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3811 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3812
3813 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3814 if (FAILED(rc)) return rc;
3815
3816 /// @todo NEWMEDIA implicit machine registration
3817 if (!mData->mRegistered)
3818 return setError(VBOX_E_INVALID_OBJECT_STATE,
3819 tr("Cannot attach storage devices to an unregistered machine"));
3820
3821 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3822
3823 /* Check for an existing controller. */
3824 ComObjPtr<StorageController> ctl;
3825 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3826 if (FAILED(rc)) return rc;
3827
3828 StorageControllerType_T ctrlType;
3829 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3830 if (FAILED(rc))
3831 return setError(E_FAIL,
3832 tr("Could not get type of controller '%s'"),
3833 aName.c_str());
3834
3835 bool fSilent = false;
3836 Utf8Str strReconfig;
3837
3838 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3839 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3840 if ( mData->mMachineState == MachineState_Paused
3841 && strReconfig == "1")
3842 fSilent = true;
3843
3844 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3845 bool fHotplug = false;
3846 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3847 fHotplug = true;
3848
3849 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3850 return setError(VBOX_E_INVALID_VM_STATE,
3851 tr("Controller '%s' does not support hotplugging"),
3852 aName.c_str());
3853
3854 // check that the port and device are not out of range
3855 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3856 if (FAILED(rc)) return rc;
3857
3858 /* check if the device slot is already busy */
3859 MediumAttachment *pAttachTemp;
3860 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3861 Bstr(aName).raw(),
3862 aControllerPort,
3863 aDevice)))
3864 {
3865 Medium *pMedium = pAttachTemp->i_getMedium();
3866 if (pMedium)
3867 {
3868 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3869 return setError(VBOX_E_OBJECT_IN_USE,
3870 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3871 pMedium->i_getLocationFull().c_str(),
3872 aControllerPort,
3873 aDevice,
3874 aName.c_str());
3875 }
3876 else
3877 return setError(VBOX_E_OBJECT_IN_USE,
3878 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3879 aControllerPort, aDevice, aName.c_str());
3880 }
3881
3882 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3883 if (aMedium && medium.isNull())
3884 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3885
3886 AutoCaller mediumCaller(medium);
3887 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3888
3889 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3890
3891 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3892 && !medium.isNull()
3893 )
3894 return setError(VBOX_E_OBJECT_IN_USE,
3895 tr("Medium '%s' is already attached to this virtual machine"),
3896 medium->i_getLocationFull().c_str());
3897
3898 if (!medium.isNull())
3899 {
3900 MediumType_T mtype = medium->i_getType();
3901 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3902 // For DVDs it's not written to the config file, so needs no global config
3903 // version bump. For floppies it's a new attribute "type", which is ignored
3904 // by older VirtualBox version, so needs no global config version bump either.
3905 // For hard disks this type is not accepted.
3906 if (mtype == MediumType_MultiAttach)
3907 {
3908 // This type is new with VirtualBox 4.0 and therefore requires settings
3909 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3910 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3911 // two reasons: The medium type is a property of the media registry tree, which
3912 // can reside in the global config file (for pre-4.0 media); we would therefore
3913 // possibly need to bump the global config version. We don't want to do that though
3914 // because that might make downgrading to pre-4.0 impossible.
3915 // As a result, we can only use these two new types if the medium is NOT in the
3916 // global registry:
3917 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3918 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3919 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3920 )
3921 return setError(VBOX_E_INVALID_OBJECT_STATE,
3922 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3923 "to machines that were created with VirtualBox 4.0 or later"),
3924 medium->i_getLocationFull().c_str());
3925 }
3926 }
3927
3928 bool fIndirect = false;
3929 if (!medium.isNull())
3930 fIndirect = medium->i_isReadOnly();
3931 bool associate = true;
3932
3933 do
3934 {
3935 if ( aType == DeviceType_HardDisk
3936 && mMediaData.isBackedUp())
3937 {
3938 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3939
3940 /* check if the medium was attached to the VM before we started
3941 * changing attachments in which case the attachment just needs to
3942 * be restored */
3943 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3944 {
3945 AssertReturn(!fIndirect, E_FAIL);
3946
3947 /* see if it's the same bus/channel/device */
3948 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3949 {
3950 /* the simplest case: restore the whole attachment
3951 * and return, nothing else to do */
3952 mMediaData->mAttachments.push_back(pAttachTemp);
3953
3954 /* Reattach the medium to the VM. */
3955 if (fHotplug || fSilent)
3956 {
3957 mediumLock.release();
3958 treeLock.release();
3959 alock.release();
3960
3961 MediumLockList *pMediumLockList(new MediumLockList());
3962
3963 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3964 medium /* pToLockWrite */,
3965 false /* fMediumLockWriteAll */,
3966 NULL,
3967 *pMediumLockList);
3968 alock.acquire();
3969 if (FAILED(rc))
3970 delete pMediumLockList;
3971 else
3972 {
3973 mData->mSession.mLockedMedia.Unlock();
3974 alock.release();
3975 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3976 mData->mSession.mLockedMedia.Lock();
3977 alock.acquire();
3978 }
3979 alock.release();
3980
3981 if (SUCCEEDED(rc))
3982 {
3983 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3984 /* Remove lock list in case of error. */
3985 if (FAILED(rc))
3986 {
3987 mData->mSession.mLockedMedia.Unlock();
3988 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3989 mData->mSession.mLockedMedia.Lock();
3990 }
3991 }
3992 }
3993
3994 return S_OK;
3995 }
3996
3997 /* bus/channel/device differ; we need a new attachment object,
3998 * but don't try to associate it again */
3999 associate = false;
4000 break;
4001 }
4002 }
4003
4004 /* go further only if the attachment is to be indirect */
4005 if (!fIndirect)
4006 break;
4007
4008 /* perform the so called smart attachment logic for indirect
4009 * attachments. Note that smart attachment is only applicable to base
4010 * hard disks. */
4011
4012 if (medium->i_getParent().isNull())
4013 {
4014 /* first, investigate the backup copy of the current hard disk
4015 * attachments to make it possible to re-attach existing diffs to
4016 * another device slot w/o losing their contents */
4017 if (mMediaData.isBackedUp())
4018 {
4019 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4020
4021 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4022 uint32_t foundLevel = 0;
4023
4024 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4025 {
4026 uint32_t level = 0;
4027 MediumAttachment *pAttach = *it;
4028 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4029 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4030 if (pMedium.isNull())
4031 continue;
4032
4033 if (pMedium->i_getBase(&level) == medium)
4034 {
4035 /* skip the hard disk if its currently attached (we
4036 * cannot attach the same hard disk twice) */
4037 if (i_findAttachment(mMediaData->mAttachments,
4038 pMedium))
4039 continue;
4040
4041 /* matched device, channel and bus (i.e. attached to the
4042 * same place) will win and immediately stop the search;
4043 * otherwise the attachment that has the youngest
4044 * descendant of medium will be used
4045 */
4046 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4047 {
4048 /* the simplest case: restore the whole attachment
4049 * and return, nothing else to do */
4050 mMediaData->mAttachments.push_back(*it);
4051
4052 /* Reattach the medium to the VM. */
4053 if (fHotplug || fSilent)
4054 {
4055 mediumLock.release();
4056 treeLock.release();
4057 alock.release();
4058
4059 MediumLockList *pMediumLockList(new MediumLockList());
4060
4061 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4062 medium /* pToLockWrite */,
4063 false /* fMediumLockWriteAll */,
4064 NULL,
4065 *pMediumLockList);
4066 alock.acquire();
4067 if (FAILED(rc))
4068 delete pMediumLockList;
4069 else
4070 {
4071 mData->mSession.mLockedMedia.Unlock();
4072 alock.release();
4073 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4074 mData->mSession.mLockedMedia.Lock();
4075 alock.acquire();
4076 }
4077 alock.release();
4078
4079 if (SUCCEEDED(rc))
4080 {
4081 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4082 /* Remove lock list in case of error. */
4083 if (FAILED(rc))
4084 {
4085 mData->mSession.mLockedMedia.Unlock();
4086 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4087 mData->mSession.mLockedMedia.Lock();
4088 }
4089 }
4090 }
4091
4092 return S_OK;
4093 }
4094 else if ( foundIt == oldAtts.end()
4095 || level > foundLevel /* prefer younger */
4096 )
4097 {
4098 foundIt = it;
4099 foundLevel = level;
4100 }
4101 }
4102 }
4103
4104 if (foundIt != oldAtts.end())
4105 {
4106 /* use the previously attached hard disk */
4107 medium = (*foundIt)->i_getMedium();
4108 mediumCaller.attach(medium);
4109 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4110 mediumLock.attach(medium);
4111 /* not implicit, doesn't require association with this VM */
4112 fIndirect = false;
4113 associate = false;
4114 /* go right to the MediumAttachment creation */
4115 break;
4116 }
4117 }
4118
4119 /* must give up the medium lock and medium tree lock as below we
4120 * go over snapshots, which needs a lock with higher lock order. */
4121 mediumLock.release();
4122 treeLock.release();
4123
4124 /* then, search through snapshots for the best diff in the given
4125 * hard disk's chain to base the new diff on */
4126
4127 ComObjPtr<Medium> base;
4128 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4129 while (snap)
4130 {
4131 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4132
4133 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4134
4135 MediumAttachment *pAttachFound = NULL;
4136 uint32_t foundLevel = 0;
4137
4138 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4139 {
4140 MediumAttachment *pAttach = *it;
4141 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4142 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4143 if (pMedium.isNull())
4144 continue;
4145
4146 uint32_t level = 0;
4147 if (pMedium->i_getBase(&level) == medium)
4148 {
4149 /* matched device, channel and bus (i.e. attached to the
4150 * same place) will win and immediately stop the search;
4151 * otherwise the attachment that has the youngest
4152 * descendant of medium will be used
4153 */
4154 if ( pAttach->i_getDevice() == aDevice
4155 && pAttach->i_getPort() == aControllerPort
4156 && pAttach->i_getControllerName() == aName
4157 )
4158 {
4159 pAttachFound = pAttach;
4160 break;
4161 }
4162 else if ( !pAttachFound
4163 || level > foundLevel /* prefer younger */
4164 )
4165 {
4166 pAttachFound = pAttach;
4167 foundLevel = level;
4168 }
4169 }
4170 }
4171
4172 if (pAttachFound)
4173 {
4174 base = pAttachFound->i_getMedium();
4175 break;
4176 }
4177
4178 snap = snap->i_getParent();
4179 }
4180
4181 /* re-lock medium tree and the medium, as we need it below */
4182 treeLock.acquire();
4183 mediumLock.acquire();
4184
4185 /* found a suitable diff, use it as a base */
4186 if (!base.isNull())
4187 {
4188 medium = base;
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 }
4193 }
4194
4195 Utf8Str strFullSnapshotFolder;
4196 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4197
4198 ComObjPtr<Medium> diff;
4199 diff.createObject();
4200 // store this diff in the same registry as the parent
4201 Guid uuidRegistryParent;
4202 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4203 {
4204 // parent image has no registry: this can happen if we're attaching a new immutable
4205 // image that has not yet been attached (medium then points to the base and we're
4206 // creating the diff image for the immutable, and the parent is not yet registered);
4207 // put the parent in the machine registry then
4208 mediumLock.release();
4209 treeLock.release();
4210 alock.release();
4211 i_addMediumToRegistry(medium);
4212 alock.acquire();
4213 treeLock.acquire();
4214 mediumLock.acquire();
4215 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4216 }
4217 rc = diff->init(mParent,
4218 medium->i_getPreferredDiffFormat(),
4219 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4220 uuidRegistryParent,
4221 DeviceType_HardDisk);
4222 if (FAILED(rc)) return rc;
4223
4224 /* Apply the normal locking logic to the entire chain. */
4225 MediumLockList *pMediumLockList(new MediumLockList());
4226 mediumLock.release();
4227 treeLock.release();
4228 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4229 diff /* pToLockWrite */,
4230 false /* fMediumLockWriteAll */,
4231 medium,
4232 *pMediumLockList);
4233 treeLock.acquire();
4234 mediumLock.acquire();
4235 if (SUCCEEDED(rc))
4236 {
4237 mediumLock.release();
4238 treeLock.release();
4239 rc = pMediumLockList->Lock();
4240 treeLock.acquire();
4241 mediumLock.acquire();
4242 if (FAILED(rc))
4243 setError(rc,
4244 tr("Could not lock medium when creating diff '%s'"),
4245 diff->i_getLocationFull().c_str());
4246 else
4247 {
4248 /* will release the lock before the potentially lengthy
4249 * operation, so protect with the special state */
4250 MachineState_T oldState = mData->mMachineState;
4251 i_setMachineState(MachineState_SettingUp);
4252
4253 mediumLock.release();
4254 treeLock.release();
4255 alock.release();
4256
4257 rc = medium->i_createDiffStorage(diff,
4258 medium->i_getPreferredDiffVariant(),
4259 pMediumLockList,
4260 NULL /* aProgress */,
4261 true /* aWait */);
4262
4263 alock.acquire();
4264 treeLock.acquire();
4265 mediumLock.acquire();
4266
4267 i_setMachineState(oldState);
4268 }
4269 }
4270
4271 /* Unlock the media and free the associated memory. */
4272 delete pMediumLockList;
4273
4274 if (FAILED(rc)) return rc;
4275
4276 /* use the created diff for the actual attachment */
4277 medium = diff;
4278 mediumCaller.attach(medium);
4279 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4280 mediumLock.attach(medium);
4281 }
4282 while (0);
4283
4284 ComObjPtr<MediumAttachment> attachment;
4285 attachment.createObject();
4286 rc = attachment->init(this,
4287 medium,
4288 aName,
4289 aControllerPort,
4290 aDevice,
4291 aType,
4292 fIndirect,
4293 false /* fPassthrough */,
4294 false /* fTempEject */,
4295 false /* fNonRotational */,
4296 false /* fDiscard */,
4297 fHotplug /* fHotPluggable */,
4298 Utf8Str::Empty);
4299 if (FAILED(rc)) return rc;
4300
4301 if (associate && !medium.isNull())
4302 {
4303 // as the last step, associate the medium to the VM
4304 rc = medium->i_addBackReference(mData->mUuid);
4305 // here we can fail because of Deleting, or being in process of creating a Diff
4306 if (FAILED(rc)) return rc;
4307
4308 mediumLock.release();
4309 treeLock.release();
4310 alock.release();
4311 i_addMediumToRegistry(medium);
4312 alock.acquire();
4313 treeLock.acquire();
4314 mediumLock.acquire();
4315 }
4316
4317 /* success: finally remember the attachment */
4318 i_setModified(IsModified_Storage);
4319 mMediaData.backup();
4320 mMediaData->mAttachments.push_back(attachment);
4321
4322 mediumLock.release();
4323 treeLock.release();
4324 alock.release();
4325
4326 if (fHotplug || fSilent)
4327 {
4328 if (!medium.isNull())
4329 {
4330 MediumLockList *pMediumLockList(new MediumLockList());
4331
4332 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4333 medium /* pToLockWrite */,
4334 false /* fMediumLockWriteAll */,
4335 NULL,
4336 *pMediumLockList);
4337 alock.acquire();
4338 if (FAILED(rc))
4339 delete pMediumLockList;
4340 else
4341 {
4342 mData->mSession.mLockedMedia.Unlock();
4343 alock.release();
4344 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4345 mData->mSession.mLockedMedia.Lock();
4346 alock.acquire();
4347 }
4348 alock.release();
4349 }
4350
4351 if (SUCCEEDED(rc))
4352 {
4353 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4354 /* Remove lock list in case of error. */
4355 if (FAILED(rc))
4356 {
4357 mData->mSession.mLockedMedia.Unlock();
4358 mData->mSession.mLockedMedia.Remove(attachment);
4359 mData->mSession.mLockedMedia.Lock();
4360 }
4361 }
4362 }
4363
4364 /* Save modified registries, but skip this machine as it's the caller's
4365 * job to save its settings like all other settings changes. */
4366 mParent->i_unmarkRegistryModified(i_getId());
4367 mParent->i_saveModifiedRegistries();
4368
4369 return rc;
4370}
4371
4372HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4373 LONG aDevice)
4374{
4375 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4376 aName.c_str(), aControllerPort, aDevice));
4377
4378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4379
4380 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4381 if (FAILED(rc)) return rc;
4382
4383 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4384
4385 /* Check for an existing controller. */
4386 ComObjPtr<StorageController> ctl;
4387 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4388 if (FAILED(rc)) return rc;
4389
4390 StorageControllerType_T ctrlType;
4391 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4392 if (FAILED(rc))
4393 return setError(E_FAIL,
4394 tr("Could not get type of controller '%s'"),
4395 aName.c_str());
4396
4397 bool fSilent = false;
4398 Utf8Str strReconfig;
4399
4400 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4401 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4402 if ( mData->mMachineState == MachineState_Paused
4403 && strReconfig == "1")
4404 fSilent = true;
4405
4406 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4407 bool fHotplug = false;
4408 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4409 fHotplug = true;
4410
4411 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4412 return setError(VBOX_E_INVALID_VM_STATE,
4413 tr("Controller '%s' does not support hotplugging"),
4414 aName.c_str());
4415
4416 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4417 Bstr(aName).raw(),
4418 aControllerPort,
4419 aDevice);
4420 if (!pAttach)
4421 return setError(VBOX_E_OBJECT_NOT_FOUND,
4422 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4423 aDevice, aControllerPort, aName.c_str());
4424
4425 if (fHotplug && !pAttach->i_getHotPluggable())
4426 return setError(VBOX_E_NOT_SUPPORTED,
4427 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4428 aDevice, aControllerPort, aName.c_str());
4429
4430 /*
4431 * The VM has to detach the device before we delete any implicit diffs.
4432 * If this fails we can roll back without loosing data.
4433 */
4434 if (fHotplug || fSilent)
4435 {
4436 alock.release();
4437 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4438 alock.acquire();
4439 }
4440 if (FAILED(rc)) return rc;
4441
4442 /* If we are here everything went well and we can delete the implicit now. */
4443 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4444
4445 alock.release();
4446
4447 /* Save modified registries, but skip this machine as it's the caller's
4448 * job to save its settings like all other settings changes. */
4449 mParent->i_unmarkRegistryModified(i_getId());
4450 mParent->i_saveModifiedRegistries();
4451
4452 return rc;
4453}
4454
4455HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4456 LONG aDevice, BOOL aPassthrough)
4457{
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4459 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = i_checkStateDependency(MutableStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4467
4468 if (Global::IsOnlineOrTransient(mData->mMachineState))
4469 return setError(VBOX_E_INVALID_VM_STATE,
4470 tr("Invalid machine state: %s"),
4471 Global::stringifyMachineState(mData->mMachineState));
4472
4473 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4474 Bstr(aName).raw(),
4475 aControllerPort,
4476 aDevice);
4477 if (!pAttach)
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4480 aDevice, aControllerPort, aName.c_str());
4481
4482
4483 i_setModified(IsModified_Storage);
4484 mMediaData.backup();
4485
4486 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4487
4488 if (pAttach->i_getType() != DeviceType_DVD)
4489 return setError(E_INVALIDARG,
4490 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4491 aDevice, aControllerPort, aName.c_str());
4492 pAttach->i_updatePassthrough(!!aPassthrough);
4493
4494 return S_OK;
4495}
4496
4497HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4498 LONG aDevice, BOOL aTemporaryEject)
4499{
4500
4501 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4502 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4503
4504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4505
4506 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4507 if (FAILED(rc)) return rc;
4508
4509 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4510 Bstr(aName).raw(),
4511 aControllerPort,
4512 aDevice);
4513 if (!pAttach)
4514 return setError(VBOX_E_OBJECT_NOT_FOUND,
4515 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4516 aDevice, aControllerPort, aName.c_str());
4517
4518
4519 i_setModified(IsModified_Storage);
4520 mMediaData.backup();
4521
4522 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4523
4524 if (pAttach->i_getType() != DeviceType_DVD)
4525 return setError(E_INVALIDARG,
4526 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4527 aDevice, aControllerPort, aName.c_str());
4528 pAttach->i_updateTempEject(!!aTemporaryEject);
4529
4530 return S_OK;
4531}
4532
4533HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4534 LONG aDevice, BOOL aNonRotational)
4535{
4536
4537 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4538 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4539
4540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4541
4542 HRESULT rc = i_checkStateDependency(MutableStateDep);
4543 if (FAILED(rc)) return rc;
4544
4545 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4546
4547 if (Global::IsOnlineOrTransient(mData->mMachineState))
4548 return setError(VBOX_E_INVALID_VM_STATE,
4549 tr("Invalid machine state: %s"),
4550 Global::stringifyMachineState(mData->mMachineState));
4551
4552 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4553 Bstr(aName).raw(),
4554 aControllerPort,
4555 aDevice);
4556 if (!pAttach)
4557 return setError(VBOX_E_OBJECT_NOT_FOUND,
4558 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4559 aDevice, aControllerPort, aName.c_str());
4560
4561
4562 i_setModified(IsModified_Storage);
4563 mMediaData.backup();
4564
4565 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4566
4567 if (pAttach->i_getType() != DeviceType_HardDisk)
4568 return setError(E_INVALIDARG,
4569 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"),
4570 aDevice, aControllerPort, aName.c_str());
4571 pAttach->i_updateNonRotational(!!aNonRotational);
4572
4573 return S_OK;
4574}
4575
4576HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4577 LONG aDevice, BOOL aDiscard)
4578{
4579
4580 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4581 aName.c_str(), aControllerPort, aDevice, aDiscard));
4582
4583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4584
4585 HRESULT rc = i_checkStateDependency(MutableStateDep);
4586 if (FAILED(rc)) return rc;
4587
4588 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4589
4590 if (Global::IsOnlineOrTransient(mData->mMachineState))
4591 return setError(VBOX_E_INVALID_VM_STATE,
4592 tr("Invalid machine state: %s"),
4593 Global::stringifyMachineState(mData->mMachineState));
4594
4595 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4596 Bstr(aName).raw(),
4597 aControllerPort,
4598 aDevice);
4599 if (!pAttach)
4600 return setError(VBOX_E_OBJECT_NOT_FOUND,
4601 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4602 aDevice, aControllerPort, aName.c_str());
4603
4604
4605 i_setModified(IsModified_Storage);
4606 mMediaData.backup();
4607
4608 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4609
4610 if (pAttach->i_getType() != DeviceType_HardDisk)
4611 return setError(E_INVALIDARG,
4612 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"),
4613 aDevice, aControllerPort, aName.c_str());
4614 pAttach->i_updateDiscard(!!aDiscard);
4615
4616 return S_OK;
4617}
4618
4619HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4620 LONG aDevice, BOOL aHotPluggable)
4621{
4622 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4623 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4624
4625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4626
4627 HRESULT rc = i_checkStateDependency(MutableStateDep);
4628 if (FAILED(rc)) return rc;
4629
4630 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4631
4632 if (Global::IsOnlineOrTransient(mData->mMachineState))
4633 return setError(VBOX_E_INVALID_VM_STATE,
4634 tr("Invalid machine state: %s"),
4635 Global::stringifyMachineState(mData->mMachineState));
4636
4637 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4638 Bstr(aName).raw(),
4639 aControllerPort,
4640 aDevice);
4641 if (!pAttach)
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4644 aDevice, aControllerPort, aName.c_str());
4645
4646 /* Check for an existing controller. */
4647 ComObjPtr<StorageController> ctl;
4648 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4649 if (FAILED(rc)) return rc;
4650
4651 StorageControllerType_T ctrlType;
4652 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4653 if (FAILED(rc))
4654 return setError(E_FAIL,
4655 tr("Could not get type of controller '%s'"),
4656 aName.c_str());
4657
4658 if (!i_isControllerHotplugCapable(ctrlType))
4659 return setError(VBOX_E_NOT_SUPPORTED,
4660 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4661 aName.c_str());
4662
4663 i_setModified(IsModified_Storage);
4664 mMediaData.backup();
4665
4666 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4667
4668 if (pAttach->i_getType() == DeviceType_Floppy)
4669 return setError(E_INVALIDARG,
4670 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"),
4671 aDevice, aControllerPort, aName.c_str());
4672 pAttach->i_updateHotPluggable(!!aHotPluggable);
4673
4674 return S_OK;
4675}
4676
4677HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4678 LONG aDevice)
4679{
4680 int rc = S_OK;
4681 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4682 aName.c_str(), aControllerPort, aDevice));
4683
4684 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4685
4686 return rc;
4687}
4688
4689HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4690 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4691{
4692 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4693 aName.c_str(), aControllerPort, aDevice));
4694
4695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4696
4697 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4698 if (FAILED(rc)) return rc;
4699
4700 if (Global::IsOnlineOrTransient(mData->mMachineState))
4701 return setError(VBOX_E_INVALID_VM_STATE,
4702 tr("Invalid machine state: %s"),
4703 Global::stringifyMachineState(mData->mMachineState));
4704
4705 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4706 Bstr(aName).raw(),
4707 aControllerPort,
4708 aDevice);
4709 if (!pAttach)
4710 return setError(VBOX_E_OBJECT_NOT_FOUND,
4711 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4712 aDevice, aControllerPort, aName.c_str());
4713
4714
4715 i_setModified(IsModified_Storage);
4716 mMediaData.backup();
4717
4718 IBandwidthGroup *iB = aBandwidthGroup;
4719 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4720 if (aBandwidthGroup && group.isNull())
4721 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4722
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724
4725 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4726 if (strBandwidthGroupOld.isNotEmpty())
4727 {
4728 /* Get the bandwidth group object and release it - this must not fail. */
4729 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4730 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4731 Assert(SUCCEEDED(rc));
4732
4733 pBandwidthGroupOld->i_release();
4734 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4735 }
4736
4737 if (!group.isNull())
4738 {
4739 group->i_reference();
4740 pAttach->i_updateBandwidthGroup(group->i_getName());
4741 }
4742
4743 return S_OK;
4744}
4745
4746HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4747 LONG aControllerPort,
4748 LONG aDevice,
4749 DeviceType_T aType)
4750{
4751 HRESULT rc = S_OK;
4752
4753 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4754 aName.c_str(), aControllerPort, aDevice, aType));
4755
4756 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4757
4758 return rc;
4759}
4760
4761
4762HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4763 LONG aControllerPort,
4764 LONG aDevice,
4765 BOOL aForce)
4766{
4767 int rc = S_OK;
4768 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4769 aName.c_str(), aControllerPort, aForce));
4770
4771 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4772
4773 return rc;
4774}
4775
4776HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4777 LONG aControllerPort,
4778 LONG aDevice,
4779 const ComPtr<IMedium> &aMedium,
4780 BOOL aForce)
4781{
4782 int rc = S_OK;
4783 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4784 aName.c_str(), aControllerPort, aDevice, aForce));
4785
4786 // request the host lock first, since might be calling Host methods for getting host drives;
4787 // next, protect the media tree all the while we're in here, as well as our member variables
4788 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4789 this->lockHandle(),
4790 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4791
4792 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4793 Bstr(aName).raw(),
4794 aControllerPort,
4795 aDevice);
4796 if (pAttach.isNull())
4797 return setError(VBOX_E_OBJECT_NOT_FOUND,
4798 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4799 aDevice, aControllerPort, aName.c_str());
4800
4801 /* Remember previously mounted medium. The medium before taking the
4802 * backup is not necessarily the same thing. */
4803 ComObjPtr<Medium> oldmedium;
4804 oldmedium = pAttach->i_getMedium();
4805
4806 IMedium *iM = aMedium;
4807 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4808 if (aMedium && pMedium.isNull())
4809 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4810
4811 AutoCaller mediumCaller(pMedium);
4812 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4813
4814 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4815 if (pMedium)
4816 {
4817 DeviceType_T mediumType = pAttach->i_getType();
4818 switch (mediumType)
4819 {
4820 case DeviceType_DVD:
4821 case DeviceType_Floppy:
4822 break;
4823
4824 default:
4825 return setError(VBOX_E_INVALID_OBJECT_STATE,
4826 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4827 aControllerPort,
4828 aDevice,
4829 aName.c_str());
4830 }
4831 }
4832
4833 i_setModified(IsModified_Storage);
4834 mMediaData.backup();
4835
4836 {
4837 // The backup operation makes the pAttach reference point to the
4838 // old settings. Re-get the correct reference.
4839 pAttach = i_findAttachment(mMediaData->mAttachments,
4840 Bstr(aName).raw(),
4841 aControllerPort,
4842 aDevice);
4843 if (!oldmedium.isNull())
4844 oldmedium->i_removeBackReference(mData->mUuid);
4845 if (!pMedium.isNull())
4846 {
4847 pMedium->i_addBackReference(mData->mUuid);
4848
4849 mediumLock.release();
4850 multiLock.release();
4851 i_addMediumToRegistry(pMedium);
4852 multiLock.acquire();
4853 mediumLock.acquire();
4854 }
4855
4856 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4857 pAttach->i_updateMedium(pMedium);
4858 }
4859
4860 i_setModified(IsModified_Storage);
4861
4862 mediumLock.release();
4863 multiLock.release();
4864 rc = i_onMediumChange(pAttach, aForce);
4865 multiLock.acquire();
4866 mediumLock.acquire();
4867
4868 /* On error roll back this change only. */
4869 if (FAILED(rc))
4870 {
4871 if (!pMedium.isNull())
4872 pMedium->i_removeBackReference(mData->mUuid);
4873 pAttach = i_findAttachment(mMediaData->mAttachments,
4874 Bstr(aName).raw(),
4875 aControllerPort,
4876 aDevice);
4877 /* If the attachment is gone in the meantime, bail out. */
4878 if (pAttach.isNull())
4879 return rc;
4880 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4881 if (!oldmedium.isNull())
4882 oldmedium->i_addBackReference(mData->mUuid);
4883 pAttach->i_updateMedium(oldmedium);
4884 }
4885
4886 mediumLock.release();
4887 multiLock.release();
4888
4889 /* Save modified registries, but skip this machine as it's the caller's
4890 * job to save its settings like all other settings changes. */
4891 mParent->i_unmarkRegistryModified(i_getId());
4892 mParent->i_saveModifiedRegistries();
4893
4894 return rc;
4895}
4896HRESULT Machine::getMedium(const com::Utf8Str &aName,
4897 LONG aControllerPort,
4898 LONG aDevice,
4899 ComPtr<IMedium> &aMedium)
4900{
4901 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4902 aName.c_str(), aControllerPort, aDevice));
4903
4904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4905
4906 aMedium = NULL;
4907
4908 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4909 Bstr(aName).raw(),
4910 aControllerPort,
4911 aDevice);
4912 if (pAttach.isNull())
4913 return setError(VBOX_E_OBJECT_NOT_FOUND,
4914 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4915 aDevice, aControllerPort, aName.c_str());
4916
4917 aMedium = pAttach->i_getMedium();
4918
4919 return S_OK;
4920}
4921
4922HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4923{
4924
4925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4926
4927 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4928
4929 return S_OK;
4930}
4931
4932HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4933{
4934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4935
4936 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4937
4938 return S_OK;
4939}
4940
4941HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4942{
4943 /* Do not assert if slot is out of range, just return the advertised
4944 status. testdriver/vbox.py triggers this in logVmInfo. */
4945 if (aSlot >= mNetworkAdapters.size())
4946 return setError(E_INVALIDARG,
4947 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4948 aSlot, mNetworkAdapters.size());
4949
4950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4951
4952 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4953
4954 return S_OK;
4955}
4956
4957HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4958{
4959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4960
4961 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4962 size_t i = 0;
4963 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4964 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4965 ++it, ++i)
4966 aKeys[i] = it->first;
4967
4968 return S_OK;
4969}
4970
4971 /**
4972 * @note Locks this object for reading.
4973 */
4974HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4975 com::Utf8Str &aValue)
4976{
4977 /* start with nothing found */
4978 aValue = "";
4979
4980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4981
4982 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4983 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4984 // found:
4985 aValue = it->second; // source is a Utf8Str
4986
4987 /* return the result to caller (may be empty) */
4988 return S_OK;
4989}
4990
4991 /**
4992 * @note Locks mParent for writing + this object for writing.
4993 */
4994HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4995{
4996 Utf8Str strOldValue; // empty
4997
4998 // locking note: we only hold the read lock briefly to look up the old value,
4999 // then release it and call the onExtraCanChange callbacks. There is a small
5000 // chance of a race insofar as the callback might be called twice if two callers
5001 // change the same key at the same time, but that's a much better solution
5002 // than the deadlock we had here before. The actual changing of the extradata
5003 // is then performed under the write lock and race-free.
5004
5005 // look up the old value first; if nothing has changed then we need not do anything
5006 {
5007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5008
5009 // For snapshots don't even think about allowing changes, extradata
5010 // is global for a machine, so there is nothing snapshot specific.
5011 if (i_isSnapshotMachine())
5012 return setError(VBOX_E_INVALID_VM_STATE,
5013 tr("Cannot set extradata for a snapshot"));
5014
5015 // check if the right IMachine instance is used
5016 if (mData->mRegistered && !i_isSessionMachine())
5017 return setError(VBOX_E_INVALID_VM_STATE,
5018 tr("Cannot set extradata for an immutable machine"));
5019
5020 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5021 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5022 strOldValue = it->second;
5023 }
5024
5025 bool fChanged;
5026 if ((fChanged = (strOldValue != aValue)))
5027 {
5028 // ask for permission from all listeners outside the locks;
5029 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5030 // lock to copy the list of callbacks to invoke
5031 Bstr error;
5032 Bstr bstrValue(aValue);
5033
5034 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5035 {
5036 const char *sep = error.isEmpty() ? "" : ": ";
5037 CBSTR err = error.raw();
5038 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5039 return setError(E_ACCESSDENIED,
5040 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5041 aKey.c_str(),
5042 aValue.c_str(),
5043 sep,
5044 err);
5045 }
5046
5047 // data is changing and change not vetoed: then write it out under the lock
5048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5049
5050 if (aValue.isEmpty())
5051 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5052 else
5053 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5054 // creates a new key if needed
5055
5056 bool fNeedsGlobalSaveSettings = false;
5057 // This saving of settings is tricky: there is no "old state" for the
5058 // extradata items at all (unlike all other settings), so the old/new
5059 // settings comparison would give a wrong result!
5060 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5061
5062 if (fNeedsGlobalSaveSettings)
5063 {
5064 // save the global settings; for that we should hold only the VirtualBox lock
5065 alock.release();
5066 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5067 mParent->i_saveSettings();
5068 }
5069 }
5070
5071 // fire notification outside the lock
5072 if (fChanged)
5073 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5074
5075 return S_OK;
5076}
5077
5078HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5079{
5080 aProgress = NULL;
5081 NOREF(aSettingsFilePath);
5082 ReturnComNotImplemented();
5083}
5084
5085HRESULT Machine::saveSettings()
5086{
5087 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5088
5089 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5090 if (FAILED(rc)) return rc;
5091
5092 /* the settings file path may never be null */
5093 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5094
5095 /* save all VM data excluding snapshots */
5096 bool fNeedsGlobalSaveSettings = false;
5097 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5098 mlock.release();
5099
5100 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5101 {
5102 // save the global settings; for that we should hold only the VirtualBox lock
5103 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5104 rc = mParent->i_saveSettings();
5105 }
5106
5107 return rc;
5108}
5109
5110
5111HRESULT Machine::discardSettings()
5112{
5113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5114
5115 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5116 if (FAILED(rc)) return rc;
5117
5118 /*
5119 * during this rollback, the session will be notified if data has
5120 * been actually changed
5121 */
5122 i_rollback(true /* aNotify */);
5123
5124 return S_OK;
5125}
5126
5127/** @note Locks objects! */
5128HRESULT Machine::unregister(AutoCaller &autoCaller,
5129 CleanupMode_T aCleanupMode,
5130 std::vector<ComPtr<IMedium> > &aMedia)
5131{
5132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5133
5134 Guid id(i_getId());
5135
5136 if (mData->mSession.mState != SessionState_Unlocked)
5137 return setError(VBOX_E_INVALID_OBJECT_STATE,
5138 tr("Cannot unregister the machine '%s' while it is locked"),
5139 mUserData->s.strName.c_str());
5140
5141 // wait for state dependents to drop to zero
5142 i_ensureNoStateDependencies();
5143
5144 if (!mData->mAccessible)
5145 {
5146 // inaccessible maschines can only be unregistered; uninitialize ourselves
5147 // here because currently there may be no unregistered that are inaccessible
5148 // (this state combination is not supported). Note releasing the caller and
5149 // leaving the lock before calling uninit()
5150 alock.release();
5151 autoCaller.release();
5152
5153 uninit();
5154
5155 mParent->i_unregisterMachine(this, id);
5156 // calls VirtualBox::i_saveSettings()
5157
5158 return S_OK;
5159 }
5160
5161 HRESULT rc = S_OK;
5162
5163 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5164 // discard saved state
5165 if (mData->mMachineState == MachineState_Saved)
5166 {
5167 // add the saved state file to the list of files the caller should delete
5168 Assert(!mSSData->strStateFilePath.isEmpty());
5169 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5170
5171 mSSData->strStateFilePath.setNull();
5172
5173 // unconditionally set the machine state to powered off, we now
5174 // know no session has locked the machine
5175 mData->mMachineState = MachineState_PoweredOff;
5176 }
5177
5178 size_t cSnapshots = 0;
5179 if (mData->mFirstSnapshot)
5180 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5181 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5182 // fail now before we start detaching media
5183 return setError(VBOX_E_INVALID_OBJECT_STATE,
5184 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5185 mUserData->s.strName.c_str(), cSnapshots);
5186
5187 // This list collects the medium objects from all medium attachments
5188 // which we will detach from the machine and its snapshots, in a specific
5189 // order which allows for closing all media without getting "media in use"
5190 // errors, simply by going through the list from the front to the back:
5191 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5192 // and must be closed before the parent media from the snapshots, or closing the parents
5193 // will fail because they still have children);
5194 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5195 // the root ("first") snapshot of the machine.
5196 MediaList llMedia;
5197
5198 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5199 && mMediaData->mAttachments.size()
5200 )
5201 {
5202 // we have media attachments: detach them all and add the Medium objects to our list
5203 if (aCleanupMode != CleanupMode_UnregisterOnly)
5204 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5205 else
5206 return setError(VBOX_E_INVALID_OBJECT_STATE,
5207 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5208 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5209 }
5210
5211 if (cSnapshots)
5212 {
5213 // add the media from the medium attachments of the snapshots to llMedia
5214 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5215 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5216 // into the children first
5217
5218 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5219 MachineState_T oldState = mData->mMachineState;
5220 mData->mMachineState = MachineState_DeletingSnapshot;
5221
5222 // make a copy of the first snapshot so the refcount does not drop to 0
5223 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5224 // because of the AutoCaller voodoo)
5225 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5226
5227 // GO!
5228 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5229
5230 mData->mMachineState = oldState;
5231 }
5232
5233 if (FAILED(rc))
5234 {
5235 i_rollbackMedia();
5236 return rc;
5237 }
5238
5239 // commit all the media changes made above
5240 i_commitMedia();
5241
5242 mData->mRegistered = false;
5243
5244 // machine lock no longer needed
5245 alock.release();
5246
5247 // return media to caller
5248 size_t i = 0;
5249 aMedia.resize(llMedia.size());
5250 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5251 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5252
5253 mParent->i_unregisterMachine(this, id);
5254 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5255
5256 return S_OK;
5257}
5258
5259/**
5260 * Task record for deleting a machine config.
5261 */
5262class Machine::DeleteConfigTask
5263 : public Machine::Task
5264{
5265public:
5266 DeleteConfigTask(Machine *m,
5267 Progress *p,
5268 const Utf8Str &t,
5269 const RTCList<ComPtr<IMedium> > &llMediums,
5270 const StringsList &llFilesToDelete)
5271 : Task(m, p, t),
5272 m_llMediums(llMediums),
5273 m_llFilesToDelete(llFilesToDelete)
5274 {}
5275
5276private:
5277 void handler()
5278 {
5279 try
5280 {
5281 m_pMachine->i_deleteConfigHandler(*this);
5282 }
5283 catch(...)
5284 {
5285 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5286 }
5287 }
5288
5289 RTCList<ComPtr<IMedium> > m_llMediums;
5290 StringsList m_llFilesToDelete;
5291
5292 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5293};
5294
5295/**
5296 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5297 * SessionMachine::taskHandler().
5298 *
5299 * @note Locks this object for writing.
5300 *
5301 * @param task
5302 * @return
5303 */
5304void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5305{
5306 LogFlowThisFuncEnter();
5307
5308 AutoCaller autoCaller(this);
5309 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5310 if (FAILED(autoCaller.rc()))
5311 {
5312 /* we might have been uninitialized because the session was accidentally
5313 * closed by the client, so don't assert */
5314 HRESULT rc = setError(E_FAIL,
5315 tr("The session has been accidentally closed"));
5316 task.m_pProgress->i_notifyComplete(rc);
5317 LogFlowThisFuncLeave();
5318 return;
5319 }
5320
5321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5322
5323 HRESULT rc = S_OK;
5324
5325 try
5326 {
5327 ULONG uLogHistoryCount = 3;
5328 ComPtr<ISystemProperties> systemProperties;
5329 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5330 if (FAILED(rc)) throw rc;
5331
5332 if (!systemProperties.isNull())
5333 {
5334 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5335 if (FAILED(rc)) throw rc;
5336 }
5337
5338 MachineState_T oldState = mData->mMachineState;
5339 i_setMachineState(MachineState_SettingUp);
5340 alock.release();
5341 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5342 {
5343 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5344 {
5345 AutoCaller mac(pMedium);
5346 if (FAILED(mac.rc())) throw mac.rc();
5347 Utf8Str strLocation = pMedium->i_getLocationFull();
5348 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5349 if (FAILED(rc)) throw rc;
5350 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5351 }
5352 if (pMedium->i_isMediumFormatFile())
5353 {
5354 ComPtr<IProgress> pProgress2;
5355 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5356 if (FAILED(rc)) throw rc;
5357 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5358 if (FAILED(rc)) throw rc;
5359 /* Check the result of the asynchronous process. */
5360 LONG iRc;
5361 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5362 if (FAILED(rc)) throw rc;
5363 /* If the thread of the progress object has an error, then
5364 * retrieve the error info from there, or it'll be lost. */
5365 if (FAILED(iRc))
5366 throw setError(ProgressErrorInfo(pProgress2));
5367 }
5368
5369 /* Close the medium, deliberately without checking the return
5370 * code, and without leaving any trace in the error info, as
5371 * a failure here is a very minor issue, which shouldn't happen
5372 * as above we even managed to delete the medium. */
5373 {
5374 ErrorInfoKeeper eik;
5375 pMedium->Close();
5376 }
5377 }
5378 i_setMachineState(oldState);
5379 alock.acquire();
5380
5381 // delete the files pushed on the task list by Machine::Delete()
5382 // (this includes saved states of the machine and snapshots and
5383 // medium storage files from the IMedium list passed in, and the
5384 // machine XML file)
5385 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5386 while (it != task.m_llFilesToDelete.end())
5387 {
5388 const Utf8Str &strFile = *it;
5389 LogFunc(("Deleting file %s\n", strFile.c_str()));
5390 int vrc = RTFileDelete(strFile.c_str());
5391 if (RT_FAILURE(vrc))
5392 throw setError(VBOX_E_IPRT_ERROR,
5393 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5394
5395 ++it;
5396 if (it == task.m_llFilesToDelete.end())
5397 {
5398 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5399 if (FAILED(rc)) throw rc;
5400 break;
5401 }
5402
5403 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5404 if (FAILED(rc)) throw rc;
5405 }
5406
5407 /* delete the settings only when the file actually exists */
5408 if (mData->pMachineConfigFile->fileExists())
5409 {
5410 /* Delete any backup or uncommitted XML files. Ignore failures.
5411 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5412 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5413 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5414 RTFileDelete(otherXml.c_str());
5415 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5416 RTFileDelete(otherXml.c_str());
5417
5418 /* delete the Logs folder, nothing important should be left
5419 * there (we don't check for errors because the user might have
5420 * some private files there that we don't want to delete) */
5421 Utf8Str logFolder;
5422 getLogFolder(logFolder);
5423 Assert(logFolder.length());
5424 if (RTDirExists(logFolder.c_str()))
5425 {
5426 /* Delete all VBox.log[.N] files from the Logs folder
5427 * (this must be in sync with the rotation logic in
5428 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5429 * files that may have been created by the GUI. */
5430 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5431 logFolder.c_str(), RTPATH_DELIMITER);
5432 RTFileDelete(log.c_str());
5433 log = Utf8StrFmt("%s%cVBox.png",
5434 logFolder.c_str(), RTPATH_DELIMITER);
5435 RTFileDelete(log.c_str());
5436 for (int i = uLogHistoryCount; i > 0; i--)
5437 {
5438 log = Utf8StrFmt("%s%cVBox.log.%d",
5439 logFolder.c_str(), RTPATH_DELIMITER, i);
5440 RTFileDelete(log.c_str());
5441 log = Utf8StrFmt("%s%cVBox.png.%d",
5442 logFolder.c_str(), RTPATH_DELIMITER, i);
5443 RTFileDelete(log.c_str());
5444 }
5445#if defined(RT_OS_WINDOWS)
5446 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5447 RTFileDelete(log.c_str());
5448 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5449 RTFileDelete(log.c_str());
5450#endif
5451
5452 RTDirRemove(logFolder.c_str());
5453 }
5454
5455 /* delete the Snapshots folder, nothing important should be left
5456 * there (we don't check for errors because the user might have
5457 * some private files there that we don't want to delete) */
5458 Utf8Str strFullSnapshotFolder;
5459 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5460 Assert(!strFullSnapshotFolder.isEmpty());
5461 if (RTDirExists(strFullSnapshotFolder.c_str()))
5462 RTDirRemove(strFullSnapshotFolder.c_str());
5463
5464 // delete the directory that contains the settings file, but only
5465 // if it matches the VM name
5466 Utf8Str settingsDir;
5467 if (i_isInOwnDir(&settingsDir))
5468 RTDirRemove(settingsDir.c_str());
5469 }
5470
5471 alock.release();
5472
5473 mParent->i_saveModifiedRegistries();
5474 }
5475 catch (HRESULT aRC) { rc = aRC; }
5476
5477 task.m_pProgress->i_notifyComplete(rc);
5478
5479 LogFlowThisFuncLeave();
5480}
5481
5482HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5483{
5484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5485
5486 HRESULT rc = i_checkStateDependency(MutableStateDep);
5487 if (FAILED(rc)) return rc;
5488
5489 if (mData->mRegistered)
5490 return setError(VBOX_E_INVALID_VM_STATE,
5491 tr("Cannot delete settings of a registered machine"));
5492
5493 // collect files to delete
5494 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5495 if (mData->pMachineConfigFile->fileExists())
5496 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5497
5498 RTCList<ComPtr<IMedium> > llMediums;
5499 for (size_t i = 0; i < aMedia.size(); ++i)
5500 {
5501 IMedium *pIMedium(aMedia[i]);
5502 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5503 if (pMedium.isNull())
5504 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5505 SafeArray<BSTR> ids;
5506 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5507 if (FAILED(rc)) return rc;
5508 /* At this point the medium should not have any back references
5509 * anymore. If it has it is attached to another VM and *must* not
5510 * deleted. */
5511 if (ids.size() < 1)
5512 llMediums.append(pMedium);
5513 }
5514
5515 ComObjPtr<Progress> pProgress;
5516 pProgress.createObject();
5517 rc = pProgress->init(i_getVirtualBox(),
5518 static_cast<IMachine*>(this) /* aInitiator */,
5519 Bstr(tr("Deleting files")).raw(),
5520 true /* fCancellable */,
5521 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5522 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5523 if (FAILED(rc))
5524 return rc;
5525
5526 /* create and start the task on a separate thread (note that it will not
5527 * start working until we release alock) */
5528 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5529 rc = pTask->createThread();
5530 if (FAILED(rc))
5531 return rc;
5532
5533 pProgress.queryInterfaceTo(aProgress.asOutParam());
5534
5535 LogFlowFuncLeave();
5536
5537 return S_OK;
5538}
5539
5540HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5541{
5542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5543
5544 ComObjPtr<Snapshot> pSnapshot;
5545 HRESULT rc;
5546
5547 if (aNameOrId.isEmpty())
5548 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5549 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5550 else
5551 {
5552 Guid uuid(aNameOrId);
5553 if (uuid.isValid())
5554 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5555 else
5556 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5557 }
5558 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5559
5560 return rc;
5561}
5562
5563HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5564{
5565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5566
5567 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5568 if (FAILED(rc)) return rc;
5569
5570 ComObjPtr<SharedFolder> sharedFolder;
5571 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5572 if (SUCCEEDED(rc))
5573 return setError(VBOX_E_OBJECT_IN_USE,
5574 tr("Shared folder named '%s' already exists"),
5575 aName.c_str());
5576
5577 sharedFolder.createObject();
5578 rc = sharedFolder->init(i_getMachine(),
5579 aName,
5580 aHostPath,
5581 !!aWritable,
5582 !!aAutomount,
5583 true /* fFailOnError */);
5584 if (FAILED(rc)) return rc;
5585
5586 i_setModified(IsModified_SharedFolders);
5587 mHWData.backup();
5588 mHWData->mSharedFolders.push_back(sharedFolder);
5589
5590 /* inform the direct session if any */
5591 alock.release();
5592 i_onSharedFolderChange();
5593
5594 return S_OK;
5595}
5596
5597HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5598{
5599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5600
5601 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5602 if (FAILED(rc)) return rc;
5603
5604 ComObjPtr<SharedFolder> sharedFolder;
5605 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5606 if (FAILED(rc)) return rc;
5607
5608 i_setModified(IsModified_SharedFolders);
5609 mHWData.backup();
5610 mHWData->mSharedFolders.remove(sharedFolder);
5611
5612 /* inform the direct session if any */
5613 alock.release();
5614 i_onSharedFolderChange();
5615
5616 return S_OK;
5617}
5618
5619HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5620{
5621 /* start with No */
5622 *aCanShow = FALSE;
5623
5624 ComPtr<IInternalSessionControl> directControl;
5625 {
5626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5627
5628 if (mData->mSession.mState != SessionState_Locked)
5629 return setError(VBOX_E_INVALID_VM_STATE,
5630 tr("Machine is not locked for session (session state: %s)"),
5631 Global::stringifySessionState(mData->mSession.mState));
5632
5633 if (mData->mSession.mLockType == LockType_VM)
5634 directControl = mData->mSession.mDirectControl;
5635 }
5636
5637 /* ignore calls made after #OnSessionEnd() is called */
5638 if (!directControl)
5639 return S_OK;
5640
5641 LONG64 dummy;
5642 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5643}
5644
5645HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5646{
5647 ComPtr<IInternalSessionControl> directControl;
5648 {
5649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5650
5651 if (mData->mSession.mState != SessionState_Locked)
5652 return setError(E_FAIL,
5653 tr("Machine is not locked for session (session state: %s)"),
5654 Global::stringifySessionState(mData->mSession.mState));
5655
5656 if (mData->mSession.mLockType == LockType_VM)
5657 directControl = mData->mSession.mDirectControl;
5658 }
5659
5660 /* ignore calls made after #OnSessionEnd() is called */
5661 if (!directControl)
5662 return S_OK;
5663
5664 BOOL dummy;
5665 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5666}
5667
5668#ifdef VBOX_WITH_GUEST_PROPS
5669/**
5670 * Look up a guest property in VBoxSVC's internal structures.
5671 */
5672HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5673 com::Utf8Str &aValue,
5674 LONG64 *aTimestamp,
5675 com::Utf8Str &aFlags) const
5676{
5677 using namespace guestProp;
5678
5679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5680 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5681
5682 if (it != mHWData->mGuestProperties.end())
5683 {
5684 char szFlags[MAX_FLAGS_LEN + 1];
5685 aValue = it->second.strValue;
5686 *aTimestamp = it->second.mTimestamp;
5687 writeFlags(it->second.mFlags, szFlags);
5688 aFlags = Utf8Str(szFlags);
5689 }
5690
5691 return S_OK;
5692}
5693
5694/**
5695 * Query the VM that a guest property belongs to for the property.
5696 * @returns E_ACCESSDENIED if the VM process is not available or not
5697 * currently handling queries and the lookup should then be done in
5698 * VBoxSVC.
5699 */
5700HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5701 com::Utf8Str &aValue,
5702 LONG64 *aTimestamp,
5703 com::Utf8Str &aFlags) const
5704{
5705 HRESULT rc = S_OK;
5706 BSTR bValue = NULL;
5707 BSTR bFlags = NULL;
5708
5709 ComPtr<IInternalSessionControl> directControl;
5710 {
5711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5712 if (mData->mSession.mLockType == LockType_VM)
5713 directControl = mData->mSession.mDirectControl;
5714 }
5715
5716 /* ignore calls made after #OnSessionEnd() is called */
5717 if (!directControl)
5718 rc = E_ACCESSDENIED;
5719 else
5720 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5721 0 /* accessMode */,
5722 &bValue, aTimestamp, &bFlags);
5723
5724 aValue = bValue;
5725 aFlags = bFlags;
5726
5727 return rc;
5728}
5729#endif // VBOX_WITH_GUEST_PROPS
5730
5731HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5732 com::Utf8Str &aValue,
5733 LONG64 *aTimestamp,
5734 com::Utf8Str &aFlags)
5735{
5736#ifndef VBOX_WITH_GUEST_PROPS
5737 ReturnComNotImplemented();
5738#else // VBOX_WITH_GUEST_PROPS
5739
5740 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5741
5742 if (rc == E_ACCESSDENIED)
5743 /* The VM is not running or the service is not (yet) accessible */
5744 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5745 return rc;
5746#endif // VBOX_WITH_GUEST_PROPS
5747}
5748
5749HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5750{
5751 LONG64 dummyTimestamp;
5752 com::Utf8Str dummyFlags;
5753 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5754 return rc;
5755
5756}
5757HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5758{
5759 com::Utf8Str dummyFlags;
5760 com::Utf8Str dummyValue;
5761 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5762 return rc;
5763}
5764
5765#ifdef VBOX_WITH_GUEST_PROPS
5766/**
5767 * Set a guest property in VBoxSVC's internal structures.
5768 */
5769HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5770 const com::Utf8Str &aFlags, bool fDelete)
5771{
5772 using namespace guestProp;
5773
5774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5775 HRESULT rc = S_OK;
5776
5777 rc = i_checkStateDependency(MutableOrSavedStateDep);
5778 if (FAILED(rc)) return rc;
5779
5780 try
5781 {
5782 uint32_t fFlags = NILFLAG;
5783 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5784 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5785
5786 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5787 if (it == mHWData->mGuestProperties.end())
5788 {
5789 if (!fDelete)
5790 {
5791 i_setModified(IsModified_MachineData);
5792 mHWData.backupEx();
5793
5794 RTTIMESPEC time;
5795 HWData::GuestProperty prop;
5796 prop.strValue = Bstr(aValue).raw();
5797 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5798 prop.mFlags = fFlags;
5799 mHWData->mGuestProperties[aName] = prop;
5800 }
5801 }
5802 else
5803 {
5804 if (it->second.mFlags & (RDONLYHOST))
5805 {
5806 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5807 }
5808 else
5809 {
5810 i_setModified(IsModified_MachineData);
5811 mHWData.backupEx();
5812
5813 /* The backupEx() operation invalidates our iterator,
5814 * so get a new one. */
5815 it = mHWData->mGuestProperties.find(aName);
5816 Assert(it != mHWData->mGuestProperties.end());
5817
5818 if (!fDelete)
5819 {
5820 RTTIMESPEC time;
5821 it->second.strValue = aValue;
5822 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5823 it->second.mFlags = fFlags;
5824 }
5825 else
5826 mHWData->mGuestProperties.erase(it);
5827 }
5828 }
5829
5830 if (SUCCEEDED(rc))
5831 {
5832 alock.release();
5833
5834 mParent->i_onGuestPropertyChange(mData->mUuid,
5835 Bstr(aName).raw(),
5836 Bstr(aValue).raw(),
5837 Bstr(aFlags).raw());
5838 }
5839 }
5840 catch (std::bad_alloc &)
5841 {
5842 rc = E_OUTOFMEMORY;
5843 }
5844
5845 return rc;
5846}
5847
5848/**
5849 * Set a property on the VM that that property belongs to.
5850 * @returns E_ACCESSDENIED if the VM process is not available or not
5851 * currently handling queries and the setting should then be done in
5852 * VBoxSVC.
5853 */
5854HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5855 const com::Utf8Str &aFlags, bool fDelete)
5856{
5857 HRESULT rc;
5858
5859 try
5860 {
5861 ComPtr<IInternalSessionControl> directControl;
5862 {
5863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5864 if (mData->mSession.mLockType == LockType_VM)
5865 directControl = mData->mSession.mDirectControl;
5866 }
5867
5868 BSTR dummy = NULL; /* will not be changed (setter) */
5869 LONG64 dummy64;
5870 if (!directControl)
5871 rc = E_ACCESSDENIED;
5872 else
5873 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5874 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5875 fDelete? 2: 1 /* accessMode */,
5876 &dummy, &dummy64, &dummy);
5877 }
5878 catch (std::bad_alloc &)
5879 {
5880 rc = E_OUTOFMEMORY;
5881 }
5882
5883 return rc;
5884}
5885#endif // VBOX_WITH_GUEST_PROPS
5886
5887HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5888 const com::Utf8Str &aFlags)
5889{
5890#ifndef VBOX_WITH_GUEST_PROPS
5891 ReturnComNotImplemented();
5892#else // VBOX_WITH_GUEST_PROPS
5893 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5894 if (rc == E_ACCESSDENIED)
5895 /* The VM is not running or the service is not (yet) accessible */
5896 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5897 return rc;
5898#endif // VBOX_WITH_GUEST_PROPS
5899}
5900
5901HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5902{
5903 return setGuestProperty(aProperty, aValue, "");
5904}
5905
5906HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5907{
5908#ifndef VBOX_WITH_GUEST_PROPS
5909 ReturnComNotImplemented();
5910#else // VBOX_WITH_GUEST_PROPS
5911 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5912 if (rc == E_ACCESSDENIED)
5913 /* The VM is not running or the service is not (yet) accessible */
5914 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5915 return rc;
5916#endif // VBOX_WITH_GUEST_PROPS
5917}
5918
5919#ifdef VBOX_WITH_GUEST_PROPS
5920/**
5921 * Enumerate the guest properties in VBoxSVC's internal structures.
5922 */
5923HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5924 std::vector<com::Utf8Str> &aNames,
5925 std::vector<com::Utf8Str> &aValues,
5926 std::vector<LONG64> &aTimestamps,
5927 std::vector<com::Utf8Str> &aFlags)
5928{
5929 using namespace guestProp;
5930
5931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5932 Utf8Str strPatterns(aPatterns);
5933
5934 HWData::GuestPropertyMap propMap;
5935
5936 /*
5937 * Look for matching patterns and build up a list.
5938 */
5939 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5940 while (it != mHWData->mGuestProperties.end())
5941 {
5942 if ( strPatterns.isEmpty()
5943 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5944 RTSTR_MAX,
5945 it->first.c_str(),
5946 RTSTR_MAX,
5947 NULL)
5948 )
5949 propMap.insert(*it);
5950 ++it;
5951 }
5952
5953 alock.release();
5954
5955 /*
5956 * And build up the arrays for returning the property information.
5957 */
5958 size_t cEntries = propMap.size();
5959
5960 aNames.resize(cEntries);
5961 aValues.resize(cEntries);
5962 aTimestamps.resize(cEntries);
5963 aFlags.resize(cEntries);
5964
5965 char szFlags[MAX_FLAGS_LEN + 1];
5966 size_t i= 0;
5967 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5968 {
5969 aNames[i] = it->first;
5970 aValues[i] = it->second.strValue;
5971 aTimestamps[i] = it->second.mTimestamp;
5972 writeFlags(it->second.mFlags, szFlags);
5973 aFlags[i] = Utf8Str(szFlags);
5974 }
5975
5976 return S_OK;
5977}
5978
5979/**
5980 * Enumerate the properties managed by a VM.
5981 * @returns E_ACCESSDENIED if the VM process is not available or not
5982 * currently handling queries and the setting should then be done in
5983 * VBoxSVC.
5984 */
5985HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5986 std::vector<com::Utf8Str> &aNames,
5987 std::vector<com::Utf8Str> &aValues,
5988 std::vector<LONG64> &aTimestamps,
5989 std::vector<com::Utf8Str> &aFlags)
5990{
5991 HRESULT rc;
5992 ComPtr<IInternalSessionControl> directControl;
5993 {
5994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5995 if (mData->mSession.mLockType == LockType_VM)
5996 directControl = mData->mSession.mDirectControl;
5997 }
5998
5999 com::SafeArray<BSTR> bNames;
6000 com::SafeArray<BSTR> bValues;
6001 com::SafeArray<LONG64> bTimestamps;
6002 com::SafeArray<BSTR> bFlags;
6003
6004 if (!directControl)
6005 rc = E_ACCESSDENIED;
6006 else
6007 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6008 ComSafeArrayAsOutParam(bNames),
6009 ComSafeArrayAsOutParam(bValues),
6010 ComSafeArrayAsOutParam(bTimestamps),
6011 ComSafeArrayAsOutParam(bFlags));
6012 size_t i;
6013 aNames.resize(bNames.size());
6014 for (i = 0; i < bNames.size(); ++i)
6015 aNames[i] = Utf8Str(bNames[i]);
6016 aValues.resize(bValues.size());
6017 for (i = 0; i < bValues.size(); ++i)
6018 aValues[i] = Utf8Str(bValues[i]);
6019 aTimestamps.resize(bTimestamps.size());
6020 for (i = 0; i < bTimestamps.size(); ++i)
6021 aTimestamps[i] = bTimestamps[i];
6022 aFlags.resize(bFlags.size());
6023 for (i = 0; i < bFlags.size(); ++i)
6024 aFlags[i] = Utf8Str(bFlags[i]);
6025
6026 return rc;
6027}
6028#endif // VBOX_WITH_GUEST_PROPS
6029HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6030 std::vector<com::Utf8Str> &aNames,
6031 std::vector<com::Utf8Str> &aValues,
6032 std::vector<LONG64> &aTimestamps,
6033 std::vector<com::Utf8Str> &aFlags)
6034{
6035#ifndef VBOX_WITH_GUEST_PROPS
6036 ReturnComNotImplemented();
6037#else // VBOX_WITH_GUEST_PROPS
6038
6039 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6040
6041 if (rc == E_ACCESSDENIED)
6042 /* The VM is not running or the service is not (yet) accessible */
6043 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6044 return rc;
6045#endif // VBOX_WITH_GUEST_PROPS
6046}
6047
6048HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6049 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6050{
6051 MediaData::AttachmentList atts;
6052
6053 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6054 if (FAILED(rc)) return rc;
6055
6056 size_t i = 0;
6057 aMediumAttachments.resize(atts.size());
6058 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6059 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6060
6061 return S_OK;
6062}
6063
6064HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6065 LONG aControllerPort,
6066 LONG aDevice,
6067 ComPtr<IMediumAttachment> &aAttachment)
6068{
6069 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6070 aName.c_str(), aControllerPort, aDevice));
6071
6072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 aAttachment = NULL;
6075
6076 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6077 Bstr(aName).raw(),
6078 aControllerPort,
6079 aDevice);
6080 if (pAttach.isNull())
6081 return setError(VBOX_E_OBJECT_NOT_FOUND,
6082 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6083 aDevice, aControllerPort, aName.c_str());
6084
6085 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6086
6087 return S_OK;
6088}
6089
6090
6091HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6092 StorageBus_T aConnectionType,
6093 ComPtr<IStorageController> &aController)
6094{
6095 if ( (aConnectionType <= StorageBus_Null)
6096 || (aConnectionType > StorageBus_PCIe))
6097 return setError(E_INVALIDARG,
6098 tr("Invalid connection type: %d"),
6099 aConnectionType);
6100
6101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6102
6103 HRESULT rc = i_checkStateDependency(MutableStateDep);
6104 if (FAILED(rc)) return rc;
6105
6106 /* try to find one with the name first. */
6107 ComObjPtr<StorageController> ctrl;
6108
6109 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6110 if (SUCCEEDED(rc))
6111 return setError(VBOX_E_OBJECT_IN_USE,
6112 tr("Storage controller named '%s' already exists"),
6113 aName.c_str());
6114
6115 ctrl.createObject();
6116
6117 /* get a new instance number for the storage controller */
6118 ULONG ulInstance = 0;
6119 bool fBootable = true;
6120 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6121 it != mStorageControllers->end();
6122 ++it)
6123 {
6124 if ((*it)->i_getStorageBus() == aConnectionType)
6125 {
6126 ULONG ulCurInst = (*it)->i_getInstance();
6127
6128 if (ulCurInst >= ulInstance)
6129 ulInstance = ulCurInst + 1;
6130
6131 /* Only one controller of each type can be marked as bootable. */
6132 if ((*it)->i_getBootable())
6133 fBootable = false;
6134 }
6135 }
6136
6137 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6138 if (FAILED(rc)) return rc;
6139
6140 i_setModified(IsModified_Storage);
6141 mStorageControllers.backup();
6142 mStorageControllers->push_back(ctrl);
6143
6144 ctrl.queryInterfaceTo(aController.asOutParam());
6145
6146 /* inform the direct session if any */
6147 alock.release();
6148 i_onStorageControllerChange();
6149
6150 return S_OK;
6151}
6152
6153HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6154 ComPtr<IStorageController> &aStorageController)
6155{
6156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6157
6158 ComObjPtr<StorageController> ctrl;
6159
6160 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6161 if (SUCCEEDED(rc))
6162 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6163
6164 return rc;
6165}
6166
6167HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6168 ULONG aInstance,
6169 ComPtr<IStorageController> &aStorageController)
6170{
6171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6172
6173 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6174 it != mStorageControllers->end();
6175 ++it)
6176 {
6177 if ( (*it)->i_getStorageBus() == aConnectionType
6178 && (*it)->i_getInstance() == aInstance)
6179 {
6180 (*it).queryInterfaceTo(aStorageController.asOutParam());
6181 return S_OK;
6182 }
6183 }
6184
6185 return setError(VBOX_E_OBJECT_NOT_FOUND,
6186 tr("Could not find a storage controller with instance number '%lu'"),
6187 aInstance);
6188}
6189
6190HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6191{
6192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6193
6194 HRESULT rc = i_checkStateDependency(MutableStateDep);
6195 if (FAILED(rc)) return rc;
6196
6197 ComObjPtr<StorageController> ctrl;
6198
6199 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6200 if (SUCCEEDED(rc))
6201 {
6202 /* Ensure that only one controller of each type is marked as bootable. */
6203 if (aBootable == TRUE)
6204 {
6205 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6206 it != mStorageControllers->end();
6207 ++it)
6208 {
6209 ComObjPtr<StorageController> aCtrl = (*it);
6210
6211 if ( (aCtrl->i_getName() != aName)
6212 && aCtrl->i_getBootable() == TRUE
6213 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6214 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6215 {
6216 aCtrl->i_setBootable(FALSE);
6217 break;
6218 }
6219 }
6220 }
6221
6222 if (SUCCEEDED(rc))
6223 {
6224 ctrl->i_setBootable(aBootable);
6225 i_setModified(IsModified_Storage);
6226 }
6227 }
6228
6229 if (SUCCEEDED(rc))
6230 {
6231 /* inform the direct session if any */
6232 alock.release();
6233 i_onStorageControllerChange();
6234 }
6235
6236 return rc;
6237}
6238
6239HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6240{
6241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6242
6243 HRESULT rc = i_checkStateDependency(MutableStateDep);
6244 if (FAILED(rc)) return rc;
6245
6246 ComObjPtr<StorageController> ctrl;
6247 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6248 if (FAILED(rc)) return rc;
6249
6250 {
6251 /* find all attached devices to the appropriate storage controller and detach them all */
6252 // make a temporary list because detachDevice invalidates iterators into
6253 // mMediaData->mAttachments
6254 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6255
6256 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6257 it != llAttachments2.end();
6258 ++it)
6259 {
6260 MediumAttachment *pAttachTemp = *it;
6261
6262 AutoCaller localAutoCaller(pAttachTemp);
6263 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6264
6265 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6266
6267 if (pAttachTemp->i_getControllerName() == aName)
6268 {
6269 rc = i_detachDevice(pAttachTemp, alock, NULL);
6270 if (FAILED(rc)) return rc;
6271 }
6272 }
6273 }
6274
6275 /* We can remove it now. */
6276 i_setModified(IsModified_Storage);
6277 mStorageControllers.backup();
6278
6279 ctrl->i_unshare();
6280
6281 mStorageControllers->remove(ctrl);
6282
6283 /* inform the direct session if any */
6284 alock.release();
6285 i_onStorageControllerChange();
6286
6287 return S_OK;
6288}
6289
6290HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6291 ComPtr<IUSBController> &aController)
6292{
6293 if ( (aType <= USBControllerType_Null)
6294 || (aType >= USBControllerType_Last))
6295 return setError(E_INVALIDARG,
6296 tr("Invalid USB controller type: %d"),
6297 aType);
6298
6299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6300
6301 HRESULT rc = i_checkStateDependency(MutableStateDep);
6302 if (FAILED(rc)) return rc;
6303
6304 /* try to find one with the same type first. */
6305 ComObjPtr<USBController> ctrl;
6306
6307 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6308 if (SUCCEEDED(rc))
6309 return setError(VBOX_E_OBJECT_IN_USE,
6310 tr("USB controller named '%s' already exists"),
6311 aName.c_str());
6312
6313 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6314 ULONG maxInstances;
6315 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6316 if (FAILED(rc))
6317 return rc;
6318
6319 ULONG cInstances = i_getUSBControllerCountByType(aType);
6320 if (cInstances >= maxInstances)
6321 return setError(E_INVALIDARG,
6322 tr("Too many USB controllers of this type"));
6323
6324 ctrl.createObject();
6325
6326 rc = ctrl->init(this, aName, aType);
6327 if (FAILED(rc)) return rc;
6328
6329 i_setModified(IsModified_USB);
6330 mUSBControllers.backup();
6331 mUSBControllers->push_back(ctrl);
6332
6333 ctrl.queryInterfaceTo(aController.asOutParam());
6334
6335 /* inform the direct session if any */
6336 alock.release();
6337 i_onUSBControllerChange();
6338
6339 return S_OK;
6340}
6341
6342HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6343{
6344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 ComObjPtr<USBController> ctrl;
6347
6348 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6349 if (SUCCEEDED(rc))
6350 ctrl.queryInterfaceTo(aController.asOutParam());
6351
6352 return rc;
6353}
6354
6355HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6356 ULONG *aControllers)
6357{
6358 if ( (aType <= USBControllerType_Null)
6359 || (aType >= USBControllerType_Last))
6360 return setError(E_INVALIDARG,
6361 tr("Invalid USB controller type: %d"),
6362 aType);
6363
6364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6365
6366 ComObjPtr<USBController> ctrl;
6367
6368 *aControllers = i_getUSBControllerCountByType(aType);
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6374{
6375
6376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6377
6378 HRESULT rc = i_checkStateDependency(MutableStateDep);
6379 if (FAILED(rc)) return rc;
6380
6381 ComObjPtr<USBController> ctrl;
6382 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6383 if (FAILED(rc)) return rc;
6384
6385 i_setModified(IsModified_USB);
6386 mUSBControllers.backup();
6387
6388 ctrl->i_unshare();
6389
6390 mUSBControllers->remove(ctrl);
6391
6392 /* inform the direct session if any */
6393 alock.release();
6394 i_onUSBControllerChange();
6395
6396 return S_OK;
6397}
6398
6399HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6400 ULONG *aOriginX,
6401 ULONG *aOriginY,
6402 ULONG *aWidth,
6403 ULONG *aHeight,
6404 BOOL *aEnabled)
6405{
6406 uint32_t u32OriginX= 0;
6407 uint32_t u32OriginY= 0;
6408 uint32_t u32Width = 0;
6409 uint32_t u32Height = 0;
6410 uint16_t u16Flags = 0;
6411
6412 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6413 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6414 if (RT_FAILURE(vrc))
6415 {
6416#ifdef RT_OS_WINDOWS
6417 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6418 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6419 * So just assign fEnable to TRUE again.
6420 * The right fix would be to change GUI API wrappers to make sure that parameters
6421 * are changed only if API succeeds.
6422 */
6423 *aEnabled = TRUE;
6424#endif
6425 return setError(VBOX_E_IPRT_ERROR,
6426 tr("Saved guest size is not available (%Rrc)"),
6427 vrc);
6428 }
6429
6430 *aOriginX = u32OriginX;
6431 *aOriginY = u32OriginY;
6432 *aWidth = u32Width;
6433 *aHeight = u32Height;
6434 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6435
6436 return S_OK;
6437}
6438
6439HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6440 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6441{
6442 if (aScreenId != 0)
6443 return E_NOTIMPL;
6444
6445 if ( aBitmapFormat != BitmapFormat_BGR0
6446 && aBitmapFormat != BitmapFormat_BGRA
6447 && aBitmapFormat != BitmapFormat_RGBA
6448 && aBitmapFormat != BitmapFormat_PNG)
6449 return setError(E_NOTIMPL,
6450 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6451
6452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6453
6454 uint8_t *pu8Data = NULL;
6455 uint32_t cbData = 0;
6456 uint32_t u32Width = 0;
6457 uint32_t u32Height = 0;
6458
6459 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6460
6461 if (RT_FAILURE(vrc))
6462 return setError(VBOX_E_IPRT_ERROR,
6463 tr("Saved thumbnail data is not available (%Rrc)"),
6464 vrc);
6465
6466 HRESULT hr = S_OK;
6467
6468 *aWidth = u32Width;
6469 *aHeight = u32Height;
6470
6471 if (cbData > 0)
6472 {
6473 /* Convert pixels to the format expected by the API caller. */
6474 if (aBitmapFormat == BitmapFormat_BGR0)
6475 {
6476 /* [0] B, [1] G, [2] R, [3] 0. */
6477 aData.resize(cbData);
6478 memcpy(&aData.front(), pu8Data, cbData);
6479 }
6480 else if (aBitmapFormat == BitmapFormat_BGRA)
6481 {
6482 /* [0] B, [1] G, [2] R, [3] A. */
6483 aData.resize(cbData);
6484 for (uint32_t i = 0; i < cbData; i += 4)
6485 {
6486 aData[i] = pu8Data[i];
6487 aData[i + 1] = pu8Data[i + 1];
6488 aData[i + 2] = pu8Data[i + 2];
6489 aData[i + 3] = 0xff;
6490 }
6491 }
6492 else if (aBitmapFormat == BitmapFormat_RGBA)
6493 {
6494 /* [0] R, [1] G, [2] B, [3] A. */
6495 aData.resize(cbData);
6496 for (uint32_t i = 0; i < cbData; i += 4)
6497 {
6498 aData[i] = pu8Data[i + 2];
6499 aData[i + 1] = pu8Data[i + 1];
6500 aData[i + 2] = pu8Data[i];
6501 aData[i + 3] = 0xff;
6502 }
6503 }
6504 else if (aBitmapFormat == BitmapFormat_PNG)
6505 {
6506 uint8_t *pu8PNG = NULL;
6507 uint32_t cbPNG = 0;
6508 uint32_t cxPNG = 0;
6509 uint32_t cyPNG = 0;
6510
6511 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6512
6513 if (RT_SUCCESS(vrc))
6514 {
6515 aData.resize(cbPNG);
6516 if (cbPNG)
6517 memcpy(&aData.front(), pu8PNG, cbPNG);
6518 }
6519 else
6520 hr = setError(VBOX_E_IPRT_ERROR,
6521 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6522 vrc);
6523
6524 RTMemFree(pu8PNG);
6525 }
6526 }
6527
6528 freeSavedDisplayScreenshot(pu8Data);
6529
6530 return hr;
6531}
6532
6533HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6534 ULONG *aWidth,
6535 ULONG *aHeight,
6536 std::vector<BitmapFormat_T> &aBitmapFormats)
6537{
6538 if (aScreenId != 0)
6539 return E_NOTIMPL;
6540
6541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6542
6543 uint8_t *pu8Data = NULL;
6544 uint32_t cbData = 0;
6545 uint32_t u32Width = 0;
6546 uint32_t u32Height = 0;
6547
6548 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6549
6550 if (RT_FAILURE(vrc))
6551 return setError(VBOX_E_IPRT_ERROR,
6552 tr("Saved screenshot data is not available (%Rrc)"),
6553 vrc);
6554
6555 *aWidth = u32Width;
6556 *aHeight = u32Height;
6557 aBitmapFormats.resize(1);
6558 aBitmapFormats[0] = BitmapFormat_PNG;
6559
6560 freeSavedDisplayScreenshot(pu8Data);
6561
6562 return S_OK;
6563}
6564
6565HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6566 BitmapFormat_T aBitmapFormat,
6567 ULONG *aWidth,
6568 ULONG *aHeight,
6569 std::vector<BYTE> &aData)
6570{
6571 if (aScreenId != 0)
6572 return E_NOTIMPL;
6573
6574 if (aBitmapFormat != BitmapFormat_PNG)
6575 return E_NOTIMPL;
6576
6577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 uint8_t *pu8Data = NULL;
6580 uint32_t cbData = 0;
6581 uint32_t u32Width = 0;
6582 uint32_t u32Height = 0;
6583
6584 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6585
6586 if (RT_FAILURE(vrc))
6587 return setError(VBOX_E_IPRT_ERROR,
6588 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6589 vrc);
6590
6591 *aWidth = u32Width;
6592 *aHeight = u32Height;
6593
6594 aData.resize(cbData);
6595 if (cbData)
6596 memcpy(&aData.front(), pu8Data, cbData);
6597
6598 freeSavedDisplayScreenshot(pu8Data);
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::hotPlugCPU(ULONG aCpu)
6604{
6605 HRESULT rc = S_OK;
6606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6607
6608 if (!mHWData->mCPUHotPlugEnabled)
6609 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6610
6611 if (aCpu >= mHWData->mCPUCount)
6612 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6613
6614 if (mHWData->mCPUAttached[aCpu])
6615 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6616
6617 alock.release();
6618 rc = i_onCPUChange(aCpu, false);
6619 alock.acquire();
6620 if (FAILED(rc)) return rc;
6621
6622 i_setModified(IsModified_MachineData);
6623 mHWData.backup();
6624 mHWData->mCPUAttached[aCpu] = true;
6625
6626 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6627 if (Global::IsOnline(mData->mMachineState))
6628 i_saveSettings(NULL);
6629
6630 return S_OK;
6631}
6632
6633HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6634{
6635 HRESULT rc = S_OK;
6636
6637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6638
6639 if (!mHWData->mCPUHotPlugEnabled)
6640 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6641
6642 if (aCpu >= SchemaDefs::MaxCPUCount)
6643 return setError(E_INVALIDARG,
6644 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6645 SchemaDefs::MaxCPUCount);
6646
6647 if (!mHWData->mCPUAttached[aCpu])
6648 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6649
6650 /* CPU 0 can't be detached */
6651 if (aCpu == 0)
6652 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6653
6654 alock.release();
6655 rc = i_onCPUChange(aCpu, true);
6656 alock.acquire();
6657 if (FAILED(rc)) return rc;
6658
6659 i_setModified(IsModified_MachineData);
6660 mHWData.backup();
6661 mHWData->mCPUAttached[aCpu] = false;
6662
6663 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6664 if (Global::IsOnline(mData->mMachineState))
6665 i_saveSettings(NULL);
6666
6667 return S_OK;
6668}
6669
6670HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6671{
6672 *aAttached = false;
6673
6674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 /* If hotplug is enabled the CPU is always enabled. */
6677 if (!mHWData->mCPUHotPlugEnabled)
6678 {
6679 if (aCpu < mHWData->mCPUCount)
6680 *aAttached = true;
6681 }
6682 else
6683 {
6684 if (aCpu < SchemaDefs::MaxCPUCount)
6685 *aAttached = mHWData->mCPUAttached[aCpu];
6686 }
6687
6688 return S_OK;
6689}
6690
6691HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6692{
6693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6694
6695 Utf8Str log = i_getLogFilename(aIdx);
6696 if (!RTFileExists(log.c_str()))
6697 log.setNull();
6698 aFilename = log;
6699
6700 return S_OK;
6701}
6702
6703HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6704{
6705 if (aSize < 0)
6706 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6707
6708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6709
6710 HRESULT rc = S_OK;
6711 Utf8Str log = i_getLogFilename(aIdx);
6712
6713 /* do not unnecessarily hold the lock while doing something which does
6714 * not need the lock and potentially takes a long time. */
6715 alock.release();
6716
6717 /* Limit the chunk size to 32K for now, as that gives better performance
6718 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6719 * One byte expands to approx. 25 bytes of breathtaking XML. */
6720 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6721 aData.resize(cbData);
6722
6723 RTFILE LogFile;
6724 int vrc = RTFileOpen(&LogFile, log.c_str(),
6725 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6726 if (RT_SUCCESS(vrc))
6727 {
6728 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6729 if (RT_SUCCESS(vrc))
6730 aData.resize(cbData);
6731 else
6732 rc = setError(VBOX_E_IPRT_ERROR,
6733 tr("Could not read log file '%s' (%Rrc)"),
6734 log.c_str(), vrc);
6735 RTFileClose(LogFile);
6736 }
6737 else
6738 rc = setError(VBOX_E_IPRT_ERROR,
6739 tr("Could not open log file '%s' (%Rrc)"),
6740 log.c_str(), vrc);
6741
6742 if (FAILED(rc))
6743 aData.resize(0);
6744
6745 return rc;
6746}
6747
6748
6749/**
6750 * Currently this method doesn't attach device to the running VM,
6751 * just makes sure it's plugged on next VM start.
6752 */
6753HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6754{
6755 // lock scope
6756 {
6757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 HRESULT rc = i_checkStateDependency(MutableStateDep);
6760 if (FAILED(rc)) return rc;
6761
6762 ChipsetType_T aChipset = ChipsetType_PIIX3;
6763 COMGETTER(ChipsetType)(&aChipset);
6764
6765 if (aChipset != ChipsetType_ICH9)
6766 {
6767 return setError(E_INVALIDARG,
6768 tr("Host PCI attachment only supported with ICH9 chipset"));
6769 }
6770
6771 // check if device with this host PCI address already attached
6772 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6773 it != mHWData->mPCIDeviceAssignments.end();
6774 ++it)
6775 {
6776 LONG iHostAddress = -1;
6777 ComPtr<PCIDeviceAttachment> pAttach;
6778 pAttach = *it;
6779 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6780 if (iHostAddress == aHostAddress)
6781 return setError(E_INVALIDARG,
6782 tr("Device with host PCI address already attached to this VM"));
6783 }
6784
6785 ComObjPtr<PCIDeviceAttachment> pda;
6786 char name[32];
6787
6788 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6789 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6790 Bstr bname(name);
6791 pda.createObject();
6792 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6793 i_setModified(IsModified_MachineData);
6794 mHWData.backup();
6795 mHWData->mPCIDeviceAssignments.push_back(pda);
6796 }
6797
6798 return S_OK;
6799}
6800
6801/**
6802 * Currently this method doesn't detach device from the running VM,
6803 * just makes sure it's not plugged on next VM start.
6804 */
6805HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6806{
6807 ComObjPtr<PCIDeviceAttachment> pAttach;
6808 bool fRemoved = false;
6809 HRESULT rc;
6810
6811 // lock scope
6812 {
6813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 rc = i_checkStateDependency(MutableStateDep);
6816 if (FAILED(rc)) return rc;
6817
6818 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6819 it != mHWData->mPCIDeviceAssignments.end();
6820 ++it)
6821 {
6822 LONG iHostAddress = -1;
6823 pAttach = *it;
6824 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6825 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6826 {
6827 i_setModified(IsModified_MachineData);
6828 mHWData.backup();
6829 mHWData->mPCIDeviceAssignments.remove(pAttach);
6830 fRemoved = true;
6831 break;
6832 }
6833 }
6834 }
6835
6836
6837 /* Fire event outside of the lock */
6838 if (fRemoved)
6839 {
6840 Assert(!pAttach.isNull());
6841 ComPtr<IEventSource> es;
6842 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6843 Assert(SUCCEEDED(rc));
6844 Bstr mid;
6845 rc = this->COMGETTER(Id)(mid.asOutParam());
6846 Assert(SUCCEEDED(rc));
6847 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6848 }
6849
6850 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6851 tr("No host PCI device %08x attached"),
6852 aHostAddress
6853 );
6854}
6855
6856HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6857{
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6861
6862 size_t i = 0;
6863 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6864 it != mHWData->mPCIDeviceAssignments.end();
6865 ++i, ++it)
6866 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6867
6868 return S_OK;
6869}
6870
6871HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6872{
6873 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6874
6875 return S_OK;
6876}
6877
6878HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6879{
6880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6881
6882 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6883
6884 return S_OK;
6885}
6886
6887HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6888{
6889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6891 if (SUCCEEDED(hrc))
6892 {
6893 hrc = mHWData.backupEx();
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6898 }
6899 }
6900 return hrc;
6901}
6902
6903HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6907 return S_OK;
6908}
6909
6910HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6911{
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6914 if (SUCCEEDED(hrc))
6915 {
6916 hrc = mHWData.backupEx();
6917 if (SUCCEEDED(hrc))
6918 {
6919 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6920 if (SUCCEEDED(hrc))
6921 i_setModified(IsModified_MachineData);
6922 }
6923 }
6924 return hrc;
6925}
6926
6927HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6928{
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930
6931 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6932
6933 return S_OK;
6934}
6935
6936HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6937{
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6940 if (SUCCEEDED(hrc))
6941 {
6942 hrc = mHWData.backupEx();
6943 if (SUCCEEDED(hrc))
6944 {
6945 i_setModified(IsModified_MachineData);
6946 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6947 }
6948 }
6949 return hrc;
6950}
6951
6952HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6953{
6954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6955
6956 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6957
6958 return S_OK;
6959}
6960
6961HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6962{
6963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6966 if ( SUCCEEDED(hrc)
6967 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6968 {
6969 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6970 int vrc;
6971
6972 if (aAutostartEnabled)
6973 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6974 else
6975 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6976
6977 if (RT_SUCCESS(vrc))
6978 {
6979 hrc = mHWData.backupEx();
6980 if (SUCCEEDED(hrc))
6981 {
6982 i_setModified(IsModified_MachineData);
6983 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6984 }
6985 }
6986 else if (vrc == VERR_NOT_SUPPORTED)
6987 hrc = setError(VBOX_E_NOT_SUPPORTED,
6988 tr("The VM autostart feature is not supported on this platform"));
6989 else if (vrc == VERR_PATH_NOT_FOUND)
6990 hrc = setError(E_FAIL,
6991 tr("The path to the autostart database is not set"));
6992 else
6993 hrc = setError(E_UNEXPECTED,
6994 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6995 aAutostartEnabled ? "Adding" : "Removing",
6996 mUserData->s.strName.c_str(), vrc);
6997 }
6998 return hrc;
6999}
7000
7001HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7002{
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004
7005 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7006
7007 return S_OK;
7008}
7009
7010HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7011{
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7014 if (SUCCEEDED(hrc))
7015 {
7016 hrc = mHWData.backupEx();
7017 if (SUCCEEDED(hrc))
7018 {
7019 i_setModified(IsModified_MachineData);
7020 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7021 }
7022 }
7023 return hrc;
7024}
7025
7026HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7027{
7028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7029
7030 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7031
7032 return S_OK;
7033}
7034
7035HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7036{
7037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7039 if ( SUCCEEDED(hrc)
7040 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7041 {
7042 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7043 int vrc;
7044
7045 if (aAutostopType != AutostopType_Disabled)
7046 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7047 else
7048 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7049
7050 if (RT_SUCCESS(vrc))
7051 {
7052 hrc = mHWData.backupEx();
7053 if (SUCCEEDED(hrc))
7054 {
7055 i_setModified(IsModified_MachineData);
7056 mHWData->mAutostart.enmAutostopType = aAutostopType;
7057 }
7058 }
7059 else if (vrc == VERR_NOT_SUPPORTED)
7060 hrc = setError(VBOX_E_NOT_SUPPORTED,
7061 tr("The VM autostop feature is not supported on this platform"));
7062 else if (vrc == VERR_PATH_NOT_FOUND)
7063 hrc = setError(E_FAIL,
7064 tr("The path to the autostart database is not set"));
7065 else
7066 hrc = setError(E_UNEXPECTED,
7067 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7068 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7069 mUserData->s.strName.c_str(), vrc);
7070 }
7071 return hrc;
7072}
7073
7074HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7075{
7076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7077
7078 aDefaultFrontend = mHWData->mDefaultFrontend;
7079
7080 return S_OK;
7081}
7082
7083HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7087 if (SUCCEEDED(hrc))
7088 {
7089 hrc = mHWData.backupEx();
7090 if (SUCCEEDED(hrc))
7091 {
7092 i_setModified(IsModified_MachineData);
7093 mHWData->mDefaultFrontend = aDefaultFrontend;
7094 }
7095 }
7096 return hrc;
7097}
7098
7099HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7100{
7101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7102 size_t cbIcon = mUserData->s.ovIcon.size();
7103 aIcon.resize(cbIcon);
7104 if (cbIcon)
7105 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7106 return S_OK;
7107}
7108
7109HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7110{
7111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7112 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7113 if (SUCCEEDED(hrc))
7114 {
7115 i_setModified(IsModified_MachineData);
7116 mUserData.backup();
7117 size_t cbIcon = aIcon.size();
7118 mUserData->s.ovIcon.resize(cbIcon);
7119 if (cbIcon)
7120 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7121 }
7122 return hrc;
7123}
7124
7125HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7126{
7127#ifdef VBOX_WITH_USB
7128 *aUSBProxyAvailable = true;
7129#else
7130 *aUSBProxyAvailable = false;
7131#endif
7132 return S_OK;
7133}
7134
7135HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7136{
7137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7138
7139 aVMProcessPriority = mUserData->s.strVMPriority;
7140
7141 return S_OK;
7142}
7143
7144HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7145{
7146 RT_NOREF(aVMProcessPriority);
7147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7148 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7149 if (SUCCEEDED(hrc))
7150 {
7151 /** @todo r=klaus: currently this is marked as not implemented, as
7152 * the code for setting the priority of the process is not there
7153 * (neither when starting the VM nor at runtime). */
7154 ReturnComNotImplemented();
7155#if 0
7156 hrc = mUserData.backupEx();
7157 if (SUCCEEDED(hrc))
7158 {
7159 i_setModified(IsModified_MachineData);
7160 mUserData->s.strVMPriority = aVMProcessPriority;
7161 }
7162#endif
7163 }
7164 return hrc;
7165}
7166
7167HRESULT Machine::getUnattended(ComPtr<IUnattended> &aUnattended)
7168{
7169#ifdef VBOX_WITH_UNATTENDED
7170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7171
7172 aUnattended = mUnattended;
7173
7174 return S_OK;
7175#else
7176 NOREF(aUnattended);
7177 return E_NOTIMPL;
7178#endif
7179}
7180
7181HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7182 ComPtr<IProgress> &aProgress)
7183{
7184 ComObjPtr<Progress> pP;
7185 Progress *ppP = pP;
7186 IProgress *iP = static_cast<IProgress *>(ppP);
7187 IProgress **pProgress = &iP;
7188
7189 IMachine *pTarget = aTarget;
7190
7191 /* Convert the options. */
7192 RTCList<CloneOptions_T> optList;
7193 if (aOptions.size())
7194 for (size_t i = 0; i < aOptions.size(); ++i)
7195 optList.append(aOptions[i]);
7196
7197 if (optList.contains(CloneOptions_Link))
7198 {
7199 if (!i_isSnapshotMachine())
7200 return setError(E_INVALIDARG,
7201 tr("Linked clone can only be created from a snapshot"));
7202 if (aMode != CloneMode_MachineState)
7203 return setError(E_INVALIDARG,
7204 tr("Linked clone can only be created for a single machine state"));
7205 }
7206 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7207
7208 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7209
7210 HRESULT rc = pWorker->start(pProgress);
7211
7212 pP = static_cast<Progress *>(*pProgress);
7213 pP.queryInterfaceTo(aProgress.asOutParam());
7214
7215 return rc;
7216
7217}
7218
7219HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7220{
7221 NOREF(aProgress);
7222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 // This check should always fail.
7225 HRESULT rc = i_checkStateDependency(MutableStateDep);
7226 if (FAILED(rc)) return rc;
7227
7228 AssertFailedReturn(E_NOTIMPL);
7229}
7230
7231HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7232{
7233 NOREF(aSavedStateFile);
7234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7235
7236 // This check should always fail.
7237 HRESULT rc = i_checkStateDependency(MutableStateDep);
7238 if (FAILED(rc)) return rc;
7239
7240 AssertFailedReturn(E_NOTIMPL);
7241}
7242
7243HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7244{
7245 NOREF(aFRemoveFile);
7246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7247
7248 // This check should always fail.
7249 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7250 if (FAILED(rc)) return rc;
7251
7252 AssertFailedReturn(E_NOTIMPL);
7253}
7254
7255// public methods for internal purposes
7256/////////////////////////////////////////////////////////////////////////////
7257
7258/**
7259 * Adds the given IsModified_* flag to the dirty flags of the machine.
7260 * This must be called either during i_loadSettings or under the machine write lock.
7261 * @param fl Flag
7262 * @param fAllowStateModification If state modifications are allowed.
7263 */
7264void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7265{
7266 mData->flModifications |= fl;
7267 if (fAllowStateModification && i_isStateModificationAllowed())
7268 mData->mCurrentStateModified = true;
7269}
7270
7271/**
7272 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7273 * care of the write locking.
7274 *
7275 * @param fModification The flag to add.
7276 * @param fAllowStateModification If state modifications are allowed.
7277 */
7278void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7279{
7280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7281 i_setModified(fModification, fAllowStateModification);
7282}
7283
7284/**
7285 * Saves the registry entry of this machine to the given configuration node.
7286 *
7287 * @param data Machine registry data.
7288 *
7289 * @note locks this object for reading.
7290 */
7291HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7292{
7293 AutoLimitedCaller autoCaller(this);
7294 AssertComRCReturnRC(autoCaller.rc());
7295
7296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7297
7298 data.uuid = mData->mUuid;
7299 data.strSettingsFile = mData->m_strConfigFile;
7300
7301 return S_OK;
7302}
7303
7304/**
7305 * Calculates the absolute path of the given path taking the directory of the
7306 * machine settings file as the current directory.
7307 *
7308 * @param strPath Path to calculate the absolute path for.
7309 * @param aResult Where to put the result (used only on success, can be the
7310 * same Utf8Str instance as passed in @a aPath).
7311 * @return IPRT result.
7312 *
7313 * @note Locks this object for reading.
7314 */
7315int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7316{
7317 AutoCaller autoCaller(this);
7318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7319
7320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7321
7322 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7323
7324 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7325
7326 strSettingsDir.stripFilename();
7327 char folder[RTPATH_MAX];
7328 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7329 if (RT_SUCCESS(vrc))
7330 aResult = folder;
7331
7332 return vrc;
7333}
7334
7335/**
7336 * Copies strSource to strTarget, making it relative to the machine folder
7337 * if it is a subdirectory thereof, or simply copying it otherwise.
7338 *
7339 * @param strSource Path to evaluate and copy.
7340 * @param strTarget Buffer to receive target path.
7341 *
7342 * @note Locks this object for reading.
7343 */
7344void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7345 Utf8Str &strTarget)
7346{
7347 AutoCaller autoCaller(this);
7348 AssertComRCReturn(autoCaller.rc(), (void)0);
7349
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351
7352 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7353 // use strTarget as a temporary buffer to hold the machine settings dir
7354 strTarget = mData->m_strConfigFileFull;
7355 strTarget.stripFilename();
7356 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7357 {
7358 // is relative: then append what's left
7359 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7360 // for empty paths (only possible for subdirs) use "." to avoid
7361 // triggering default settings for not present config attributes.
7362 if (strTarget.isEmpty())
7363 strTarget = ".";
7364 }
7365 else
7366 // is not relative: then overwrite
7367 strTarget = strSource;
7368}
7369
7370/**
7371 * Returns the full path to the machine's log folder in the
7372 * \a aLogFolder argument.
7373 */
7374void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7375{
7376 AutoCaller autoCaller(this);
7377 AssertComRCReturnVoid(autoCaller.rc());
7378
7379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 char szTmp[RTPATH_MAX];
7382 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7383 if (RT_SUCCESS(vrc))
7384 {
7385 if (szTmp[0] && !mUserData.isNull())
7386 {
7387 char szTmp2[RTPATH_MAX];
7388 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7389 if (RT_SUCCESS(vrc))
7390 aLogFolder = BstrFmt("%s%c%s",
7391 szTmp2,
7392 RTPATH_DELIMITER,
7393 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7394 }
7395 else
7396 vrc = VERR_PATH_IS_RELATIVE;
7397 }
7398
7399 if (RT_FAILURE(vrc))
7400 {
7401 // fallback if VBOX_USER_LOGHOME is not set or invalid
7402 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7403 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7404 aLogFolder.append(RTPATH_DELIMITER);
7405 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7406 }
7407}
7408
7409/**
7410 * Returns the full path to the machine's log file for an given index.
7411 */
7412Utf8Str Machine::i_getLogFilename(ULONG idx)
7413{
7414 Utf8Str logFolder;
7415 getLogFolder(logFolder);
7416 Assert(logFolder.length());
7417
7418 Utf8Str log;
7419 if (idx == 0)
7420 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7421#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7422 else if (idx == 1)
7423 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7424 else
7425 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7426#else
7427 else
7428 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7429#endif
7430 return log;
7431}
7432
7433/**
7434 * Returns the full path to the machine's hardened log file.
7435 */
7436Utf8Str Machine::i_getHardeningLogFilename(void)
7437{
7438 Utf8Str strFilename;
7439 getLogFolder(strFilename);
7440 Assert(strFilename.length());
7441 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7442 return strFilename;
7443}
7444
7445
7446/**
7447 * Composes a unique saved state filename based on the current system time. The filename is
7448 * granular to the second so this will work so long as no more than one snapshot is taken on
7449 * a machine per second.
7450 *
7451 * Before version 4.1, we used this formula for saved state files:
7452 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7453 * which no longer works because saved state files can now be shared between the saved state of the
7454 * "saved" machine and an online snapshot, and the following would cause problems:
7455 * 1) save machine
7456 * 2) create online snapshot from that machine state --> reusing saved state file
7457 * 3) save machine again --> filename would be reused, breaking the online snapshot
7458 *
7459 * So instead we now use a timestamp.
7460 *
7461 * @param strStateFilePath
7462 */
7463
7464void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7465{
7466 AutoCaller autoCaller(this);
7467 AssertComRCReturnVoid(autoCaller.rc());
7468
7469 {
7470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7471 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7472 }
7473
7474 RTTIMESPEC ts;
7475 RTTimeNow(&ts);
7476 RTTIME time;
7477 RTTimeExplode(&time, &ts);
7478
7479 strStateFilePath += RTPATH_DELIMITER;
7480 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7481 time.i32Year, time.u8Month, time.u8MonthDay,
7482 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7483}
7484
7485/**
7486 * Returns the full path to the default video capture file.
7487 */
7488void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7489{
7490 AutoCaller autoCaller(this);
7491 AssertComRCReturnVoid(autoCaller.rc());
7492
7493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7494
7495 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7496 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7497 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7498}
7499
7500/**
7501 * Returns whether at least one USB controller is present for the VM.
7502 */
7503bool Machine::i_isUSBControllerPresent()
7504{
7505 AutoCaller autoCaller(this);
7506 AssertComRCReturn(autoCaller.rc(), false);
7507
7508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7509
7510 return (mUSBControllers->size() > 0);
7511}
7512
7513/**
7514 * @note Locks this object for writing, calls the client process
7515 * (inside the lock).
7516 */
7517HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7518 const Utf8Str &strFrontend,
7519 const Utf8Str &strEnvironment,
7520 ProgressProxy *aProgress)
7521{
7522 LogFlowThisFuncEnter();
7523
7524 AssertReturn(aControl, E_FAIL);
7525 AssertReturn(aProgress, E_FAIL);
7526 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7527
7528 AutoCaller autoCaller(this);
7529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7530
7531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7532
7533 if (!mData->mRegistered)
7534 return setError(E_UNEXPECTED,
7535 tr("The machine '%s' is not registered"),
7536 mUserData->s.strName.c_str());
7537
7538 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7539
7540 /* The process started when launching a VM with separate UI/VM processes is always
7541 * the UI process, i.e. needs special handling as it won't claim the session. */
7542 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7543
7544 if (fSeparate)
7545 {
7546 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7547 return setError(VBOX_E_INVALID_OBJECT_STATE,
7548 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7549 mUserData->s.strName.c_str());
7550 }
7551 else
7552 {
7553 if ( mData->mSession.mState == SessionState_Locked
7554 || mData->mSession.mState == SessionState_Spawning
7555 || mData->mSession.mState == SessionState_Unlocking)
7556 return setError(VBOX_E_INVALID_OBJECT_STATE,
7557 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7558 mUserData->s.strName.c_str());
7559
7560 /* may not be busy */
7561 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7562 }
7563
7564 /* get the path to the executable */
7565 char szPath[RTPATH_MAX];
7566 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7567 size_t cchBufLeft = strlen(szPath);
7568 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7569 szPath[cchBufLeft] = 0;
7570 char *pszNamePart = szPath + cchBufLeft;
7571 cchBufLeft = sizeof(szPath) - cchBufLeft;
7572
7573 int vrc = VINF_SUCCESS;
7574 RTPROCESS pid = NIL_RTPROCESS;
7575
7576 RTENV env = RTENV_DEFAULT;
7577
7578 if (!strEnvironment.isEmpty())
7579 {
7580 char *newEnvStr = NULL;
7581
7582 do
7583 {
7584 /* clone the current environment */
7585 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7586 AssertRCBreakStmt(vrc2, vrc = vrc2);
7587
7588 newEnvStr = RTStrDup(strEnvironment.c_str());
7589 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7590
7591 /* put new variables to the environment
7592 * (ignore empty variable names here since RTEnv API
7593 * intentionally doesn't do that) */
7594 char *var = newEnvStr;
7595 for (char *p = newEnvStr; *p; ++p)
7596 {
7597 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7598 {
7599 *p = '\0';
7600 if (*var)
7601 {
7602 char *val = strchr(var, '=');
7603 if (val)
7604 {
7605 *val++ = '\0';
7606 vrc2 = RTEnvSetEx(env, var, val);
7607 }
7608 else
7609 vrc2 = RTEnvUnsetEx(env, var);
7610 if (RT_FAILURE(vrc2))
7611 break;
7612 }
7613 var = p + 1;
7614 }
7615 }
7616 if (RT_SUCCESS(vrc2) && *var)
7617 vrc2 = RTEnvPutEx(env, var);
7618
7619 AssertRCBreakStmt(vrc2, vrc = vrc2);
7620 }
7621 while (0);
7622
7623 if (newEnvStr != NULL)
7624 RTStrFree(newEnvStr);
7625 }
7626
7627 /* Hardening logging */
7628#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7629 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7630 {
7631 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7632 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7633 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7634 {
7635 Utf8Str strStartupLogDir = strHardeningLogFile;
7636 strStartupLogDir.stripFilename();
7637 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7638 file without stripping the file. */
7639 }
7640 strSupHardeningLogArg.append(strHardeningLogFile);
7641
7642 /* Remove legacy log filename to avoid confusion. */
7643 Utf8Str strOldStartupLogFile;
7644 getLogFolder(strOldStartupLogFile);
7645 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7646 RTFileDelete(strOldStartupLogFile.c_str());
7647 }
7648 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7649#else
7650 const char *pszSupHardeningLogArg = NULL;
7651#endif
7652
7653 Utf8Str strCanonicalName;
7654
7655#ifdef VBOX_WITH_QTGUI
7656 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7657 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7658 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7659 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7660 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7661 {
7662 strCanonicalName = "GUI/Qt";
7663# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7664 /* Modify the base path so that we don't need to use ".." below. */
7665 RTPathStripTrailingSlash(szPath);
7666 RTPathStripFilename(szPath);
7667 cchBufLeft = strlen(szPath);
7668 pszNamePart = szPath + cchBufLeft;
7669 cchBufLeft = sizeof(szPath) - cchBufLeft;
7670
7671# define OSX_APP_NAME "VirtualBoxVM"
7672# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7673
7674 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7675 if ( strAppOverride.contains(".")
7676 || strAppOverride.contains("/")
7677 || strAppOverride.contains("\\")
7678 || strAppOverride.contains(":"))
7679 strAppOverride.setNull();
7680 Utf8Str strAppPath;
7681 if (!strAppOverride.isEmpty())
7682 {
7683 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7684 Utf8Str strFullPath(szPath);
7685 strFullPath.append(strAppPath);
7686 /* there is a race, but people using this deserve the failure */
7687 if (!RTFileExists(strFullPath.c_str()))
7688 strAppOverride.setNull();
7689 }
7690 if (strAppOverride.isEmpty())
7691 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7692 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7693 strcpy(pszNamePart, strAppPath.c_str());
7694# else
7695 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7696 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7697 strcpy(pszNamePart, s_szVirtualBox_exe);
7698# endif
7699
7700 Utf8Str idStr = mData->mUuid.toString();
7701 const char *apszArgs[] =
7702 {
7703 szPath,
7704 "--comment", mUserData->s.strName.c_str(),
7705 "--startvm", idStr.c_str(),
7706 "--no-startvm-errormsgbox",
7707 NULL, /* For "--separate". */
7708 NULL, /* For "--sup-startup-log". */
7709 NULL
7710 };
7711 unsigned iArg = 6;
7712 if (fSeparate)
7713 apszArgs[iArg++] = "--separate";
7714 apszArgs[iArg++] = pszSupHardeningLogArg;
7715
7716 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7717 }
7718#else /* !VBOX_WITH_QTGUI */
7719 if (0)
7720 ;
7721#endif /* VBOX_WITH_QTGUI */
7722
7723 else
7724
7725#ifdef VBOX_WITH_VBOXSDL
7726 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7727 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7728 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7729 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7730 {
7731 strCanonicalName = "GUI/SDL";
7732 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7733 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7734 strcpy(pszNamePart, s_szVBoxSDL_exe);
7735
7736 Utf8Str idStr = mData->mUuid.toString();
7737 const char *apszArgs[] =
7738 {
7739 szPath,
7740 "--comment", mUserData->s.strName.c_str(),
7741 "--startvm", idStr.c_str(),
7742 NULL, /* For "--separate". */
7743 NULL, /* For "--sup-startup-log". */
7744 NULL
7745 };
7746 unsigned iArg = 5;
7747 if (fSeparate)
7748 apszArgs[iArg++] = "--separate";
7749 apszArgs[iArg++] = pszSupHardeningLogArg;
7750
7751 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7752 }
7753#else /* !VBOX_WITH_VBOXSDL */
7754 if (0)
7755 ;
7756#endif /* !VBOX_WITH_VBOXSDL */
7757
7758 else
7759
7760#ifdef VBOX_WITH_HEADLESS
7761 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7762 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7763 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7764 )
7765 {
7766 strCanonicalName = "headless";
7767 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7768 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7769 * and a VM works even if the server has not been installed.
7770 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7771 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7772 * differently in 4.0 and 3.x.
7773 */
7774 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7775 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7776 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7777
7778 Utf8Str idStr = mData->mUuid.toString();
7779 const char *apszArgs[] =
7780 {
7781 szPath,
7782 "--comment", mUserData->s.strName.c_str(),
7783 "--startvm", idStr.c_str(),
7784 "--vrde", "config",
7785 NULL, /* For "--capture". */
7786 NULL, /* For "--sup-startup-log". */
7787 NULL
7788 };
7789 unsigned iArg = 7;
7790 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7791 apszArgs[iArg++] = "--capture";
7792 apszArgs[iArg++] = pszSupHardeningLogArg;
7793
7794# ifdef RT_OS_WINDOWS
7795 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7796# else
7797 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7798# endif
7799 }
7800#else /* !VBOX_WITH_HEADLESS */
7801 if (0)
7802 ;
7803#endif /* !VBOX_WITH_HEADLESS */
7804 else
7805 {
7806 RTEnvDestroy(env);
7807 return setError(E_INVALIDARG,
7808 tr("Invalid frontend name: '%s'"),
7809 strFrontend.c_str());
7810 }
7811
7812 RTEnvDestroy(env);
7813
7814 if (RT_FAILURE(vrc))
7815 return setError(VBOX_E_IPRT_ERROR,
7816 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7817 mUserData->s.strName.c_str(), vrc);
7818
7819 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7820
7821 if (!fSeparate)
7822 {
7823 /*
7824 * Note that we don't release the lock here before calling the client,
7825 * because it doesn't need to call us back if called with a NULL argument.
7826 * Releasing the lock here is dangerous because we didn't prepare the
7827 * launch data yet, but the client we've just started may happen to be
7828 * too fast and call LockMachine() that will fail (because of PID, etc.),
7829 * so that the Machine will never get out of the Spawning session state.
7830 */
7831
7832 /* inform the session that it will be a remote one */
7833 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7834#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7835 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7836#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7837 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7838#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7839 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7840
7841 if (FAILED(rc))
7842 {
7843 /* restore the session state */
7844 mData->mSession.mState = SessionState_Unlocked;
7845 alock.release();
7846 mParent->i_addProcessToReap(pid);
7847 /* The failure may occur w/o any error info (from RPC), so provide one */
7848 return setError(VBOX_E_VM_ERROR,
7849 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7850 }
7851
7852 /* attach launch data to the machine */
7853 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7854 mData->mSession.mRemoteControls.push_back(aControl);
7855 mData->mSession.mProgress = aProgress;
7856 mData->mSession.mPID = pid;
7857 mData->mSession.mState = SessionState_Spawning;
7858 Assert(strCanonicalName.isNotEmpty());
7859 mData->mSession.mName = strCanonicalName;
7860 }
7861 else
7862 {
7863 /* For separate UI process we declare the launch as completed instantly, as the
7864 * actual headless VM start may or may not come. No point in remembering anything
7865 * yet, as what matters for us is when the headless VM gets started. */
7866 aProgress->i_notifyComplete(S_OK);
7867 }
7868
7869 alock.release();
7870 mParent->i_addProcessToReap(pid);
7871
7872 LogFlowThisFuncLeave();
7873 return S_OK;
7874}
7875
7876/**
7877 * Returns @c true if the given session machine instance has an open direct
7878 * session (and optionally also for direct sessions which are closing) and
7879 * returns the session control machine instance if so.
7880 *
7881 * Note that when the method returns @c false, the arguments remain unchanged.
7882 *
7883 * @param aMachine Session machine object.
7884 * @param aControl Direct session control object (optional).
7885 * @param aRequireVM If true then only allow VM sessions.
7886 * @param aAllowClosing If true then additionally a session which is currently
7887 * being closed will also be allowed.
7888 *
7889 * @note locks this object for reading.
7890 */
7891bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7892 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7893 bool aRequireVM /*= false*/,
7894 bool aAllowClosing /*= false*/)
7895{
7896 AutoLimitedCaller autoCaller(this);
7897 AssertComRCReturn(autoCaller.rc(), false);
7898
7899 /* just return false for inaccessible machines */
7900 if (getObjectState().getState() != ObjectState::Ready)
7901 return false;
7902
7903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7904
7905 if ( ( mData->mSession.mState == SessionState_Locked
7906 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7907 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7908 )
7909 {
7910 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7911
7912 aMachine = mData->mSession.mMachine;
7913
7914 if (aControl != NULL)
7915 *aControl = mData->mSession.mDirectControl;
7916
7917 return true;
7918 }
7919
7920 return false;
7921}
7922
7923/**
7924 * Returns @c true if the given machine has an spawning direct session.
7925 *
7926 * @note locks this object for reading.
7927 */
7928bool Machine::i_isSessionSpawning()
7929{
7930 AutoLimitedCaller autoCaller(this);
7931 AssertComRCReturn(autoCaller.rc(), false);
7932
7933 /* just return false for inaccessible machines */
7934 if (getObjectState().getState() != ObjectState::Ready)
7935 return false;
7936
7937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7938
7939 if (mData->mSession.mState == SessionState_Spawning)
7940 return true;
7941
7942 return false;
7943}
7944
7945/**
7946 * Called from the client watcher thread to check for unexpected client process
7947 * death during Session_Spawning state (e.g. before it successfully opened a
7948 * direct session).
7949 *
7950 * On Win32 and on OS/2, this method is called only when we've got the
7951 * direct client's process termination notification, so it always returns @c
7952 * true.
7953 *
7954 * On other platforms, this method returns @c true if the client process is
7955 * terminated and @c false if it's still alive.
7956 *
7957 * @note Locks this object for writing.
7958 */
7959bool Machine::i_checkForSpawnFailure()
7960{
7961 AutoCaller autoCaller(this);
7962 if (!autoCaller.isOk())
7963 {
7964 /* nothing to do */
7965 LogFlowThisFunc(("Already uninitialized!\n"));
7966 return true;
7967 }
7968
7969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7970
7971 if (mData->mSession.mState != SessionState_Spawning)
7972 {
7973 /* nothing to do */
7974 LogFlowThisFunc(("Not spawning any more!\n"));
7975 return true;
7976 }
7977
7978 HRESULT rc = S_OK;
7979
7980 /* PID not yet initialized, skip check. */
7981 if (mData->mSession.mPID == NIL_RTPROCESS)
7982 return false;
7983
7984 RTPROCSTATUS status;
7985 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7986
7987 if (vrc != VERR_PROCESS_RUNNING)
7988 {
7989 Utf8Str strExtraInfo;
7990
7991#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7992 /* If the startup logfile exists and is of non-zero length, tell the
7993 user to look there for more details to encourage them to attach it
7994 when reporting startup issues. */
7995 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7996 uint64_t cbStartupLogFile = 0;
7997 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7998 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7999 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8000#endif
8001
8002 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8003 rc = setError(E_FAIL,
8004 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8005 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8006 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8007 rc = setError(E_FAIL,
8008 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8009 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8010 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8011 rc = setError(E_FAIL,
8012 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8013 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8014 else
8015 rc = setError(E_FAIL,
8016 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8017 i_getName().c_str(), vrc, strExtraInfo.c_str());
8018 }
8019
8020 if (FAILED(rc))
8021 {
8022 /* Close the remote session, remove the remote control from the list
8023 * and reset session state to Closed (@note keep the code in sync with
8024 * the relevant part in LockMachine()). */
8025
8026 Assert(mData->mSession.mRemoteControls.size() == 1);
8027 if (mData->mSession.mRemoteControls.size() == 1)
8028 {
8029 ErrorInfoKeeper eik;
8030 mData->mSession.mRemoteControls.front()->Uninitialize();
8031 }
8032
8033 mData->mSession.mRemoteControls.clear();
8034 mData->mSession.mState = SessionState_Unlocked;
8035
8036 /* finalize the progress after setting the state */
8037 if (!mData->mSession.mProgress.isNull())
8038 {
8039 mData->mSession.mProgress->notifyComplete(rc);
8040 mData->mSession.mProgress.setNull();
8041 }
8042
8043 mData->mSession.mPID = NIL_RTPROCESS;
8044
8045 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8046 return true;
8047 }
8048
8049 return false;
8050}
8051
8052/**
8053 * Checks whether the machine can be registered. If so, commits and saves
8054 * all settings.
8055 *
8056 * @note Must be called from mParent's write lock. Locks this object and
8057 * children for writing.
8058 */
8059HRESULT Machine::i_prepareRegister()
8060{
8061 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8062
8063 AutoLimitedCaller autoCaller(this);
8064 AssertComRCReturnRC(autoCaller.rc());
8065
8066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8067
8068 /* wait for state dependents to drop to zero */
8069 i_ensureNoStateDependencies();
8070
8071 if (!mData->mAccessible)
8072 return setError(VBOX_E_INVALID_OBJECT_STATE,
8073 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8074 mUserData->s.strName.c_str(),
8075 mData->mUuid.toString().c_str());
8076
8077 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8078
8079 if (mData->mRegistered)
8080 return setError(VBOX_E_INVALID_OBJECT_STATE,
8081 tr("The machine '%s' with UUID {%s} is already registered"),
8082 mUserData->s.strName.c_str(),
8083 mData->mUuid.toString().c_str());
8084
8085 HRESULT rc = S_OK;
8086
8087 // Ensure the settings are saved. If we are going to be registered and
8088 // no config file exists yet, create it by calling i_saveSettings() too.
8089 if ( (mData->flModifications)
8090 || (!mData->pMachineConfigFile->fileExists())
8091 )
8092 {
8093 rc = i_saveSettings(NULL);
8094 // no need to check whether VirtualBox.xml needs saving too since
8095 // we can't have a machine XML file rename pending
8096 if (FAILED(rc)) return rc;
8097 }
8098
8099 /* more config checking goes here */
8100
8101 if (SUCCEEDED(rc))
8102 {
8103 /* we may have had implicit modifications we want to fix on success */
8104 i_commit();
8105
8106 mData->mRegistered = true;
8107 }
8108 else
8109 {
8110 /* we may have had implicit modifications we want to cancel on failure*/
8111 i_rollback(false /* aNotify */);
8112 }
8113
8114 return rc;
8115}
8116
8117/**
8118 * Increases the number of objects dependent on the machine state or on the
8119 * registered state. Guarantees that these two states will not change at least
8120 * until #i_releaseStateDependency() is called.
8121 *
8122 * Depending on the @a aDepType value, additional state checks may be made.
8123 * These checks will set extended error info on failure. See
8124 * #i_checkStateDependency() for more info.
8125 *
8126 * If this method returns a failure, the dependency is not added and the caller
8127 * is not allowed to rely on any particular machine state or registration state
8128 * value and may return the failed result code to the upper level.
8129 *
8130 * @param aDepType Dependency type to add.
8131 * @param aState Current machine state (NULL if not interested).
8132 * @param aRegistered Current registered state (NULL if not interested).
8133 *
8134 * @note Locks this object for writing.
8135 */
8136HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8137 MachineState_T *aState /* = NULL */,
8138 BOOL *aRegistered /* = NULL */)
8139{
8140 AutoCaller autoCaller(this);
8141 AssertComRCReturnRC(autoCaller.rc());
8142
8143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8144
8145 HRESULT rc = i_checkStateDependency(aDepType);
8146 if (FAILED(rc)) return rc;
8147
8148 {
8149 if (mData->mMachineStateChangePending != 0)
8150 {
8151 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8152 * drop to zero so don't add more. It may make sense to wait a bit
8153 * and retry before reporting an error (since the pending state
8154 * transition should be really quick) but let's just assert for
8155 * now to see if it ever happens on practice. */
8156
8157 AssertFailed();
8158
8159 return setError(E_ACCESSDENIED,
8160 tr("Machine state change is in progress. Please retry the operation later."));
8161 }
8162
8163 ++mData->mMachineStateDeps;
8164 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8165 }
8166
8167 if (aState)
8168 *aState = mData->mMachineState;
8169 if (aRegistered)
8170 *aRegistered = mData->mRegistered;
8171
8172 return S_OK;
8173}
8174
8175/**
8176 * Decreases the number of objects dependent on the machine state.
8177 * Must always complete the #i_addStateDependency() call after the state
8178 * dependency is no more necessary.
8179 */
8180void Machine::i_releaseStateDependency()
8181{
8182 AutoCaller autoCaller(this);
8183 AssertComRCReturnVoid(autoCaller.rc());
8184
8185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8186
8187 /* releaseStateDependency() w/o addStateDependency()? */
8188 AssertReturnVoid(mData->mMachineStateDeps != 0);
8189 -- mData->mMachineStateDeps;
8190
8191 if (mData->mMachineStateDeps == 0)
8192 {
8193 /* inform i_ensureNoStateDependencies() that there are no more deps */
8194 if (mData->mMachineStateChangePending != 0)
8195 {
8196 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8197 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8198 }
8199 }
8200}
8201
8202Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8203{
8204 /* start with nothing found */
8205 Utf8Str strResult("");
8206
8207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8208
8209 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8210 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8211 // found:
8212 strResult = it->second; // source is a Utf8Str
8213
8214 return strResult;
8215}
8216
8217// protected methods
8218/////////////////////////////////////////////////////////////////////////////
8219
8220/**
8221 * Performs machine state checks based on the @a aDepType value. If a check
8222 * fails, this method will set extended error info, otherwise it will return
8223 * S_OK. It is supposed, that on failure, the caller will immediately return
8224 * the return value of this method to the upper level.
8225 *
8226 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8227 *
8228 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8229 * current state of this machine object allows to change settings of the
8230 * machine (i.e. the machine is not registered, or registered but not running
8231 * and not saved). It is useful to call this method from Machine setters
8232 * before performing any change.
8233 *
8234 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8235 * as for MutableStateDep except that if the machine is saved, S_OK is also
8236 * returned. This is useful in setters which allow changing machine
8237 * properties when it is in the saved state.
8238 *
8239 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8240 * if the current state of this machine object allows to change runtime
8241 * changeable settings of the machine (i.e. the machine is not registered, or
8242 * registered but either running or not running and not saved). It is useful
8243 * to call this method from Machine setters before performing any changes to
8244 * runtime changeable settings.
8245 *
8246 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8247 * the same as for MutableOrRunningStateDep except that if the machine is
8248 * saved, S_OK is also returned. This is useful in setters which allow
8249 * changing runtime and saved state changeable machine properties.
8250 *
8251 * @param aDepType Dependency type to check.
8252 *
8253 * @note Non Machine based classes should use #i_addStateDependency() and
8254 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8255 * template.
8256 *
8257 * @note This method must be called from under this object's read or write
8258 * lock.
8259 */
8260HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8261{
8262 switch (aDepType)
8263 {
8264 case AnyStateDep:
8265 {
8266 break;
8267 }
8268 case MutableStateDep:
8269 {
8270 if ( mData->mRegistered
8271 && ( !i_isSessionMachine()
8272 || ( mData->mMachineState != MachineState_Aborted
8273 && mData->mMachineState != MachineState_Teleported
8274 && mData->mMachineState != MachineState_PoweredOff
8275 )
8276 )
8277 )
8278 return setError(VBOX_E_INVALID_VM_STATE,
8279 tr("The machine is not mutable (state is %s)"),
8280 Global::stringifyMachineState(mData->mMachineState));
8281 break;
8282 }
8283 case MutableOrSavedStateDep:
8284 {
8285 if ( mData->mRegistered
8286 && ( !i_isSessionMachine()
8287 || ( mData->mMachineState != MachineState_Aborted
8288 && mData->mMachineState != MachineState_Teleported
8289 && mData->mMachineState != MachineState_Saved
8290 && mData->mMachineState != MachineState_PoweredOff
8291 )
8292 )
8293 )
8294 return setError(VBOX_E_INVALID_VM_STATE,
8295 tr("The machine is not mutable or saved (state is %s)"),
8296 Global::stringifyMachineState(mData->mMachineState));
8297 break;
8298 }
8299 case MutableOrRunningStateDep:
8300 {
8301 if ( mData->mRegistered
8302 && ( !i_isSessionMachine()
8303 || ( mData->mMachineState != MachineState_Aborted
8304 && mData->mMachineState != MachineState_Teleported
8305 && mData->mMachineState != MachineState_PoweredOff
8306 && !Global::IsOnline(mData->mMachineState)
8307 )
8308 )
8309 )
8310 return setError(VBOX_E_INVALID_VM_STATE,
8311 tr("The machine is not mutable or running (state is %s)"),
8312 Global::stringifyMachineState(mData->mMachineState));
8313 break;
8314 }
8315 case MutableOrSavedOrRunningStateDep:
8316 {
8317 if ( mData->mRegistered
8318 && ( !i_isSessionMachine()
8319 || ( mData->mMachineState != MachineState_Aborted
8320 && mData->mMachineState != MachineState_Teleported
8321 && mData->mMachineState != MachineState_Saved
8322 && mData->mMachineState != MachineState_PoweredOff
8323 && !Global::IsOnline(mData->mMachineState)
8324 )
8325 )
8326 )
8327 return setError(VBOX_E_INVALID_VM_STATE,
8328 tr("The machine is not mutable, saved or running (state is %s)"),
8329 Global::stringifyMachineState(mData->mMachineState));
8330 break;
8331 }
8332 }
8333
8334 return S_OK;
8335}
8336
8337/**
8338 * Helper to initialize all associated child objects and allocate data
8339 * structures.
8340 *
8341 * This method must be called as a part of the object's initialization procedure
8342 * (usually done in the #init() method).
8343 *
8344 * @note Must be called only from #init() or from #i_registeredInit().
8345 */
8346HRESULT Machine::initDataAndChildObjects()
8347{
8348 AutoCaller autoCaller(this);
8349 AssertComRCReturnRC(autoCaller.rc());
8350 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8351 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8352
8353 AssertReturn(!mData->mAccessible, E_FAIL);
8354
8355 /* allocate data structures */
8356 mSSData.allocate();
8357 mUserData.allocate();
8358 mHWData.allocate();
8359 mMediaData.allocate();
8360 mStorageControllers.allocate();
8361 mUSBControllers.allocate();
8362
8363 /* initialize mOSTypeId */
8364 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8365
8366 /* create associated BIOS settings object */
8367 unconst(mBIOSSettings).createObject();
8368 mBIOSSettings->init(this);
8369
8370 /* create an associated VRDE object (default is disabled) */
8371 unconst(mVRDEServer).createObject();
8372 mVRDEServer->init(this);
8373
8374 /* create associated serial port objects */
8375 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8376 {
8377 unconst(mSerialPorts[slot]).createObject();
8378 mSerialPorts[slot]->init(this, slot);
8379 }
8380
8381 /* create associated parallel port objects */
8382 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8383 {
8384 unconst(mParallelPorts[slot]).createObject();
8385 mParallelPorts[slot]->init(this, slot);
8386 }
8387
8388 /* create the audio adapter object (always present, default is disabled) */
8389 unconst(mAudioAdapter).createObject();
8390 mAudioAdapter->init(this);
8391
8392 /* create the USB device filters object (always present) */
8393 unconst(mUSBDeviceFilters).createObject();
8394 mUSBDeviceFilters->init(this);
8395
8396 /* create associated network adapter objects */
8397 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8398 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8399 {
8400 unconst(mNetworkAdapters[slot]).createObject();
8401 mNetworkAdapters[slot]->init(this, slot);
8402 }
8403
8404 /* create the bandwidth control */
8405 unconst(mBandwidthControl).createObject();
8406 mBandwidthControl->init(this);
8407
8408#ifdef VBOX_WITH_UNATTENDED
8409 /* create the unattended object (always present) */
8410 unconst(mUnattended).createObject();
8411 mUnattended->init(this);
8412#endif
8413
8414 return S_OK;
8415}
8416
8417/**
8418 * Helper to uninitialize all associated child objects and to free all data
8419 * structures.
8420 *
8421 * This method must be called as a part of the object's uninitialization
8422 * procedure (usually done in the #uninit() method).
8423 *
8424 * @note Must be called only from #uninit() or from #i_registeredInit().
8425 */
8426void Machine::uninitDataAndChildObjects()
8427{
8428 AutoCaller autoCaller(this);
8429 AssertComRCReturnVoid(autoCaller.rc());
8430 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8431 || getObjectState().getState() == ObjectState::Limited);
8432
8433 /* tell all our other child objects we've been uninitialized */
8434 if (mBandwidthControl)
8435 {
8436 mBandwidthControl->uninit();
8437 unconst(mBandwidthControl).setNull();
8438 }
8439
8440 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8441 {
8442 if (mNetworkAdapters[slot])
8443 {
8444 mNetworkAdapters[slot]->uninit();
8445 unconst(mNetworkAdapters[slot]).setNull();
8446 }
8447 }
8448
8449 if (mUSBDeviceFilters)
8450 {
8451 mUSBDeviceFilters->uninit();
8452 unconst(mUSBDeviceFilters).setNull();
8453 }
8454
8455 if (mAudioAdapter)
8456 {
8457 mAudioAdapter->uninit();
8458 unconst(mAudioAdapter).setNull();
8459 }
8460
8461 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8462 {
8463 if (mParallelPorts[slot])
8464 {
8465 mParallelPorts[slot]->uninit();
8466 unconst(mParallelPorts[slot]).setNull();
8467 }
8468 }
8469
8470 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8471 {
8472 if (mSerialPorts[slot])
8473 {
8474 mSerialPorts[slot]->uninit();
8475 unconst(mSerialPorts[slot]).setNull();
8476 }
8477 }
8478
8479 if (mVRDEServer)
8480 {
8481 mVRDEServer->uninit();
8482 unconst(mVRDEServer).setNull();
8483 }
8484
8485 if (mBIOSSettings)
8486 {
8487 mBIOSSettings->uninit();
8488 unconst(mBIOSSettings).setNull();
8489 }
8490
8491#ifdef VBOX_WITH_UNATTENDED
8492 if (mUnattended)
8493 {
8494 mUnattended->uninit();
8495 unconst(mUnattended).setNull();
8496 }
8497#endif
8498
8499 /* Deassociate media (only when a real Machine or a SnapshotMachine
8500 * instance is uninitialized; SessionMachine instances refer to real
8501 * Machine media). This is necessary for a clean re-initialization of
8502 * the VM after successfully re-checking the accessibility state. Note
8503 * that in case of normal Machine or SnapshotMachine uninitialization (as
8504 * a result of unregistering or deleting the snapshot), outdated media
8505 * attachments will already be uninitialized and deleted, so this
8506 * code will not affect them. */
8507 if ( !!mMediaData
8508 && (!i_isSessionMachine())
8509 )
8510 {
8511 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8512 it != mMediaData->mAttachments.end();
8513 ++it)
8514 {
8515 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8516 if (pMedium.isNull())
8517 continue;
8518 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8519 AssertComRC(rc);
8520 }
8521 }
8522
8523 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8524 {
8525 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8526 if (mData->mFirstSnapshot)
8527 {
8528 // snapshots tree is protected by machine write lock; strictly
8529 // this isn't necessary here since we're deleting the entire
8530 // machine, but otherwise we assert in Snapshot::uninit()
8531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8532 mData->mFirstSnapshot->uninit();
8533 mData->mFirstSnapshot.setNull();
8534 }
8535
8536 mData->mCurrentSnapshot.setNull();
8537 }
8538
8539 /* free data structures (the essential mData structure is not freed here
8540 * since it may be still in use) */
8541 mMediaData.free();
8542 mStorageControllers.free();
8543 mUSBControllers.free();
8544 mHWData.free();
8545 mUserData.free();
8546 mSSData.free();
8547}
8548
8549/**
8550 * Returns a pointer to the Machine object for this machine that acts like a
8551 * parent for complex machine data objects such as shared folders, etc.
8552 *
8553 * For primary Machine objects and for SnapshotMachine objects, returns this
8554 * object's pointer itself. For SessionMachine objects, returns the peer
8555 * (primary) machine pointer.
8556 */
8557Machine* Machine::i_getMachine()
8558{
8559 if (i_isSessionMachine())
8560 return (Machine*)mPeer;
8561 return this;
8562}
8563
8564/**
8565 * Makes sure that there are no machine state dependents. If necessary, waits
8566 * for the number of dependents to drop to zero.
8567 *
8568 * Make sure this method is called from under this object's write lock to
8569 * guarantee that no new dependents may be added when this method returns
8570 * control to the caller.
8571 *
8572 * @note Locks this object for writing. The lock will be released while waiting
8573 * (if necessary).
8574 *
8575 * @warning To be used only in methods that change the machine state!
8576 */
8577void Machine::i_ensureNoStateDependencies()
8578{
8579 AssertReturnVoid(isWriteLockOnCurrentThread());
8580
8581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8582
8583 /* Wait for all state dependents if necessary */
8584 if (mData->mMachineStateDeps != 0)
8585 {
8586 /* lazy semaphore creation */
8587 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8588 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8589
8590 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8591 mData->mMachineStateDeps));
8592
8593 ++mData->mMachineStateChangePending;
8594
8595 /* reset the semaphore before waiting, the last dependent will signal
8596 * it */
8597 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8598
8599 alock.release();
8600
8601 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8602
8603 alock.acquire();
8604
8605 -- mData->mMachineStateChangePending;
8606 }
8607}
8608
8609/**
8610 * Changes the machine state and informs callbacks.
8611 *
8612 * This method is not intended to fail so it either returns S_OK or asserts (and
8613 * returns a failure).
8614 *
8615 * @note Locks this object for writing.
8616 */
8617HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8618{
8619 LogFlowThisFuncEnter();
8620 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8621 Assert(aMachineState != MachineState_Null);
8622
8623 AutoCaller autoCaller(this);
8624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8625
8626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8627
8628 /* wait for state dependents to drop to zero */
8629 i_ensureNoStateDependencies();
8630
8631 MachineState_T const enmOldState = mData->mMachineState;
8632 if (enmOldState != aMachineState)
8633 {
8634 mData->mMachineState = aMachineState;
8635 RTTimeNow(&mData->mLastStateChange);
8636
8637#ifdef VBOX_WITH_DTRACE_R3_MAIN
8638 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8639#endif
8640 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8641 }
8642
8643 LogFlowThisFuncLeave();
8644 return S_OK;
8645}
8646
8647/**
8648 * Searches for a shared folder with the given logical name
8649 * in the collection of shared folders.
8650 *
8651 * @param aName logical name of the shared folder
8652 * @param aSharedFolder where to return the found object
8653 * @param aSetError whether to set the error info if the folder is
8654 * not found
8655 * @return
8656 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8657 *
8658 * @note
8659 * must be called from under the object's lock!
8660 */
8661HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8662 ComObjPtr<SharedFolder> &aSharedFolder,
8663 bool aSetError /* = false */)
8664{
8665 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8666 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8667 it != mHWData->mSharedFolders.end();
8668 ++it)
8669 {
8670 SharedFolder *pSF = *it;
8671 AutoCaller autoCaller(pSF);
8672 if (pSF->i_getName() == aName)
8673 {
8674 aSharedFolder = pSF;
8675 rc = S_OK;
8676 break;
8677 }
8678 }
8679
8680 if (aSetError && FAILED(rc))
8681 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8682
8683 return rc;
8684}
8685
8686/**
8687 * Initializes all machine instance data from the given settings structures
8688 * from XML. The exception is the machine UUID which needs special handling
8689 * depending on the caller's use case, so the caller needs to set that herself.
8690 *
8691 * This gets called in several contexts during machine initialization:
8692 *
8693 * -- When machine XML exists on disk already and needs to be loaded into memory,
8694 * for example, from #i_registeredInit() to load all registered machines on
8695 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8696 * attached to the machine should be part of some media registry already.
8697 *
8698 * -- During OVF import, when a machine config has been constructed from an
8699 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8700 * ensure that the media listed as attachments in the config (which have
8701 * been imported from the OVF) receive the correct registry ID.
8702 *
8703 * -- During VM cloning.
8704 *
8705 * @param config Machine settings from XML.
8706 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8707 * for each attached medium in the config.
8708 * @return
8709 */
8710HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8711 const Guid *puuidRegistry)
8712{
8713 // copy name, description, OS type, teleporter, UTC etc.
8714 mUserData->s = config.machineUserData;
8715
8716 // look up the object by Id to check it is valid
8717 ComPtr<IGuestOSType> guestOSType;
8718 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8719 guestOSType.asOutParam());
8720 if (FAILED(rc)) return rc;
8721
8722 // stateFile (optional)
8723 if (config.strStateFile.isEmpty())
8724 mSSData->strStateFilePath.setNull();
8725 else
8726 {
8727 Utf8Str stateFilePathFull(config.strStateFile);
8728 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8729 if (RT_FAILURE(vrc))
8730 return setError(E_FAIL,
8731 tr("Invalid saved state file path '%s' (%Rrc)"),
8732 config.strStateFile.c_str(),
8733 vrc);
8734 mSSData->strStateFilePath = stateFilePathFull;
8735 }
8736
8737 // snapshot folder needs special processing so set it again
8738 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8739 if (FAILED(rc)) return rc;
8740
8741 /* Copy the extra data items (config may or may not be the same as
8742 * mData->pMachineConfigFile) if necessary. When loading the XML files
8743 * from disk they are the same, but not for OVF import. */
8744 if (mData->pMachineConfigFile != &config)
8745 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8746
8747 /* currentStateModified (optional, default is true) */
8748 mData->mCurrentStateModified = config.fCurrentStateModified;
8749
8750 mData->mLastStateChange = config.timeLastStateChange;
8751
8752 /*
8753 * note: all mUserData members must be assigned prior this point because
8754 * we need to commit changes in order to let mUserData be shared by all
8755 * snapshot machine instances.
8756 */
8757 mUserData.commitCopy();
8758
8759 // machine registry, if present (must be loaded before snapshots)
8760 if (config.canHaveOwnMediaRegistry())
8761 {
8762 // determine machine folder
8763 Utf8Str strMachineFolder = i_getSettingsFileFull();
8764 strMachineFolder.stripFilename();
8765 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8766 config.mediaRegistry,
8767 strMachineFolder);
8768 if (FAILED(rc)) return rc;
8769 }
8770
8771 /* Snapshot node (optional) */
8772 size_t cRootSnapshots;
8773 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8774 {
8775 // there must be only one root snapshot
8776 Assert(cRootSnapshots == 1);
8777
8778 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8779
8780 rc = i_loadSnapshot(snap,
8781 config.uuidCurrentSnapshot,
8782 NULL); // no parent == first snapshot
8783 if (FAILED(rc)) return rc;
8784 }
8785
8786 // hardware data
8787 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8788 if (FAILED(rc)) return rc;
8789
8790 /*
8791 * NOTE: the assignment below must be the last thing to do,
8792 * otherwise it will be not possible to change the settings
8793 * somewhere in the code above because all setters will be
8794 * blocked by i_checkStateDependency(MutableStateDep).
8795 */
8796
8797 /* set the machine state to Aborted or Saved when appropriate */
8798 if (config.fAborted)
8799 {
8800 mSSData->strStateFilePath.setNull();
8801
8802 /* no need to use i_setMachineState() during init() */
8803 mData->mMachineState = MachineState_Aborted;
8804 }
8805 else if (!mSSData->strStateFilePath.isEmpty())
8806 {
8807 /* no need to use i_setMachineState() during init() */
8808 mData->mMachineState = MachineState_Saved;
8809 }
8810
8811 // after loading settings, we are no longer different from the XML on disk
8812 mData->flModifications = 0;
8813
8814 return S_OK;
8815}
8816
8817/**
8818 * Recursively loads all snapshots starting from the given.
8819 *
8820 * @param data snapshot settings.
8821 * @param aCurSnapshotId Current snapshot ID from the settings file.
8822 * @param aParentSnapshot Parent snapshot.
8823 */
8824HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8825 const Guid &aCurSnapshotId,
8826 Snapshot *aParentSnapshot)
8827{
8828 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8829 AssertReturn(!i_isSessionMachine(), E_FAIL);
8830
8831 HRESULT rc = S_OK;
8832
8833 Utf8Str strStateFile;
8834 if (!data.strStateFile.isEmpty())
8835 {
8836 /* optional */
8837 strStateFile = data.strStateFile;
8838 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8839 if (RT_FAILURE(vrc))
8840 return setError(E_FAIL,
8841 tr("Invalid saved state file path '%s' (%Rrc)"),
8842 strStateFile.c_str(),
8843 vrc);
8844 }
8845
8846 /* create a snapshot machine object */
8847 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8848 pSnapshotMachine.createObject();
8849 rc = pSnapshotMachine->initFromSettings(this,
8850 data.hardware,
8851 &data.debugging,
8852 &data.autostart,
8853 data.uuid.ref(),
8854 strStateFile);
8855 if (FAILED(rc)) return rc;
8856
8857 /* create a snapshot object */
8858 ComObjPtr<Snapshot> pSnapshot;
8859 pSnapshot.createObject();
8860 /* initialize the snapshot */
8861 rc = pSnapshot->init(mParent, // VirtualBox object
8862 data.uuid,
8863 data.strName,
8864 data.strDescription,
8865 data.timestamp,
8866 pSnapshotMachine,
8867 aParentSnapshot);
8868 if (FAILED(rc)) return rc;
8869
8870 /* memorize the first snapshot if necessary */
8871 if (!mData->mFirstSnapshot)
8872 mData->mFirstSnapshot = pSnapshot;
8873
8874 /* memorize the current snapshot when appropriate */
8875 if ( !mData->mCurrentSnapshot
8876 && pSnapshot->i_getId() == aCurSnapshotId
8877 )
8878 mData->mCurrentSnapshot = pSnapshot;
8879
8880 // now create the children
8881 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8882 it != data.llChildSnapshots.end();
8883 ++it)
8884 {
8885 const settings::Snapshot &childData = *it;
8886 // recurse
8887 rc = i_loadSnapshot(childData,
8888 aCurSnapshotId,
8889 pSnapshot); // parent = the one we created above
8890 if (FAILED(rc)) return rc;
8891 }
8892
8893 return rc;
8894}
8895
8896/**
8897 * Loads settings into mHWData.
8898 *
8899 * @param puuidRegistry Registry ID.
8900 * @param puuidSnapshot Snapshot ID
8901 * @param data Reference to the hardware settings.
8902 * @param pDbg Pointer to the debugging settings.
8903 * @param pAutostart Pointer to the autostart settings.
8904 */
8905HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8906 const Guid *puuidSnapshot,
8907 const settings::Hardware &data,
8908 const settings::Debugging *pDbg,
8909 const settings::Autostart *pAutostart)
8910{
8911 AssertReturn(!i_isSessionMachine(), E_FAIL);
8912
8913 HRESULT rc = S_OK;
8914
8915 try
8916 {
8917 /* The hardware version attribute (optional). */
8918 mHWData->mHWVersion = data.strVersion;
8919 mHWData->mHardwareUUID = data.uuid;
8920
8921 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8922 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8923 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8924 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8925 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8926 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8927 mHWData->mPAEEnabled = data.fPAE;
8928 mHWData->mLongMode = data.enmLongMode;
8929 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8930 mHWData->mAPIC = data.fAPIC;
8931 mHWData->mX2APIC = data.fX2APIC;
8932 mHWData->mCPUCount = data.cCPUs;
8933 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8934 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8935 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8936 mHWData->mCpuProfile = data.strCpuProfile;
8937
8938 // cpu
8939 if (mHWData->mCPUHotPlugEnabled)
8940 {
8941 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8942 it != data.llCpus.end();
8943 ++it)
8944 {
8945 const settings::Cpu &cpu = *it;
8946
8947 mHWData->mCPUAttached[cpu.ulId] = true;
8948 }
8949 }
8950
8951 // cpuid leafs
8952 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8953 it != data.llCpuIdLeafs.end();
8954 ++it)
8955 {
8956 const settings::CpuIdLeaf &leaf = *it;
8957
8958 switch (leaf.ulId)
8959 {
8960 case 0x0:
8961 case 0x1:
8962 case 0x2:
8963 case 0x3:
8964 case 0x4:
8965 case 0x5:
8966 case 0x6:
8967 case 0x7:
8968 case 0x8:
8969 case 0x9:
8970 case 0xA:
8971 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8972 break;
8973
8974 case 0x80000000:
8975 case 0x80000001:
8976 case 0x80000002:
8977 case 0x80000003:
8978 case 0x80000004:
8979 case 0x80000005:
8980 case 0x80000006:
8981 case 0x80000007:
8982 case 0x80000008:
8983 case 0x80000009:
8984 case 0x8000000A:
8985 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8986 break;
8987
8988 default:
8989 /* just ignore */
8990 break;
8991 }
8992 }
8993
8994 mHWData->mMemorySize = data.ulMemorySizeMB;
8995 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8996
8997 // boot order
8998 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8999 {
9000 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9001 if (it == data.mapBootOrder.end())
9002 mHWData->mBootOrder[i] = DeviceType_Null;
9003 else
9004 mHWData->mBootOrder[i] = it->second;
9005 }
9006
9007 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9008 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9009 mHWData->mMonitorCount = data.cMonitors;
9010 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9011 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9012 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9013 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9014 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9015 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9016 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9017 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9018 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9019 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9020 if (!data.strVideoCaptureFile.isEmpty())
9021 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9022 else
9023 mHWData->mVideoCaptureFile.setNull();
9024 mHWData->mFirmwareType = data.firmwareType;
9025 mHWData->mPointingHIDType = data.pointingHIDType;
9026 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9027 mHWData->mChipsetType = data.chipsetType;
9028 mHWData->mParavirtProvider = data.paravirtProvider;
9029 mHWData->mParavirtDebug = data.strParavirtDebug;
9030 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9031 mHWData->mHPETEnabled = data.fHPETEnabled;
9032
9033 /* VRDEServer */
9034 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9035 if (FAILED(rc)) return rc;
9036
9037 /* BIOS */
9038 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9039 if (FAILED(rc)) return rc;
9040
9041 // Bandwidth control (must come before network adapters)
9042 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9043 if (FAILED(rc)) return rc;
9044
9045 /* Shared folders */
9046 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9047 it != data.usbSettings.llUSBControllers.end();
9048 ++it)
9049 {
9050 const settings::USBController &settingsCtrl = *it;
9051 ComObjPtr<USBController> newCtrl;
9052
9053 newCtrl.createObject();
9054 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9055 mUSBControllers->push_back(newCtrl);
9056 }
9057
9058 /* USB device filters */
9059 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9060 if (FAILED(rc)) return rc;
9061
9062 // network adapters
9063 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9064 size_t oldCount = mNetworkAdapters.size();
9065 if (newCount > oldCount)
9066 {
9067 mNetworkAdapters.resize(newCount);
9068 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9069 {
9070 unconst(mNetworkAdapters[slot]).createObject();
9071 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9072 }
9073 }
9074 else if (newCount < oldCount)
9075 mNetworkAdapters.resize(newCount);
9076 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9077 it != data.llNetworkAdapters.end();
9078 ++it)
9079 {
9080 const settings::NetworkAdapter &nic = *it;
9081
9082 /* slot unicity is guaranteed by XML Schema */
9083 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9084 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9085 if (FAILED(rc)) return rc;
9086 }
9087
9088 // serial ports
9089 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9090 it != data.llSerialPorts.end();
9091 ++it)
9092 {
9093 const settings::SerialPort &s = *it;
9094
9095 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9096 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9097 if (FAILED(rc)) return rc;
9098 }
9099
9100 // parallel ports (optional)
9101 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9102 it != data.llParallelPorts.end();
9103 ++it)
9104 {
9105 const settings::ParallelPort &p = *it;
9106
9107 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9108 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9109 if (FAILED(rc)) return rc;
9110 }
9111
9112 /* AudioAdapter */
9113 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9114 if (FAILED(rc)) return rc;
9115
9116 /* storage controllers */
9117 rc = i_loadStorageControllers(data.storage,
9118 puuidRegistry,
9119 puuidSnapshot);
9120 if (FAILED(rc)) return rc;
9121
9122 /* Shared folders */
9123 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9124 it != data.llSharedFolders.end();
9125 ++it)
9126 {
9127 const settings::SharedFolder &sf = *it;
9128
9129 ComObjPtr<SharedFolder> sharedFolder;
9130 /* Check for double entries. Not allowed! */
9131 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9132 if (SUCCEEDED(rc))
9133 return setError(VBOX_E_OBJECT_IN_USE,
9134 tr("Shared folder named '%s' already exists"),
9135 sf.strName.c_str());
9136
9137 /* Create the new shared folder. Don't break on error. This will be
9138 * reported when the machine starts. */
9139 sharedFolder.createObject();
9140 rc = sharedFolder->init(i_getMachine(),
9141 sf.strName,
9142 sf.strHostPath,
9143 RT_BOOL(sf.fWritable),
9144 RT_BOOL(sf.fAutoMount),
9145 false /* fFailOnError */);
9146 if (FAILED(rc)) return rc;
9147 mHWData->mSharedFolders.push_back(sharedFolder);
9148 }
9149
9150 // Clipboard
9151 mHWData->mClipboardMode = data.clipboardMode;
9152
9153 // drag'n'drop
9154 mHWData->mDnDMode = data.dndMode;
9155
9156 // guest settings
9157 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9158
9159 // IO settings
9160 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9161 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9162
9163 // Host PCI devices
9164 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9165 it != data.pciAttachments.end();
9166 ++it)
9167 {
9168 const settings::HostPCIDeviceAttachment &hpda = *it;
9169 ComObjPtr<PCIDeviceAttachment> pda;
9170
9171 pda.createObject();
9172 pda->i_loadSettings(this, hpda);
9173 mHWData->mPCIDeviceAssignments.push_back(pda);
9174 }
9175
9176 /*
9177 * (The following isn't really real hardware, but it lives in HWData
9178 * for reasons of convenience.)
9179 */
9180
9181#ifdef VBOX_WITH_GUEST_PROPS
9182 /* Guest properties (optional) */
9183
9184 /* Only load transient guest properties for configs which have saved
9185 * state, because there shouldn't be any for powered off VMs. The same
9186 * logic applies for snapshots, as offline snapshots shouldn't have
9187 * any such properties. They confuse the code in various places.
9188 * Note: can't rely on the machine state, as it isn't set yet. */
9189 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9190 /* apologies for the hacky unconst() usage, but this needs hacking
9191 * actually inconsistent settings into consistency, otherwise there
9192 * will be some corner cases where the inconsistency survives
9193 * surprisingly long without getting fixed, especially for snapshots
9194 * as there are no config changes. */
9195 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9196 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9197 it != llGuestProperties.end();
9198 /*nothing*/)
9199 {
9200 const settings::GuestProperty &prop = *it;
9201 uint32_t fFlags = guestProp::NILFLAG;
9202 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9203 if ( fSkipTransientGuestProperties
9204 && ( fFlags & guestProp::TRANSIENT
9205 || fFlags & guestProp::TRANSRESET))
9206 {
9207 it = llGuestProperties.erase(it);
9208 continue;
9209 }
9210 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9211 mHWData->mGuestProperties[prop.strName] = property;
9212 ++it;
9213 }
9214#endif /* VBOX_WITH_GUEST_PROPS defined */
9215
9216 rc = i_loadDebugging(pDbg);
9217 if (FAILED(rc))
9218 return rc;
9219
9220 mHWData->mAutostart = *pAutostart;
9221
9222 /* default frontend */
9223 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9224 }
9225 catch(std::bad_alloc &)
9226 {
9227 return E_OUTOFMEMORY;
9228 }
9229
9230 AssertComRC(rc);
9231 return rc;
9232}
9233
9234/**
9235 * Called from Machine::loadHardware() to load the debugging settings of the
9236 * machine.
9237 *
9238 * @param pDbg Pointer to the settings.
9239 */
9240HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9241{
9242 mHWData->mDebugging = *pDbg;
9243 /* no more processing currently required, this will probably change. */
9244 return S_OK;
9245}
9246
9247/**
9248 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9249 *
9250 * @param data storage settings.
9251 * @param puuidRegistry media registry ID to set media to or NULL;
9252 * see Machine::i_loadMachineDataFromSettings()
9253 * @param puuidSnapshot snapshot ID
9254 * @return
9255 */
9256HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9257 const Guid *puuidRegistry,
9258 const Guid *puuidSnapshot)
9259{
9260 AssertReturn(!i_isSessionMachine(), E_FAIL);
9261
9262 HRESULT rc = S_OK;
9263
9264 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9265 it != data.llStorageControllers.end();
9266 ++it)
9267 {
9268 const settings::StorageController &ctlData = *it;
9269
9270 ComObjPtr<StorageController> pCtl;
9271 /* Try to find one with the name first. */
9272 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9273 if (SUCCEEDED(rc))
9274 return setError(VBOX_E_OBJECT_IN_USE,
9275 tr("Storage controller named '%s' already exists"),
9276 ctlData.strName.c_str());
9277
9278 pCtl.createObject();
9279 rc = pCtl->init(this,
9280 ctlData.strName,
9281 ctlData.storageBus,
9282 ctlData.ulInstance,
9283 ctlData.fBootable);
9284 if (FAILED(rc)) return rc;
9285
9286 mStorageControllers->push_back(pCtl);
9287
9288 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9289 if (FAILED(rc)) return rc;
9290
9291 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9292 if (FAILED(rc)) return rc;
9293
9294 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9295 if (FAILED(rc)) return rc;
9296
9297 /* Load the attached devices now. */
9298 rc = i_loadStorageDevices(pCtl,
9299 ctlData,
9300 puuidRegistry,
9301 puuidSnapshot);
9302 if (FAILED(rc)) return rc;
9303 }
9304
9305 return S_OK;
9306}
9307
9308/**
9309 * Called from i_loadStorageControllers for a controller's devices.
9310 *
9311 * @param aStorageController
9312 * @param data
9313 * @param puuidRegistry media registry ID to set media to or NULL; see
9314 * Machine::i_loadMachineDataFromSettings()
9315 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9316 * @return
9317 */
9318HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9319 const settings::StorageController &data,
9320 const Guid *puuidRegistry,
9321 const Guid *puuidSnapshot)
9322{
9323 HRESULT rc = S_OK;
9324
9325 /* paranoia: detect duplicate attachments */
9326 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9327 it != data.llAttachedDevices.end();
9328 ++it)
9329 {
9330 const settings::AttachedDevice &ad = *it;
9331
9332 for (settings::AttachedDevicesList::const_iterator it2 = it;
9333 it2 != data.llAttachedDevices.end();
9334 ++it2)
9335 {
9336 if (it == it2)
9337 continue;
9338
9339 const settings::AttachedDevice &ad2 = *it2;
9340
9341 if ( ad.lPort == ad2.lPort
9342 && ad.lDevice == ad2.lDevice)
9343 {
9344 return setError(E_FAIL,
9345 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9346 aStorageController->i_getName().c_str(),
9347 ad.lPort,
9348 ad.lDevice,
9349 mUserData->s.strName.c_str());
9350 }
9351 }
9352 }
9353
9354 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9355 it != data.llAttachedDevices.end();
9356 ++it)
9357 {
9358 const settings::AttachedDevice &dev = *it;
9359 ComObjPtr<Medium> medium;
9360
9361 switch (dev.deviceType)
9362 {
9363 case DeviceType_Floppy:
9364 case DeviceType_DVD:
9365 if (dev.strHostDriveSrc.isNotEmpty())
9366 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9367 false /* fRefresh */, medium);
9368 else
9369 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9370 dev.uuid,
9371 false /* fRefresh */,
9372 false /* aSetError */,
9373 medium);
9374 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9375 // This is not an error. The host drive or UUID might have vanished, so just go
9376 // ahead without this removeable medium attachment
9377 rc = S_OK;
9378 break;
9379
9380 case DeviceType_HardDisk:
9381 {
9382 /* find a hard disk by UUID */
9383 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9384 if (FAILED(rc))
9385 {
9386 if (i_isSnapshotMachine())
9387 {
9388 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9389 // so the user knows that the bad disk is in a snapshot somewhere
9390 com::ErrorInfo info;
9391 return setError(E_FAIL,
9392 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9393 puuidSnapshot->raw(),
9394 info.getText().raw());
9395 }
9396 else
9397 return rc;
9398 }
9399
9400 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9401
9402 if (medium->i_getType() == MediumType_Immutable)
9403 {
9404 if (i_isSnapshotMachine())
9405 return setError(E_FAIL,
9406 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9407 "of the virtual machine '%s' ('%s')"),
9408 medium->i_getLocationFull().c_str(),
9409 dev.uuid.raw(),
9410 puuidSnapshot->raw(),
9411 mUserData->s.strName.c_str(),
9412 mData->m_strConfigFileFull.c_str());
9413
9414 return setError(E_FAIL,
9415 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9416 medium->i_getLocationFull().c_str(),
9417 dev.uuid.raw(),
9418 mUserData->s.strName.c_str(),
9419 mData->m_strConfigFileFull.c_str());
9420 }
9421
9422 if (medium->i_getType() == MediumType_MultiAttach)
9423 {
9424 if (i_isSnapshotMachine())
9425 return setError(E_FAIL,
9426 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9427 "of the virtual machine '%s' ('%s')"),
9428 medium->i_getLocationFull().c_str(),
9429 dev.uuid.raw(),
9430 puuidSnapshot->raw(),
9431 mUserData->s.strName.c_str(),
9432 mData->m_strConfigFileFull.c_str());
9433
9434 return setError(E_FAIL,
9435 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9436 medium->i_getLocationFull().c_str(),
9437 dev.uuid.raw(),
9438 mUserData->s.strName.c_str(),
9439 mData->m_strConfigFileFull.c_str());
9440 }
9441
9442 if ( !i_isSnapshotMachine()
9443 && medium->i_getChildren().size() != 0
9444 )
9445 return setError(E_FAIL,
9446 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9447 "because it has %d differencing child hard disks"),
9448 medium->i_getLocationFull().c_str(),
9449 dev.uuid.raw(),
9450 mUserData->s.strName.c_str(),
9451 mData->m_strConfigFileFull.c_str(),
9452 medium->i_getChildren().size());
9453
9454 if (i_findAttachment(mMediaData->mAttachments,
9455 medium))
9456 return setError(E_FAIL,
9457 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9458 medium->i_getLocationFull().c_str(),
9459 dev.uuid.raw(),
9460 mUserData->s.strName.c_str(),
9461 mData->m_strConfigFileFull.c_str());
9462
9463 break;
9464 }
9465
9466 default:
9467 return setError(E_FAIL,
9468 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9469 medium->i_getLocationFull().c_str(),
9470 mUserData->s.strName.c_str(),
9471 mData->m_strConfigFileFull.c_str());
9472 }
9473
9474 if (FAILED(rc))
9475 break;
9476
9477 /* Bandwidth groups are loaded at this point. */
9478 ComObjPtr<BandwidthGroup> pBwGroup;
9479
9480 if (!dev.strBwGroup.isEmpty())
9481 {
9482 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9483 if (FAILED(rc))
9484 return setError(E_FAIL,
9485 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9486 medium->i_getLocationFull().c_str(),
9487 dev.strBwGroup.c_str(),
9488 mUserData->s.strName.c_str(),
9489 mData->m_strConfigFileFull.c_str());
9490 pBwGroup->i_reference();
9491 }
9492
9493 const Bstr controllerName = aStorageController->i_getName();
9494 ComObjPtr<MediumAttachment> pAttachment;
9495 pAttachment.createObject();
9496 rc = pAttachment->init(this,
9497 medium,
9498 controllerName,
9499 dev.lPort,
9500 dev.lDevice,
9501 dev.deviceType,
9502 false,
9503 dev.fPassThrough,
9504 dev.fTempEject,
9505 dev.fNonRotational,
9506 dev.fDiscard,
9507 dev.fHotPluggable,
9508 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9509 if (FAILED(rc)) break;
9510
9511 /* associate the medium with this machine and snapshot */
9512 if (!medium.isNull())
9513 {
9514 AutoCaller medCaller(medium);
9515 if (FAILED(medCaller.rc())) return medCaller.rc();
9516 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9517
9518 if (i_isSnapshotMachine())
9519 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9520 else
9521 rc = medium->i_addBackReference(mData->mUuid);
9522 /* If the medium->addBackReference fails it sets an appropriate
9523 * error message, so no need to do any guesswork here. */
9524
9525 if (puuidRegistry)
9526 // caller wants registry ID to be set on all attached media (OVF import case)
9527 medium->i_addRegistry(*puuidRegistry);
9528 }
9529
9530 if (FAILED(rc))
9531 break;
9532
9533 /* back up mMediaData to let registeredInit() properly rollback on failure
9534 * (= limited accessibility) */
9535 i_setModified(IsModified_Storage);
9536 mMediaData.backup();
9537 mMediaData->mAttachments.push_back(pAttachment);
9538 }
9539
9540 return rc;
9541}
9542
9543/**
9544 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9545 *
9546 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9547 * @param aSnapshot where to return the found snapshot
9548 * @param aSetError true to set extended error info on failure
9549 */
9550HRESULT Machine::i_findSnapshotById(const Guid &aId,
9551 ComObjPtr<Snapshot> &aSnapshot,
9552 bool aSetError /* = false */)
9553{
9554 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9555
9556 if (!mData->mFirstSnapshot)
9557 {
9558 if (aSetError)
9559 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9560 return E_FAIL;
9561 }
9562
9563 if (aId.isZero())
9564 aSnapshot = mData->mFirstSnapshot;
9565 else
9566 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9567
9568 if (!aSnapshot)
9569 {
9570 if (aSetError)
9571 return setError(E_FAIL,
9572 tr("Could not find a snapshot with UUID {%s}"),
9573 aId.toString().c_str());
9574 return E_FAIL;
9575 }
9576
9577 return S_OK;
9578}
9579
9580/**
9581 * Returns the snapshot with the given name or fails of no such snapshot.
9582 *
9583 * @param strName snapshot name to find
9584 * @param aSnapshot where to return the found snapshot
9585 * @param aSetError true to set extended error info on failure
9586 */
9587HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9588 ComObjPtr<Snapshot> &aSnapshot,
9589 bool aSetError /* = false */)
9590{
9591 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9592
9593 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9594
9595 if (!mData->mFirstSnapshot)
9596 {
9597 if (aSetError)
9598 return setError(VBOX_E_OBJECT_NOT_FOUND,
9599 tr("This machine does not have any snapshots"));
9600 return VBOX_E_OBJECT_NOT_FOUND;
9601 }
9602
9603 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9604
9605 if (!aSnapshot)
9606 {
9607 if (aSetError)
9608 return setError(VBOX_E_OBJECT_NOT_FOUND,
9609 tr("Could not find a snapshot named '%s'"), strName.c_str());
9610 return VBOX_E_OBJECT_NOT_FOUND;
9611 }
9612
9613 return S_OK;
9614}
9615
9616/**
9617 * Returns a storage controller object with the given name.
9618 *
9619 * @param aName storage controller name to find
9620 * @param aStorageController where to return the found storage controller
9621 * @param aSetError true to set extended error info on failure
9622 */
9623HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9624 ComObjPtr<StorageController> &aStorageController,
9625 bool aSetError /* = false */)
9626{
9627 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9628
9629 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9630 it != mStorageControllers->end();
9631 ++it)
9632 {
9633 if ((*it)->i_getName() == aName)
9634 {
9635 aStorageController = (*it);
9636 return S_OK;
9637 }
9638 }
9639
9640 if (aSetError)
9641 return setError(VBOX_E_OBJECT_NOT_FOUND,
9642 tr("Could not find a storage controller named '%s'"),
9643 aName.c_str());
9644 return VBOX_E_OBJECT_NOT_FOUND;
9645}
9646
9647/**
9648 * Returns a USB controller object with the given name.
9649 *
9650 * @param aName USB controller name to find
9651 * @param aUSBController where to return the found USB controller
9652 * @param aSetError true to set extended error info on failure
9653 */
9654HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9655 ComObjPtr<USBController> &aUSBController,
9656 bool aSetError /* = false */)
9657{
9658 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9659
9660 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9661 it != mUSBControllers->end();
9662 ++it)
9663 {
9664 if ((*it)->i_getName() == aName)
9665 {
9666 aUSBController = (*it);
9667 return S_OK;
9668 }
9669 }
9670
9671 if (aSetError)
9672 return setError(VBOX_E_OBJECT_NOT_FOUND,
9673 tr("Could not find a storage controller named '%s'"),
9674 aName.c_str());
9675 return VBOX_E_OBJECT_NOT_FOUND;
9676}
9677
9678/**
9679 * Returns the number of USB controller instance of the given type.
9680 *
9681 * @param enmType USB controller type.
9682 */
9683ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9684{
9685 ULONG cCtrls = 0;
9686
9687 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9688 it != mUSBControllers->end();
9689 ++it)
9690 {
9691 if ((*it)->i_getControllerType() == enmType)
9692 cCtrls++;
9693 }
9694
9695 return cCtrls;
9696}
9697
9698HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9699 MediaData::AttachmentList &atts)
9700{
9701 AutoCaller autoCaller(this);
9702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9703
9704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9705
9706 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9707 it != mMediaData->mAttachments.end();
9708 ++it)
9709 {
9710 const ComObjPtr<MediumAttachment> &pAtt = *it;
9711 // should never happen, but deal with NULL pointers in the list.
9712 AssertContinue(!pAtt.isNull());
9713
9714 // getControllerName() needs caller+read lock
9715 AutoCaller autoAttCaller(pAtt);
9716 if (FAILED(autoAttCaller.rc()))
9717 {
9718 atts.clear();
9719 return autoAttCaller.rc();
9720 }
9721 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9722
9723 if (pAtt->i_getControllerName() == aName)
9724 atts.push_back(pAtt);
9725 }
9726
9727 return S_OK;
9728}
9729
9730
9731/**
9732 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9733 * file if the machine name was changed and about creating a new settings file
9734 * if this is a new machine.
9735 *
9736 * @note Must be never called directly but only from #saveSettings().
9737 */
9738HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9739{
9740 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9741
9742 HRESULT rc = S_OK;
9743
9744 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9745
9746 /// @todo need to handle primary group change, too
9747
9748 /* attempt to rename the settings file if machine name is changed */
9749 if ( mUserData->s.fNameSync
9750 && mUserData.isBackedUp()
9751 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9752 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9753 )
9754 {
9755 bool dirRenamed = false;
9756 bool fileRenamed = false;
9757
9758 Utf8Str configFile, newConfigFile;
9759 Utf8Str configFilePrev, newConfigFilePrev;
9760 Utf8Str configDir, newConfigDir;
9761
9762 do
9763 {
9764 int vrc = VINF_SUCCESS;
9765
9766 Utf8Str name = mUserData.backedUpData()->s.strName;
9767 Utf8Str newName = mUserData->s.strName;
9768 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9769 if (group == "/")
9770 group.setNull();
9771 Utf8Str newGroup = mUserData->s.llGroups.front();
9772 if (newGroup == "/")
9773 newGroup.setNull();
9774
9775 configFile = mData->m_strConfigFileFull;
9776
9777 /* first, rename the directory if it matches the group and machine name */
9778 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9779 group.c_str(), RTPATH_DELIMITER, name.c_str());
9780 /** @todo hack, make somehow use of ComposeMachineFilename */
9781 if (mUserData->s.fDirectoryIncludesUUID)
9782 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9783 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9784 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9785 /** @todo hack, make somehow use of ComposeMachineFilename */
9786 if (mUserData->s.fDirectoryIncludesUUID)
9787 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9788 configDir = configFile;
9789 configDir.stripFilename();
9790 newConfigDir = configDir;
9791 if ( configDir.length() >= groupPlusName.length()
9792 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9793 groupPlusName.c_str()))
9794 {
9795 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9796 Utf8Str newConfigBaseDir(newConfigDir);
9797 newConfigDir.append(newGroupPlusName);
9798 /* consistency: use \ if appropriate on the platform */
9799 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9800 /* new dir and old dir cannot be equal here because of 'if'
9801 * above and because name != newName */
9802 Assert(configDir != newConfigDir);
9803 if (!fSettingsFileIsNew)
9804 {
9805 /* perform real rename only if the machine is not new */
9806 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9807 if ( vrc == VERR_FILE_NOT_FOUND
9808 || vrc == VERR_PATH_NOT_FOUND)
9809 {
9810 /* create the parent directory, then retry renaming */
9811 Utf8Str parent(newConfigDir);
9812 parent.stripFilename();
9813 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9814 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9815 }
9816 if (RT_FAILURE(vrc))
9817 {
9818 rc = setError(E_FAIL,
9819 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9820 configDir.c_str(),
9821 newConfigDir.c_str(),
9822 vrc);
9823 break;
9824 }
9825 /* delete subdirectories which are no longer needed */
9826 Utf8Str dir(configDir);
9827 dir.stripFilename();
9828 while (dir != newConfigBaseDir && dir != ".")
9829 {
9830 vrc = RTDirRemove(dir.c_str());
9831 if (RT_FAILURE(vrc))
9832 break;
9833 dir.stripFilename();
9834 }
9835 dirRenamed = true;
9836 }
9837 }
9838
9839 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9840 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9841
9842 /* then try to rename the settings file itself */
9843 if (newConfigFile != configFile)
9844 {
9845 /* get the path to old settings file in renamed directory */
9846 configFile = Utf8StrFmt("%s%c%s",
9847 newConfigDir.c_str(),
9848 RTPATH_DELIMITER,
9849 RTPathFilename(configFile.c_str()));
9850 if (!fSettingsFileIsNew)
9851 {
9852 /* perform real rename only if the machine is not new */
9853 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9854 if (RT_FAILURE(vrc))
9855 {
9856 rc = setError(E_FAIL,
9857 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9858 configFile.c_str(),
9859 newConfigFile.c_str(),
9860 vrc);
9861 break;
9862 }
9863 fileRenamed = true;
9864 configFilePrev = configFile;
9865 configFilePrev += "-prev";
9866 newConfigFilePrev = newConfigFile;
9867 newConfigFilePrev += "-prev";
9868 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9869 }
9870 }
9871
9872 // update m_strConfigFileFull amd mConfigFile
9873 mData->m_strConfigFileFull = newConfigFile;
9874 // compute the relative path too
9875 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9876
9877 // store the old and new so that VirtualBox::i_saveSettings() can update
9878 // the media registry
9879 if ( mData->mRegistered
9880 && (configDir != newConfigDir || configFile != newConfigFile))
9881 {
9882 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9883
9884 if (pfNeedsGlobalSaveSettings)
9885 *pfNeedsGlobalSaveSettings = true;
9886 }
9887
9888 // in the saved state file path, replace the old directory with the new directory
9889 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9890 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9891
9892 // and do the same thing for the saved state file paths of all the online snapshots
9893 if (mData->mFirstSnapshot)
9894 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9895 newConfigDir.c_str());
9896 }
9897 while (0);
9898
9899 if (FAILED(rc))
9900 {
9901 /* silently try to rename everything back */
9902 if (fileRenamed)
9903 {
9904 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9905 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9906 }
9907 if (dirRenamed)
9908 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9909 }
9910
9911 if (FAILED(rc)) return rc;
9912 }
9913
9914 if (fSettingsFileIsNew)
9915 {
9916 /* create a virgin config file */
9917 int vrc = VINF_SUCCESS;
9918
9919 /* ensure the settings directory exists */
9920 Utf8Str path(mData->m_strConfigFileFull);
9921 path.stripFilename();
9922 if (!RTDirExists(path.c_str()))
9923 {
9924 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9925 if (RT_FAILURE(vrc))
9926 {
9927 return setError(E_FAIL,
9928 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9929 path.c_str(),
9930 vrc);
9931 }
9932 }
9933
9934 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9935 path = Utf8Str(mData->m_strConfigFileFull);
9936 RTFILE f = NIL_RTFILE;
9937 vrc = RTFileOpen(&f, path.c_str(),
9938 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9939 if (RT_FAILURE(vrc))
9940 return setError(E_FAIL,
9941 tr("Could not create the settings file '%s' (%Rrc)"),
9942 path.c_str(),
9943 vrc);
9944 RTFileClose(f);
9945 }
9946
9947 return rc;
9948}
9949
9950/**
9951 * Saves and commits machine data, user data and hardware data.
9952 *
9953 * Note that on failure, the data remains uncommitted.
9954 *
9955 * @a aFlags may combine the following flags:
9956 *
9957 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9958 * Used when saving settings after an operation that makes them 100%
9959 * correspond to the settings from the current snapshot.
9960 * - SaveS_Force: settings will be saved without doing a deep compare of the
9961 * settings structures. This is used when this is called because snapshots
9962 * have changed to avoid the overhead of the deep compare.
9963 *
9964 * @note Must be called from under this object's write lock. Locks children for
9965 * writing.
9966 *
9967 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9968 * initialized to false and that will be set to true by this function if
9969 * the caller must invoke VirtualBox::i_saveSettings() because the global
9970 * settings have changed. This will happen if a machine rename has been
9971 * saved and the global machine and media registries will therefore need
9972 * updating.
9973 * @param aFlags Flags.
9974 */
9975HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9976 int aFlags /*= 0*/)
9977{
9978 LogFlowThisFuncEnter();
9979
9980 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9981
9982 /* make sure child objects are unable to modify the settings while we are
9983 * saving them */
9984 i_ensureNoStateDependencies();
9985
9986 AssertReturn(!i_isSnapshotMachine(),
9987 E_FAIL);
9988
9989 HRESULT rc = S_OK;
9990 bool fNeedsWrite = false;
9991
9992 /* First, prepare to save settings. It will care about renaming the
9993 * settings directory and file if the machine name was changed and about
9994 * creating a new settings file if this is a new machine. */
9995 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9996 if (FAILED(rc)) return rc;
9997
9998 // keep a pointer to the current settings structures
9999 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10000 settings::MachineConfigFile *pNewConfig = NULL;
10001
10002 try
10003 {
10004 // make a fresh one to have everyone write stuff into
10005 pNewConfig = new settings::MachineConfigFile(NULL);
10006 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10007
10008 // now go and copy all the settings data from COM to the settings structures
10009 // (this calls i_saveSettings() on all the COM objects in the machine)
10010 i_copyMachineDataToSettings(*pNewConfig);
10011
10012 if (aFlags & SaveS_ResetCurStateModified)
10013 {
10014 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10015 mData->mCurrentStateModified = FALSE;
10016 fNeedsWrite = true; // always, no need to compare
10017 }
10018 else if (aFlags & SaveS_Force)
10019 {
10020 fNeedsWrite = true; // always, no need to compare
10021 }
10022 else
10023 {
10024 if (!mData->mCurrentStateModified)
10025 {
10026 // do a deep compare of the settings that we just saved with the settings
10027 // previously stored in the config file; this invokes MachineConfigFile::operator==
10028 // which does a deep compare of all the settings, which is expensive but less expensive
10029 // than writing out XML in vain
10030 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10031
10032 // could still be modified if any settings changed
10033 mData->mCurrentStateModified = fAnySettingsChanged;
10034
10035 fNeedsWrite = fAnySettingsChanged;
10036 }
10037 else
10038 fNeedsWrite = true;
10039 }
10040
10041 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10042
10043 if (fNeedsWrite)
10044 // now spit it all out!
10045 pNewConfig->write(mData->m_strConfigFileFull);
10046
10047 mData->pMachineConfigFile = pNewConfig;
10048 delete pOldConfig;
10049 i_commit();
10050
10051 // after saving settings, we are no longer different from the XML on disk
10052 mData->flModifications = 0;
10053 }
10054 catch (HRESULT err)
10055 {
10056 // we assume that error info is set by the thrower
10057 rc = err;
10058
10059 // restore old config
10060 delete pNewConfig;
10061 mData->pMachineConfigFile = pOldConfig;
10062 }
10063 catch (...)
10064 {
10065 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10066 }
10067
10068 if (fNeedsWrite)
10069 {
10070 /* Fire the data change event, even on failure (since we've already
10071 * committed all data). This is done only for SessionMachines because
10072 * mutable Machine instances are always not registered (i.e. private
10073 * to the client process that creates them) and thus don't need to
10074 * inform callbacks. */
10075 if (i_isSessionMachine())
10076 mParent->i_onMachineDataChange(mData->mUuid);
10077 }
10078
10079 LogFlowThisFunc(("rc=%08X\n", rc));
10080 LogFlowThisFuncLeave();
10081 return rc;
10082}
10083
10084/**
10085 * Implementation for saving the machine settings into the given
10086 * settings::MachineConfigFile instance. This copies machine extradata
10087 * from the previous machine config file in the instance data, if any.
10088 *
10089 * This gets called from two locations:
10090 *
10091 * -- Machine::i_saveSettings(), during the regular XML writing;
10092 *
10093 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10094 * exported to OVF and we write the VirtualBox proprietary XML
10095 * into a <vbox:Machine> tag.
10096 *
10097 * This routine fills all the fields in there, including snapshots, *except*
10098 * for the following:
10099 *
10100 * -- fCurrentStateModified. There is some special logic associated with that.
10101 *
10102 * The caller can then call MachineConfigFile::write() or do something else
10103 * with it.
10104 *
10105 * Caller must hold the machine lock!
10106 *
10107 * This throws XML errors and HRESULT, so the caller must have a catch block!
10108 */
10109void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10110{
10111 // deep copy extradata, being extra careful with self assignment (the STL
10112 // map assignment on Mac OS X clang based Xcode isn't checking)
10113 if (&config != mData->pMachineConfigFile)
10114 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10115
10116 config.uuid = mData->mUuid;
10117
10118 // copy name, description, OS type, teleport, UTC etc.
10119 config.machineUserData = mUserData->s;
10120
10121 if ( mData->mMachineState == MachineState_Saved
10122 || mData->mMachineState == MachineState_Restoring
10123 // when doing certain snapshot operations we may or may not have
10124 // a saved state in the current state, so keep everything as is
10125 || ( ( mData->mMachineState == MachineState_Snapshotting
10126 || mData->mMachineState == MachineState_DeletingSnapshot
10127 || mData->mMachineState == MachineState_RestoringSnapshot)
10128 && (!mSSData->strStateFilePath.isEmpty())
10129 )
10130 )
10131 {
10132 Assert(!mSSData->strStateFilePath.isEmpty());
10133 /* try to make the file name relative to the settings file dir */
10134 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10135 }
10136 else
10137 {
10138 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10139 config.strStateFile.setNull();
10140 }
10141
10142 if (mData->mCurrentSnapshot)
10143 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10144 else
10145 config.uuidCurrentSnapshot.clear();
10146
10147 config.timeLastStateChange = mData->mLastStateChange;
10148 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10149 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10150
10151 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10152 if (FAILED(rc)) throw rc;
10153
10154 // save machine's media registry if this is VirtualBox 4.0 or later
10155 if (config.canHaveOwnMediaRegistry())
10156 {
10157 // determine machine folder
10158 Utf8Str strMachineFolder = i_getSettingsFileFull();
10159 strMachineFolder.stripFilename();
10160 mParent->i_saveMediaRegistry(config.mediaRegistry,
10161 i_getId(), // only media with registry ID == machine UUID
10162 strMachineFolder);
10163 // this throws HRESULT
10164 }
10165
10166 // save snapshots
10167 rc = i_saveAllSnapshots(config);
10168 if (FAILED(rc)) throw rc;
10169}
10170
10171/**
10172 * Saves all snapshots of the machine into the given machine config file. Called
10173 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10174 * @param config
10175 * @return
10176 */
10177HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10178{
10179 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10180
10181 HRESULT rc = S_OK;
10182
10183 try
10184 {
10185 config.llFirstSnapshot.clear();
10186
10187 if (mData->mFirstSnapshot)
10188 {
10189 // the settings use a list for "the first snapshot"
10190 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10191
10192 // get reference to the snapshot on the list and work on that
10193 // element straight in the list to avoid excessive copying later
10194 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10195 if (FAILED(rc)) throw rc;
10196 }
10197
10198// if (mType == IsSessionMachine)
10199// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10200
10201 }
10202 catch (HRESULT err)
10203 {
10204 /* we assume that error info is set by the thrower */
10205 rc = err;
10206 }
10207 catch (...)
10208 {
10209 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10210 }
10211
10212 return rc;
10213}
10214
10215/**
10216 * Saves the VM hardware configuration. It is assumed that the
10217 * given node is empty.
10218 *
10219 * @param data Reference to the settings object for the hardware config.
10220 * @param pDbg Pointer to the settings object for the debugging config
10221 * which happens to live in mHWData.
10222 * @param pAutostart Pointer to the settings object for the autostart config
10223 * which happens to live in mHWData.
10224 */
10225HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10226 settings::Autostart *pAutostart)
10227{
10228 HRESULT rc = S_OK;
10229
10230 try
10231 {
10232 /* The hardware version attribute (optional).
10233 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10234 if ( mHWData->mHWVersion == "1"
10235 && mSSData->strStateFilePath.isEmpty()
10236 )
10237 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10238 other point needs to be found where this can be done. */
10239
10240 data.strVersion = mHWData->mHWVersion;
10241 data.uuid = mHWData->mHardwareUUID;
10242
10243 // CPU
10244 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10245 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10246 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10247 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10248 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10249 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10250 data.fPAE = !!mHWData->mPAEEnabled;
10251 data.enmLongMode = mHWData->mLongMode;
10252 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10253 data.fAPIC = !!mHWData->mAPIC;
10254 data.fX2APIC = !!mHWData->mX2APIC;
10255 data.cCPUs = mHWData->mCPUCount;
10256 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10257 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10258 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10259 data.strCpuProfile = mHWData->mCpuProfile;
10260
10261 data.llCpus.clear();
10262 if (data.fCpuHotPlug)
10263 {
10264 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10265 {
10266 if (mHWData->mCPUAttached[idx])
10267 {
10268 settings::Cpu cpu;
10269 cpu.ulId = idx;
10270 data.llCpus.push_back(cpu);
10271 }
10272 }
10273 }
10274
10275 /* Standard and Extended CPUID leafs. */
10276 data.llCpuIdLeafs.clear();
10277 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10278 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10279 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10280 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10281 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10282 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10283
10284 // memory
10285 data.ulMemorySizeMB = mHWData->mMemorySize;
10286 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10287
10288 // firmware
10289 data.firmwareType = mHWData->mFirmwareType;
10290
10291 // HID
10292 data.pointingHIDType = mHWData->mPointingHIDType;
10293 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10294
10295 // chipset
10296 data.chipsetType = mHWData->mChipsetType;
10297
10298 // paravirt
10299 data.paravirtProvider = mHWData->mParavirtProvider;
10300 data.strParavirtDebug = mHWData->mParavirtDebug;
10301
10302 // emulated USB card reader
10303 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10304
10305 // HPET
10306 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10307
10308 // boot order
10309 data.mapBootOrder.clear();
10310 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10311 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10312
10313 // display
10314 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10315 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10316 data.cMonitors = mHWData->mMonitorCount;
10317 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10318 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10319 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10320 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10321 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10322 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10323 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10324 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10325 {
10326 if (mHWData->maVideoCaptureScreens[i])
10327 ASMBitSet(&data.u64VideoCaptureScreens, i);
10328 else
10329 ASMBitClear(&data.u64VideoCaptureScreens, i);
10330 }
10331 /* store relative video capture file if possible */
10332 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10333
10334 /* VRDEServer settings (optional) */
10335 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10336 if (FAILED(rc)) throw rc;
10337
10338 /* BIOS (required) */
10339 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10340 if (FAILED(rc)) throw rc;
10341
10342 /* USB Controller (required) */
10343 data.usbSettings.llUSBControllers.clear();
10344 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10345 {
10346 ComObjPtr<USBController> ctrl = *it;
10347 settings::USBController settingsCtrl;
10348
10349 settingsCtrl.strName = ctrl->i_getName();
10350 settingsCtrl.enmType = ctrl->i_getControllerType();
10351
10352 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10353 }
10354
10355 /* USB device filters (required) */
10356 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10357 if (FAILED(rc)) throw rc;
10358
10359 /* Network adapters (required) */
10360 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10361 data.llNetworkAdapters.clear();
10362 /* Write out only the nominal number of network adapters for this
10363 * chipset type. Since Machine::commit() hasn't been called there
10364 * may be extra NIC settings in the vector. */
10365 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10366 {
10367 settings::NetworkAdapter nic;
10368 nic.ulSlot = (uint32_t)slot;
10369 /* paranoia check... must not be NULL, but must not crash either. */
10370 if (mNetworkAdapters[slot])
10371 {
10372 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10373 if (FAILED(rc)) throw rc;
10374
10375 data.llNetworkAdapters.push_back(nic);
10376 }
10377 }
10378
10379 /* Serial ports */
10380 data.llSerialPorts.clear();
10381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10382 {
10383 if (mSerialPorts[slot]->i_hasDefaults())
10384 continue;
10385
10386 settings::SerialPort s;
10387 s.ulSlot = slot;
10388 rc = mSerialPorts[slot]->i_saveSettings(s);
10389 if (FAILED(rc)) return rc;
10390
10391 data.llSerialPorts.push_back(s);
10392 }
10393
10394 /* Parallel ports */
10395 data.llParallelPorts.clear();
10396 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10397 {
10398 if (mParallelPorts[slot]->i_hasDefaults())
10399 continue;
10400
10401 settings::ParallelPort p;
10402 p.ulSlot = slot;
10403 rc = mParallelPorts[slot]->i_saveSettings(p);
10404 if (FAILED(rc)) return rc;
10405
10406 data.llParallelPorts.push_back(p);
10407 }
10408
10409 /* Audio adapter */
10410 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10411 if (FAILED(rc)) return rc;
10412
10413 rc = i_saveStorageControllers(data.storage);
10414 if (FAILED(rc)) return rc;
10415
10416 /* Shared folders */
10417 data.llSharedFolders.clear();
10418 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10419 it != mHWData->mSharedFolders.end();
10420 ++it)
10421 {
10422 SharedFolder *pSF = *it;
10423 AutoCaller sfCaller(pSF);
10424 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10425 settings::SharedFolder sf;
10426 sf.strName = pSF->i_getName();
10427 sf.strHostPath = pSF->i_getHostPath();
10428 sf.fWritable = !!pSF->i_isWritable();
10429 sf.fAutoMount = !!pSF->i_isAutoMounted();
10430
10431 data.llSharedFolders.push_back(sf);
10432 }
10433
10434 // clipboard
10435 data.clipboardMode = mHWData->mClipboardMode;
10436
10437 // drag'n'drop
10438 data.dndMode = mHWData->mDnDMode;
10439
10440 /* Guest */
10441 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10442
10443 // IO settings
10444 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10445 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10446
10447 /* BandwidthControl (required) */
10448 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10449 if (FAILED(rc)) throw rc;
10450
10451 /* Host PCI devices */
10452 data.pciAttachments.clear();
10453 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10454 it != mHWData->mPCIDeviceAssignments.end();
10455 ++it)
10456 {
10457 ComObjPtr<PCIDeviceAttachment> pda = *it;
10458 settings::HostPCIDeviceAttachment hpda;
10459
10460 rc = pda->i_saveSettings(hpda);
10461 if (FAILED(rc)) throw rc;
10462
10463 data.pciAttachments.push_back(hpda);
10464 }
10465
10466 // guest properties
10467 data.llGuestProperties.clear();
10468#ifdef VBOX_WITH_GUEST_PROPS
10469 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10470 it != mHWData->mGuestProperties.end();
10471 ++it)
10472 {
10473 HWData::GuestProperty property = it->second;
10474
10475 /* Remove transient guest properties at shutdown unless we
10476 * are saving state. Note that restoring snapshot intentionally
10477 * keeps them, they will be removed if appropriate once the final
10478 * machine state is set (as crashes etc. need to work). */
10479 if ( ( mData->mMachineState == MachineState_PoweredOff
10480 || mData->mMachineState == MachineState_Aborted
10481 || mData->mMachineState == MachineState_Teleported)
10482 && ( property.mFlags & guestProp::TRANSIENT
10483 || property.mFlags & guestProp::TRANSRESET))
10484 continue;
10485 settings::GuestProperty prop;
10486 prop.strName = it->first;
10487 prop.strValue = property.strValue;
10488 prop.timestamp = property.mTimestamp;
10489 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10490 guestProp::writeFlags(property.mFlags, szFlags);
10491 prop.strFlags = szFlags;
10492
10493 data.llGuestProperties.push_back(prop);
10494 }
10495
10496 /* I presume this doesn't require a backup(). */
10497 mData->mGuestPropertiesModified = FALSE;
10498#endif /* VBOX_WITH_GUEST_PROPS defined */
10499
10500 *pDbg = mHWData->mDebugging;
10501 *pAutostart = mHWData->mAutostart;
10502
10503 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10504 }
10505 catch(std::bad_alloc &)
10506 {
10507 return E_OUTOFMEMORY;
10508 }
10509
10510 AssertComRC(rc);
10511 return rc;
10512}
10513
10514/**
10515 * Saves the storage controller configuration.
10516 *
10517 * @param data storage settings.
10518 */
10519HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10520{
10521 data.llStorageControllers.clear();
10522
10523 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10524 it != mStorageControllers->end();
10525 ++it)
10526 {
10527 HRESULT rc;
10528 ComObjPtr<StorageController> pCtl = *it;
10529
10530 settings::StorageController ctl;
10531 ctl.strName = pCtl->i_getName();
10532 ctl.controllerType = pCtl->i_getControllerType();
10533 ctl.storageBus = pCtl->i_getStorageBus();
10534 ctl.ulInstance = pCtl->i_getInstance();
10535 ctl.fBootable = pCtl->i_getBootable();
10536
10537 /* Save the port count. */
10538 ULONG portCount;
10539 rc = pCtl->COMGETTER(PortCount)(&portCount);
10540 ComAssertComRCRet(rc, rc);
10541 ctl.ulPortCount = portCount;
10542
10543 /* Save fUseHostIOCache */
10544 BOOL fUseHostIOCache;
10545 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10546 ComAssertComRCRet(rc, rc);
10547 ctl.fUseHostIOCache = !!fUseHostIOCache;
10548
10549 /* save the devices now. */
10550 rc = i_saveStorageDevices(pCtl, ctl);
10551 ComAssertComRCRet(rc, rc);
10552
10553 data.llStorageControllers.push_back(ctl);
10554 }
10555
10556 return S_OK;
10557}
10558
10559/**
10560 * Saves the hard disk configuration.
10561 */
10562HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10563 settings::StorageController &data)
10564{
10565 MediaData::AttachmentList atts;
10566
10567 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10568 if (FAILED(rc)) return rc;
10569
10570 data.llAttachedDevices.clear();
10571 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10572 it != atts.end();
10573 ++it)
10574 {
10575 settings::AttachedDevice dev;
10576 IMediumAttachment *iA = *it;
10577 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10578 Medium *pMedium = pAttach->i_getMedium();
10579
10580 dev.deviceType = pAttach->i_getType();
10581 dev.lPort = pAttach->i_getPort();
10582 dev.lDevice = pAttach->i_getDevice();
10583 dev.fPassThrough = pAttach->i_getPassthrough();
10584 dev.fHotPluggable = pAttach->i_getHotPluggable();
10585 if (pMedium)
10586 {
10587 if (pMedium->i_isHostDrive())
10588 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10589 else
10590 dev.uuid = pMedium->i_getId();
10591 dev.fTempEject = pAttach->i_getTempEject();
10592 dev.fNonRotational = pAttach->i_getNonRotational();
10593 dev.fDiscard = pAttach->i_getDiscard();
10594 }
10595
10596 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10597
10598 data.llAttachedDevices.push_back(dev);
10599 }
10600
10601 return S_OK;
10602}
10603
10604/**
10605 * Saves machine state settings as defined by aFlags
10606 * (SaveSTS_* values).
10607 *
10608 * @param aFlags Combination of SaveSTS_* flags.
10609 *
10610 * @note Locks objects for writing.
10611 */
10612HRESULT Machine::i_saveStateSettings(int aFlags)
10613{
10614 if (aFlags == 0)
10615 return S_OK;
10616
10617 AutoCaller autoCaller(this);
10618 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10619
10620 /* This object's write lock is also necessary to serialize file access
10621 * (prevent concurrent reads and writes) */
10622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10623
10624 HRESULT rc = S_OK;
10625
10626 Assert(mData->pMachineConfigFile);
10627
10628 try
10629 {
10630 if (aFlags & SaveSTS_CurStateModified)
10631 mData->pMachineConfigFile->fCurrentStateModified = true;
10632
10633 if (aFlags & SaveSTS_StateFilePath)
10634 {
10635 if (!mSSData->strStateFilePath.isEmpty())
10636 /* try to make the file name relative to the settings file dir */
10637 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10638 else
10639 mData->pMachineConfigFile->strStateFile.setNull();
10640 }
10641
10642 if (aFlags & SaveSTS_StateTimeStamp)
10643 {
10644 Assert( mData->mMachineState != MachineState_Aborted
10645 || mSSData->strStateFilePath.isEmpty());
10646
10647 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10648
10649 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10650/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10651 }
10652
10653 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10654 }
10655 catch (...)
10656 {
10657 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10658 }
10659
10660 return rc;
10661}
10662
10663/**
10664 * Ensures that the given medium is added to a media registry. If this machine
10665 * was created with 4.0 or later, then the machine registry is used. Otherwise
10666 * the global VirtualBox media registry is used.
10667 *
10668 * Caller must NOT hold machine lock, media tree or any medium locks!
10669 *
10670 * @param pMedium
10671 */
10672void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10673{
10674 /* Paranoia checks: do not hold machine or media tree locks. */
10675 AssertReturnVoid(!isWriteLockOnCurrentThread());
10676 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10677
10678 ComObjPtr<Medium> pBase;
10679 {
10680 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10681 pBase = pMedium->i_getBase();
10682 }
10683
10684 /* Paranoia checks: do not hold medium locks. */
10685 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10686 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10687
10688 // decide which medium registry to use now that the medium is attached:
10689 Guid uuid;
10690 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10691 // machine XML is VirtualBox 4.0 or higher:
10692 uuid = i_getId(); // machine UUID
10693 else
10694 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10695
10696 if (pMedium->i_addRegistry(uuid))
10697 mParent->i_markRegistryModified(uuid);
10698
10699 /* For more complex hard disk structures it can happen that the base
10700 * medium isn't yet associated with any medium registry. Do that now. */
10701 if (pMedium != pBase)
10702 {
10703 /* Tree lock needed by Medium::addRegistry when recursing. */
10704 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10705 if (pBase->i_addRegistryRecursive(uuid))
10706 {
10707 treeLock.release();
10708 mParent->i_markRegistryModified(uuid);
10709 }
10710 }
10711}
10712
10713/**
10714 * Creates differencing hard disks for all normal hard disks attached to this
10715 * machine and a new set of attachments to refer to created disks.
10716 *
10717 * Used when taking a snapshot or when deleting the current state. Gets called
10718 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10719 *
10720 * This method assumes that mMediaData contains the original hard disk attachments
10721 * it needs to create diffs for. On success, these attachments will be replaced
10722 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10723 * called to delete created diffs which will also rollback mMediaData and restore
10724 * whatever was backed up before calling this method.
10725 *
10726 * Attachments with non-normal hard disks are left as is.
10727 *
10728 * If @a aOnline is @c false then the original hard disks that require implicit
10729 * diffs will be locked for reading. Otherwise it is assumed that they are
10730 * already locked for writing (when the VM was started). Note that in the latter
10731 * case it is responsibility of the caller to lock the newly created diffs for
10732 * writing if this method succeeds.
10733 *
10734 * @param aProgress Progress object to run (must contain at least as
10735 * many operations left as the number of hard disks
10736 * attached).
10737 * @param aWeight Weight of this operation.
10738 * @param aOnline Whether the VM was online prior to this operation.
10739 *
10740 * @note The progress object is not marked as completed, neither on success nor
10741 * on failure. This is a responsibility of the caller.
10742 *
10743 * @note Locks this object and the media tree for writing.
10744 */
10745HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10746 ULONG aWeight,
10747 bool aOnline)
10748{
10749 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10750
10751 AutoCaller autoCaller(this);
10752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10753
10754 AutoMultiWriteLock2 alock(this->lockHandle(),
10755 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10756
10757 /* must be in a protective state because we release the lock below */
10758 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10759 || mData->mMachineState == MachineState_OnlineSnapshotting
10760 || mData->mMachineState == MachineState_LiveSnapshotting
10761 || mData->mMachineState == MachineState_RestoringSnapshot
10762 || mData->mMachineState == MachineState_DeletingSnapshot
10763 , E_FAIL);
10764
10765 HRESULT rc = S_OK;
10766
10767 // use appropriate locked media map (online or offline)
10768 MediumLockListMap lockedMediaOffline;
10769 MediumLockListMap *lockedMediaMap;
10770 if (aOnline)
10771 lockedMediaMap = &mData->mSession.mLockedMedia;
10772 else
10773 lockedMediaMap = &lockedMediaOffline;
10774
10775 try
10776 {
10777 if (!aOnline)
10778 {
10779 /* lock all attached hard disks early to detect "in use"
10780 * situations before creating actual diffs */
10781 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10782 it != mMediaData->mAttachments.end();
10783 ++it)
10784 {
10785 MediumAttachment* pAtt = *it;
10786 if (pAtt->i_getType() == DeviceType_HardDisk)
10787 {
10788 Medium* pMedium = pAtt->i_getMedium();
10789 Assert(pMedium);
10790
10791 MediumLockList *pMediumLockList(new MediumLockList());
10792 alock.release();
10793 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10794 NULL /* pToLockWrite */,
10795 false /* fMediumLockWriteAll */,
10796 NULL,
10797 *pMediumLockList);
10798 alock.acquire();
10799 if (FAILED(rc))
10800 {
10801 delete pMediumLockList;
10802 throw rc;
10803 }
10804 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10805 if (FAILED(rc))
10806 {
10807 throw setError(rc,
10808 tr("Collecting locking information for all attached media failed"));
10809 }
10810 }
10811 }
10812
10813 /* Now lock all media. If this fails, nothing is locked. */
10814 alock.release();
10815 rc = lockedMediaMap->Lock();
10816 alock.acquire();
10817 if (FAILED(rc))
10818 {
10819 throw setError(rc,
10820 tr("Locking of attached media failed"));
10821 }
10822 }
10823
10824 /* remember the current list (note that we don't use backup() since
10825 * mMediaData may be already backed up) */
10826 MediaData::AttachmentList atts = mMediaData->mAttachments;
10827
10828 /* start from scratch */
10829 mMediaData->mAttachments.clear();
10830
10831 /* go through remembered attachments and create diffs for normal hard
10832 * disks and attach them */
10833 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10834 it != atts.end();
10835 ++it)
10836 {
10837 MediumAttachment* pAtt = *it;
10838
10839 DeviceType_T devType = pAtt->i_getType();
10840 Medium* pMedium = pAtt->i_getMedium();
10841
10842 if ( devType != DeviceType_HardDisk
10843 || pMedium == NULL
10844 || pMedium->i_getType() != MediumType_Normal)
10845 {
10846 /* copy the attachment as is */
10847
10848 /** @todo the progress object created in SessionMachine::TakeSnaphot
10849 * only expects operations for hard disks. Later other
10850 * device types need to show up in the progress as well. */
10851 if (devType == DeviceType_HardDisk)
10852 {
10853 if (pMedium == NULL)
10854 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10855 aWeight); // weight
10856 else
10857 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10858 pMedium->i_getBase()->i_getName().c_str()).raw(),
10859 aWeight); // weight
10860 }
10861
10862 mMediaData->mAttachments.push_back(pAtt);
10863 continue;
10864 }
10865
10866 /* need a diff */
10867 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10868 pMedium->i_getBase()->i_getName().c_str()).raw(),
10869 aWeight); // weight
10870
10871 Utf8Str strFullSnapshotFolder;
10872 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10873
10874 ComObjPtr<Medium> diff;
10875 diff.createObject();
10876 // store the diff in the same registry as the parent
10877 // (this cannot fail here because we can't create implicit diffs for
10878 // unregistered images)
10879 Guid uuidRegistryParent;
10880 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10881 Assert(fInRegistry); NOREF(fInRegistry);
10882 rc = diff->init(mParent,
10883 pMedium->i_getPreferredDiffFormat(),
10884 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10885 uuidRegistryParent,
10886 DeviceType_HardDisk);
10887 if (FAILED(rc)) throw rc;
10888
10889 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10890 * the push_back? Looks like we're going to release medium with the
10891 * wrong kind of lock (general issue with if we fail anywhere at all)
10892 * and an orphaned VDI in the snapshots folder. */
10893
10894 /* update the appropriate lock list */
10895 MediumLockList *pMediumLockList;
10896 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10897 AssertComRCThrowRC(rc);
10898 if (aOnline)
10899 {
10900 alock.release();
10901 /* The currently attached medium will be read-only, change
10902 * the lock type to read. */
10903 rc = pMediumLockList->Update(pMedium, false);
10904 alock.acquire();
10905 AssertComRCThrowRC(rc);
10906 }
10907
10908 /* release the locks before the potentially lengthy operation */
10909 alock.release();
10910 rc = pMedium->i_createDiffStorage(diff,
10911 pMedium->i_getPreferredDiffVariant(),
10912 pMediumLockList,
10913 NULL /* aProgress */,
10914 true /* aWait */);
10915 alock.acquire();
10916 if (FAILED(rc)) throw rc;
10917
10918 /* actual lock list update is done in Machine::i_commitMedia */
10919
10920 rc = diff->i_addBackReference(mData->mUuid);
10921 AssertComRCThrowRC(rc);
10922
10923 /* add a new attachment */
10924 ComObjPtr<MediumAttachment> attachment;
10925 attachment.createObject();
10926 rc = attachment->init(this,
10927 diff,
10928 pAtt->i_getControllerName(),
10929 pAtt->i_getPort(),
10930 pAtt->i_getDevice(),
10931 DeviceType_HardDisk,
10932 true /* aImplicit */,
10933 false /* aPassthrough */,
10934 false /* aTempEject */,
10935 pAtt->i_getNonRotational(),
10936 pAtt->i_getDiscard(),
10937 pAtt->i_getHotPluggable(),
10938 pAtt->i_getBandwidthGroup());
10939 if (FAILED(rc)) throw rc;
10940
10941 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10942 AssertComRCThrowRC(rc);
10943 mMediaData->mAttachments.push_back(attachment);
10944 }
10945 }
10946 catch (HRESULT aRC) { rc = aRC; }
10947
10948 /* unlock all hard disks we locked when there is no VM */
10949 if (!aOnline)
10950 {
10951 ErrorInfoKeeper eik;
10952
10953 HRESULT rc1 = lockedMediaMap->Clear();
10954 AssertComRC(rc1);
10955 }
10956
10957 return rc;
10958}
10959
10960/**
10961 * Deletes implicit differencing hard disks created either by
10962 * #i_createImplicitDiffs() or by #attachDevice() and rolls back mMediaData.
10963 *
10964 * Note that to delete hard disks created by #attachDevice() this method is
10965 * called from #fixupMedia() when the changes are rolled back.
10966 *
10967 * @note Locks this object and the media tree for writing.
10968 */
10969HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10970{
10971 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10972
10973 AutoCaller autoCaller(this);
10974 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10975
10976 AutoMultiWriteLock2 alock(this->lockHandle(),
10977 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10978
10979 /* We absolutely must have backed up state. */
10980 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10981
10982 /* Check if there are any implicitly created diff images. */
10983 bool fImplicitDiffs = false;
10984 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10985 it != mMediaData->mAttachments.end();
10986 ++it)
10987 {
10988 const ComObjPtr<MediumAttachment> &pAtt = *it;
10989 if (pAtt->i_isImplicit())
10990 {
10991 fImplicitDiffs = true;
10992 break;
10993 }
10994 }
10995 /* If there is nothing to do, leave early. This saves lots of image locking
10996 * effort. It also avoids a MachineStateChanged event without real reason.
10997 * This is important e.g. when loading a VM config, because there should be
10998 * no events. Otherwise API clients can become thoroughly confused for
10999 * inaccessible VMs (the code for loading VM configs uses this method for
11000 * cleanup if the config makes no sense), as they take such events as an
11001 * indication that the VM is alive, and they would force the VM config to
11002 * be reread, leading to an endless loop. */
11003 if (!fImplicitDiffs)
11004 return S_OK;
11005
11006 HRESULT rc = S_OK;
11007 MachineState_T oldState = mData->mMachineState;
11008
11009 /* will release the lock before the potentially lengthy operation,
11010 * so protect with the special state (unless already protected) */
11011 if ( oldState != MachineState_Snapshotting
11012 && oldState != MachineState_OnlineSnapshotting
11013 && oldState != MachineState_LiveSnapshotting
11014 && oldState != MachineState_RestoringSnapshot
11015 && oldState != MachineState_DeletingSnapshot
11016 && oldState != MachineState_DeletingSnapshotOnline
11017 && oldState != MachineState_DeletingSnapshotPaused
11018 )
11019 i_setMachineState(MachineState_SettingUp);
11020
11021 // use appropriate locked media map (online or offline)
11022 MediumLockListMap lockedMediaOffline;
11023 MediumLockListMap *lockedMediaMap;
11024 if (aOnline)
11025 lockedMediaMap = &mData->mSession.mLockedMedia;
11026 else
11027 lockedMediaMap = &lockedMediaOffline;
11028
11029 try
11030 {
11031 if (!aOnline)
11032 {
11033 /* lock all attached hard disks early to detect "in use"
11034 * situations before deleting actual diffs */
11035 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11036 it != mMediaData->mAttachments.end();
11037 ++it)
11038 {
11039 MediumAttachment* pAtt = *it;
11040 if (pAtt->i_getType() == DeviceType_HardDisk)
11041 {
11042 Medium* pMedium = pAtt->i_getMedium();
11043 Assert(pMedium);
11044
11045 MediumLockList *pMediumLockList(new MediumLockList());
11046 alock.release();
11047 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11048 NULL /* pToLockWrite */,
11049 false /* fMediumLockWriteAll */,
11050 NULL,
11051 *pMediumLockList);
11052 alock.acquire();
11053
11054 if (FAILED(rc))
11055 {
11056 delete pMediumLockList;
11057 throw rc;
11058 }
11059
11060 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11061 if (FAILED(rc))
11062 throw rc;
11063 }
11064 }
11065
11066 if (FAILED(rc))
11067 throw rc;
11068 } // end of offline
11069
11070 /* Lock lists are now up to date and include implicitly created media */
11071
11072 /* Go through remembered attachments and delete all implicitly created
11073 * diffs and fix up the attachment information */
11074 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11075 MediaData::AttachmentList implicitAtts;
11076 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11077 it != mMediaData->mAttachments.end();
11078 ++it)
11079 {
11080 ComObjPtr<MediumAttachment> pAtt = *it;
11081 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11082 if (pMedium.isNull())
11083 continue;
11084
11085 // Implicit attachments go on the list for deletion and back references are removed.
11086 if (pAtt->i_isImplicit())
11087 {
11088 /* Deassociate and mark for deletion */
11089 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11090 rc = pMedium->i_removeBackReference(mData->mUuid);
11091 if (FAILED(rc))
11092 throw rc;
11093 implicitAtts.push_back(pAtt);
11094 continue;
11095 }
11096
11097 /* Was this medium attached before? */
11098 if (!i_findAttachment(oldAtts, pMedium))
11099 {
11100 /* no: de-associate */
11101 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11102 rc = pMedium->i_removeBackReference(mData->mUuid);
11103 if (FAILED(rc))
11104 throw rc;
11105 continue;
11106 }
11107 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11108 }
11109
11110 /* If there are implicit attachments to delete, throw away the lock
11111 * map contents (which will unlock all media) since the medium
11112 * attachments will be rolled back. Below we need to completely
11113 * recreate the lock map anyway since it is infinitely complex to
11114 * do this incrementally (would need reconstructing each attachment
11115 * change, which would be extremely hairy). */
11116 if (implicitAtts.size() != 0)
11117 {
11118 ErrorInfoKeeper eik;
11119
11120 HRESULT rc1 = lockedMediaMap->Clear();
11121 AssertComRC(rc1);
11122 }
11123
11124 /* rollback hard disk changes */
11125 mMediaData.rollback();
11126
11127 MultiResult mrc(S_OK);
11128
11129 // Delete unused implicit diffs.
11130 if (implicitAtts.size() != 0)
11131 {
11132 alock.release();
11133
11134 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11135 {
11136 // Remove medium associated with this attachment.
11137 ComObjPtr<MediumAttachment> pAtt = *it;
11138 Assert(pAtt);
11139 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11140 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11141 Assert(pMedium);
11142
11143 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11144 // continue on delete failure, just collect error messages
11145 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11146 pMedium->i_getLocationFull().c_str() ));
11147 mrc = rc;
11148 }
11149 // Clear the list of deleted implicit attachments now, while not
11150 // holding the lock, as it will ultimately trigger Medium::uninit()
11151 // calls which assume that the media tree lock isn't held.
11152 implicitAtts.clear();
11153
11154 alock.acquire();
11155
11156 /* if there is a VM recreate media lock map as mentioned above,
11157 * otherwise it is a waste of time and we leave things unlocked */
11158 if (aOnline)
11159 {
11160 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11161 /* must never be NULL, but better safe than sorry */
11162 if (!pMachine.isNull())
11163 {
11164 alock.release();
11165 rc = mData->mSession.mMachine->i_lockMedia();
11166 alock.acquire();
11167 if (FAILED(rc))
11168 throw rc;
11169 }
11170 }
11171 }
11172 }
11173 catch (HRESULT aRC) {rc = aRC;}
11174
11175 if (mData->mMachineState == MachineState_SettingUp)
11176 i_setMachineState(oldState);
11177
11178 /* unlock all hard disks we locked when there is no VM */
11179 if (!aOnline)
11180 {
11181 ErrorInfoKeeper eik;
11182
11183 HRESULT rc1 = lockedMediaMap->Clear();
11184 AssertComRC(rc1);
11185 }
11186
11187 return rc;
11188}
11189
11190
11191/**
11192 * Looks through the given list of media attachments for one with the given parameters
11193 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11194 * can be searched as well if needed.
11195 *
11196 * @param ll
11197 * @param aControllerName
11198 * @param aControllerPort
11199 * @param aDevice
11200 * @return
11201 */
11202MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11203 const Utf8Str &aControllerName,
11204 LONG aControllerPort,
11205 LONG aDevice)
11206{
11207 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11208 {
11209 MediumAttachment *pAttach = *it;
11210 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11211 return pAttach;
11212 }
11213
11214 return NULL;
11215}
11216
11217/**
11218 * Looks through the given list of media attachments for one with the given parameters
11219 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11220 * can be searched as well if needed.
11221 *
11222 * @param ll
11223 * @param pMedium
11224 * @return
11225 */
11226MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11227 ComObjPtr<Medium> pMedium)
11228{
11229 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11230 {
11231 MediumAttachment *pAttach = *it;
11232 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11233 if (pMediumThis == pMedium)
11234 return pAttach;
11235 }
11236
11237 return NULL;
11238}
11239
11240/**
11241 * Looks through the given list of media attachments for one with the given parameters
11242 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11243 * can be searched as well if needed.
11244 *
11245 * @param ll
11246 * @param id
11247 * @return
11248 */
11249MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11250 Guid &id)
11251{
11252 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11253 {
11254 MediumAttachment *pAttach = *it;
11255 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11256 if (pMediumThis->i_getId() == id)
11257 return pAttach;
11258 }
11259
11260 return NULL;
11261}
11262
11263/**
11264 * Main implementation for Machine::DetachDevice. This also gets called
11265 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11266 *
11267 * @param pAttach Medium attachment to detach.
11268 * @param writeLock Machine write lock which the caller must have locked once.
11269 * This may be released temporarily in here.
11270 * @param pSnapshot If NULL, then the detachment is for the current machine.
11271 * Otherwise this is for a SnapshotMachine, and this must be
11272 * its snapshot.
11273 * @return
11274 */
11275HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11276 AutoWriteLock &writeLock,
11277 Snapshot *pSnapshot)
11278{
11279 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11280 DeviceType_T mediumType = pAttach->i_getType();
11281
11282 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11283
11284 if (pAttach->i_isImplicit())
11285 {
11286 /* attempt to implicitly delete the implicitly created diff */
11287
11288 /// @todo move the implicit flag from MediumAttachment to Medium
11289 /// and forbid any hard disk operation when it is implicit. Or maybe
11290 /// a special media state for it to make it even more simple.
11291
11292 Assert(mMediaData.isBackedUp());
11293
11294 /* will release the lock before the potentially lengthy operation, so
11295 * protect with the special state */
11296 MachineState_T oldState = mData->mMachineState;
11297 i_setMachineState(MachineState_SettingUp);
11298
11299 writeLock.release();
11300
11301 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11302 true /*aWait*/);
11303
11304 writeLock.acquire();
11305
11306 i_setMachineState(oldState);
11307
11308 if (FAILED(rc)) return rc;
11309 }
11310
11311 i_setModified(IsModified_Storage);
11312 mMediaData.backup();
11313 mMediaData->mAttachments.remove(pAttach);
11314
11315 if (!oldmedium.isNull())
11316 {
11317 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11318 if (pSnapshot)
11319 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11320 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11321 else if (mediumType != DeviceType_HardDisk)
11322 oldmedium->i_removeBackReference(mData->mUuid);
11323 }
11324
11325 return S_OK;
11326}
11327
11328/**
11329 * Goes thru all media of the given list and
11330 *
11331 * 1) calls i_detachDevice() on each of them for this machine and
11332 * 2) adds all Medium objects found in the process to the given list,
11333 * depending on cleanupMode.
11334 *
11335 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11336 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11337 * media to the list.
11338 *
11339 * This gets called from Machine::Unregister, both for the actual Machine and
11340 * the SnapshotMachine objects that might be found in the snapshots.
11341 *
11342 * Requires caller and locking. The machine lock must be passed in because it
11343 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11344 *
11345 * @param writeLock Machine lock from top-level caller; this gets passed to
11346 * i_detachDevice.
11347 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11348 * object if called for a SnapshotMachine.
11349 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11350 * added to llMedia; if Full, then all media get added;
11351 * otherwise no media get added.
11352 * @param llMedia Caller's list to receive Medium objects which got detached so
11353 * caller can close() them, depending on cleanupMode.
11354 * @return
11355 */
11356HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11357 Snapshot *pSnapshot,
11358 CleanupMode_T cleanupMode,
11359 MediaList &llMedia)
11360{
11361 Assert(isWriteLockOnCurrentThread());
11362
11363 HRESULT rc;
11364
11365 // make a temporary list because i_detachDevice invalidates iterators into
11366 // mMediaData->mAttachments
11367 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11368
11369 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11370 {
11371 ComObjPtr<MediumAttachment> &pAttach = *it;
11372 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11373
11374 if (!pMedium.isNull())
11375 {
11376 AutoCaller mac(pMedium);
11377 if (FAILED(mac.rc())) return mac.rc();
11378 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11379 DeviceType_T devType = pMedium->i_getDeviceType();
11380 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11381 && devType == DeviceType_HardDisk)
11382 || (cleanupMode == CleanupMode_Full)
11383 )
11384 {
11385 llMedia.push_back(pMedium);
11386 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11387 /* Not allowed to keep this lock as below we need the parent
11388 * medium lock, and the lock order is parent to child. */
11389 lock.release();
11390 /*
11391 * Search for medias which are not attached to any machine, but
11392 * in the chain to an attached disk. Mediums are only consided
11393 * if they are:
11394 * - have only one child
11395 * - no references to any machines
11396 * - are of normal medium type
11397 */
11398 while (!pParent.isNull())
11399 {
11400 AutoCaller mac1(pParent);
11401 if (FAILED(mac1.rc())) return mac1.rc();
11402 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11403 if (pParent->i_getChildren().size() == 1)
11404 {
11405 if ( pParent->i_getMachineBackRefCount() == 0
11406 && pParent->i_getType() == MediumType_Normal
11407 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11408 llMedia.push_back(pParent);
11409 }
11410 else
11411 break;
11412 pParent = pParent->i_getParent();
11413 }
11414 }
11415 }
11416
11417 // real machine: then we need to use the proper method
11418 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11419
11420 if (FAILED(rc))
11421 return rc;
11422 }
11423
11424 return S_OK;
11425}
11426
11427/**
11428 * Perform deferred hard disk detachments.
11429 *
11430 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11431 * backed up).
11432 *
11433 * If @a aOnline is @c true then this method will also unlock the old hard disks
11434 * for which the new implicit diffs were created and will lock these new diffs for
11435 * writing.
11436 *
11437 * @param aOnline Whether the VM was online prior to this operation.
11438 *
11439 * @note Locks this object for writing!
11440 */
11441void Machine::i_commitMedia(bool aOnline /*= false*/)
11442{
11443 AutoCaller autoCaller(this);
11444 AssertComRCReturnVoid(autoCaller.rc());
11445
11446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11447
11448 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11449
11450 HRESULT rc = S_OK;
11451
11452 /* no attach/detach operations -- nothing to do */
11453 if (!mMediaData.isBackedUp())
11454 return;
11455
11456 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11457 bool fMediaNeedsLocking = false;
11458
11459 /* enumerate new attachments */
11460 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11461 it != mMediaData->mAttachments.end();
11462 ++it)
11463 {
11464 MediumAttachment *pAttach = *it;
11465
11466 pAttach->i_commit();
11467
11468 Medium* pMedium = pAttach->i_getMedium();
11469 bool fImplicit = pAttach->i_isImplicit();
11470
11471 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11472 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11473 fImplicit));
11474
11475 /** @todo convert all this Machine-based voodoo to MediumAttachment
11476 * based commit logic. */
11477 if (fImplicit)
11478 {
11479 /* convert implicit attachment to normal */
11480 pAttach->i_setImplicit(false);
11481
11482 if ( aOnline
11483 && pMedium
11484 && pAttach->i_getType() == DeviceType_HardDisk
11485 )
11486 {
11487 /* update the appropriate lock list */
11488 MediumLockList *pMediumLockList;
11489 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11490 AssertComRC(rc);
11491 if (pMediumLockList)
11492 {
11493 /* unlock if there's a need to change the locking */
11494 if (!fMediaNeedsLocking)
11495 {
11496 rc = mData->mSession.mLockedMedia.Unlock();
11497 AssertComRC(rc);
11498 fMediaNeedsLocking = true;
11499 }
11500 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11501 AssertComRC(rc);
11502 rc = pMediumLockList->Append(pMedium, true);
11503 AssertComRC(rc);
11504 }
11505 }
11506
11507 continue;
11508 }
11509
11510 if (pMedium)
11511 {
11512 /* was this medium attached before? */
11513 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11514 {
11515 MediumAttachment *pOldAttach = *oldIt;
11516 if (pOldAttach->i_getMedium() == pMedium)
11517 {
11518 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11519
11520 /* yes: remove from old to avoid de-association */
11521 oldAtts.erase(oldIt);
11522 break;
11523 }
11524 }
11525 }
11526 }
11527
11528 /* enumerate remaining old attachments and de-associate from the
11529 * current machine state */
11530 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11531 {
11532 MediumAttachment *pAttach = *it;
11533 Medium* pMedium = pAttach->i_getMedium();
11534
11535 /* Detach only hard disks, since DVD/floppy media is detached
11536 * instantly in MountMedium. */
11537 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11538 {
11539 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11540
11541 /* now de-associate from the current machine state */
11542 rc = pMedium->i_removeBackReference(mData->mUuid);
11543 AssertComRC(rc);
11544
11545 if (aOnline)
11546 {
11547 /* unlock since medium is not used anymore */
11548 MediumLockList *pMediumLockList;
11549 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11550 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11551 {
11552 /* this happens for online snapshots, there the attachment
11553 * is changing, but only to a diff image created under
11554 * the old one, so there is no separate lock list */
11555 Assert(!pMediumLockList);
11556 }
11557 else
11558 {
11559 AssertComRC(rc);
11560 if (pMediumLockList)
11561 {
11562 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11563 AssertComRC(rc);
11564 }
11565 }
11566 }
11567 }
11568 }
11569
11570 /* take media locks again so that the locking state is consistent */
11571 if (fMediaNeedsLocking)
11572 {
11573 Assert(aOnline);
11574 rc = mData->mSession.mLockedMedia.Lock();
11575 AssertComRC(rc);
11576 }
11577
11578 /* commit the hard disk changes */
11579 mMediaData.commit();
11580
11581 if (i_isSessionMachine())
11582 {
11583 /*
11584 * Update the parent machine to point to the new owner.
11585 * This is necessary because the stored parent will point to the
11586 * session machine otherwise and cause crashes or errors later
11587 * when the session machine gets invalid.
11588 */
11589 /** @todo Change the MediumAttachment class to behave like any other
11590 * class in this regard by creating peer MediumAttachment
11591 * objects for session machines and share the data with the peer
11592 * machine.
11593 */
11594 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11595 it != mMediaData->mAttachments.end();
11596 ++it)
11597 (*it)->i_updateParentMachine(mPeer);
11598
11599 /* attach new data to the primary machine and reshare it */
11600 mPeer->mMediaData.attach(mMediaData);
11601 }
11602
11603 return;
11604}
11605
11606/**
11607 * Perform deferred deletion of implicitly created diffs.
11608 *
11609 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11610 * backed up).
11611 *
11612 * @note Locks this object for writing!
11613 */
11614void Machine::i_rollbackMedia()
11615{
11616 AutoCaller autoCaller(this);
11617 AssertComRCReturnVoid(autoCaller.rc());
11618
11619 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11620 LogFlowThisFunc(("Entering rollbackMedia\n"));
11621
11622 HRESULT rc = S_OK;
11623
11624 /* no attach/detach operations -- nothing to do */
11625 if (!mMediaData.isBackedUp())
11626 return;
11627
11628 /* enumerate new attachments */
11629 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11630 it != mMediaData->mAttachments.end();
11631 ++it)
11632 {
11633 MediumAttachment *pAttach = *it;
11634 /* Fix up the backrefs for DVD/floppy media. */
11635 if (pAttach->i_getType() != DeviceType_HardDisk)
11636 {
11637 Medium* pMedium = pAttach->i_getMedium();
11638 if (pMedium)
11639 {
11640 rc = pMedium->i_removeBackReference(mData->mUuid);
11641 AssertComRC(rc);
11642 }
11643 }
11644
11645 (*it)->i_rollback();
11646
11647 pAttach = *it;
11648 /* Fix up the backrefs for DVD/floppy media. */
11649 if (pAttach->i_getType() != DeviceType_HardDisk)
11650 {
11651 Medium* pMedium = pAttach->i_getMedium();
11652 if (pMedium)
11653 {
11654 rc = pMedium->i_addBackReference(mData->mUuid);
11655 AssertComRC(rc);
11656 }
11657 }
11658 }
11659
11660 /** @todo convert all this Machine-based voodoo to MediumAttachment
11661 * based rollback logic. */
11662 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11663
11664 return;
11665}
11666
11667/**
11668 * Returns true if the settings file is located in the directory named exactly
11669 * as the machine; this means, among other things, that the machine directory
11670 * should be auto-renamed.
11671 *
11672 * @param aSettingsDir if not NULL, the full machine settings file directory
11673 * name will be assigned there.
11674 *
11675 * @note Doesn't lock anything.
11676 * @note Not thread safe (must be called from this object's lock).
11677 */
11678bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11679{
11680 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11681 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11682 if (aSettingsDir)
11683 *aSettingsDir = strMachineDirName;
11684 strMachineDirName.stripPath(); // vmname
11685 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11686 strConfigFileOnly.stripPath() // vmname.vbox
11687 .stripSuffix(); // vmname
11688 /** @todo hack, make somehow use of ComposeMachineFilename */
11689 if (mUserData->s.fDirectoryIncludesUUID)
11690 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11691
11692 AssertReturn(!strMachineDirName.isEmpty(), false);
11693 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11694
11695 return strMachineDirName == strConfigFileOnly;
11696}
11697
11698/**
11699 * Discards all changes to machine settings.
11700 *
11701 * @param aNotify Whether to notify the direct session about changes or not.
11702 *
11703 * @note Locks objects for writing!
11704 */
11705void Machine::i_rollback(bool aNotify)
11706{
11707 AutoCaller autoCaller(this);
11708 AssertComRCReturn(autoCaller.rc(), (void)0);
11709
11710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11711
11712 if (!mStorageControllers.isNull())
11713 {
11714 if (mStorageControllers.isBackedUp())
11715 {
11716 /* unitialize all new devices (absent in the backed up list). */
11717 StorageControllerList::const_iterator it = mStorageControllers->begin();
11718 StorageControllerList *backedList = mStorageControllers.backedUpData();
11719 while (it != mStorageControllers->end())
11720 {
11721 if ( std::find(backedList->begin(), backedList->end(), *it)
11722 == backedList->end()
11723 )
11724 {
11725 (*it)->uninit();
11726 }
11727 ++it;
11728 }
11729
11730 /* restore the list */
11731 mStorageControllers.rollback();
11732 }
11733
11734 /* rollback any changes to devices after restoring the list */
11735 if (mData->flModifications & IsModified_Storage)
11736 {
11737 StorageControllerList::const_iterator it = mStorageControllers->begin();
11738 while (it != mStorageControllers->end())
11739 {
11740 (*it)->i_rollback();
11741 ++it;
11742 }
11743 }
11744 }
11745
11746 if (!mUSBControllers.isNull())
11747 {
11748 if (mUSBControllers.isBackedUp())
11749 {
11750 /* unitialize all new devices (absent in the backed up list). */
11751 USBControllerList::const_iterator it = mUSBControllers->begin();
11752 USBControllerList *backedList = mUSBControllers.backedUpData();
11753 while (it != mUSBControllers->end())
11754 {
11755 if ( std::find(backedList->begin(), backedList->end(), *it)
11756 == backedList->end()
11757 )
11758 {
11759 (*it)->uninit();
11760 }
11761 ++it;
11762 }
11763
11764 /* restore the list */
11765 mUSBControllers.rollback();
11766 }
11767
11768 /* rollback any changes to devices after restoring the list */
11769 if (mData->flModifications & IsModified_USB)
11770 {
11771 USBControllerList::const_iterator it = mUSBControllers->begin();
11772 while (it != mUSBControllers->end())
11773 {
11774 (*it)->i_rollback();
11775 ++it;
11776 }
11777 }
11778 }
11779
11780 mUserData.rollback();
11781
11782 mHWData.rollback();
11783
11784 if (mData->flModifications & IsModified_Storage)
11785 i_rollbackMedia();
11786
11787 if (mBIOSSettings)
11788 mBIOSSettings->i_rollback();
11789
11790 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11791 mVRDEServer->i_rollback();
11792
11793 if (mAudioAdapter)
11794 mAudioAdapter->i_rollback();
11795
11796 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11797 mUSBDeviceFilters->i_rollback();
11798
11799 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11800 mBandwidthControl->i_rollback();
11801
11802 if (!mHWData.isNull())
11803 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11804 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11805 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11806 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11807
11808 if (mData->flModifications & IsModified_NetworkAdapters)
11809 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11810 if ( mNetworkAdapters[slot]
11811 && mNetworkAdapters[slot]->i_isModified())
11812 {
11813 mNetworkAdapters[slot]->i_rollback();
11814 networkAdapters[slot] = mNetworkAdapters[slot];
11815 }
11816
11817 if (mData->flModifications & IsModified_SerialPorts)
11818 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11819 if ( mSerialPorts[slot]
11820 && mSerialPorts[slot]->i_isModified())
11821 {
11822 mSerialPorts[slot]->i_rollback();
11823 serialPorts[slot] = mSerialPorts[slot];
11824 }
11825
11826 if (mData->flModifications & IsModified_ParallelPorts)
11827 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11828 if ( mParallelPorts[slot]
11829 && mParallelPorts[slot]->i_isModified())
11830 {
11831 mParallelPorts[slot]->i_rollback();
11832 parallelPorts[slot] = mParallelPorts[slot];
11833 }
11834
11835 if (aNotify)
11836 {
11837 /* inform the direct session about changes */
11838
11839 ComObjPtr<Machine> that = this;
11840 uint32_t flModifications = mData->flModifications;
11841 alock.release();
11842
11843 if (flModifications & IsModified_SharedFolders)
11844 that->i_onSharedFolderChange();
11845
11846 if (flModifications & IsModified_VRDEServer)
11847 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11848 if (flModifications & IsModified_USB)
11849 that->i_onUSBControllerChange();
11850
11851 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11852 if (networkAdapters[slot])
11853 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11854 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11855 if (serialPorts[slot])
11856 that->i_onSerialPortChange(serialPorts[slot]);
11857 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11858 if (parallelPorts[slot])
11859 that->i_onParallelPortChange(parallelPorts[slot]);
11860
11861 if (flModifications & IsModified_Storage)
11862 that->i_onStorageControllerChange();
11863
11864#if 0
11865 if (flModifications & IsModified_BandwidthControl)
11866 that->onBandwidthControlChange();
11867#endif
11868 }
11869}
11870
11871/**
11872 * Commits all the changes to machine settings.
11873 *
11874 * Note that this operation is supposed to never fail.
11875 *
11876 * @note Locks this object and children for writing.
11877 */
11878void Machine::i_commit()
11879{
11880 AutoCaller autoCaller(this);
11881 AssertComRCReturnVoid(autoCaller.rc());
11882
11883 AutoCaller peerCaller(mPeer);
11884 AssertComRCReturnVoid(peerCaller.rc());
11885
11886 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11887
11888 /*
11889 * use safe commit to ensure Snapshot machines (that share mUserData)
11890 * will still refer to a valid memory location
11891 */
11892 mUserData.commitCopy();
11893
11894 mHWData.commit();
11895
11896 if (mMediaData.isBackedUp())
11897 i_commitMedia(Global::IsOnline(mData->mMachineState));
11898
11899 mBIOSSettings->i_commit();
11900 mVRDEServer->i_commit();
11901 mAudioAdapter->i_commit();
11902 mUSBDeviceFilters->i_commit();
11903 mBandwidthControl->i_commit();
11904
11905 /* Since mNetworkAdapters is a list which might have been changed (resized)
11906 * without using the Backupable<> template we need to handle the copying
11907 * of the list entries manually, including the creation of peers for the
11908 * new objects. */
11909 bool commitNetworkAdapters = false;
11910 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11911 if (mPeer)
11912 {
11913 /* commit everything, even the ones which will go away */
11914 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11915 mNetworkAdapters[slot]->i_commit();
11916 /* copy over the new entries, creating a peer and uninit the original */
11917 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11918 for (size_t slot = 0; slot < newSize; slot++)
11919 {
11920 /* look if this adapter has a peer device */
11921 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11922 if (!peer)
11923 {
11924 /* no peer means the adapter is a newly created one;
11925 * create a peer owning data this data share it with */
11926 peer.createObject();
11927 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11928 }
11929 mPeer->mNetworkAdapters[slot] = peer;
11930 }
11931 /* uninit any no longer needed network adapters */
11932 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11933 mNetworkAdapters[slot]->uninit();
11934 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11935 {
11936 if (mPeer->mNetworkAdapters[slot])
11937 mPeer->mNetworkAdapters[slot]->uninit();
11938 }
11939 /* Keep the original network adapter count until this point, so that
11940 * discarding a chipset type change will not lose settings. */
11941 mNetworkAdapters.resize(newSize);
11942 mPeer->mNetworkAdapters.resize(newSize);
11943 }
11944 else
11945 {
11946 /* we have no peer (our parent is the newly created machine);
11947 * just commit changes to the network adapters */
11948 commitNetworkAdapters = true;
11949 }
11950 if (commitNetworkAdapters)
11951 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11952 mNetworkAdapters[slot]->i_commit();
11953
11954 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11955 mSerialPorts[slot]->i_commit();
11956 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11957 mParallelPorts[slot]->i_commit();
11958
11959 bool commitStorageControllers = false;
11960
11961 if (mStorageControllers.isBackedUp())
11962 {
11963 mStorageControllers.commit();
11964
11965 if (mPeer)
11966 {
11967 /* Commit all changes to new controllers (this will reshare data with
11968 * peers for those who have peers) */
11969 StorageControllerList *newList = new StorageControllerList();
11970 StorageControllerList::const_iterator it = mStorageControllers->begin();
11971 while (it != mStorageControllers->end())
11972 {
11973 (*it)->i_commit();
11974
11975 /* look if this controller has a peer device */
11976 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11977 if (!peer)
11978 {
11979 /* no peer means the device is a newly created one;
11980 * create a peer owning data this device share it with */
11981 peer.createObject();
11982 peer->init(mPeer, *it, true /* aReshare */);
11983 }
11984 else
11985 {
11986 /* remove peer from the old list */
11987 mPeer->mStorageControllers->remove(peer);
11988 }
11989 /* and add it to the new list */
11990 newList->push_back(peer);
11991
11992 ++it;
11993 }
11994
11995 /* uninit old peer's controllers that are left */
11996 it = mPeer->mStorageControllers->begin();
11997 while (it != mPeer->mStorageControllers->end())
11998 {
11999 (*it)->uninit();
12000 ++it;
12001 }
12002
12003 /* attach new list of controllers to our peer */
12004 mPeer->mStorageControllers.attach(newList);
12005 }
12006 else
12007 {
12008 /* we have no peer (our parent is the newly created machine);
12009 * just commit changes to devices */
12010 commitStorageControllers = true;
12011 }
12012 }
12013 else
12014 {
12015 /* the list of controllers itself is not changed,
12016 * just commit changes to controllers themselves */
12017 commitStorageControllers = true;
12018 }
12019
12020 if (commitStorageControllers)
12021 {
12022 StorageControllerList::const_iterator it = mStorageControllers->begin();
12023 while (it != mStorageControllers->end())
12024 {
12025 (*it)->i_commit();
12026 ++it;
12027 }
12028 }
12029
12030 bool commitUSBControllers = false;
12031
12032 if (mUSBControllers.isBackedUp())
12033 {
12034 mUSBControllers.commit();
12035
12036 if (mPeer)
12037 {
12038 /* Commit all changes to new controllers (this will reshare data with
12039 * peers for those who have peers) */
12040 USBControllerList *newList = new USBControllerList();
12041 USBControllerList::const_iterator it = mUSBControllers->begin();
12042 while (it != mUSBControllers->end())
12043 {
12044 (*it)->i_commit();
12045
12046 /* look if this controller has a peer device */
12047 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12048 if (!peer)
12049 {
12050 /* no peer means the device is a newly created one;
12051 * create a peer owning data this device share it with */
12052 peer.createObject();
12053 peer->init(mPeer, *it, true /* aReshare */);
12054 }
12055 else
12056 {
12057 /* remove peer from the old list */
12058 mPeer->mUSBControllers->remove(peer);
12059 }
12060 /* and add it to the new list */
12061 newList->push_back(peer);
12062
12063 ++it;
12064 }
12065
12066 /* uninit old peer's controllers that are left */
12067 it = mPeer->mUSBControllers->begin();
12068 while (it != mPeer->mUSBControllers->end())
12069 {
12070 (*it)->uninit();
12071 ++it;
12072 }
12073
12074 /* attach new list of controllers to our peer */
12075 mPeer->mUSBControllers.attach(newList);
12076 }
12077 else
12078 {
12079 /* we have no peer (our parent is the newly created machine);
12080 * just commit changes to devices */
12081 commitUSBControllers = true;
12082 }
12083 }
12084 else
12085 {
12086 /* the list of controllers itself is not changed,
12087 * just commit changes to controllers themselves */
12088 commitUSBControllers = true;
12089 }
12090
12091 if (commitUSBControllers)
12092 {
12093 USBControllerList::const_iterator it = mUSBControllers->begin();
12094 while (it != mUSBControllers->end())
12095 {
12096 (*it)->i_commit();
12097 ++it;
12098 }
12099 }
12100
12101 if (i_isSessionMachine())
12102 {
12103 /* attach new data to the primary machine and reshare it */
12104 mPeer->mUserData.attach(mUserData);
12105 mPeer->mHWData.attach(mHWData);
12106 /* mMediaData is reshared by fixupMedia */
12107 // mPeer->mMediaData.attach(mMediaData);
12108 Assert(mPeer->mMediaData.data() == mMediaData.data());
12109 }
12110}
12111
12112/**
12113 * Copies all the hardware data from the given machine.
12114 *
12115 * Currently, only called when the VM is being restored from a snapshot. In
12116 * particular, this implies that the VM is not running during this method's
12117 * call.
12118 *
12119 * @note This method must be called from under this object's lock.
12120 *
12121 * @note This method doesn't call #commit(), so all data remains backed up and
12122 * unsaved.
12123 */
12124void Machine::i_copyFrom(Machine *aThat)
12125{
12126 AssertReturnVoid(!i_isSnapshotMachine());
12127 AssertReturnVoid(aThat->i_isSnapshotMachine());
12128
12129 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12130
12131 mHWData.assignCopy(aThat->mHWData);
12132
12133 // create copies of all shared folders (mHWData after attaching a copy
12134 // contains just references to original objects)
12135 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12136 it != mHWData->mSharedFolders.end();
12137 ++it)
12138 {
12139 ComObjPtr<SharedFolder> folder;
12140 folder.createObject();
12141 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12142 AssertComRC(rc);
12143 *it = folder;
12144 }
12145
12146 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12147 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12148 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12149 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12150 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12151
12152 /* create private copies of all controllers */
12153 mStorageControllers.backup();
12154 mStorageControllers->clear();
12155 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12156 it != aThat->mStorageControllers->end();
12157 ++it)
12158 {
12159 ComObjPtr<StorageController> ctrl;
12160 ctrl.createObject();
12161 ctrl->initCopy(this, *it);
12162 mStorageControllers->push_back(ctrl);
12163 }
12164
12165 /* create private copies of all USB controllers */
12166 mUSBControllers.backup();
12167 mUSBControllers->clear();
12168 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12169 it != aThat->mUSBControllers->end();
12170 ++it)
12171 {
12172 ComObjPtr<USBController> ctrl;
12173 ctrl.createObject();
12174 ctrl->initCopy(this, *it);
12175 mUSBControllers->push_back(ctrl);
12176 }
12177
12178 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12179 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12180 {
12181 if (mNetworkAdapters[slot].isNotNull())
12182 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12183 else
12184 {
12185 unconst(mNetworkAdapters[slot]).createObject();
12186 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12187 }
12188 }
12189 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12190 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12191 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12192 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12193}
12194
12195/**
12196 * Returns whether the given storage controller is hotplug capable.
12197 *
12198 * @returns true if the controller supports hotplugging
12199 * false otherwise.
12200 * @param enmCtrlType The controller type to check for.
12201 */
12202bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12203{
12204 ComPtr<ISystemProperties> systemProperties;
12205 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12206 if (FAILED(rc))
12207 return false;
12208
12209 BOOL aHotplugCapable = FALSE;
12210 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12211
12212 return RT_BOOL(aHotplugCapable);
12213}
12214
12215#ifdef VBOX_WITH_RESOURCE_USAGE_API
12216
12217void Machine::i_getDiskList(MediaList &list)
12218{
12219 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12220 it != mMediaData->mAttachments.end();
12221 ++it)
12222 {
12223 MediumAttachment* pAttach = *it;
12224 /* just in case */
12225 AssertContinue(pAttach);
12226
12227 AutoCaller localAutoCallerA(pAttach);
12228 if (FAILED(localAutoCallerA.rc())) continue;
12229
12230 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12231
12232 if (pAttach->i_getType() == DeviceType_HardDisk)
12233 list.push_back(pAttach->i_getMedium());
12234 }
12235}
12236
12237void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12238{
12239 AssertReturnVoid(isWriteLockOnCurrentThread());
12240 AssertPtrReturnVoid(aCollector);
12241
12242 pm::CollectorHAL *hal = aCollector->getHAL();
12243 /* Create sub metrics */
12244 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12245 "Percentage of processor time spent in user mode by the VM process.");
12246 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12247 "Percentage of processor time spent in kernel mode by the VM process.");
12248 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12249 "Size of resident portion of VM process in memory.");
12250 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12251 "Actual size of all VM disks combined.");
12252 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12253 "Network receive rate.");
12254 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12255 "Network transmit rate.");
12256 /* Create and register base metrics */
12257 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12258 cpuLoadUser, cpuLoadKernel);
12259 aCollector->registerBaseMetric(cpuLoad);
12260 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12261 ramUsageUsed);
12262 aCollector->registerBaseMetric(ramUsage);
12263 MediaList disks;
12264 i_getDiskList(disks);
12265 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12266 diskUsageUsed);
12267 aCollector->registerBaseMetric(diskUsage);
12268
12269 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12270 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12271 new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12273 new pm::AggregateMin()));
12274 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12275 new pm::AggregateMax()));
12276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12277 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12278 new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12280 new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12282 new pm::AggregateMax()));
12283
12284 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12285 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12286 new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12288 new pm::AggregateMin()));
12289 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12290 new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12293 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12294 new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12296 new pm::AggregateMin()));
12297 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12298 new pm::AggregateMax()));
12299
12300
12301 /* Guest metrics collector */
12302 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12303 aCollector->registerGuest(mCollectorGuest);
12304 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12305
12306 /* Create sub metrics */
12307 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12308 "Percentage of processor time spent in user mode as seen by the guest.");
12309 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12310 "Percentage of processor time spent in kernel mode as seen by the guest.");
12311 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12312 "Percentage of processor time spent idling as seen by the guest.");
12313
12314 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12315 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12316 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12317 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12318 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12319 pm::SubMetric *guestMemCache = new pm::SubMetric(
12320 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12321
12322 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12323 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12324
12325 /* Create and register base metrics */
12326 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12327 machineNetRx, machineNetTx);
12328 aCollector->registerBaseMetric(machineNetRate);
12329
12330 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12331 guestLoadUser, guestLoadKernel, guestLoadIdle);
12332 aCollector->registerBaseMetric(guestCpuLoad);
12333
12334 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12335 guestMemTotal, guestMemFree,
12336 guestMemBalloon, guestMemShared,
12337 guestMemCache, guestPagedTotal);
12338 aCollector->registerBaseMetric(guestCpuMem);
12339
12340 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12341 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12346 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12354
12355 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12356 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12359
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12364
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12369
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12374
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12379
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12384
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12389
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12394}
12395
12396void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12397{
12398 AssertReturnVoid(isWriteLockOnCurrentThread());
12399
12400 if (aCollector)
12401 {
12402 aCollector->unregisterMetricsFor(aMachine);
12403 aCollector->unregisterBaseMetricsFor(aMachine);
12404 }
12405}
12406
12407#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12408
12409
12410////////////////////////////////////////////////////////////////////////////////
12411
12412DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12413
12414HRESULT SessionMachine::FinalConstruct()
12415{
12416 LogFlowThisFunc(("\n"));
12417
12418 mClientToken = NULL;
12419
12420 return BaseFinalConstruct();
12421}
12422
12423void SessionMachine::FinalRelease()
12424{
12425 LogFlowThisFunc(("\n"));
12426
12427 Assert(!mClientToken);
12428 /* paranoia, should not hang around any more */
12429 if (mClientToken)
12430 {
12431 delete mClientToken;
12432 mClientToken = NULL;
12433 }
12434
12435 uninit(Uninit::Unexpected);
12436
12437 BaseFinalRelease();
12438}
12439
12440/**
12441 * @note Must be called only by Machine::LockMachine() from its own write lock.
12442 */
12443HRESULT SessionMachine::init(Machine *aMachine)
12444{
12445 LogFlowThisFuncEnter();
12446 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12447
12448 AssertReturn(aMachine, E_INVALIDARG);
12449
12450 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12451
12452 /* Enclose the state transition NotReady->InInit->Ready */
12453 AutoInitSpan autoInitSpan(this);
12454 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12455
12456 HRESULT rc = S_OK;
12457
12458 RT_ZERO(mAuthLibCtx);
12459
12460 /* create the machine client token */
12461 try
12462 {
12463 mClientToken = new ClientToken(aMachine, this);
12464 if (!mClientToken->isReady())
12465 {
12466 delete mClientToken;
12467 mClientToken = NULL;
12468 rc = E_FAIL;
12469 }
12470 }
12471 catch (std::bad_alloc &)
12472 {
12473 rc = E_OUTOFMEMORY;
12474 }
12475 if (FAILED(rc))
12476 return rc;
12477
12478 /* memorize the peer Machine */
12479 unconst(mPeer) = aMachine;
12480 /* share the parent pointer */
12481 unconst(mParent) = aMachine->mParent;
12482
12483 /* take the pointers to data to share */
12484 mData.share(aMachine->mData);
12485 mSSData.share(aMachine->mSSData);
12486
12487 mUserData.share(aMachine->mUserData);
12488 mHWData.share(aMachine->mHWData);
12489 mMediaData.share(aMachine->mMediaData);
12490
12491 mStorageControllers.allocate();
12492 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12493 it != aMachine->mStorageControllers->end();
12494 ++it)
12495 {
12496 ComObjPtr<StorageController> ctl;
12497 ctl.createObject();
12498 ctl->init(this, *it);
12499 mStorageControllers->push_back(ctl);
12500 }
12501
12502 mUSBControllers.allocate();
12503 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12504 it != aMachine->mUSBControllers->end();
12505 ++it)
12506 {
12507 ComObjPtr<USBController> ctl;
12508 ctl.createObject();
12509 ctl->init(this, *it);
12510 mUSBControllers->push_back(ctl);
12511 }
12512
12513 unconst(mBIOSSettings).createObject();
12514 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12515 /* create another VRDEServer object that will be mutable */
12516 unconst(mVRDEServer).createObject();
12517 mVRDEServer->init(this, aMachine->mVRDEServer);
12518 /* create another audio adapter object that will be mutable */
12519 unconst(mAudioAdapter).createObject();
12520 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12521 /* create a list of serial ports that will be mutable */
12522 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12523 {
12524 unconst(mSerialPorts[slot]).createObject();
12525 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12526 }
12527 /* create a list of parallel ports that will be mutable */
12528 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12529 {
12530 unconst(mParallelPorts[slot]).createObject();
12531 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12532 }
12533
12534 /* create another USB device filters object that will be mutable */
12535 unconst(mUSBDeviceFilters).createObject();
12536 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12537
12538 /* create a list of network adapters that will be mutable */
12539 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12540 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12541 {
12542 unconst(mNetworkAdapters[slot]).createObject();
12543 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12544 }
12545
12546 /* create another bandwidth control object that will be mutable */
12547 unconst(mBandwidthControl).createObject();
12548 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12549
12550#ifdef VBOX_WITH_UNATTENDED
12551 /* create another unattended object that will be mutable */
12552 unconst(mUnattended).createObject();
12553 mUnattended->init(this, aMachine->mUnattended);
12554#endif
12555
12556 /* default is to delete saved state on Saved -> PoweredOff transition */
12557 mRemoveSavedState = true;
12558
12559 /* Confirm a successful initialization when it's the case */
12560 autoInitSpan.setSucceeded();
12561
12562 miNATNetworksStarted = 0;
12563
12564 LogFlowThisFuncLeave();
12565 return rc;
12566}
12567
12568/**
12569 * Uninitializes this session object. If the reason is other than
12570 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12571 * or the client watcher code.
12572 *
12573 * @param aReason uninitialization reason
12574 *
12575 * @note Locks mParent + this object for writing.
12576 */
12577void SessionMachine::uninit(Uninit::Reason aReason)
12578{
12579 LogFlowThisFuncEnter();
12580 LogFlowThisFunc(("reason=%d\n", aReason));
12581
12582 /*
12583 * Strongly reference ourselves to prevent this object deletion after
12584 * mData->mSession.mMachine.setNull() below (which can release the last
12585 * reference and call the destructor). Important: this must be done before
12586 * accessing any members (and before AutoUninitSpan that does it as well).
12587 * This self reference will be released as the very last step on return.
12588 */
12589 ComObjPtr<SessionMachine> selfRef;
12590 if (aReason != Uninit::Unexpected)
12591 selfRef = this;
12592
12593 /* Enclose the state transition Ready->InUninit->NotReady */
12594 AutoUninitSpan autoUninitSpan(this);
12595 if (autoUninitSpan.uninitDone())
12596 {
12597 LogFlowThisFunc(("Already uninitialized\n"));
12598 LogFlowThisFuncLeave();
12599 return;
12600 }
12601
12602 if (autoUninitSpan.initFailed())
12603 {
12604 /* We've been called by init() because it's failed. It's not really
12605 * necessary (nor it's safe) to perform the regular uninit sequence
12606 * below, the following is enough.
12607 */
12608 LogFlowThisFunc(("Initialization failed.\n"));
12609 /* destroy the machine client token */
12610 if (mClientToken)
12611 {
12612 delete mClientToken;
12613 mClientToken = NULL;
12614 }
12615 uninitDataAndChildObjects();
12616 mData.free();
12617 unconst(mParent) = NULL;
12618 unconst(mPeer) = NULL;
12619 LogFlowThisFuncLeave();
12620 return;
12621 }
12622
12623 MachineState_T lastState;
12624 {
12625 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12626 lastState = mData->mMachineState;
12627 }
12628 NOREF(lastState);
12629
12630#ifdef VBOX_WITH_USB
12631 // release all captured USB devices, but do this before requesting the locks below
12632 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12633 {
12634 /* Console::captureUSBDevices() is called in the VM process only after
12635 * setting the machine state to Starting or Restoring.
12636 * Console::detachAllUSBDevices() will be called upon successful
12637 * termination. So, we need to release USB devices only if there was
12638 * an abnormal termination of a running VM.
12639 *
12640 * This is identical to SessionMachine::DetachAllUSBDevices except
12641 * for the aAbnormal argument. */
12642 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12643 AssertComRC(rc);
12644 NOREF(rc);
12645
12646 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12647 if (service)
12648 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12649 }
12650#endif /* VBOX_WITH_USB */
12651
12652 // we need to lock this object in uninit() because the lock is shared
12653 // with mPeer (as well as data we modify below). mParent lock is needed
12654 // by several calls to it.
12655 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12656
12657#ifdef VBOX_WITH_RESOURCE_USAGE_API
12658 /*
12659 * It is safe to call Machine::i_unregisterMetrics() here because
12660 * PerformanceCollector::samplerCallback no longer accesses guest methods
12661 * holding the lock.
12662 */
12663 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12664 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12665 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12666 if (mCollectorGuest)
12667 {
12668 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12669 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12670 mCollectorGuest = NULL;
12671 }
12672#endif
12673
12674 if (aReason == Uninit::Abnormal)
12675 {
12676 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12677
12678 /* reset the state to Aborted */
12679 if (mData->mMachineState != MachineState_Aborted)
12680 i_setMachineState(MachineState_Aborted);
12681 }
12682
12683 // any machine settings modified?
12684 if (mData->flModifications)
12685 {
12686 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12687 i_rollback(false /* aNotify */);
12688 }
12689
12690 mData->mSession.mPID = NIL_RTPROCESS;
12691
12692 if (aReason == Uninit::Unexpected)
12693 {
12694 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12695 * client watcher thread to update the set of machines that have open
12696 * sessions. */
12697 mParent->i_updateClientWatcher();
12698 }
12699
12700 /* uninitialize all remote controls */
12701 if (mData->mSession.mRemoteControls.size())
12702 {
12703 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12704 mData->mSession.mRemoteControls.size()));
12705
12706 Data::Session::RemoteControlList::iterator it =
12707 mData->mSession.mRemoteControls.begin();
12708 while (it != mData->mSession.mRemoteControls.end())
12709 {
12710 ComPtr<IInternalSessionControl> pControl = *it;
12711 mData->mSession.mRemoteControls.erase(it);
12712 multilock.release();
12713 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12714 HRESULT rc = pControl->Uninitialize();
12715 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12716 if (FAILED(rc))
12717 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12718 multilock.acquire();
12719 it = mData->mSession.mRemoteControls.begin();
12720 }
12721 mData->mSession.mRemoteControls.clear();
12722 }
12723
12724 /* Remove all references to the NAT network service. The service will stop
12725 * if all references (also from other VMs) are removed. */
12726 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12727 {
12728 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12729 {
12730 BOOL enabled;
12731 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12732 if ( FAILED(hrc)
12733 || !enabled)
12734 continue;
12735
12736 NetworkAttachmentType_T type;
12737 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12738 if ( SUCCEEDED(hrc)
12739 && type == NetworkAttachmentType_NATNetwork)
12740 {
12741 Bstr name;
12742 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12743 if (SUCCEEDED(hrc))
12744 {
12745 multilock.release();
12746 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12747 mUserData->s.strName.c_str(), name.raw()));
12748 mParent->i_natNetworkRefDec(name.raw());
12749 multilock.acquire();
12750 }
12751 }
12752 }
12753 }
12754
12755 /*
12756 * An expected uninitialization can come only from #i_checkForDeath().
12757 * Otherwise it means that something's gone really wrong (for example,
12758 * the Session implementation has released the VirtualBox reference
12759 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12760 * etc). However, it's also possible, that the client releases the IPC
12761 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12762 * but the VirtualBox release event comes first to the server process.
12763 * This case is practically possible, so we should not assert on an
12764 * unexpected uninit, just log a warning.
12765 */
12766
12767 if (aReason == Uninit::Unexpected)
12768 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12769
12770 if (aReason != Uninit::Normal)
12771 {
12772 mData->mSession.mDirectControl.setNull();
12773 }
12774 else
12775 {
12776 /* this must be null here (see #OnSessionEnd()) */
12777 Assert(mData->mSession.mDirectControl.isNull());
12778 Assert(mData->mSession.mState == SessionState_Unlocking);
12779 Assert(!mData->mSession.mProgress.isNull());
12780 }
12781 if (mData->mSession.mProgress)
12782 {
12783 if (aReason == Uninit::Normal)
12784 mData->mSession.mProgress->i_notifyComplete(S_OK);
12785 else
12786 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12787 COM_IIDOF(ISession),
12788 getComponentName(),
12789 tr("The VM session was aborted"));
12790 mData->mSession.mProgress.setNull();
12791 }
12792
12793 if (mConsoleTaskData.mProgress)
12794 {
12795 Assert(aReason == Uninit::Abnormal);
12796 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12797 COM_IIDOF(ISession),
12798 getComponentName(),
12799 tr("The VM session was aborted"));
12800 mConsoleTaskData.mProgress.setNull();
12801 }
12802
12803 /* remove the association between the peer machine and this session machine */
12804 Assert( (SessionMachine*)mData->mSession.mMachine == this
12805 || aReason == Uninit::Unexpected);
12806
12807 /* reset the rest of session data */
12808 mData->mSession.mLockType = LockType_Null;
12809 mData->mSession.mMachine.setNull();
12810 mData->mSession.mState = SessionState_Unlocked;
12811 mData->mSession.mName.setNull();
12812
12813 /* destroy the machine client token before leaving the exclusive lock */
12814 if (mClientToken)
12815 {
12816 delete mClientToken;
12817 mClientToken = NULL;
12818 }
12819
12820 /* fire an event */
12821 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12822
12823 uninitDataAndChildObjects();
12824
12825 /* free the essential data structure last */
12826 mData.free();
12827
12828 /* release the exclusive lock before setting the below two to NULL */
12829 multilock.release();
12830
12831 unconst(mParent) = NULL;
12832 unconst(mPeer) = NULL;
12833
12834 AuthLibUnload(&mAuthLibCtx);
12835
12836 LogFlowThisFuncLeave();
12837}
12838
12839// util::Lockable interface
12840////////////////////////////////////////////////////////////////////////////////
12841
12842/**
12843 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12844 * with the primary Machine instance (mPeer).
12845 */
12846RWLockHandle *SessionMachine::lockHandle() const
12847{
12848 AssertReturn(mPeer != NULL, NULL);
12849 return mPeer->lockHandle();
12850}
12851
12852// IInternalMachineControl methods
12853////////////////////////////////////////////////////////////////////////////////
12854
12855/**
12856 * Passes collected guest statistics to performance collector object
12857 */
12858HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12859 ULONG aCpuKernel, ULONG aCpuIdle,
12860 ULONG aMemTotal, ULONG aMemFree,
12861 ULONG aMemBalloon, ULONG aMemShared,
12862 ULONG aMemCache, ULONG aPageTotal,
12863 ULONG aAllocVMM, ULONG aFreeVMM,
12864 ULONG aBalloonedVMM, ULONG aSharedVMM,
12865 ULONG aVmNetRx, ULONG aVmNetTx)
12866{
12867#ifdef VBOX_WITH_RESOURCE_USAGE_API
12868 if (mCollectorGuest)
12869 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12870 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12871 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12872 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12873
12874 return S_OK;
12875#else
12876 NOREF(aValidStats);
12877 NOREF(aCpuUser);
12878 NOREF(aCpuKernel);
12879 NOREF(aCpuIdle);
12880 NOREF(aMemTotal);
12881 NOREF(aMemFree);
12882 NOREF(aMemBalloon);
12883 NOREF(aMemShared);
12884 NOREF(aMemCache);
12885 NOREF(aPageTotal);
12886 NOREF(aAllocVMM);
12887 NOREF(aFreeVMM);
12888 NOREF(aBalloonedVMM);
12889 NOREF(aSharedVMM);
12890 NOREF(aVmNetRx);
12891 NOREF(aVmNetTx);
12892 return E_NOTIMPL;
12893#endif
12894}
12895
12896////////////////////////////////////////////////////////////////////////////////
12897//
12898// SessionMachine task records
12899//
12900////////////////////////////////////////////////////////////////////////////////
12901
12902/**
12903 * Task record for saving the machine state.
12904 */
12905class SessionMachine::SaveStateTask
12906 : public Machine::Task
12907{
12908public:
12909 SaveStateTask(SessionMachine *m,
12910 Progress *p,
12911 const Utf8Str &t,
12912 Reason_T enmReason,
12913 const Utf8Str &strStateFilePath)
12914 : Task(m, p, t),
12915 m_enmReason(enmReason),
12916 m_strStateFilePath(strStateFilePath)
12917 {}
12918
12919private:
12920 void handler()
12921 {
12922 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12923 }
12924
12925 Reason_T m_enmReason;
12926 Utf8Str m_strStateFilePath;
12927
12928 friend class SessionMachine;
12929};
12930
12931/**
12932 * Task thread implementation for SessionMachine::SaveState(), called from
12933 * SessionMachine::taskHandler().
12934 *
12935 * @note Locks this object for writing.
12936 *
12937 * @param task
12938 * @return
12939 */
12940void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12941{
12942 LogFlowThisFuncEnter();
12943
12944 AutoCaller autoCaller(this);
12945 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12946 if (FAILED(autoCaller.rc()))
12947 {
12948 /* we might have been uninitialized because the session was accidentally
12949 * closed by the client, so don't assert */
12950 HRESULT rc = setError(E_FAIL,
12951 tr("The session has been accidentally closed"));
12952 task.m_pProgress->i_notifyComplete(rc);
12953 LogFlowThisFuncLeave();
12954 return;
12955 }
12956
12957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12958
12959 HRESULT rc = S_OK;
12960
12961 try
12962 {
12963 ComPtr<IInternalSessionControl> directControl;
12964 if (mData->mSession.mLockType == LockType_VM)
12965 directControl = mData->mSession.mDirectControl;
12966 if (directControl.isNull())
12967 throw setError(VBOX_E_INVALID_VM_STATE,
12968 tr("Trying to save state without a running VM"));
12969 alock.release();
12970 BOOL fSuspendedBySave;
12971 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12972 Assert(!fSuspendedBySave);
12973 alock.acquire();
12974
12975 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12976 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12977 throw E_FAIL);
12978
12979 if (SUCCEEDED(rc))
12980 {
12981 mSSData->strStateFilePath = task.m_strStateFilePath;
12982
12983 /* save all VM settings */
12984 rc = i_saveSettings(NULL);
12985 // no need to check whether VirtualBox.xml needs saving also since
12986 // we can't have a name change pending at this point
12987 }
12988 else
12989 {
12990 // On failure, set the state to the state we had at the beginning.
12991 i_setMachineState(task.m_machineStateBackup);
12992 i_updateMachineStateOnClient();
12993
12994 // Delete the saved state file (might have been already created).
12995 // No need to check whether this is shared with a snapshot here
12996 // because we certainly created a fresh saved state file here.
12997 RTFileDelete(task.m_strStateFilePath.c_str());
12998 }
12999 }
13000 catch (HRESULT aRC) { rc = aRC; }
13001
13002 task.m_pProgress->i_notifyComplete(rc);
13003
13004 LogFlowThisFuncLeave();
13005}
13006
13007/**
13008 * @note Locks this object for writing.
13009 */
13010HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13011{
13012 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13013}
13014
13015HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13016{
13017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13018
13019 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13020 if (FAILED(rc)) return rc;
13021
13022 if ( mData->mMachineState != MachineState_Running
13023 && mData->mMachineState != MachineState_Paused
13024 )
13025 return setError(VBOX_E_INVALID_VM_STATE,
13026 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13027 Global::stringifyMachineState(mData->mMachineState));
13028
13029 ComObjPtr<Progress> pProgress;
13030 pProgress.createObject();
13031 rc = pProgress->init(i_getVirtualBox(),
13032 static_cast<IMachine *>(this) /* aInitiator */,
13033 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13034 FALSE /* aCancelable */);
13035 if (FAILED(rc))
13036 return rc;
13037
13038 Utf8Str strStateFilePath;
13039 i_composeSavedStateFilename(strStateFilePath);
13040
13041 /* create and start the task on a separate thread (note that it will not
13042 * start working until we release alock) */
13043 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13044 rc = pTask->createThread();
13045 if (FAILED(rc))
13046 return rc;
13047
13048 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13049 i_setMachineState(MachineState_Saving);
13050 i_updateMachineStateOnClient();
13051
13052 pProgress.queryInterfaceTo(aProgress.asOutParam());
13053
13054 return S_OK;
13055}
13056
13057/**
13058 * @note Locks this object for writing.
13059 */
13060HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13061{
13062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13063
13064 HRESULT rc = i_checkStateDependency(MutableStateDep);
13065 if (FAILED(rc)) return rc;
13066
13067 if ( mData->mMachineState != MachineState_PoweredOff
13068 && mData->mMachineState != MachineState_Teleported
13069 && mData->mMachineState != MachineState_Aborted
13070 )
13071 return setError(VBOX_E_INVALID_VM_STATE,
13072 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13073 Global::stringifyMachineState(mData->mMachineState));
13074
13075 com::Utf8Str stateFilePathFull;
13076 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13077 if (RT_FAILURE(vrc))
13078 return setError(VBOX_E_FILE_ERROR,
13079 tr("Invalid saved state file path '%s' (%Rrc)"),
13080 aSavedStateFile.c_str(),
13081 vrc);
13082
13083 mSSData->strStateFilePath = stateFilePathFull;
13084
13085 /* The below i_setMachineState() will detect the state transition and will
13086 * update the settings file */
13087
13088 return i_setMachineState(MachineState_Saved);
13089}
13090
13091/**
13092 * @note Locks this object for writing.
13093 */
13094HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13095{
13096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13097
13098 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13099 if (FAILED(rc)) return rc;
13100
13101 if (mData->mMachineState != MachineState_Saved)
13102 return setError(VBOX_E_INVALID_VM_STATE,
13103 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13104 Global::stringifyMachineState(mData->mMachineState));
13105
13106 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13107
13108 /*
13109 * Saved -> PoweredOff transition will be detected in the SessionMachine
13110 * and properly handled.
13111 */
13112 rc = i_setMachineState(MachineState_PoweredOff);
13113 return rc;
13114}
13115
13116
13117/**
13118 * @note Locks the same as #i_setMachineState() does.
13119 */
13120HRESULT SessionMachine::updateState(MachineState_T aState)
13121{
13122 return i_setMachineState(aState);
13123}
13124
13125/**
13126 * @note Locks this object for writing.
13127 */
13128HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13129{
13130 IProgress* pProgress(aProgress);
13131
13132 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13133
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 if (mData->mSession.mState != SessionState_Locked)
13137 return VBOX_E_INVALID_OBJECT_STATE;
13138
13139 if (!mData->mSession.mProgress.isNull())
13140 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13141
13142 /* If we didn't reference the NAT network service yet, add a reference to
13143 * force a start */
13144 if (miNATNetworksStarted < 1)
13145 {
13146 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13147 {
13148 BOOL enabled;
13149 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13150 if ( FAILED(hrc)
13151 || !enabled)
13152 continue;
13153
13154 NetworkAttachmentType_T type;
13155 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13156 if ( SUCCEEDED(hrc)
13157 && type == NetworkAttachmentType_NATNetwork)
13158 {
13159 Bstr name;
13160 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13161 if (SUCCEEDED(hrc))
13162 {
13163 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13164 mUserData->s.strName.c_str(), name.raw()));
13165 mPeer->lockHandle()->unlockWrite();
13166 mParent->i_natNetworkRefInc(name.raw());
13167#ifdef RT_LOCK_STRICT
13168 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13169#else
13170 mPeer->lockHandle()->lockWrite();
13171#endif
13172 }
13173 }
13174 }
13175 miNATNetworksStarted++;
13176 }
13177
13178 LogFlowThisFunc(("returns S_OK.\n"));
13179 return S_OK;
13180}
13181
13182/**
13183 * @note Locks this object for writing.
13184 */
13185HRESULT SessionMachine::endPowerUp(LONG aResult)
13186{
13187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13188
13189 if (mData->mSession.mState != SessionState_Locked)
13190 return VBOX_E_INVALID_OBJECT_STATE;
13191
13192 /* Finalize the LaunchVMProcess progress object. */
13193 if (mData->mSession.mProgress)
13194 {
13195 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13196 mData->mSession.mProgress.setNull();
13197 }
13198
13199 if (SUCCEEDED((HRESULT)aResult))
13200 {
13201#ifdef VBOX_WITH_RESOURCE_USAGE_API
13202 /* The VM has been powered up successfully, so it makes sense
13203 * now to offer the performance metrics for a running machine
13204 * object. Doing it earlier wouldn't be safe. */
13205 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13206 mData->mSession.mPID);
13207#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13208 }
13209
13210 return S_OK;
13211}
13212
13213/**
13214 * @note Locks this object for writing.
13215 */
13216HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13217{
13218 LogFlowThisFuncEnter();
13219
13220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13221
13222 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13223 E_FAIL);
13224
13225 /* create a progress object to track operation completion */
13226 ComObjPtr<Progress> pProgress;
13227 pProgress.createObject();
13228 pProgress->init(i_getVirtualBox(),
13229 static_cast<IMachine *>(this) /* aInitiator */,
13230 Bstr(tr("Stopping the virtual machine")).raw(),
13231 FALSE /* aCancelable */);
13232
13233 /* fill in the console task data */
13234 mConsoleTaskData.mLastState = mData->mMachineState;
13235 mConsoleTaskData.mProgress = pProgress;
13236
13237 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13238 i_setMachineState(MachineState_Stopping);
13239
13240 pProgress.queryInterfaceTo(aProgress.asOutParam());
13241
13242 return S_OK;
13243}
13244
13245/**
13246 * @note Locks this object for writing.
13247 */
13248HRESULT SessionMachine::endPoweringDown(LONG aResult,
13249 const com::Utf8Str &aErrMsg)
13250{
13251 LogFlowThisFuncEnter();
13252
13253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13254
13255 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13256 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13257 && mConsoleTaskData.mLastState != MachineState_Null,
13258 E_FAIL);
13259
13260 /*
13261 * On failure, set the state to the state we had when BeginPoweringDown()
13262 * was called (this is expected by Console::PowerDown() and the associated
13263 * task). On success the VM process already changed the state to
13264 * MachineState_PoweredOff, so no need to do anything.
13265 */
13266 if (FAILED(aResult))
13267 i_setMachineState(mConsoleTaskData.mLastState);
13268
13269 /* notify the progress object about operation completion */
13270 Assert(mConsoleTaskData.mProgress);
13271 if (SUCCEEDED(aResult))
13272 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13273 else
13274 {
13275 if (aErrMsg.length())
13276 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13277 COM_IIDOF(ISession),
13278 getComponentName(),
13279 aErrMsg.c_str());
13280 else
13281 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13282 }
13283
13284 /* clear out the temporary saved state data */
13285 mConsoleTaskData.mLastState = MachineState_Null;
13286 mConsoleTaskData.mProgress.setNull();
13287
13288 LogFlowThisFuncLeave();
13289 return S_OK;
13290}
13291
13292
13293/**
13294 * Goes through the USB filters of the given machine to see if the given
13295 * device matches any filter or not.
13296 *
13297 * @note Locks the same as USBController::hasMatchingFilter() does.
13298 */
13299HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13300 BOOL *aMatched,
13301 ULONG *aMaskedInterfaces)
13302{
13303 LogFlowThisFunc(("\n"));
13304
13305#ifdef VBOX_WITH_USB
13306 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13307#else
13308 NOREF(aDevice);
13309 NOREF(aMaskedInterfaces);
13310 *aMatched = FALSE;
13311#endif
13312
13313 return S_OK;
13314}
13315
13316/**
13317 * @note Locks the same as Host::captureUSBDevice() does.
13318 */
13319HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13320{
13321 LogFlowThisFunc(("\n"));
13322
13323#ifdef VBOX_WITH_USB
13324 /* if captureDeviceForVM() fails, it must have set extended error info */
13325 clearError();
13326 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13327 if (FAILED(rc)) return rc;
13328
13329 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13330 AssertReturn(service, E_FAIL);
13331 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13332#else
13333 NOREF(aId);
13334 return E_NOTIMPL;
13335#endif
13336}
13337
13338/**
13339 * @note Locks the same as Host::detachUSBDevice() does.
13340 */
13341HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13342 BOOL aDone)
13343{
13344 LogFlowThisFunc(("\n"));
13345
13346#ifdef VBOX_WITH_USB
13347 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13348 AssertReturn(service, E_FAIL);
13349 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13350#else
13351 NOREF(aId);
13352 NOREF(aDone);
13353 return E_NOTIMPL;
13354#endif
13355}
13356
13357/**
13358 * Inserts all machine filters to the USB proxy service and then calls
13359 * Host::autoCaptureUSBDevices().
13360 *
13361 * Called by Console from the VM process upon VM startup.
13362 *
13363 * @note Locks what called methods lock.
13364 */
13365HRESULT SessionMachine::autoCaptureUSBDevices()
13366{
13367 LogFlowThisFunc(("\n"));
13368
13369#ifdef VBOX_WITH_USB
13370 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13371 AssertComRC(rc);
13372 NOREF(rc);
13373
13374 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13375 AssertReturn(service, E_FAIL);
13376 return service->autoCaptureDevicesForVM(this);
13377#else
13378 return S_OK;
13379#endif
13380}
13381
13382/**
13383 * Removes all machine filters from the USB proxy service and then calls
13384 * Host::detachAllUSBDevices().
13385 *
13386 * Called by Console from the VM process upon normal VM termination or by
13387 * SessionMachine::uninit() upon abnormal VM termination (from under the
13388 * Machine/SessionMachine lock).
13389 *
13390 * @note Locks what called methods lock.
13391 */
13392HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13393{
13394 LogFlowThisFunc(("\n"));
13395
13396#ifdef VBOX_WITH_USB
13397 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13398 AssertComRC(rc);
13399 NOREF(rc);
13400
13401 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13402 AssertReturn(service, E_FAIL);
13403 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13404#else
13405 NOREF(aDone);
13406 return S_OK;
13407#endif
13408}
13409
13410/**
13411 * @note Locks this object for writing.
13412 */
13413HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13414 ComPtr<IProgress> &aProgress)
13415{
13416 LogFlowThisFuncEnter();
13417
13418 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13419 /*
13420 * We don't assert below because it might happen that a non-direct session
13421 * informs us it is closed right after we've been uninitialized -- it's ok.
13422 */
13423
13424 /* get IInternalSessionControl interface */
13425 ComPtr<IInternalSessionControl> control(aSession);
13426
13427 ComAssertRet(!control.isNull(), E_INVALIDARG);
13428
13429 /* Creating a Progress object requires the VirtualBox lock, and
13430 * thus locking it here is required by the lock order rules. */
13431 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13432
13433 if (control == mData->mSession.mDirectControl)
13434 {
13435 /* The direct session is being normally closed by the client process
13436 * ----------------------------------------------------------------- */
13437
13438 /* go to the closing state (essential for all open*Session() calls and
13439 * for #i_checkForDeath()) */
13440 Assert(mData->mSession.mState == SessionState_Locked);
13441 mData->mSession.mState = SessionState_Unlocking;
13442
13443 /* set direct control to NULL to release the remote instance */
13444 mData->mSession.mDirectControl.setNull();
13445 LogFlowThisFunc(("Direct control is set to NULL\n"));
13446
13447 if (mData->mSession.mProgress)
13448 {
13449 /* finalize the progress, someone might wait if a frontend
13450 * closes the session before powering on the VM. */
13451 mData->mSession.mProgress->notifyComplete(E_FAIL,
13452 COM_IIDOF(ISession),
13453 getComponentName(),
13454 tr("The VM session was closed before any attempt to power it on"));
13455 mData->mSession.mProgress.setNull();
13456 }
13457
13458 /* Create the progress object the client will use to wait until
13459 * #i_checkForDeath() is called to uninitialize this session object after
13460 * it releases the IPC semaphore.
13461 * Note! Because we're "reusing" mProgress here, this must be a proxy
13462 * object just like for LaunchVMProcess. */
13463 Assert(mData->mSession.mProgress.isNull());
13464 ComObjPtr<ProgressProxy> progress;
13465 progress.createObject();
13466 ComPtr<IUnknown> pPeer(mPeer);
13467 progress->init(mParent, pPeer,
13468 Bstr(tr("Closing session")).raw(),
13469 FALSE /* aCancelable */);
13470 progress.queryInterfaceTo(aProgress.asOutParam());
13471 mData->mSession.mProgress = progress;
13472 }
13473 else
13474 {
13475 /* the remote session is being normally closed */
13476 Data::Session::RemoteControlList::iterator it =
13477 mData->mSession.mRemoteControls.begin();
13478 while (it != mData->mSession.mRemoteControls.end())
13479 {
13480 if (control == *it)
13481 break;
13482 ++it;
13483 }
13484 BOOL found = it != mData->mSession.mRemoteControls.end();
13485 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13486 E_INVALIDARG);
13487 // This MUST be erase(it), not remove(*it) as the latter triggers a
13488 // very nasty use after free due to the place where the value "lives".
13489 mData->mSession.mRemoteControls.erase(it);
13490 }
13491
13492 /* signal the client watcher thread, because the client is going away */
13493 mParent->i_updateClientWatcher();
13494
13495 LogFlowThisFuncLeave();
13496 return S_OK;
13497}
13498
13499HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13500 std::vector<com::Utf8Str> &aValues,
13501 std::vector<LONG64> &aTimestamps,
13502 std::vector<com::Utf8Str> &aFlags)
13503{
13504 LogFlowThisFunc(("\n"));
13505
13506#ifdef VBOX_WITH_GUEST_PROPS
13507 using namespace guestProp;
13508
13509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13510
13511 size_t cEntries = mHWData->mGuestProperties.size();
13512 aNames.resize(cEntries);
13513 aValues.resize(cEntries);
13514 aTimestamps.resize(cEntries);
13515 aFlags.resize(cEntries);
13516
13517 size_t i = 0;
13518 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13519 it != mHWData->mGuestProperties.end();
13520 ++it, ++i)
13521 {
13522 char szFlags[MAX_FLAGS_LEN + 1];
13523 aNames[i] = it->first;
13524 aValues[i] = it->second.strValue;
13525 aTimestamps[i] = it->second.mTimestamp;
13526
13527 /* If it is NULL, keep it NULL. */
13528 if (it->second.mFlags)
13529 {
13530 writeFlags(it->second.mFlags, szFlags);
13531 aFlags[i] = szFlags;
13532 }
13533 else
13534 aFlags[i] = "";
13535 }
13536 return S_OK;
13537#else
13538 ReturnComNotImplemented();
13539#endif
13540}
13541
13542HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13543 const com::Utf8Str &aValue,
13544 LONG64 aTimestamp,
13545 const com::Utf8Str &aFlags)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549#ifdef VBOX_WITH_GUEST_PROPS
13550 using namespace guestProp;
13551
13552 try
13553 {
13554 /*
13555 * Convert input up front.
13556 */
13557 uint32_t fFlags = NILFLAG;
13558 if (aFlags.length())
13559 {
13560 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13561 AssertRCReturn(vrc, E_INVALIDARG);
13562 }
13563
13564 /*
13565 * Now grab the object lock, validate the state and do the update.
13566 */
13567
13568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13569
13570 if (!Global::IsOnline(mData->mMachineState))
13571 {
13572 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13573 VBOX_E_INVALID_VM_STATE);
13574 }
13575
13576 i_setModified(IsModified_MachineData);
13577 mHWData.backup();
13578
13579 bool fDelete = !aValue.length();
13580 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13581 if (it != mHWData->mGuestProperties.end())
13582 {
13583 if (!fDelete)
13584 {
13585 it->second.strValue = aValue;
13586 it->second.mTimestamp = aTimestamp;
13587 it->second.mFlags = fFlags;
13588 }
13589 else
13590 mHWData->mGuestProperties.erase(it);
13591
13592 mData->mGuestPropertiesModified = TRUE;
13593 }
13594 else if (!fDelete)
13595 {
13596 HWData::GuestProperty prop;
13597 prop.strValue = aValue;
13598 prop.mTimestamp = aTimestamp;
13599 prop.mFlags = fFlags;
13600
13601 mHWData->mGuestProperties[aName] = prop;
13602 mData->mGuestPropertiesModified = TRUE;
13603 }
13604
13605 alock.release();
13606
13607 mParent->i_onGuestPropertyChange(mData->mUuid,
13608 Bstr(aName).raw(),
13609 Bstr(aValue).raw(),
13610 Bstr(aFlags).raw());
13611 }
13612 catch (...)
13613 {
13614 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13615 }
13616 return S_OK;
13617#else
13618 ReturnComNotImplemented();
13619#endif
13620}
13621
13622
13623HRESULT SessionMachine::lockMedia()
13624{
13625 AutoMultiWriteLock2 alock(this->lockHandle(),
13626 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13627
13628 AssertReturn( mData->mMachineState == MachineState_Starting
13629 || mData->mMachineState == MachineState_Restoring
13630 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13631
13632 clearError();
13633 alock.release();
13634 return i_lockMedia();
13635}
13636
13637HRESULT SessionMachine::unlockMedia()
13638{
13639 HRESULT hrc = i_unlockMedia();
13640 return hrc;
13641}
13642
13643HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13644 ComPtr<IMediumAttachment> &aNewAttachment)
13645{
13646 // request the host lock first, since might be calling Host methods for getting host drives;
13647 // next, protect the media tree all the while we're in here, as well as our member variables
13648 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13649 this->lockHandle(),
13650 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13651
13652 IMediumAttachment *iAttach = aAttachment;
13653 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13654
13655 Bstr ctrlName;
13656 LONG lPort;
13657 LONG lDevice;
13658 bool fTempEject;
13659 {
13660 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13661
13662 /* Need to query the details first, as the IMediumAttachment reference
13663 * might be to the original settings, which we are going to change. */
13664 ctrlName = pAttach->i_getControllerName();
13665 lPort = pAttach->i_getPort();
13666 lDevice = pAttach->i_getDevice();
13667 fTempEject = pAttach->i_getTempEject();
13668 }
13669
13670 if (!fTempEject)
13671 {
13672 /* Remember previously mounted medium. The medium before taking the
13673 * backup is not necessarily the same thing. */
13674 ComObjPtr<Medium> oldmedium;
13675 oldmedium = pAttach->i_getMedium();
13676
13677 i_setModified(IsModified_Storage);
13678 mMediaData.backup();
13679
13680 // The backup operation makes the pAttach reference point to the
13681 // old settings. Re-get the correct reference.
13682 pAttach = i_findAttachment(mMediaData->mAttachments,
13683 ctrlName.raw(),
13684 lPort,
13685 lDevice);
13686
13687 {
13688 AutoCaller autoAttachCaller(this);
13689 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13690
13691 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13692 if (!oldmedium.isNull())
13693 oldmedium->i_removeBackReference(mData->mUuid);
13694
13695 pAttach->i_updateMedium(NULL);
13696 pAttach->i_updateEjected();
13697 }
13698
13699 i_setModified(IsModified_Storage);
13700 }
13701 else
13702 {
13703 {
13704 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13705 pAttach->i_updateEjected();
13706 }
13707 }
13708
13709 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13710
13711 return S_OK;
13712}
13713
13714HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13715 com::Utf8Str &aResult)
13716{
13717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13718
13719 HRESULT hr = S_OK;
13720
13721 if (!mAuthLibCtx.hAuthLibrary)
13722 {
13723 /* Load the external authentication library. */
13724 Bstr authLibrary;
13725 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13726
13727 Utf8Str filename = authLibrary;
13728
13729 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13730 if (RT_FAILURE(rc))
13731 {
13732 hr = setError(E_FAIL,
13733 tr("Could not load the external authentication library '%s' (%Rrc)"),
13734 filename.c_str(), rc);
13735 }
13736 }
13737
13738 /* The auth library might need the machine lock. */
13739 alock.release();
13740
13741 if (FAILED(hr))
13742 return hr;
13743
13744 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13745 {
13746 enum VRDEAuthParams
13747 {
13748 parmUuid = 1,
13749 parmGuestJudgement,
13750 parmUser,
13751 parmPassword,
13752 parmDomain,
13753 parmClientId
13754 };
13755
13756 AuthResult result = AuthResultAccessDenied;
13757
13758 Guid uuid(aAuthParams[parmUuid]);
13759 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13760 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13761
13762 result = AuthLibAuthenticate(&mAuthLibCtx,
13763 uuid.raw(), guestJudgement,
13764 aAuthParams[parmUser].c_str(),
13765 aAuthParams[parmPassword].c_str(),
13766 aAuthParams[parmDomain].c_str(),
13767 u32ClientId);
13768
13769 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13770 size_t cbPassword = aAuthParams[parmPassword].length();
13771 if (cbPassword)
13772 {
13773 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13774 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13775 }
13776
13777 if (result == AuthResultAccessGranted)
13778 aResult = "granted";
13779 else
13780 aResult = "denied";
13781
13782 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13783 aAuthParams[parmUser].c_str(), aResult.c_str()));
13784 }
13785 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13786 {
13787 enum VRDEAuthDisconnectParams
13788 {
13789 parmUuid = 1,
13790 parmClientId
13791 };
13792
13793 Guid uuid(aAuthParams[parmUuid]);
13794 uint32_t u32ClientId = 0;
13795 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13796 }
13797 else
13798 {
13799 hr = E_INVALIDARG;
13800 }
13801
13802 return hr;
13803}
13804
13805// public methods only for internal purposes
13806/////////////////////////////////////////////////////////////////////////////
13807
13808#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13809/**
13810 * Called from the client watcher thread to check for expected or unexpected
13811 * death of the client process that has a direct session to this machine.
13812 *
13813 * On Win32 and on OS/2, this method is called only when we've got the
13814 * mutex (i.e. the client has either died or terminated normally) so it always
13815 * returns @c true (the client is terminated, the session machine is
13816 * uninitialized).
13817 *
13818 * On other platforms, the method returns @c true if the client process has
13819 * terminated normally or abnormally and the session machine was uninitialized,
13820 * and @c false if the client process is still alive.
13821 *
13822 * @note Locks this object for writing.
13823 */
13824bool SessionMachine::i_checkForDeath()
13825{
13826 Uninit::Reason reason;
13827 bool terminated = false;
13828
13829 /* Enclose autoCaller with a block because calling uninit() from under it
13830 * will deadlock. */
13831 {
13832 AutoCaller autoCaller(this);
13833 if (!autoCaller.isOk())
13834 {
13835 /* return true if not ready, to cause the client watcher to exclude
13836 * the corresponding session from watching */
13837 LogFlowThisFunc(("Already uninitialized!\n"));
13838 return true;
13839 }
13840
13841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13842
13843 /* Determine the reason of death: if the session state is Closing here,
13844 * everything is fine. Otherwise it means that the client did not call
13845 * OnSessionEnd() before it released the IPC semaphore. This may happen
13846 * either because the client process has abnormally terminated, or
13847 * because it simply forgot to call ISession::Close() before exiting. We
13848 * threat the latter also as an abnormal termination (see
13849 * Session::uninit() for details). */
13850 reason = mData->mSession.mState == SessionState_Unlocking ?
13851 Uninit::Normal :
13852 Uninit::Abnormal;
13853
13854 if (mClientToken)
13855 terminated = mClientToken->release();
13856 } /* AutoCaller block */
13857
13858 if (terminated)
13859 uninit(reason);
13860
13861 return terminated;
13862}
13863
13864void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 strTokenId.setNull();
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturnVoid(autoCaller.rc());
13872
13873 Assert(mClientToken);
13874 if (mClientToken)
13875 mClientToken->getId(strTokenId);
13876}
13877#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13878IToken *SessionMachine::i_getToken()
13879{
13880 LogFlowThisFunc(("\n"));
13881
13882 AutoCaller autoCaller(this);
13883 AssertComRCReturn(autoCaller.rc(), NULL);
13884
13885 Assert(mClientToken);
13886 if (mClientToken)
13887 return mClientToken->getToken();
13888 else
13889 return NULL;
13890}
13891#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13892
13893Machine::ClientToken *SessionMachine::i_getClientToken()
13894{
13895 LogFlowThisFunc(("\n"));
13896
13897 AutoCaller autoCaller(this);
13898 AssertComRCReturn(autoCaller.rc(), NULL);
13899
13900 return mClientToken;
13901}
13902
13903
13904/**
13905 * @note Locks this object for reading.
13906 */
13907HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13908{
13909 LogFlowThisFunc(("\n"));
13910
13911 AutoCaller autoCaller(this);
13912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13913
13914 ComPtr<IInternalSessionControl> directControl;
13915 {
13916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13917 if (mData->mSession.mLockType == LockType_VM)
13918 directControl = mData->mSession.mDirectControl;
13919 }
13920
13921 /* ignore notifications sent after #OnSessionEnd() is called */
13922 if (!directControl)
13923 return S_OK;
13924
13925 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13926}
13927
13928/**
13929 * @note Locks this object for reading.
13930 */
13931HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13932 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13933 IN_BSTR aGuestIp, LONG aGuestPort)
13934{
13935 LogFlowThisFunc(("\n"));
13936
13937 AutoCaller autoCaller(this);
13938 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13939
13940 ComPtr<IInternalSessionControl> directControl;
13941 {
13942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13943 if (mData->mSession.mLockType == LockType_VM)
13944 directControl = mData->mSession.mDirectControl;
13945 }
13946
13947 /* ignore notifications sent after #OnSessionEnd() is called */
13948 if (!directControl)
13949 return S_OK;
13950 /*
13951 * instead acting like callback we ask IVirtualBox deliver corresponding event
13952 */
13953
13954 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13955 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13956 return S_OK;
13957}
13958
13959/**
13960 * @note Locks this object for reading.
13961 */
13962HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979
13980 return directControl->OnSerialPortChange(serialPort);
13981}
13982
13983/**
13984 * @note Locks this object for reading.
13985 */
13986HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13987{
13988 LogFlowThisFunc(("\n"));
13989
13990 AutoCaller autoCaller(this);
13991 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13992
13993 ComPtr<IInternalSessionControl> directControl;
13994 {
13995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13996 if (mData->mSession.mLockType == LockType_VM)
13997 directControl = mData->mSession.mDirectControl;
13998 }
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003
14004 return directControl->OnParallelPortChange(parallelPort);
14005}
14006
14007/**
14008 * @note Locks this object for reading.
14009 */
14010HRESULT SessionMachine::i_onStorageControllerChange()
14011{
14012 LogFlowThisFunc(("\n"));
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14016
14017 ComPtr<IInternalSessionControl> directControl;
14018 {
14019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14020 if (mData->mSession.mLockType == LockType_VM)
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* ignore notifications sent after #OnSessionEnd() is called */
14025 if (!directControl)
14026 return S_OK;
14027
14028 return directControl->OnStorageControllerChange();
14029}
14030
14031/**
14032 * @note Locks this object for reading.
14033 */
14034HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14040
14041 ComPtr<IInternalSessionControl> directControl;
14042 {
14043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14044 if (mData->mSession.mLockType == LockType_VM)
14045 directControl = mData->mSession.mDirectControl;
14046 }
14047
14048 /* ignore notifications sent after #OnSessionEnd() is called */
14049 if (!directControl)
14050 return S_OK;
14051
14052 return directControl->OnMediumChange(aAttachment, aForce);
14053}
14054
14055/**
14056 * @note Locks this object for reading.
14057 */
14058HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14059{
14060 LogFlowThisFunc(("\n"));
14061
14062 AutoCaller autoCaller(this);
14063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14064
14065 ComPtr<IInternalSessionControl> directControl;
14066 {
14067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14068 if (mData->mSession.mLockType == LockType_VM)
14069 directControl = mData->mSession.mDirectControl;
14070 }
14071
14072 /* ignore notifications sent after #OnSessionEnd() is called */
14073 if (!directControl)
14074 return S_OK;
14075
14076 return directControl->OnCPUChange(aCPU, aRemove);
14077}
14078
14079HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14080{
14081 LogFlowThisFunc(("\n"));
14082
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14085
14086 ComPtr<IInternalSessionControl> directControl;
14087 {
14088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14089 if (mData->mSession.mLockType == LockType_VM)
14090 directControl = mData->mSession.mDirectControl;
14091 }
14092
14093 /* ignore notifications sent after #OnSessionEnd() is called */
14094 if (!directControl)
14095 return S_OK;
14096
14097 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14098}
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnVRDEServerChange(aRestart);
14122}
14123
14124/**
14125 * @note Locks this object for reading.
14126 */
14127HRESULT SessionMachine::i_onVideoCaptureChange()
14128{
14129 LogFlowThisFunc(("\n"));
14130
14131 AutoCaller autoCaller(this);
14132 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14133
14134 ComPtr<IInternalSessionControl> directControl;
14135 {
14136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14137 if (mData->mSession.mLockType == LockType_VM)
14138 directControl = mData->mSession.mDirectControl;
14139 }
14140
14141 /* ignore notifications sent after #OnSessionEnd() is called */
14142 if (!directControl)
14143 return S_OK;
14144
14145 return directControl->OnVideoCaptureChange();
14146}
14147
14148/**
14149 * @note Locks this object for reading.
14150 */
14151HRESULT SessionMachine::i_onUSBControllerChange()
14152{
14153 LogFlowThisFunc(("\n"));
14154
14155 AutoCaller autoCaller(this);
14156 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14157
14158 ComPtr<IInternalSessionControl> directControl;
14159 {
14160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14161 if (mData->mSession.mLockType == LockType_VM)
14162 directControl = mData->mSession.mDirectControl;
14163 }
14164
14165 /* ignore notifications sent after #OnSessionEnd() is called */
14166 if (!directControl)
14167 return S_OK;
14168
14169 return directControl->OnUSBControllerChange();
14170}
14171
14172/**
14173 * @note Locks this object for reading.
14174 */
14175HRESULT SessionMachine::i_onSharedFolderChange()
14176{
14177 LogFlowThisFunc(("\n"));
14178
14179 AutoCaller autoCaller(this);
14180 AssertComRCReturnRC(autoCaller.rc());
14181
14182 ComPtr<IInternalSessionControl> directControl;
14183 {
14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14185 if (mData->mSession.mLockType == LockType_VM)
14186 directControl = mData->mSession.mDirectControl;
14187 }
14188
14189 /* ignore notifications sent after #OnSessionEnd() is called */
14190 if (!directControl)
14191 return S_OK;
14192
14193 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14194}
14195
14196/**
14197 * @note Locks this object for reading.
14198 */
14199HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14200{
14201 LogFlowThisFunc(("\n"));
14202
14203 AutoCaller autoCaller(this);
14204 AssertComRCReturnRC(autoCaller.rc());
14205
14206 ComPtr<IInternalSessionControl> directControl;
14207 {
14208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14209 if (mData->mSession.mLockType == LockType_VM)
14210 directControl = mData->mSession.mDirectControl;
14211 }
14212
14213 /* ignore notifications sent after #OnSessionEnd() is called */
14214 if (!directControl)
14215 return S_OK;
14216
14217 return directControl->OnClipboardModeChange(aClipboardMode);
14218}
14219
14220/**
14221 * @note Locks this object for reading.
14222 */
14223HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14224{
14225 LogFlowThisFunc(("\n"));
14226
14227 AutoCaller autoCaller(this);
14228 AssertComRCReturnRC(autoCaller.rc());
14229
14230 ComPtr<IInternalSessionControl> directControl;
14231 {
14232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14233 if (mData->mSession.mLockType == LockType_VM)
14234 directControl = mData->mSession.mDirectControl;
14235 }
14236
14237 /* ignore notifications sent after #OnSessionEnd() is called */
14238 if (!directControl)
14239 return S_OK;
14240
14241 return directControl->OnDnDModeChange(aDnDMode);
14242}
14243
14244/**
14245 * @note Locks this object for reading.
14246 */
14247HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14248{
14249 LogFlowThisFunc(("\n"));
14250
14251 AutoCaller autoCaller(this);
14252 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14253
14254 ComPtr<IInternalSessionControl> directControl;
14255 {
14256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14257 if (mData->mSession.mLockType == LockType_VM)
14258 directControl = mData->mSession.mDirectControl;
14259 }
14260
14261 /* ignore notifications sent after #OnSessionEnd() is called */
14262 if (!directControl)
14263 return S_OK;
14264
14265 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14266}
14267
14268/**
14269 * @note Locks this object for reading.
14270 */
14271HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14272{
14273 LogFlowThisFunc(("\n"));
14274
14275 AutoCaller autoCaller(this);
14276 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14277
14278 ComPtr<IInternalSessionControl> directControl;
14279 {
14280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14281 if (mData->mSession.mLockType == LockType_VM)
14282 directControl = mData->mSession.mDirectControl;
14283 }
14284
14285 /* ignore notifications sent after #OnSessionEnd() is called */
14286 if (!directControl)
14287 return S_OK;
14288
14289 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14290}
14291
14292/**
14293 * Returns @c true if this machine's USB controller reports it has a matching
14294 * filter for the given USB device and @c false otherwise.
14295 *
14296 * @note locks this object for reading.
14297 */
14298bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14299{
14300 AutoCaller autoCaller(this);
14301 /* silently return if not ready -- this method may be called after the
14302 * direct machine session has been called */
14303 if (!autoCaller.isOk())
14304 return false;
14305
14306#ifdef VBOX_WITH_USB
14307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14308
14309 switch (mData->mMachineState)
14310 {
14311 case MachineState_Starting:
14312 case MachineState_Restoring:
14313 case MachineState_TeleportingIn:
14314 case MachineState_Paused:
14315 case MachineState_Running:
14316 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14317 * elsewhere... */
14318 alock.release();
14319 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14320 default: break;
14321 }
14322#else
14323 NOREF(aDevice);
14324 NOREF(aMaskedIfs);
14325#endif
14326 return false;
14327}
14328
14329/**
14330 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14331 */
14332HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14333 IVirtualBoxErrorInfo *aError,
14334 ULONG aMaskedIfs,
14335 const com::Utf8Str &aCaptureFilename)
14336{
14337 LogFlowThisFunc(("\n"));
14338
14339 AutoCaller autoCaller(this);
14340
14341 /* This notification may happen after the machine object has been
14342 * uninitialized (the session was closed), so don't assert. */
14343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14344
14345 ComPtr<IInternalSessionControl> directControl;
14346 {
14347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14348 if (mData->mSession.mLockType == LockType_VM)
14349 directControl = mData->mSession.mDirectControl;
14350 }
14351
14352 /* fail on notifications sent after #OnSessionEnd() is called, it is
14353 * expected by the caller */
14354 if (!directControl)
14355 return E_FAIL;
14356
14357 /* No locks should be held at this point. */
14358 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14359 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14360
14361 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14362}
14363
14364/**
14365 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14366 */
14367HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14368 IVirtualBoxErrorInfo *aError)
14369{
14370 LogFlowThisFunc(("\n"));
14371
14372 AutoCaller autoCaller(this);
14373
14374 /* This notification may happen after the machine object has been
14375 * uninitialized (the session was closed), so don't assert. */
14376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* fail on notifications sent after #OnSessionEnd() is called, it is
14386 * expected by the caller */
14387 if (!directControl)
14388 return E_FAIL;
14389
14390 /* No locks should be held at this point. */
14391 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14392 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14393
14394 return directControl->OnUSBDeviceDetach(aId, aError);
14395}
14396
14397// protected methods
14398/////////////////////////////////////////////////////////////////////////////
14399
14400/**
14401 * Deletes the given file if it is no longer in use by either the current machine state
14402 * (if the machine is "saved") or any of the machine's snapshots.
14403 *
14404 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14405 * but is different for each SnapshotMachine. When calling this, the order of calling this
14406 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14407 * is therefore critical. I know, it's all rather messy.
14408 *
14409 * @param strStateFile
14410 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14411 * the test for whether the saved state file is in use.
14412 */
14413void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14414 Snapshot *pSnapshotToIgnore)
14415{
14416 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14417 if ( (strStateFile.isNotEmpty())
14418 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14419 )
14420 // ... and it must also not be shared with other snapshots
14421 if ( !mData->mFirstSnapshot
14422 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14423 // this checks the SnapshotMachine's state file paths
14424 )
14425 RTFileDelete(strStateFile.c_str());
14426}
14427
14428/**
14429 * Locks the attached media.
14430 *
14431 * All attached hard disks are locked for writing and DVD/floppy are locked for
14432 * reading. Parents of attached hard disks (if any) are locked for reading.
14433 *
14434 * This method also performs accessibility check of all media it locks: if some
14435 * media is inaccessible, the method will return a failure and a bunch of
14436 * extended error info objects per each inaccessible medium.
14437 *
14438 * Note that this method is atomic: if it returns a success, all media are
14439 * locked as described above; on failure no media is locked at all (all
14440 * succeeded individual locks will be undone).
14441 *
14442 * The caller is responsible for doing the necessary state sanity checks.
14443 *
14444 * The locks made by this method must be undone by calling #unlockMedia() when
14445 * no more needed.
14446 */
14447HRESULT SessionMachine::i_lockMedia()
14448{
14449 AutoCaller autoCaller(this);
14450 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14451
14452 AutoMultiWriteLock2 alock(this->lockHandle(),
14453 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14454
14455 /* bail out if trying to lock things with already set up locking */
14456 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14457
14458 MultiResult mrc(S_OK);
14459
14460 /* Collect locking information for all medium objects attached to the VM. */
14461 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14462 it != mMediaData->mAttachments.end();
14463 ++it)
14464 {
14465 MediumAttachment* pAtt = *it;
14466 DeviceType_T devType = pAtt->i_getType();
14467 Medium *pMedium = pAtt->i_getMedium();
14468
14469 MediumLockList *pMediumLockList(new MediumLockList());
14470 // There can be attachments without a medium (floppy/dvd), and thus
14471 // it's impossible to create a medium lock list. It still makes sense
14472 // to have the empty medium lock list in the map in case a medium is
14473 // attached later.
14474 if (pMedium != NULL)
14475 {
14476 MediumType_T mediumType = pMedium->i_getType();
14477 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14478 || mediumType == MediumType_Shareable;
14479 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14480
14481 alock.release();
14482 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14483 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14484 false /* fMediumLockWriteAll */,
14485 NULL,
14486 *pMediumLockList);
14487 alock.acquire();
14488 if (FAILED(mrc))
14489 {
14490 delete pMediumLockList;
14491 mData->mSession.mLockedMedia.Clear();
14492 break;
14493 }
14494 }
14495
14496 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14497 if (FAILED(rc))
14498 {
14499 mData->mSession.mLockedMedia.Clear();
14500 mrc = setError(rc,
14501 tr("Collecting locking information for all attached media failed"));
14502 break;
14503 }
14504 }
14505
14506 if (SUCCEEDED(mrc))
14507 {
14508 /* Now lock all media. If this fails, nothing is locked. */
14509 alock.release();
14510 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14511 alock.acquire();
14512 if (FAILED(rc))
14513 {
14514 mrc = setError(rc,
14515 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14516 }
14517 }
14518
14519 return mrc;
14520}
14521
14522/**
14523 * Undoes the locks made by by #lockMedia().
14524 */
14525HRESULT SessionMachine::i_unlockMedia()
14526{
14527 AutoCaller autoCaller(this);
14528 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14529
14530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14531
14532 /* we may be holding important error info on the current thread;
14533 * preserve it */
14534 ErrorInfoKeeper eik;
14535
14536 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14537 AssertComRC(rc);
14538 return rc;
14539}
14540
14541/**
14542 * Helper to change the machine state (reimplementation).
14543 *
14544 * @note Locks this object for writing.
14545 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14546 * it can cause crashes in random places due to unexpectedly committing
14547 * the current settings. The caller is responsible for that. The call
14548 * to saveStateSettings is fine, because this method does not commit.
14549 */
14550HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14551{
14552 LogFlowThisFuncEnter();
14553 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14554
14555 AutoCaller autoCaller(this);
14556 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14557
14558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14559
14560 MachineState_T oldMachineState = mData->mMachineState;
14561
14562 AssertMsgReturn(oldMachineState != aMachineState,
14563 ("oldMachineState=%s, aMachineState=%s\n",
14564 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14565 E_FAIL);
14566
14567 HRESULT rc = S_OK;
14568
14569 int stsFlags = 0;
14570 bool deleteSavedState = false;
14571
14572 /* detect some state transitions */
14573
14574 if ( ( oldMachineState == MachineState_Saved
14575 && aMachineState == MachineState_Restoring)
14576 || ( ( oldMachineState == MachineState_PoweredOff
14577 || oldMachineState == MachineState_Teleported
14578 || oldMachineState == MachineState_Aborted
14579 )
14580 && ( aMachineState == MachineState_TeleportingIn
14581 || aMachineState == MachineState_Starting
14582 )
14583 )
14584 )
14585 {
14586 /* The EMT thread is about to start */
14587
14588 /* Nothing to do here for now... */
14589
14590 /// @todo NEWMEDIA don't let mDVDDrive and other children
14591 /// change anything when in the Starting/Restoring state
14592 }
14593 else if ( ( oldMachineState == MachineState_Running
14594 || oldMachineState == MachineState_Paused
14595 || oldMachineState == MachineState_Teleporting
14596 || oldMachineState == MachineState_OnlineSnapshotting
14597 || oldMachineState == MachineState_LiveSnapshotting
14598 || oldMachineState == MachineState_Stuck
14599 || oldMachineState == MachineState_Starting
14600 || oldMachineState == MachineState_Stopping
14601 || oldMachineState == MachineState_Saving
14602 || oldMachineState == MachineState_Restoring
14603 || oldMachineState == MachineState_TeleportingPausedVM
14604 || oldMachineState == MachineState_TeleportingIn
14605 )
14606 && ( aMachineState == MachineState_PoweredOff
14607 || aMachineState == MachineState_Saved
14608 || aMachineState == MachineState_Teleported
14609 || aMachineState == MachineState_Aborted
14610 )
14611 )
14612 {
14613 /* The EMT thread has just stopped, unlock attached media. Note that as
14614 * opposed to locking that is done from Console, we do unlocking here
14615 * because the VM process may have aborted before having a chance to
14616 * properly unlock all media it locked. */
14617
14618 unlockMedia();
14619 }
14620
14621 if (oldMachineState == MachineState_Restoring)
14622 {
14623 if (aMachineState != MachineState_Saved)
14624 {
14625 /*
14626 * delete the saved state file once the machine has finished
14627 * restoring from it (note that Console sets the state from
14628 * Restoring to Saved if the VM couldn't restore successfully,
14629 * to give the user an ability to fix an error and retry --
14630 * we keep the saved state file in this case)
14631 */
14632 deleteSavedState = true;
14633 }
14634 }
14635 else if ( oldMachineState == MachineState_Saved
14636 && ( aMachineState == MachineState_PoweredOff
14637 || aMachineState == MachineState_Aborted
14638 || aMachineState == MachineState_Teleported
14639 )
14640 )
14641 {
14642 /*
14643 * delete the saved state after SessionMachine::ForgetSavedState() is called
14644 * or if the VM process (owning a direct VM session) crashed while the
14645 * VM was Saved
14646 */
14647
14648 /// @todo (dmik)
14649 // Not sure that deleting the saved state file just because of the
14650 // client death before it attempted to restore the VM is a good
14651 // thing. But when it crashes we need to go to the Aborted state
14652 // which cannot have the saved state file associated... The only
14653 // way to fix this is to make the Aborted condition not a VM state
14654 // but a bool flag: i.e., when a crash occurs, set it to true and
14655 // change the state to PoweredOff or Saved depending on the
14656 // saved state presence.
14657
14658 deleteSavedState = true;
14659 mData->mCurrentStateModified = TRUE;
14660 stsFlags |= SaveSTS_CurStateModified;
14661 }
14662
14663 if ( aMachineState == MachineState_Starting
14664 || aMachineState == MachineState_Restoring
14665 || aMachineState == MachineState_TeleportingIn
14666 )
14667 {
14668 /* set the current state modified flag to indicate that the current
14669 * state is no more identical to the state in the
14670 * current snapshot */
14671 if (!mData->mCurrentSnapshot.isNull())
14672 {
14673 mData->mCurrentStateModified = TRUE;
14674 stsFlags |= SaveSTS_CurStateModified;
14675 }
14676 }
14677
14678 if (deleteSavedState)
14679 {
14680 if (mRemoveSavedState)
14681 {
14682 Assert(!mSSData->strStateFilePath.isEmpty());
14683
14684 // it is safe to delete the saved state file if ...
14685 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14686 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14687 // ... none of the snapshots share the saved state file
14688 )
14689 RTFileDelete(mSSData->strStateFilePath.c_str());
14690 }
14691
14692 mSSData->strStateFilePath.setNull();
14693 stsFlags |= SaveSTS_StateFilePath;
14694 }
14695
14696 /* redirect to the underlying peer machine */
14697 mPeer->i_setMachineState(aMachineState);
14698
14699 if ( oldMachineState != MachineState_RestoringSnapshot
14700 && ( aMachineState == MachineState_PoweredOff
14701 || aMachineState == MachineState_Teleported
14702 || aMachineState == MachineState_Aborted
14703 || aMachineState == MachineState_Saved))
14704 {
14705 /* the machine has stopped execution
14706 * (or the saved state file was adopted) */
14707 stsFlags |= SaveSTS_StateTimeStamp;
14708 }
14709
14710 if ( ( oldMachineState == MachineState_PoweredOff
14711 || oldMachineState == MachineState_Aborted
14712 || oldMachineState == MachineState_Teleported
14713 )
14714 && aMachineState == MachineState_Saved)
14715 {
14716 /* the saved state file was adopted */
14717 Assert(!mSSData->strStateFilePath.isEmpty());
14718 stsFlags |= SaveSTS_StateFilePath;
14719 }
14720
14721#ifdef VBOX_WITH_GUEST_PROPS
14722 if ( aMachineState == MachineState_PoweredOff
14723 || aMachineState == MachineState_Aborted
14724 || aMachineState == MachineState_Teleported)
14725 {
14726 /* Make sure any transient guest properties get removed from the
14727 * property store on shutdown. */
14728 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14729
14730 /* remove it from the settings representation */
14731 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14732 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14733 it != llGuestProperties.end();
14734 /*nothing*/)
14735 {
14736 const settings::GuestProperty &prop = *it;
14737 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14738 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14739 {
14740 it = llGuestProperties.erase(it);
14741 fNeedsSaving = true;
14742 }
14743 else
14744 {
14745 ++it;
14746 }
14747 }
14748
14749 /* Additionally remove it from the HWData representation. Required to
14750 * keep everything in sync, as this is what the API keeps using. */
14751 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14752 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14753 it != llHWGuestProperties.end();
14754 /*nothing*/)
14755 {
14756 uint32_t fFlags = it->second.mFlags;
14757 if ( fFlags & guestProp::TRANSIENT
14758 || fFlags & guestProp::TRANSRESET)
14759 {
14760 /* iterator where we need to continue after the erase call
14761 * (C++03 is a fact still, and it doesn't return the iterator
14762 * which would allow continuing) */
14763 HWData::GuestPropertyMap::iterator it2 = it;
14764 ++it2;
14765 llHWGuestProperties.erase(it);
14766 it = it2;
14767 fNeedsSaving = true;
14768 }
14769 else
14770 {
14771 ++it;
14772 }
14773 }
14774
14775 if (fNeedsSaving)
14776 {
14777 mData->mCurrentStateModified = TRUE;
14778 stsFlags |= SaveSTS_CurStateModified;
14779 }
14780 }
14781#endif /* VBOX_WITH_GUEST_PROPS */
14782
14783 rc = i_saveStateSettings(stsFlags);
14784
14785 if ( ( oldMachineState != MachineState_PoweredOff
14786 && oldMachineState != MachineState_Aborted
14787 && oldMachineState != MachineState_Teleported
14788 )
14789 && ( aMachineState == MachineState_PoweredOff
14790 || aMachineState == MachineState_Aborted
14791 || aMachineState == MachineState_Teleported
14792 )
14793 )
14794 {
14795 /* we've been shut down for any reason */
14796 /* no special action so far */
14797 }
14798
14799 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14800 LogFlowThisFuncLeave();
14801 return rc;
14802}
14803
14804/**
14805 * Sends the current machine state value to the VM process.
14806 *
14807 * @note Locks this object for reading, then calls a client process.
14808 */
14809HRESULT SessionMachine::i_updateMachineStateOnClient()
14810{
14811 AutoCaller autoCaller(this);
14812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14813
14814 ComPtr<IInternalSessionControl> directControl;
14815 {
14816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14817 AssertReturn(!!mData, E_FAIL);
14818 if (mData->mSession.mLockType == LockType_VM)
14819 directControl = mData->mSession.mDirectControl;
14820
14821 /* directControl may be already set to NULL here in #OnSessionEnd()
14822 * called too early by the direct session process while there is still
14823 * some operation (like deleting the snapshot) in progress. The client
14824 * process in this case is waiting inside Session::close() for the
14825 * "end session" process object to complete, while #uninit() called by
14826 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14827 * operation to complete. For now, we accept this inconsistent behavior
14828 * and simply do nothing here. */
14829
14830 if (mData->mSession.mState == SessionState_Unlocking)
14831 return S_OK;
14832 }
14833
14834 /* ignore notifications sent after #OnSessionEnd() is called */
14835 if (!directControl)
14836 return S_OK;
14837
14838 return directControl->UpdateMachineState(mData->mMachineState);
14839}
14840
14841
14842/*static*/
14843HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14844{
14845 va_list args;
14846 va_start(args, pcszMsg);
14847 HRESULT rc = setErrorInternal(aResultCode,
14848 getStaticClassIID(),
14849 getStaticComponentName(),
14850 Utf8Str(pcszMsg, args),
14851 false /* aWarning */,
14852 true /* aLogIt */);
14853 va_end(args);
14854 return rc;
14855}
14856
14857
14858HRESULT Machine::updateState(MachineState_T aState)
14859{
14860 NOREF(aState);
14861 ReturnComNotImplemented();
14862}
14863
14864HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14865{
14866 NOREF(aProgress);
14867 ReturnComNotImplemented();
14868}
14869
14870HRESULT Machine::endPowerUp(LONG aResult)
14871{
14872 NOREF(aResult);
14873 ReturnComNotImplemented();
14874}
14875
14876HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14877{
14878 NOREF(aProgress);
14879 ReturnComNotImplemented();
14880}
14881
14882HRESULT Machine::endPoweringDown(LONG aResult,
14883 const com::Utf8Str &aErrMsg)
14884{
14885 NOREF(aResult);
14886 NOREF(aErrMsg);
14887 ReturnComNotImplemented();
14888}
14889
14890HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14891 BOOL *aMatched,
14892 ULONG *aMaskedInterfaces)
14893{
14894 NOREF(aDevice);
14895 NOREF(aMatched);
14896 NOREF(aMaskedInterfaces);
14897 ReturnComNotImplemented();
14898
14899}
14900
14901HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14902{
14903 NOREF(aId); NOREF(aCaptureFilename);
14904 ReturnComNotImplemented();
14905}
14906
14907HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14908 BOOL aDone)
14909{
14910 NOREF(aId);
14911 NOREF(aDone);
14912 ReturnComNotImplemented();
14913}
14914
14915HRESULT Machine::autoCaptureUSBDevices()
14916{
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14921{
14922 NOREF(aDone);
14923 ReturnComNotImplemented();
14924}
14925
14926HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14927 ComPtr<IProgress> &aProgress)
14928{
14929 NOREF(aSession);
14930 NOREF(aProgress);
14931 ReturnComNotImplemented();
14932}
14933
14934HRESULT Machine::finishOnlineMergeMedium()
14935{
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14940 std::vector<com::Utf8Str> &aValues,
14941 std::vector<LONG64> &aTimestamps,
14942 std::vector<com::Utf8Str> &aFlags)
14943{
14944 NOREF(aNames);
14945 NOREF(aValues);
14946 NOREF(aTimestamps);
14947 NOREF(aFlags);
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14952 const com::Utf8Str &aValue,
14953 LONG64 aTimestamp,
14954 const com::Utf8Str &aFlags)
14955{
14956 NOREF(aName);
14957 NOREF(aValue);
14958 NOREF(aTimestamp);
14959 NOREF(aFlags);
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::lockMedia()
14964{
14965 ReturnComNotImplemented();
14966}
14967
14968HRESULT Machine::unlockMedia()
14969{
14970 ReturnComNotImplemented();
14971}
14972
14973HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14974 ComPtr<IMediumAttachment> &aNewAttachment)
14975{
14976 NOREF(aAttachment);
14977 NOREF(aNewAttachment);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14982 ULONG aCpuUser,
14983 ULONG aCpuKernel,
14984 ULONG aCpuIdle,
14985 ULONG aMemTotal,
14986 ULONG aMemFree,
14987 ULONG aMemBalloon,
14988 ULONG aMemShared,
14989 ULONG aMemCache,
14990 ULONG aPagedTotal,
14991 ULONG aMemAllocTotal,
14992 ULONG aMemFreeTotal,
14993 ULONG aMemBalloonTotal,
14994 ULONG aMemSharedTotal,
14995 ULONG aVmNetRx,
14996 ULONG aVmNetTx)
14997{
14998 NOREF(aValidStats);
14999 NOREF(aCpuUser);
15000 NOREF(aCpuKernel);
15001 NOREF(aCpuIdle);
15002 NOREF(aMemTotal);
15003 NOREF(aMemFree);
15004 NOREF(aMemBalloon);
15005 NOREF(aMemShared);
15006 NOREF(aMemCache);
15007 NOREF(aPagedTotal);
15008 NOREF(aMemAllocTotal);
15009 NOREF(aMemFreeTotal);
15010 NOREF(aMemBalloonTotal);
15011 NOREF(aMemSharedTotal);
15012 NOREF(aVmNetRx);
15013 NOREF(aVmNetTx);
15014 ReturnComNotImplemented();
15015}
15016
15017HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15018 com::Utf8Str &aResult)
15019{
15020 NOREF(aAuthParams);
15021 NOREF(aResult);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15026{
15027 NOREF(aFlags);
15028 ReturnComNotImplemented();
15029}
15030
15031/* This isn't handled entirely by the wrapper generator yet. */
15032#ifdef VBOX_WITH_XPCOM
15033NS_DECL_CLASSINFO(SessionMachine)
15034NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15035
15036NS_DECL_CLASSINFO(SnapshotMachine)
15037NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15038#endif
Note: See TracBrowser for help on using the repository browser.

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