VirtualBox

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

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

Main: doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 517.3 KB
Line 
1/* $Id: MachineImpl.cpp 65125 2017-01-04 17:34:28Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 * @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 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 #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 * #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 #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 #addStateDependency() and
8254 * #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 #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 #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 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_InformCallbacksAnyway: Callbacks will be informed even if
9961 * #isReallyModified() returns false. This is necessary for cases when we
9962 * change machine data directly, not through the backup()/commit() mechanism.
9963 * - SaveS_Force: settings will be saved without doing a deep compare of the
9964 * settings structures. This is used when this is called because snapshots
9965 * have changed to avoid the overhead of the deep compare.
9966 *
9967 * @note Must be called from under this object's write lock. Locks children for
9968 * writing.
9969 *
9970 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9971 * initialized to false and that will be set to true by this function if
9972 * the caller must invoke VirtualBox::i_saveSettings() because the global
9973 * settings have changed. This will happen if a machine rename has been
9974 * saved and the global machine and media registries will therefore need
9975 * updating.
9976 * @param aFlags Flags.
9977 */
9978HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9979 int aFlags /*= 0*/)
9980{
9981 LogFlowThisFuncEnter();
9982
9983 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9984
9985 /* make sure child objects are unable to modify the settings while we are
9986 * saving them */
9987 i_ensureNoStateDependencies();
9988
9989 AssertReturn(!i_isSnapshotMachine(),
9990 E_FAIL);
9991
9992 HRESULT rc = S_OK;
9993 bool fNeedsWrite = false;
9994
9995 /* First, prepare to save settings. It will care about renaming the
9996 * settings directory and file if the machine name was changed and about
9997 * creating a new settings file if this is a new machine. */
9998 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9999 if (FAILED(rc)) return rc;
10000
10001 // keep a pointer to the current settings structures
10002 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10003 settings::MachineConfigFile *pNewConfig = NULL;
10004
10005 try
10006 {
10007 // make a fresh one to have everyone write stuff into
10008 pNewConfig = new settings::MachineConfigFile(NULL);
10009 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10010
10011 // now go and copy all the settings data from COM to the settings structures
10012 // (this calls i_saveSettings() on all the COM objects in the machine)
10013 i_copyMachineDataToSettings(*pNewConfig);
10014
10015 if (aFlags & SaveS_ResetCurStateModified)
10016 {
10017 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10018 mData->mCurrentStateModified = FALSE;
10019 fNeedsWrite = true; // always, no need to compare
10020 }
10021 else if (aFlags & SaveS_Force)
10022 {
10023 fNeedsWrite = true; // always, no need to compare
10024 }
10025 else
10026 {
10027 if (!mData->mCurrentStateModified)
10028 {
10029 // do a deep compare of the settings that we just saved with the settings
10030 // previously stored in the config file; this invokes MachineConfigFile::operator==
10031 // which does a deep compare of all the settings, which is expensive but less expensive
10032 // than writing out XML in vain
10033 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10034
10035 // could still be modified if any settings changed
10036 mData->mCurrentStateModified = fAnySettingsChanged;
10037
10038 fNeedsWrite = fAnySettingsChanged;
10039 }
10040 else
10041 fNeedsWrite = true;
10042 }
10043
10044 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10045
10046 if (fNeedsWrite)
10047 // now spit it all out!
10048 pNewConfig->write(mData->m_strConfigFileFull);
10049
10050 mData->pMachineConfigFile = pNewConfig;
10051 delete pOldConfig;
10052 i_commit();
10053
10054 // after saving settings, we are no longer different from the XML on disk
10055 mData->flModifications = 0;
10056 }
10057 catch (HRESULT err)
10058 {
10059 // we assume that error info is set by the thrower
10060 rc = err;
10061
10062 // restore old config
10063 delete pNewConfig;
10064 mData->pMachineConfigFile = pOldConfig;
10065 }
10066 catch (...)
10067 {
10068 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10069 }
10070
10071 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10072 {
10073 /* Fire the data change event, even on failure (since we've already
10074 * committed all data). This is done only for SessionMachines because
10075 * mutable Machine instances are always not registered (i.e. private
10076 * to the client process that creates them) and thus don't need to
10077 * inform callbacks. */
10078 if (i_isSessionMachine())
10079 mParent->i_onMachineDataChange(mData->mUuid);
10080 }
10081
10082 LogFlowThisFunc(("rc=%08X\n", rc));
10083 LogFlowThisFuncLeave();
10084 return rc;
10085}
10086
10087/**
10088 * Implementation for saving the machine settings into the given
10089 * settings::MachineConfigFile instance. This copies machine extradata
10090 * from the previous machine config file in the instance data, if any.
10091 *
10092 * This gets called from two locations:
10093 *
10094 * -- Machine::i_saveSettings(), during the regular XML writing;
10095 *
10096 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10097 * exported to OVF and we write the VirtualBox proprietary XML
10098 * into a <vbox:Machine> tag.
10099 *
10100 * This routine fills all the fields in there, including snapshots, *except*
10101 * for the following:
10102 *
10103 * -- fCurrentStateModified. There is some special logic associated with that.
10104 *
10105 * The caller can then call MachineConfigFile::write() or do something else
10106 * with it.
10107 *
10108 * Caller must hold the machine lock!
10109 *
10110 * This throws XML errors and HRESULT, so the caller must have a catch block!
10111 */
10112void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10113{
10114 // deep copy extradata, being extra careful with self assignment (the STL
10115 // map assignment on Mac OS X clang based Xcode isn't checking)
10116 if (&config != mData->pMachineConfigFile)
10117 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10118
10119 config.uuid = mData->mUuid;
10120
10121 // copy name, description, OS type, teleport, UTC etc.
10122 config.machineUserData = mUserData->s;
10123
10124 if ( mData->mMachineState == MachineState_Saved
10125 || mData->mMachineState == MachineState_Restoring
10126 // when doing certain snapshot operations we may or may not have
10127 // a saved state in the current state, so keep everything as is
10128 || ( ( mData->mMachineState == MachineState_Snapshotting
10129 || mData->mMachineState == MachineState_DeletingSnapshot
10130 || mData->mMachineState == MachineState_RestoringSnapshot)
10131 && (!mSSData->strStateFilePath.isEmpty())
10132 )
10133 )
10134 {
10135 Assert(!mSSData->strStateFilePath.isEmpty());
10136 /* try to make the file name relative to the settings file dir */
10137 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10138 }
10139 else
10140 {
10141 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10142 config.strStateFile.setNull();
10143 }
10144
10145 if (mData->mCurrentSnapshot)
10146 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10147 else
10148 config.uuidCurrentSnapshot.clear();
10149
10150 config.timeLastStateChange = mData->mLastStateChange;
10151 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10152 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10153
10154 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10155 if (FAILED(rc)) throw rc;
10156
10157 // save machine's media registry if this is VirtualBox 4.0 or later
10158 if (config.canHaveOwnMediaRegistry())
10159 {
10160 // determine machine folder
10161 Utf8Str strMachineFolder = i_getSettingsFileFull();
10162 strMachineFolder.stripFilename();
10163 mParent->i_saveMediaRegistry(config.mediaRegistry,
10164 i_getId(), // only media with registry ID == machine UUID
10165 strMachineFolder);
10166 // this throws HRESULT
10167 }
10168
10169 // save snapshots
10170 rc = i_saveAllSnapshots(config);
10171 if (FAILED(rc)) throw rc;
10172}
10173
10174/**
10175 * Saves all snapshots of the machine into the given machine config file. Called
10176 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10177 * @param config
10178 * @return
10179 */
10180HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10181{
10182 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10183
10184 HRESULT rc = S_OK;
10185
10186 try
10187 {
10188 config.llFirstSnapshot.clear();
10189
10190 if (mData->mFirstSnapshot)
10191 {
10192 // the settings use a list for "the first snapshot"
10193 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10194
10195 // get reference to the snapshot on the list and work on that
10196 // element straight in the list to avoid excessive copying later
10197 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10198 if (FAILED(rc)) throw rc;
10199 }
10200
10201// if (mType == IsSessionMachine)
10202// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10203
10204 }
10205 catch (HRESULT err)
10206 {
10207 /* we assume that error info is set by the thrower */
10208 rc = err;
10209 }
10210 catch (...)
10211 {
10212 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10213 }
10214
10215 return rc;
10216}
10217
10218/**
10219 * Saves the VM hardware configuration. It is assumed that the
10220 * given node is empty.
10221 *
10222 * @param data Reference to the settings object for the hardware config.
10223 * @param pDbg Pointer to the settings object for the debugging config
10224 * which happens to live in mHWData.
10225 * @param pAutostart Pointer to the settings object for the autostart config
10226 * which happens to live in mHWData.
10227 */
10228HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10229 settings::Autostart *pAutostart)
10230{
10231 HRESULT rc = S_OK;
10232
10233 try
10234 {
10235 /* The hardware version attribute (optional).
10236 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10237 if ( mHWData->mHWVersion == "1"
10238 && mSSData->strStateFilePath.isEmpty()
10239 )
10240 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10241 other point needs to be found where this can be done. */
10242
10243 data.strVersion = mHWData->mHWVersion;
10244 data.uuid = mHWData->mHardwareUUID;
10245
10246 // CPU
10247 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10248 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10249 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10250 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10251 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10252 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10253 data.fPAE = !!mHWData->mPAEEnabled;
10254 data.enmLongMode = mHWData->mLongMode;
10255 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10256 data.fAPIC = !!mHWData->mAPIC;
10257 data.fX2APIC = !!mHWData->mX2APIC;
10258 data.cCPUs = mHWData->mCPUCount;
10259 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10260 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10261 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10262 data.strCpuProfile = mHWData->mCpuProfile;
10263
10264 data.llCpus.clear();
10265 if (data.fCpuHotPlug)
10266 {
10267 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10268 {
10269 if (mHWData->mCPUAttached[idx])
10270 {
10271 settings::Cpu cpu;
10272 cpu.ulId = idx;
10273 data.llCpus.push_back(cpu);
10274 }
10275 }
10276 }
10277
10278 /* Standard and Extended CPUID leafs. */
10279 data.llCpuIdLeafs.clear();
10280 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10281 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10282 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10283 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10284 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10285 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10286
10287 // memory
10288 data.ulMemorySizeMB = mHWData->mMemorySize;
10289 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10290
10291 // firmware
10292 data.firmwareType = mHWData->mFirmwareType;
10293
10294 // HID
10295 data.pointingHIDType = mHWData->mPointingHIDType;
10296 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10297
10298 // chipset
10299 data.chipsetType = mHWData->mChipsetType;
10300
10301 // paravirt
10302 data.paravirtProvider = mHWData->mParavirtProvider;
10303 data.strParavirtDebug = mHWData->mParavirtDebug;
10304
10305 // emulated USB card reader
10306 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10307
10308 // HPET
10309 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10310
10311 // boot order
10312 data.mapBootOrder.clear();
10313 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10314 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10315
10316 // display
10317 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10318 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10319 data.cMonitors = mHWData->mMonitorCount;
10320 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10321 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10322 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10323 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10324 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10325 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10326 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10327 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10328 {
10329 if (mHWData->maVideoCaptureScreens[i])
10330 ASMBitSet(&data.u64VideoCaptureScreens, i);
10331 else
10332 ASMBitClear(&data.u64VideoCaptureScreens, i);
10333 }
10334 /* store relative video capture file if possible */
10335 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10336
10337 /* VRDEServer settings (optional) */
10338 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10339 if (FAILED(rc)) throw rc;
10340
10341 /* BIOS (required) */
10342 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10343 if (FAILED(rc)) throw rc;
10344
10345 /* USB Controller (required) */
10346 data.usbSettings.llUSBControllers.clear();
10347 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10348 {
10349 ComObjPtr<USBController> ctrl = *it;
10350 settings::USBController settingsCtrl;
10351
10352 settingsCtrl.strName = ctrl->i_getName();
10353 settingsCtrl.enmType = ctrl->i_getControllerType();
10354
10355 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10356 }
10357
10358 /* USB device filters (required) */
10359 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10360 if (FAILED(rc)) throw rc;
10361
10362 /* Network adapters (required) */
10363 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10364 data.llNetworkAdapters.clear();
10365 /* Write out only the nominal number of network adapters for this
10366 * chipset type. Since Machine::commit() hasn't been called there
10367 * may be extra NIC settings in the vector. */
10368 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10369 {
10370 settings::NetworkAdapter nic;
10371 nic.ulSlot = (uint32_t)slot;
10372 /* paranoia check... must not be NULL, but must not crash either. */
10373 if (mNetworkAdapters[slot])
10374 {
10375 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10376 if (FAILED(rc)) throw rc;
10377
10378 data.llNetworkAdapters.push_back(nic);
10379 }
10380 }
10381
10382 /* Serial ports */
10383 data.llSerialPorts.clear();
10384 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10385 {
10386 if (mSerialPorts[slot]->i_hasDefaults())
10387 continue;
10388
10389 settings::SerialPort s;
10390 s.ulSlot = slot;
10391 rc = mSerialPorts[slot]->i_saveSettings(s);
10392 if (FAILED(rc)) return rc;
10393
10394 data.llSerialPorts.push_back(s);
10395 }
10396
10397 /* Parallel ports */
10398 data.llParallelPorts.clear();
10399 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10400 {
10401 if (mParallelPorts[slot]->i_hasDefaults())
10402 continue;
10403
10404 settings::ParallelPort p;
10405 p.ulSlot = slot;
10406 rc = mParallelPorts[slot]->i_saveSettings(p);
10407 if (FAILED(rc)) return rc;
10408
10409 data.llParallelPorts.push_back(p);
10410 }
10411
10412 /* Audio adapter */
10413 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10414 if (FAILED(rc)) return rc;
10415
10416 rc = i_saveStorageControllers(data.storage);
10417 if (FAILED(rc)) return rc;
10418
10419 /* Shared folders */
10420 data.llSharedFolders.clear();
10421 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10422 it != mHWData->mSharedFolders.end();
10423 ++it)
10424 {
10425 SharedFolder *pSF = *it;
10426 AutoCaller sfCaller(pSF);
10427 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10428 settings::SharedFolder sf;
10429 sf.strName = pSF->i_getName();
10430 sf.strHostPath = pSF->i_getHostPath();
10431 sf.fWritable = !!pSF->i_isWritable();
10432 sf.fAutoMount = !!pSF->i_isAutoMounted();
10433
10434 data.llSharedFolders.push_back(sf);
10435 }
10436
10437 // clipboard
10438 data.clipboardMode = mHWData->mClipboardMode;
10439
10440 // drag'n'drop
10441 data.dndMode = mHWData->mDnDMode;
10442
10443 /* Guest */
10444 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10445
10446 // IO settings
10447 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10448 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10449
10450 /* BandwidthControl (required) */
10451 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10452 if (FAILED(rc)) throw rc;
10453
10454 /* Host PCI devices */
10455 data.pciAttachments.clear();
10456 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10457 it != mHWData->mPCIDeviceAssignments.end();
10458 ++it)
10459 {
10460 ComObjPtr<PCIDeviceAttachment> pda = *it;
10461 settings::HostPCIDeviceAttachment hpda;
10462
10463 rc = pda->i_saveSettings(hpda);
10464 if (FAILED(rc)) throw rc;
10465
10466 data.pciAttachments.push_back(hpda);
10467 }
10468
10469 // guest properties
10470 data.llGuestProperties.clear();
10471#ifdef VBOX_WITH_GUEST_PROPS
10472 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10473 it != mHWData->mGuestProperties.end();
10474 ++it)
10475 {
10476 HWData::GuestProperty property = it->second;
10477
10478 /* Remove transient guest properties at shutdown unless we
10479 * are saving state. Note that restoring snapshot intentionally
10480 * keeps them, they will be removed if appropriate once the final
10481 * machine state is set (as crashes etc. need to work). */
10482 if ( ( mData->mMachineState == MachineState_PoweredOff
10483 || mData->mMachineState == MachineState_Aborted
10484 || mData->mMachineState == MachineState_Teleported)
10485 && ( property.mFlags & guestProp::TRANSIENT
10486 || property.mFlags & guestProp::TRANSRESET))
10487 continue;
10488 settings::GuestProperty prop;
10489 prop.strName = it->first;
10490 prop.strValue = property.strValue;
10491 prop.timestamp = property.mTimestamp;
10492 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10493 guestProp::writeFlags(property.mFlags, szFlags);
10494 prop.strFlags = szFlags;
10495
10496 data.llGuestProperties.push_back(prop);
10497 }
10498
10499 /* I presume this doesn't require a backup(). */
10500 mData->mGuestPropertiesModified = FALSE;
10501#endif /* VBOX_WITH_GUEST_PROPS defined */
10502
10503 *pDbg = mHWData->mDebugging;
10504 *pAutostart = mHWData->mAutostart;
10505
10506 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10507 }
10508 catch(std::bad_alloc &)
10509 {
10510 return E_OUTOFMEMORY;
10511 }
10512
10513 AssertComRC(rc);
10514 return rc;
10515}
10516
10517/**
10518 * Saves the storage controller configuration.
10519 *
10520 * @param data storage settings.
10521 */
10522HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10523{
10524 data.llStorageControllers.clear();
10525
10526 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10527 it != mStorageControllers->end();
10528 ++it)
10529 {
10530 HRESULT rc;
10531 ComObjPtr<StorageController> pCtl = *it;
10532
10533 settings::StorageController ctl;
10534 ctl.strName = pCtl->i_getName();
10535 ctl.controllerType = pCtl->i_getControllerType();
10536 ctl.storageBus = pCtl->i_getStorageBus();
10537 ctl.ulInstance = pCtl->i_getInstance();
10538 ctl.fBootable = pCtl->i_getBootable();
10539
10540 /* Save the port count. */
10541 ULONG portCount;
10542 rc = pCtl->COMGETTER(PortCount)(&portCount);
10543 ComAssertComRCRet(rc, rc);
10544 ctl.ulPortCount = portCount;
10545
10546 /* Save fUseHostIOCache */
10547 BOOL fUseHostIOCache;
10548 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10549 ComAssertComRCRet(rc, rc);
10550 ctl.fUseHostIOCache = !!fUseHostIOCache;
10551
10552 /* save the devices now. */
10553 rc = i_saveStorageDevices(pCtl, ctl);
10554 ComAssertComRCRet(rc, rc);
10555
10556 data.llStorageControllers.push_back(ctl);
10557 }
10558
10559 return S_OK;
10560}
10561
10562/**
10563 * Saves the hard disk configuration.
10564 */
10565HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10566 settings::StorageController &data)
10567{
10568 MediaData::AttachmentList atts;
10569
10570 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10571 if (FAILED(rc)) return rc;
10572
10573 data.llAttachedDevices.clear();
10574 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10575 it != atts.end();
10576 ++it)
10577 {
10578 settings::AttachedDevice dev;
10579 IMediumAttachment *iA = *it;
10580 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10581 Medium *pMedium = pAttach->i_getMedium();
10582
10583 dev.deviceType = pAttach->i_getType();
10584 dev.lPort = pAttach->i_getPort();
10585 dev.lDevice = pAttach->i_getDevice();
10586 dev.fPassThrough = pAttach->i_getPassthrough();
10587 dev.fHotPluggable = pAttach->i_getHotPluggable();
10588 if (pMedium)
10589 {
10590 if (pMedium->i_isHostDrive())
10591 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10592 else
10593 dev.uuid = pMedium->i_getId();
10594 dev.fTempEject = pAttach->i_getTempEject();
10595 dev.fNonRotational = pAttach->i_getNonRotational();
10596 dev.fDiscard = pAttach->i_getDiscard();
10597 }
10598
10599 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10600
10601 data.llAttachedDevices.push_back(dev);
10602 }
10603
10604 return S_OK;
10605}
10606
10607/**
10608 * Saves machine state settings as defined by aFlags
10609 * (SaveSTS_* values).
10610 *
10611 * @param aFlags Combination of SaveSTS_* flags.
10612 *
10613 * @note Locks objects for writing.
10614 */
10615HRESULT Machine::i_saveStateSettings(int aFlags)
10616{
10617 if (aFlags == 0)
10618 return S_OK;
10619
10620 AutoCaller autoCaller(this);
10621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10622
10623 /* This object's write lock is also necessary to serialize file access
10624 * (prevent concurrent reads and writes) */
10625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10626
10627 HRESULT rc = S_OK;
10628
10629 Assert(mData->pMachineConfigFile);
10630
10631 try
10632 {
10633 if (aFlags & SaveSTS_CurStateModified)
10634 mData->pMachineConfigFile->fCurrentStateModified = true;
10635
10636 if (aFlags & SaveSTS_StateFilePath)
10637 {
10638 if (!mSSData->strStateFilePath.isEmpty())
10639 /* try to make the file name relative to the settings file dir */
10640 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10641 else
10642 mData->pMachineConfigFile->strStateFile.setNull();
10643 }
10644
10645 if (aFlags & SaveSTS_StateTimeStamp)
10646 {
10647 Assert( mData->mMachineState != MachineState_Aborted
10648 || mSSData->strStateFilePath.isEmpty());
10649
10650 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10651
10652 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10653/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10654 }
10655
10656 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10657 }
10658 catch (...)
10659 {
10660 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10661 }
10662
10663 return rc;
10664}
10665
10666/**
10667 * Ensures that the given medium is added to a media registry. If this machine
10668 * was created with 4.0 or later, then the machine registry is used. Otherwise
10669 * the global VirtualBox media registry is used.
10670 *
10671 * Caller must NOT hold machine lock, media tree or any medium locks!
10672 *
10673 * @param pMedium
10674 */
10675void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10676{
10677 /* Paranoia checks: do not hold machine or media tree locks. */
10678 AssertReturnVoid(!isWriteLockOnCurrentThread());
10679 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10680
10681 ComObjPtr<Medium> pBase;
10682 {
10683 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10684 pBase = pMedium->i_getBase();
10685 }
10686
10687 /* Paranoia checks: do not hold medium locks. */
10688 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10689 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10690
10691 // decide which medium registry to use now that the medium is attached:
10692 Guid uuid;
10693 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10694 // machine XML is VirtualBox 4.0 or higher:
10695 uuid = i_getId(); // machine UUID
10696 else
10697 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10698
10699 if (pMedium->i_addRegistry(uuid))
10700 mParent->i_markRegistryModified(uuid);
10701
10702 /* For more complex hard disk structures it can happen that the base
10703 * medium isn't yet associated with any medium registry. Do that now. */
10704 if (pMedium != pBase)
10705 {
10706 /* Tree lock needed by Medium::addRegistry when recursing. */
10707 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10708 if (pBase->i_addRegistryRecursive(uuid))
10709 {
10710 treeLock.release();
10711 mParent->i_markRegistryModified(uuid);
10712 }
10713 }
10714}
10715
10716/**
10717 * Creates differencing hard disks for all normal hard disks attached to this
10718 * machine and a new set of attachments to refer to created disks.
10719 *
10720 * Used when taking a snapshot or when deleting the current state. Gets called
10721 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10722 *
10723 * This method assumes that mMediaData contains the original hard disk attachments
10724 * it needs to create diffs for. On success, these attachments will be replaced
10725 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10726 * called to delete created diffs which will also rollback mMediaData and restore
10727 * whatever was backed up before calling this method.
10728 *
10729 * Attachments with non-normal hard disks are left as is.
10730 *
10731 * If @a aOnline is @c false then the original hard disks that require implicit
10732 * diffs will be locked for reading. Otherwise it is assumed that they are
10733 * already locked for writing (when the VM was started). Note that in the latter
10734 * case it is responsibility of the caller to lock the newly created diffs for
10735 * writing if this method succeeds.
10736 *
10737 * @param aProgress Progress object to run (must contain at least as
10738 * many operations left as the number of hard disks
10739 * attached).
10740 * @param aWeight Weight of this operation.
10741 * @param aOnline Whether the VM was online prior to this operation.
10742 *
10743 * @note The progress object is not marked as completed, neither on success nor
10744 * on failure. This is a responsibility of the caller.
10745 *
10746 * @note Locks this object and the media tree for writing.
10747 */
10748HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10749 ULONG aWeight,
10750 bool aOnline)
10751{
10752 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10753
10754 AutoCaller autoCaller(this);
10755 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10756
10757 AutoMultiWriteLock2 alock(this->lockHandle(),
10758 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10759
10760 /* must be in a protective state because we release the lock below */
10761 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10762 || mData->mMachineState == MachineState_OnlineSnapshotting
10763 || mData->mMachineState == MachineState_LiveSnapshotting
10764 || mData->mMachineState == MachineState_RestoringSnapshot
10765 || mData->mMachineState == MachineState_DeletingSnapshot
10766 , E_FAIL);
10767
10768 HRESULT rc = S_OK;
10769
10770 // use appropriate locked media map (online or offline)
10771 MediumLockListMap lockedMediaOffline;
10772 MediumLockListMap *lockedMediaMap;
10773 if (aOnline)
10774 lockedMediaMap = &mData->mSession.mLockedMedia;
10775 else
10776 lockedMediaMap = &lockedMediaOffline;
10777
10778 try
10779 {
10780 if (!aOnline)
10781 {
10782 /* lock all attached hard disks early to detect "in use"
10783 * situations before creating actual diffs */
10784 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10785 it != mMediaData->mAttachments.end();
10786 ++it)
10787 {
10788 MediumAttachment* pAtt = *it;
10789 if (pAtt->i_getType() == DeviceType_HardDisk)
10790 {
10791 Medium* pMedium = pAtt->i_getMedium();
10792 Assert(pMedium);
10793
10794 MediumLockList *pMediumLockList(new MediumLockList());
10795 alock.release();
10796 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10797 NULL /* pToLockWrite */,
10798 false /* fMediumLockWriteAll */,
10799 NULL,
10800 *pMediumLockList);
10801 alock.acquire();
10802 if (FAILED(rc))
10803 {
10804 delete pMediumLockList;
10805 throw rc;
10806 }
10807 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10808 if (FAILED(rc))
10809 {
10810 throw setError(rc,
10811 tr("Collecting locking information for all attached media failed"));
10812 }
10813 }
10814 }
10815
10816 /* Now lock all media. If this fails, nothing is locked. */
10817 alock.release();
10818 rc = lockedMediaMap->Lock();
10819 alock.acquire();
10820 if (FAILED(rc))
10821 {
10822 throw setError(rc,
10823 tr("Locking of attached media failed"));
10824 }
10825 }
10826
10827 /* remember the current list (note that we don't use backup() since
10828 * mMediaData may be already backed up) */
10829 MediaData::AttachmentList atts = mMediaData->mAttachments;
10830
10831 /* start from scratch */
10832 mMediaData->mAttachments.clear();
10833
10834 /* go through remembered attachments and create diffs for normal hard
10835 * disks and attach them */
10836 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10837 it != atts.end();
10838 ++it)
10839 {
10840 MediumAttachment* pAtt = *it;
10841
10842 DeviceType_T devType = pAtt->i_getType();
10843 Medium* pMedium = pAtt->i_getMedium();
10844
10845 if ( devType != DeviceType_HardDisk
10846 || pMedium == NULL
10847 || pMedium->i_getType() != MediumType_Normal)
10848 {
10849 /* copy the attachment as is */
10850
10851 /** @todo the progress object created in SessionMachine::TakeSnaphot
10852 * only expects operations for hard disks. Later other
10853 * device types need to show up in the progress as well. */
10854 if (devType == DeviceType_HardDisk)
10855 {
10856 if (pMedium == NULL)
10857 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10858 aWeight); // weight
10859 else
10860 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10861 pMedium->i_getBase()->i_getName().c_str()).raw(),
10862 aWeight); // weight
10863 }
10864
10865 mMediaData->mAttachments.push_back(pAtt);
10866 continue;
10867 }
10868
10869 /* need a diff */
10870 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10871 pMedium->i_getBase()->i_getName().c_str()).raw(),
10872 aWeight); // weight
10873
10874 Utf8Str strFullSnapshotFolder;
10875 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10876
10877 ComObjPtr<Medium> diff;
10878 diff.createObject();
10879 // store the diff in the same registry as the parent
10880 // (this cannot fail here because we can't create implicit diffs for
10881 // unregistered images)
10882 Guid uuidRegistryParent;
10883 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10884 Assert(fInRegistry); NOREF(fInRegistry);
10885 rc = diff->init(mParent,
10886 pMedium->i_getPreferredDiffFormat(),
10887 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10888 uuidRegistryParent,
10889 DeviceType_HardDisk);
10890 if (FAILED(rc)) throw rc;
10891
10892 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10893 * the push_back? Looks like we're going to release medium with the
10894 * wrong kind of lock (general issue with if we fail anywhere at all)
10895 * and an orphaned VDI in the snapshots folder. */
10896
10897 /* update the appropriate lock list */
10898 MediumLockList *pMediumLockList;
10899 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10900 AssertComRCThrowRC(rc);
10901 if (aOnline)
10902 {
10903 alock.release();
10904 /* The currently attached medium will be read-only, change
10905 * the lock type to read. */
10906 rc = pMediumLockList->Update(pMedium, false);
10907 alock.acquire();
10908 AssertComRCThrowRC(rc);
10909 }
10910
10911 /* release the locks before the potentially lengthy operation */
10912 alock.release();
10913 rc = pMedium->i_createDiffStorage(diff,
10914 pMedium->i_getPreferredDiffVariant(),
10915 pMediumLockList,
10916 NULL /* aProgress */,
10917 true /* aWait */);
10918 alock.acquire();
10919 if (FAILED(rc)) throw rc;
10920
10921 /* actual lock list update is done in Machine::i_commitMedia */
10922
10923 rc = diff->i_addBackReference(mData->mUuid);
10924 AssertComRCThrowRC(rc);
10925
10926 /* add a new attachment */
10927 ComObjPtr<MediumAttachment> attachment;
10928 attachment.createObject();
10929 rc = attachment->init(this,
10930 diff,
10931 pAtt->i_getControllerName(),
10932 pAtt->i_getPort(),
10933 pAtt->i_getDevice(),
10934 DeviceType_HardDisk,
10935 true /* aImplicit */,
10936 false /* aPassthrough */,
10937 false /* aTempEject */,
10938 pAtt->i_getNonRotational(),
10939 pAtt->i_getDiscard(),
10940 pAtt->i_getHotPluggable(),
10941 pAtt->i_getBandwidthGroup());
10942 if (FAILED(rc)) throw rc;
10943
10944 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10945 AssertComRCThrowRC(rc);
10946 mMediaData->mAttachments.push_back(attachment);
10947 }
10948 }
10949 catch (HRESULT aRC) { rc = aRC; }
10950
10951 /* unlock all hard disks we locked when there is no VM */
10952 if (!aOnline)
10953 {
10954 ErrorInfoKeeper eik;
10955
10956 HRESULT rc1 = lockedMediaMap->Clear();
10957 AssertComRC(rc1);
10958 }
10959
10960 return rc;
10961}
10962
10963/**
10964 * Deletes implicit differencing hard disks created either by
10965 * #i_createImplicitDiffs() or by #attachDevice() and rolls back mMediaData.
10966 *
10967 * Note that to delete hard disks created by #attachDevice() this method is
10968 * called from #fixupMedia() when the changes are rolled back.
10969 *
10970 * @note Locks this object and the media tree for writing.
10971 */
10972HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10973{
10974 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10975
10976 AutoCaller autoCaller(this);
10977 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10978
10979 AutoMultiWriteLock2 alock(this->lockHandle(),
10980 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10981
10982 /* We absolutely must have backed up state. */
10983 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10984
10985 /* Check if there are any implicitly created diff images. */
10986 bool fImplicitDiffs = false;
10987 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10988 it != mMediaData->mAttachments.end();
10989 ++it)
10990 {
10991 const ComObjPtr<MediumAttachment> &pAtt = *it;
10992 if (pAtt->i_isImplicit())
10993 {
10994 fImplicitDiffs = true;
10995 break;
10996 }
10997 }
10998 /* If there is nothing to do, leave early. This saves lots of image locking
10999 * effort. It also avoids a MachineStateChanged event without real reason.
11000 * This is important e.g. when loading a VM config, because there should be
11001 * no events. Otherwise API clients can become thoroughly confused for
11002 * inaccessible VMs (the code for loading VM configs uses this method for
11003 * cleanup if the config makes no sense), as they take such events as an
11004 * indication that the VM is alive, and they would force the VM config to
11005 * be reread, leading to an endless loop. */
11006 if (!fImplicitDiffs)
11007 return S_OK;
11008
11009 HRESULT rc = S_OK;
11010 MachineState_T oldState = mData->mMachineState;
11011
11012 /* will release the lock before the potentially lengthy operation,
11013 * so protect with the special state (unless already protected) */
11014 if ( oldState != MachineState_Snapshotting
11015 && oldState != MachineState_OnlineSnapshotting
11016 && oldState != MachineState_LiveSnapshotting
11017 && oldState != MachineState_RestoringSnapshot
11018 && oldState != MachineState_DeletingSnapshot
11019 && oldState != MachineState_DeletingSnapshotOnline
11020 && oldState != MachineState_DeletingSnapshotPaused
11021 )
11022 i_setMachineState(MachineState_SettingUp);
11023
11024 // use appropriate locked media map (online or offline)
11025 MediumLockListMap lockedMediaOffline;
11026 MediumLockListMap *lockedMediaMap;
11027 if (aOnline)
11028 lockedMediaMap = &mData->mSession.mLockedMedia;
11029 else
11030 lockedMediaMap = &lockedMediaOffline;
11031
11032 try
11033 {
11034 if (!aOnline)
11035 {
11036 /* lock all attached hard disks early to detect "in use"
11037 * situations before deleting actual diffs */
11038 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11039 it != mMediaData->mAttachments.end();
11040 ++it)
11041 {
11042 MediumAttachment* pAtt = *it;
11043 if (pAtt->i_getType() == DeviceType_HardDisk)
11044 {
11045 Medium* pMedium = pAtt->i_getMedium();
11046 Assert(pMedium);
11047
11048 MediumLockList *pMediumLockList(new MediumLockList());
11049 alock.release();
11050 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11051 NULL /* pToLockWrite */,
11052 false /* fMediumLockWriteAll */,
11053 NULL,
11054 *pMediumLockList);
11055 alock.acquire();
11056
11057 if (FAILED(rc))
11058 {
11059 delete pMediumLockList;
11060 throw rc;
11061 }
11062
11063 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11064 if (FAILED(rc))
11065 throw rc;
11066 }
11067 }
11068
11069 if (FAILED(rc))
11070 throw rc;
11071 } // end of offline
11072
11073 /* Lock lists are now up to date and include implicitly created media */
11074
11075 /* Go through remembered attachments and delete all implicitly created
11076 * diffs and fix up the attachment information */
11077 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11078 MediaData::AttachmentList implicitAtts;
11079 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11080 it != mMediaData->mAttachments.end();
11081 ++it)
11082 {
11083 ComObjPtr<MediumAttachment> pAtt = *it;
11084 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11085 if (pMedium.isNull())
11086 continue;
11087
11088 // Implicit attachments go on the list for deletion and back references are removed.
11089 if (pAtt->i_isImplicit())
11090 {
11091 /* Deassociate and mark for deletion */
11092 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11093 rc = pMedium->i_removeBackReference(mData->mUuid);
11094 if (FAILED(rc))
11095 throw rc;
11096 implicitAtts.push_back(pAtt);
11097 continue;
11098 }
11099
11100 /* Was this medium attached before? */
11101 if (!i_findAttachment(oldAtts, pMedium))
11102 {
11103 /* no: de-associate */
11104 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11105 rc = pMedium->i_removeBackReference(mData->mUuid);
11106 if (FAILED(rc))
11107 throw rc;
11108 continue;
11109 }
11110 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11111 }
11112
11113 /* If there are implicit attachments to delete, throw away the lock
11114 * map contents (which will unlock all media) since the medium
11115 * attachments will be rolled back. Below we need to completely
11116 * recreate the lock map anyway since it is infinitely complex to
11117 * do this incrementally (would need reconstructing each attachment
11118 * change, which would be extremely hairy). */
11119 if (implicitAtts.size() != 0)
11120 {
11121 ErrorInfoKeeper eik;
11122
11123 HRESULT rc1 = lockedMediaMap->Clear();
11124 AssertComRC(rc1);
11125 }
11126
11127 /* rollback hard disk changes */
11128 mMediaData.rollback();
11129
11130 MultiResult mrc(S_OK);
11131
11132 // Delete unused implicit diffs.
11133 if (implicitAtts.size() != 0)
11134 {
11135 alock.release();
11136
11137 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11138 {
11139 // Remove medium associated with this attachment.
11140 ComObjPtr<MediumAttachment> pAtt = *it;
11141 Assert(pAtt);
11142 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11143 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11144 Assert(pMedium);
11145
11146 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11147 // continue on delete failure, just collect error messages
11148 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11149 pMedium->i_getLocationFull().c_str() ));
11150 mrc = rc;
11151 }
11152 // Clear the list of deleted implicit attachments now, while not
11153 // holding the lock, as it will ultimately trigger Medium::uninit()
11154 // calls which assume that the media tree lock isn't held.
11155 implicitAtts.clear();
11156
11157 alock.acquire();
11158
11159 /* if there is a VM recreate media lock map as mentioned above,
11160 * otherwise it is a waste of time and we leave things unlocked */
11161 if (aOnline)
11162 {
11163 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11164 /* must never be NULL, but better safe than sorry */
11165 if (!pMachine.isNull())
11166 {
11167 alock.release();
11168 rc = mData->mSession.mMachine->i_lockMedia();
11169 alock.acquire();
11170 if (FAILED(rc))
11171 throw rc;
11172 }
11173 }
11174 }
11175 }
11176 catch (HRESULT aRC) {rc = aRC;}
11177
11178 if (mData->mMachineState == MachineState_SettingUp)
11179 i_setMachineState(oldState);
11180
11181 /* unlock all hard disks we locked when there is no VM */
11182 if (!aOnline)
11183 {
11184 ErrorInfoKeeper eik;
11185
11186 HRESULT rc1 = lockedMediaMap->Clear();
11187 AssertComRC(rc1);
11188 }
11189
11190 return rc;
11191}
11192
11193
11194/**
11195 * Looks through the given list of media attachments for one with the given parameters
11196 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11197 * can be searched as well if needed.
11198 *
11199 * @param ll
11200 * @param aControllerName
11201 * @param aControllerPort
11202 * @param aDevice
11203 * @return
11204 */
11205MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11206 const Utf8Str &aControllerName,
11207 LONG aControllerPort,
11208 LONG aDevice)
11209{
11210 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11211 {
11212 MediumAttachment *pAttach = *it;
11213 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11214 return pAttach;
11215 }
11216
11217 return NULL;
11218}
11219
11220/**
11221 * Looks through the given list of media attachments for one with the given parameters
11222 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11223 * can be searched as well if needed.
11224 *
11225 * @param ll
11226 * @param pMedium
11227 * @return
11228 */
11229MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11230 ComObjPtr<Medium> pMedium)
11231{
11232 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11233 {
11234 MediumAttachment *pAttach = *it;
11235 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11236 if (pMediumThis == pMedium)
11237 return pAttach;
11238 }
11239
11240 return NULL;
11241}
11242
11243/**
11244 * Looks through the given list of media attachments for one with the given parameters
11245 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11246 * can be searched as well if needed.
11247 *
11248 * @param ll
11249 * @param id
11250 * @return
11251 */
11252MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11253 Guid &id)
11254{
11255 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11256 {
11257 MediumAttachment *pAttach = *it;
11258 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11259 if (pMediumThis->i_getId() == id)
11260 return pAttach;
11261 }
11262
11263 return NULL;
11264}
11265
11266/**
11267 * Main implementation for Machine::DetachDevice. This also gets called
11268 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11269 *
11270 * @param pAttach Medium attachment to detach.
11271 * @param writeLock Machine write lock which the caller must have locked once.
11272 * This may be released temporarily in here.
11273 * @param pSnapshot If NULL, then the detachment is for the current machine.
11274 * Otherwise this is for a SnapshotMachine, and this must be
11275 * its snapshot.
11276 * @return
11277 */
11278HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11279 AutoWriteLock &writeLock,
11280 Snapshot *pSnapshot)
11281{
11282 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11283 DeviceType_T mediumType = pAttach->i_getType();
11284
11285 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11286
11287 if (pAttach->i_isImplicit())
11288 {
11289 /* attempt to implicitly delete the implicitly created diff */
11290
11291 /// @todo move the implicit flag from MediumAttachment to Medium
11292 /// and forbid any hard disk operation when it is implicit. Or maybe
11293 /// a special media state for it to make it even more simple.
11294
11295 Assert(mMediaData.isBackedUp());
11296
11297 /* will release the lock before the potentially lengthy operation, so
11298 * protect with the special state */
11299 MachineState_T oldState = mData->mMachineState;
11300 i_setMachineState(MachineState_SettingUp);
11301
11302 writeLock.release();
11303
11304 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11305 true /*aWait*/);
11306
11307 writeLock.acquire();
11308
11309 i_setMachineState(oldState);
11310
11311 if (FAILED(rc)) return rc;
11312 }
11313
11314 i_setModified(IsModified_Storage);
11315 mMediaData.backup();
11316 mMediaData->mAttachments.remove(pAttach);
11317
11318 if (!oldmedium.isNull())
11319 {
11320 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11321 if (pSnapshot)
11322 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11323 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11324 else if (mediumType != DeviceType_HardDisk)
11325 oldmedium->i_removeBackReference(mData->mUuid);
11326 }
11327
11328 return S_OK;
11329}
11330
11331/**
11332 * Goes thru all media of the given list and
11333 *
11334 * 1) calls i_detachDevice() on each of them for this machine and
11335 * 2) adds all Medium objects found in the process to the given list,
11336 * depending on cleanupMode.
11337 *
11338 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11339 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11340 * media to the list.
11341 *
11342 * This gets called from Machine::Unregister, both for the actual Machine and
11343 * the SnapshotMachine objects that might be found in the snapshots.
11344 *
11345 * Requires caller and locking. The machine lock must be passed in because it
11346 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11347 *
11348 * @param writeLock Machine lock from top-level caller; this gets passed to
11349 * i_detachDevice.
11350 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11351 * object if called for a SnapshotMachine.
11352 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11353 * added to llMedia; if Full, then all media get added;
11354 * otherwise no media get added.
11355 * @param llMedia Caller's list to receive Medium objects which got detached so
11356 * caller can close() them, depending on cleanupMode.
11357 * @return
11358 */
11359HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11360 Snapshot *pSnapshot,
11361 CleanupMode_T cleanupMode,
11362 MediaList &llMedia)
11363{
11364 Assert(isWriteLockOnCurrentThread());
11365
11366 HRESULT rc;
11367
11368 // make a temporary list because i_detachDevice invalidates iterators into
11369 // mMediaData->mAttachments
11370 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11371
11372 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11373 {
11374 ComObjPtr<MediumAttachment> &pAttach = *it;
11375 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11376
11377 if (!pMedium.isNull())
11378 {
11379 AutoCaller mac(pMedium);
11380 if (FAILED(mac.rc())) return mac.rc();
11381 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11382 DeviceType_T devType = pMedium->i_getDeviceType();
11383 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11384 && devType == DeviceType_HardDisk)
11385 || (cleanupMode == CleanupMode_Full)
11386 )
11387 {
11388 llMedia.push_back(pMedium);
11389 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11390 /* Not allowed to keep this lock as below we need the parent
11391 * medium lock, and the lock order is parent to child. */
11392 lock.release();
11393 /*
11394 * Search for medias which are not attached to any machine, but
11395 * in the chain to an attached disk. Mediums are only consided
11396 * if they are:
11397 * - have only one child
11398 * - no references to any machines
11399 * - are of normal medium type
11400 */
11401 while (!pParent.isNull())
11402 {
11403 AutoCaller mac1(pParent);
11404 if (FAILED(mac1.rc())) return mac1.rc();
11405 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11406 if (pParent->i_getChildren().size() == 1)
11407 {
11408 if ( pParent->i_getMachineBackRefCount() == 0
11409 && pParent->i_getType() == MediumType_Normal
11410 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11411 llMedia.push_back(pParent);
11412 }
11413 else
11414 break;
11415 pParent = pParent->i_getParent();
11416 }
11417 }
11418 }
11419
11420 // real machine: then we need to use the proper method
11421 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11422
11423 if (FAILED(rc))
11424 return rc;
11425 }
11426
11427 return S_OK;
11428}
11429
11430/**
11431 * Perform deferred hard disk detachments.
11432 *
11433 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11434 * backed up).
11435 *
11436 * If @a aOnline is @c true then this method will also unlock the old hard disks
11437 * for which the new implicit diffs were created and will lock these new diffs for
11438 * writing.
11439 *
11440 * @param aOnline Whether the VM was online prior to this operation.
11441 *
11442 * @note Locks this object for writing!
11443 */
11444void Machine::i_commitMedia(bool aOnline /*= false*/)
11445{
11446 AutoCaller autoCaller(this);
11447 AssertComRCReturnVoid(autoCaller.rc());
11448
11449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11450
11451 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11452
11453 HRESULT rc = S_OK;
11454
11455 /* no attach/detach operations -- nothing to do */
11456 if (!mMediaData.isBackedUp())
11457 return;
11458
11459 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11460 bool fMediaNeedsLocking = false;
11461
11462 /* enumerate new attachments */
11463 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11464 it != mMediaData->mAttachments.end();
11465 ++it)
11466 {
11467 MediumAttachment *pAttach = *it;
11468
11469 pAttach->i_commit();
11470
11471 Medium* pMedium = pAttach->i_getMedium();
11472 bool fImplicit = pAttach->i_isImplicit();
11473
11474 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11475 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11476 fImplicit));
11477
11478 /** @todo convert all this Machine-based voodoo to MediumAttachment
11479 * based commit logic. */
11480 if (fImplicit)
11481 {
11482 /* convert implicit attachment to normal */
11483 pAttach->i_setImplicit(false);
11484
11485 if ( aOnline
11486 && pMedium
11487 && pAttach->i_getType() == DeviceType_HardDisk
11488 )
11489 {
11490 /* update the appropriate lock list */
11491 MediumLockList *pMediumLockList;
11492 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11493 AssertComRC(rc);
11494 if (pMediumLockList)
11495 {
11496 /* unlock if there's a need to change the locking */
11497 if (!fMediaNeedsLocking)
11498 {
11499 rc = mData->mSession.mLockedMedia.Unlock();
11500 AssertComRC(rc);
11501 fMediaNeedsLocking = true;
11502 }
11503 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11504 AssertComRC(rc);
11505 rc = pMediumLockList->Append(pMedium, true);
11506 AssertComRC(rc);
11507 }
11508 }
11509
11510 continue;
11511 }
11512
11513 if (pMedium)
11514 {
11515 /* was this medium attached before? */
11516 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11517 {
11518 MediumAttachment *pOldAttach = *oldIt;
11519 if (pOldAttach->i_getMedium() == pMedium)
11520 {
11521 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11522
11523 /* yes: remove from old to avoid de-association */
11524 oldAtts.erase(oldIt);
11525 break;
11526 }
11527 }
11528 }
11529 }
11530
11531 /* enumerate remaining old attachments and de-associate from the
11532 * current machine state */
11533 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11534 {
11535 MediumAttachment *pAttach = *it;
11536 Medium* pMedium = pAttach->i_getMedium();
11537
11538 /* Detach only hard disks, since DVD/floppy media is detached
11539 * instantly in MountMedium. */
11540 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11541 {
11542 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11543
11544 /* now de-associate from the current machine state */
11545 rc = pMedium->i_removeBackReference(mData->mUuid);
11546 AssertComRC(rc);
11547
11548 if (aOnline)
11549 {
11550 /* unlock since medium is not used anymore */
11551 MediumLockList *pMediumLockList;
11552 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11553 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11554 {
11555 /* this happens for online snapshots, there the attachment
11556 * is changing, but only to a diff image created under
11557 * the old one, so there is no separate lock list */
11558 Assert(!pMediumLockList);
11559 }
11560 else
11561 {
11562 AssertComRC(rc);
11563 if (pMediumLockList)
11564 {
11565 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11566 AssertComRC(rc);
11567 }
11568 }
11569 }
11570 }
11571 }
11572
11573 /* take media locks again so that the locking state is consistent */
11574 if (fMediaNeedsLocking)
11575 {
11576 Assert(aOnline);
11577 rc = mData->mSession.mLockedMedia.Lock();
11578 AssertComRC(rc);
11579 }
11580
11581 /* commit the hard disk changes */
11582 mMediaData.commit();
11583
11584 if (i_isSessionMachine())
11585 {
11586 /*
11587 * Update the parent machine to point to the new owner.
11588 * This is necessary because the stored parent will point to the
11589 * session machine otherwise and cause crashes or errors later
11590 * when the session machine gets invalid.
11591 */
11592 /** @todo Change the MediumAttachment class to behave like any other
11593 * class in this regard by creating peer MediumAttachment
11594 * objects for session machines and share the data with the peer
11595 * machine.
11596 */
11597 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11598 it != mMediaData->mAttachments.end();
11599 ++it)
11600 (*it)->i_updateParentMachine(mPeer);
11601
11602 /* attach new data to the primary machine and reshare it */
11603 mPeer->mMediaData.attach(mMediaData);
11604 }
11605
11606 return;
11607}
11608
11609/**
11610 * Perform deferred deletion of implicitly created diffs.
11611 *
11612 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11613 * backed up).
11614 *
11615 * @note Locks this object for writing!
11616 */
11617void Machine::i_rollbackMedia()
11618{
11619 AutoCaller autoCaller(this);
11620 AssertComRCReturnVoid(autoCaller.rc());
11621
11622 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11623 LogFlowThisFunc(("Entering rollbackMedia\n"));
11624
11625 HRESULT rc = S_OK;
11626
11627 /* no attach/detach operations -- nothing to do */
11628 if (!mMediaData.isBackedUp())
11629 return;
11630
11631 /* enumerate new attachments */
11632 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11633 it != mMediaData->mAttachments.end();
11634 ++it)
11635 {
11636 MediumAttachment *pAttach = *it;
11637 /* Fix up the backrefs for DVD/floppy media. */
11638 if (pAttach->i_getType() != DeviceType_HardDisk)
11639 {
11640 Medium* pMedium = pAttach->i_getMedium();
11641 if (pMedium)
11642 {
11643 rc = pMedium->i_removeBackReference(mData->mUuid);
11644 AssertComRC(rc);
11645 }
11646 }
11647
11648 (*it)->i_rollback();
11649
11650 pAttach = *it;
11651 /* Fix up the backrefs for DVD/floppy media. */
11652 if (pAttach->i_getType() != DeviceType_HardDisk)
11653 {
11654 Medium* pMedium = pAttach->i_getMedium();
11655 if (pMedium)
11656 {
11657 rc = pMedium->i_addBackReference(mData->mUuid);
11658 AssertComRC(rc);
11659 }
11660 }
11661 }
11662
11663 /** @todo convert all this Machine-based voodoo to MediumAttachment
11664 * based rollback logic. */
11665 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11666
11667 return;
11668}
11669
11670/**
11671 * Returns true if the settings file is located in the directory named exactly
11672 * as the machine; this means, among other things, that the machine directory
11673 * should be auto-renamed.
11674 *
11675 * @param aSettingsDir if not NULL, the full machine settings file directory
11676 * name will be assigned there.
11677 *
11678 * @note Doesn't lock anything.
11679 * @note Not thread safe (must be called from this object's lock).
11680 */
11681bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11682{
11683 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11684 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11685 if (aSettingsDir)
11686 *aSettingsDir = strMachineDirName;
11687 strMachineDirName.stripPath(); // vmname
11688 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11689 strConfigFileOnly.stripPath() // vmname.vbox
11690 .stripSuffix(); // vmname
11691 /** @todo hack, make somehow use of ComposeMachineFilename */
11692 if (mUserData->s.fDirectoryIncludesUUID)
11693 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11694
11695 AssertReturn(!strMachineDirName.isEmpty(), false);
11696 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11697
11698 return strMachineDirName == strConfigFileOnly;
11699}
11700
11701/**
11702 * Discards all changes to machine settings.
11703 *
11704 * @param aNotify Whether to notify the direct session about changes or not.
11705 *
11706 * @note Locks objects for writing!
11707 */
11708void Machine::i_rollback(bool aNotify)
11709{
11710 AutoCaller autoCaller(this);
11711 AssertComRCReturn(autoCaller.rc(), (void)0);
11712
11713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11714
11715 if (!mStorageControllers.isNull())
11716 {
11717 if (mStorageControllers.isBackedUp())
11718 {
11719 /* unitialize all new devices (absent in the backed up list). */
11720 StorageControllerList::const_iterator it = mStorageControllers->begin();
11721 StorageControllerList *backedList = mStorageControllers.backedUpData();
11722 while (it != mStorageControllers->end())
11723 {
11724 if ( std::find(backedList->begin(), backedList->end(), *it)
11725 == backedList->end()
11726 )
11727 {
11728 (*it)->uninit();
11729 }
11730 ++it;
11731 }
11732
11733 /* restore the list */
11734 mStorageControllers.rollback();
11735 }
11736
11737 /* rollback any changes to devices after restoring the list */
11738 if (mData->flModifications & IsModified_Storage)
11739 {
11740 StorageControllerList::const_iterator it = mStorageControllers->begin();
11741 while (it != mStorageControllers->end())
11742 {
11743 (*it)->i_rollback();
11744 ++it;
11745 }
11746 }
11747 }
11748
11749 if (!mUSBControllers.isNull())
11750 {
11751 if (mUSBControllers.isBackedUp())
11752 {
11753 /* unitialize all new devices (absent in the backed up list). */
11754 USBControllerList::const_iterator it = mUSBControllers->begin();
11755 USBControllerList *backedList = mUSBControllers.backedUpData();
11756 while (it != mUSBControllers->end())
11757 {
11758 if ( std::find(backedList->begin(), backedList->end(), *it)
11759 == backedList->end()
11760 )
11761 {
11762 (*it)->uninit();
11763 }
11764 ++it;
11765 }
11766
11767 /* restore the list */
11768 mUSBControllers.rollback();
11769 }
11770
11771 /* rollback any changes to devices after restoring the list */
11772 if (mData->flModifications & IsModified_USB)
11773 {
11774 USBControllerList::const_iterator it = mUSBControllers->begin();
11775 while (it != mUSBControllers->end())
11776 {
11777 (*it)->i_rollback();
11778 ++it;
11779 }
11780 }
11781 }
11782
11783 mUserData.rollback();
11784
11785 mHWData.rollback();
11786
11787 if (mData->flModifications & IsModified_Storage)
11788 i_rollbackMedia();
11789
11790 if (mBIOSSettings)
11791 mBIOSSettings->i_rollback();
11792
11793 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11794 mVRDEServer->i_rollback();
11795
11796 if (mAudioAdapter)
11797 mAudioAdapter->i_rollback();
11798
11799 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11800 mUSBDeviceFilters->i_rollback();
11801
11802 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11803 mBandwidthControl->i_rollback();
11804
11805 if (!mHWData.isNull())
11806 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11807 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11808 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11809 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11810
11811 if (mData->flModifications & IsModified_NetworkAdapters)
11812 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11813 if ( mNetworkAdapters[slot]
11814 && mNetworkAdapters[slot]->i_isModified())
11815 {
11816 mNetworkAdapters[slot]->i_rollback();
11817 networkAdapters[slot] = mNetworkAdapters[slot];
11818 }
11819
11820 if (mData->flModifications & IsModified_SerialPorts)
11821 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11822 if ( mSerialPorts[slot]
11823 && mSerialPorts[slot]->i_isModified())
11824 {
11825 mSerialPorts[slot]->i_rollback();
11826 serialPorts[slot] = mSerialPorts[slot];
11827 }
11828
11829 if (mData->flModifications & IsModified_ParallelPorts)
11830 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11831 if ( mParallelPorts[slot]
11832 && mParallelPorts[slot]->i_isModified())
11833 {
11834 mParallelPorts[slot]->i_rollback();
11835 parallelPorts[slot] = mParallelPorts[slot];
11836 }
11837
11838 if (aNotify)
11839 {
11840 /* inform the direct session about changes */
11841
11842 ComObjPtr<Machine> that = this;
11843 uint32_t flModifications = mData->flModifications;
11844 alock.release();
11845
11846 if (flModifications & IsModified_SharedFolders)
11847 that->i_onSharedFolderChange();
11848
11849 if (flModifications & IsModified_VRDEServer)
11850 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11851 if (flModifications & IsModified_USB)
11852 that->i_onUSBControllerChange();
11853
11854 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11855 if (networkAdapters[slot])
11856 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11857 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11858 if (serialPorts[slot])
11859 that->i_onSerialPortChange(serialPorts[slot]);
11860 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11861 if (parallelPorts[slot])
11862 that->i_onParallelPortChange(parallelPorts[slot]);
11863
11864 if (flModifications & IsModified_Storage)
11865 that->i_onStorageControllerChange();
11866
11867#if 0
11868 if (flModifications & IsModified_BandwidthControl)
11869 that->onBandwidthControlChange();
11870#endif
11871 }
11872}
11873
11874/**
11875 * Commits all the changes to machine settings.
11876 *
11877 * Note that this operation is supposed to never fail.
11878 *
11879 * @note Locks this object and children for writing.
11880 */
11881void Machine::i_commit()
11882{
11883 AutoCaller autoCaller(this);
11884 AssertComRCReturnVoid(autoCaller.rc());
11885
11886 AutoCaller peerCaller(mPeer);
11887 AssertComRCReturnVoid(peerCaller.rc());
11888
11889 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11890
11891 /*
11892 * use safe commit to ensure Snapshot machines (that share mUserData)
11893 * will still refer to a valid memory location
11894 */
11895 mUserData.commitCopy();
11896
11897 mHWData.commit();
11898
11899 if (mMediaData.isBackedUp())
11900 i_commitMedia(Global::IsOnline(mData->mMachineState));
11901
11902 mBIOSSettings->i_commit();
11903 mVRDEServer->i_commit();
11904 mAudioAdapter->i_commit();
11905 mUSBDeviceFilters->i_commit();
11906 mBandwidthControl->i_commit();
11907
11908 /* Since mNetworkAdapters is a list which might have been changed (resized)
11909 * without using the Backupable<> template we need to handle the copying
11910 * of the list entries manually, including the creation of peers for the
11911 * new objects. */
11912 bool commitNetworkAdapters = false;
11913 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11914 if (mPeer)
11915 {
11916 /* commit everything, even the ones which will go away */
11917 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11918 mNetworkAdapters[slot]->i_commit();
11919 /* copy over the new entries, creating a peer and uninit the original */
11920 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11921 for (size_t slot = 0; slot < newSize; slot++)
11922 {
11923 /* look if this adapter has a peer device */
11924 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11925 if (!peer)
11926 {
11927 /* no peer means the adapter is a newly created one;
11928 * create a peer owning data this data share it with */
11929 peer.createObject();
11930 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11931 }
11932 mPeer->mNetworkAdapters[slot] = peer;
11933 }
11934 /* uninit any no longer needed network adapters */
11935 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11936 mNetworkAdapters[slot]->uninit();
11937 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11938 {
11939 if (mPeer->mNetworkAdapters[slot])
11940 mPeer->mNetworkAdapters[slot]->uninit();
11941 }
11942 /* Keep the original network adapter count until this point, so that
11943 * discarding a chipset type change will not lose settings. */
11944 mNetworkAdapters.resize(newSize);
11945 mPeer->mNetworkAdapters.resize(newSize);
11946 }
11947 else
11948 {
11949 /* we have no peer (our parent is the newly created machine);
11950 * just commit changes to the network adapters */
11951 commitNetworkAdapters = true;
11952 }
11953 if (commitNetworkAdapters)
11954 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11955 mNetworkAdapters[slot]->i_commit();
11956
11957 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11958 mSerialPorts[slot]->i_commit();
11959 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11960 mParallelPorts[slot]->i_commit();
11961
11962 bool commitStorageControllers = false;
11963
11964 if (mStorageControllers.isBackedUp())
11965 {
11966 mStorageControllers.commit();
11967
11968 if (mPeer)
11969 {
11970 /* Commit all changes to new controllers (this will reshare data with
11971 * peers for those who have peers) */
11972 StorageControllerList *newList = new StorageControllerList();
11973 StorageControllerList::const_iterator it = mStorageControllers->begin();
11974 while (it != mStorageControllers->end())
11975 {
11976 (*it)->i_commit();
11977
11978 /* look if this controller has a peer device */
11979 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11980 if (!peer)
11981 {
11982 /* no peer means the device is a newly created one;
11983 * create a peer owning data this device share it with */
11984 peer.createObject();
11985 peer->init(mPeer, *it, true /* aReshare */);
11986 }
11987 else
11988 {
11989 /* remove peer from the old list */
11990 mPeer->mStorageControllers->remove(peer);
11991 }
11992 /* and add it to the new list */
11993 newList->push_back(peer);
11994
11995 ++it;
11996 }
11997
11998 /* uninit old peer's controllers that are left */
11999 it = mPeer->mStorageControllers->begin();
12000 while (it != mPeer->mStorageControllers->end())
12001 {
12002 (*it)->uninit();
12003 ++it;
12004 }
12005
12006 /* attach new list of controllers to our peer */
12007 mPeer->mStorageControllers.attach(newList);
12008 }
12009 else
12010 {
12011 /* we have no peer (our parent is the newly created machine);
12012 * just commit changes to devices */
12013 commitStorageControllers = true;
12014 }
12015 }
12016 else
12017 {
12018 /* the list of controllers itself is not changed,
12019 * just commit changes to controllers themselves */
12020 commitStorageControllers = true;
12021 }
12022
12023 if (commitStorageControllers)
12024 {
12025 StorageControllerList::const_iterator it = mStorageControllers->begin();
12026 while (it != mStorageControllers->end())
12027 {
12028 (*it)->i_commit();
12029 ++it;
12030 }
12031 }
12032
12033 bool commitUSBControllers = false;
12034
12035 if (mUSBControllers.isBackedUp())
12036 {
12037 mUSBControllers.commit();
12038
12039 if (mPeer)
12040 {
12041 /* Commit all changes to new controllers (this will reshare data with
12042 * peers for those who have peers) */
12043 USBControllerList *newList = new USBControllerList();
12044 USBControllerList::const_iterator it = mUSBControllers->begin();
12045 while (it != mUSBControllers->end())
12046 {
12047 (*it)->i_commit();
12048
12049 /* look if this controller has a peer device */
12050 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12051 if (!peer)
12052 {
12053 /* no peer means the device is a newly created one;
12054 * create a peer owning data this device share it with */
12055 peer.createObject();
12056 peer->init(mPeer, *it, true /* aReshare */);
12057 }
12058 else
12059 {
12060 /* remove peer from the old list */
12061 mPeer->mUSBControllers->remove(peer);
12062 }
12063 /* and add it to the new list */
12064 newList->push_back(peer);
12065
12066 ++it;
12067 }
12068
12069 /* uninit old peer's controllers that are left */
12070 it = mPeer->mUSBControllers->begin();
12071 while (it != mPeer->mUSBControllers->end())
12072 {
12073 (*it)->uninit();
12074 ++it;
12075 }
12076
12077 /* attach new list of controllers to our peer */
12078 mPeer->mUSBControllers.attach(newList);
12079 }
12080 else
12081 {
12082 /* we have no peer (our parent is the newly created machine);
12083 * just commit changes to devices */
12084 commitUSBControllers = true;
12085 }
12086 }
12087 else
12088 {
12089 /* the list of controllers itself is not changed,
12090 * just commit changes to controllers themselves */
12091 commitUSBControllers = true;
12092 }
12093
12094 if (commitUSBControllers)
12095 {
12096 USBControllerList::const_iterator it = mUSBControllers->begin();
12097 while (it != mUSBControllers->end())
12098 {
12099 (*it)->i_commit();
12100 ++it;
12101 }
12102 }
12103
12104 if (i_isSessionMachine())
12105 {
12106 /* attach new data to the primary machine and reshare it */
12107 mPeer->mUserData.attach(mUserData);
12108 mPeer->mHWData.attach(mHWData);
12109 /* mMediaData is reshared by fixupMedia */
12110 // mPeer->mMediaData.attach(mMediaData);
12111 Assert(mPeer->mMediaData.data() == mMediaData.data());
12112 }
12113}
12114
12115/**
12116 * Copies all the hardware data from the given machine.
12117 *
12118 * Currently, only called when the VM is being restored from a snapshot. In
12119 * particular, this implies that the VM is not running during this method's
12120 * call.
12121 *
12122 * @note This method must be called from under this object's lock.
12123 *
12124 * @note This method doesn't call #commit(), so all data remains backed up and
12125 * unsaved.
12126 */
12127void Machine::i_copyFrom(Machine *aThat)
12128{
12129 AssertReturnVoid(!i_isSnapshotMachine());
12130 AssertReturnVoid(aThat->i_isSnapshotMachine());
12131
12132 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12133
12134 mHWData.assignCopy(aThat->mHWData);
12135
12136 // create copies of all shared folders (mHWData after attaching a copy
12137 // contains just references to original objects)
12138 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12139 it != mHWData->mSharedFolders.end();
12140 ++it)
12141 {
12142 ComObjPtr<SharedFolder> folder;
12143 folder.createObject();
12144 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12145 AssertComRC(rc);
12146 *it = folder;
12147 }
12148
12149 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12150 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12151 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12152 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12153 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12154
12155 /* create private copies of all controllers */
12156 mStorageControllers.backup();
12157 mStorageControllers->clear();
12158 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12159 it != aThat->mStorageControllers->end();
12160 ++it)
12161 {
12162 ComObjPtr<StorageController> ctrl;
12163 ctrl.createObject();
12164 ctrl->initCopy(this, *it);
12165 mStorageControllers->push_back(ctrl);
12166 }
12167
12168 /* create private copies of all USB controllers */
12169 mUSBControllers.backup();
12170 mUSBControllers->clear();
12171 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12172 it != aThat->mUSBControllers->end();
12173 ++it)
12174 {
12175 ComObjPtr<USBController> ctrl;
12176 ctrl.createObject();
12177 ctrl->initCopy(this, *it);
12178 mUSBControllers->push_back(ctrl);
12179 }
12180
12181 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12182 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12183 {
12184 if (mNetworkAdapters[slot].isNotNull())
12185 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12186 else
12187 {
12188 unconst(mNetworkAdapters[slot]).createObject();
12189 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12190 }
12191 }
12192 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12193 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12194 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12195 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12196}
12197
12198/**
12199 * Returns whether the given storage controller is hotplug capable.
12200 *
12201 * @returns true if the controller supports hotplugging
12202 * false otherwise.
12203 * @param enmCtrlType The controller type to check for.
12204 */
12205bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12206{
12207 ComPtr<ISystemProperties> systemProperties;
12208 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12209 if (FAILED(rc))
12210 return false;
12211
12212 BOOL aHotplugCapable = FALSE;
12213 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12214
12215 return RT_BOOL(aHotplugCapable);
12216}
12217
12218#ifdef VBOX_WITH_RESOURCE_USAGE_API
12219
12220void Machine::i_getDiskList(MediaList &list)
12221{
12222 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12223 it != mMediaData->mAttachments.end();
12224 ++it)
12225 {
12226 MediumAttachment* pAttach = *it;
12227 /* just in case */
12228 AssertContinue(pAttach);
12229
12230 AutoCaller localAutoCallerA(pAttach);
12231 if (FAILED(localAutoCallerA.rc())) continue;
12232
12233 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12234
12235 if (pAttach->i_getType() == DeviceType_HardDisk)
12236 list.push_back(pAttach->i_getMedium());
12237 }
12238}
12239
12240void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12241{
12242 AssertReturnVoid(isWriteLockOnCurrentThread());
12243 AssertPtrReturnVoid(aCollector);
12244
12245 pm::CollectorHAL *hal = aCollector->getHAL();
12246 /* Create sub metrics */
12247 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12248 "Percentage of processor time spent in user mode by the VM process.");
12249 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12250 "Percentage of processor time spent in kernel mode by the VM process.");
12251 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12252 "Size of resident portion of VM process in memory.");
12253 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12254 "Actual size of all VM disks combined.");
12255 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12256 "Network receive rate.");
12257 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12258 "Network transmit rate.");
12259 /* Create and register base metrics */
12260 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12261 cpuLoadUser, cpuLoadKernel);
12262 aCollector->registerBaseMetric(cpuLoad);
12263 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12264 ramUsageUsed);
12265 aCollector->registerBaseMetric(ramUsage);
12266 MediaList disks;
12267 i_getDiskList(disks);
12268 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12269 diskUsageUsed);
12270 aCollector->registerBaseMetric(diskUsage);
12271
12272 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12273 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12274 new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12276 new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12278 new pm::AggregateMax()));
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12281 new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12283 new pm::AggregateMin()));
12284 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12285 new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12288 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12289 new pm::AggregateAvg()));
12290 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12291 new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12293 new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12296 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12297 new pm::AggregateAvg()));
12298 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12299 new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12301 new pm::AggregateMax()));
12302
12303
12304 /* Guest metrics collector */
12305 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12306 aCollector->registerGuest(mCollectorGuest);
12307 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12308
12309 /* Create sub metrics */
12310 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12311 "Percentage of processor time spent in user mode as seen by the guest.");
12312 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12313 "Percentage of processor time spent in kernel mode as seen by the guest.");
12314 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12315 "Percentage of processor time spent idling as seen by the guest.");
12316
12317 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12318 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12319 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12320 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12321 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12322 pm::SubMetric *guestMemCache = new pm::SubMetric(
12323 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12324
12325 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12326 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12327
12328 /* Create and register base metrics */
12329 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12330 machineNetRx, machineNetTx);
12331 aCollector->registerBaseMetric(machineNetRate);
12332
12333 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12334 guestLoadUser, guestLoadKernel, guestLoadIdle);
12335 aCollector->registerBaseMetric(guestCpuLoad);
12336
12337 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12338 guestMemTotal, guestMemFree,
12339 guestMemBalloon, guestMemShared,
12340 guestMemCache, guestPagedTotal);
12341 aCollector->registerBaseMetric(guestCpuMem);
12342
12343 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12344 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12345 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12346 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12347
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12349 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12351 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12352
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12354 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12356 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12357
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12359 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12362
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12367
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12372
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12377
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12382
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12387
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12392
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12397}
12398
12399void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12400{
12401 AssertReturnVoid(isWriteLockOnCurrentThread());
12402
12403 if (aCollector)
12404 {
12405 aCollector->unregisterMetricsFor(aMachine);
12406 aCollector->unregisterBaseMetricsFor(aMachine);
12407 }
12408}
12409
12410#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12411
12412
12413////////////////////////////////////////////////////////////////////////////////
12414
12415DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12416
12417HRESULT SessionMachine::FinalConstruct()
12418{
12419 LogFlowThisFunc(("\n"));
12420
12421 mClientToken = NULL;
12422
12423 return BaseFinalConstruct();
12424}
12425
12426void SessionMachine::FinalRelease()
12427{
12428 LogFlowThisFunc(("\n"));
12429
12430 Assert(!mClientToken);
12431 /* paranoia, should not hang around any more */
12432 if (mClientToken)
12433 {
12434 delete mClientToken;
12435 mClientToken = NULL;
12436 }
12437
12438 uninit(Uninit::Unexpected);
12439
12440 BaseFinalRelease();
12441}
12442
12443/**
12444 * @note Must be called only by Machine::LockMachine() from its own write lock.
12445 */
12446HRESULT SessionMachine::init(Machine *aMachine)
12447{
12448 LogFlowThisFuncEnter();
12449 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12450
12451 AssertReturn(aMachine, E_INVALIDARG);
12452
12453 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12454
12455 /* Enclose the state transition NotReady->InInit->Ready */
12456 AutoInitSpan autoInitSpan(this);
12457 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12458
12459 HRESULT rc = S_OK;
12460
12461 RT_ZERO(mAuthLibCtx);
12462
12463 /* create the machine client token */
12464 try
12465 {
12466 mClientToken = new ClientToken(aMachine, this);
12467 if (!mClientToken->isReady())
12468 {
12469 delete mClientToken;
12470 mClientToken = NULL;
12471 rc = E_FAIL;
12472 }
12473 }
12474 catch (std::bad_alloc &)
12475 {
12476 rc = E_OUTOFMEMORY;
12477 }
12478 if (FAILED(rc))
12479 return rc;
12480
12481 /* memorize the peer Machine */
12482 unconst(mPeer) = aMachine;
12483 /* share the parent pointer */
12484 unconst(mParent) = aMachine->mParent;
12485
12486 /* take the pointers to data to share */
12487 mData.share(aMachine->mData);
12488 mSSData.share(aMachine->mSSData);
12489
12490 mUserData.share(aMachine->mUserData);
12491 mHWData.share(aMachine->mHWData);
12492 mMediaData.share(aMachine->mMediaData);
12493
12494 mStorageControllers.allocate();
12495 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12496 it != aMachine->mStorageControllers->end();
12497 ++it)
12498 {
12499 ComObjPtr<StorageController> ctl;
12500 ctl.createObject();
12501 ctl->init(this, *it);
12502 mStorageControllers->push_back(ctl);
12503 }
12504
12505 mUSBControllers.allocate();
12506 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12507 it != aMachine->mUSBControllers->end();
12508 ++it)
12509 {
12510 ComObjPtr<USBController> ctl;
12511 ctl.createObject();
12512 ctl->init(this, *it);
12513 mUSBControllers->push_back(ctl);
12514 }
12515
12516 unconst(mBIOSSettings).createObject();
12517 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12518 /* create another VRDEServer object that will be mutable */
12519 unconst(mVRDEServer).createObject();
12520 mVRDEServer->init(this, aMachine->mVRDEServer);
12521 /* create another audio adapter object that will be mutable */
12522 unconst(mAudioAdapter).createObject();
12523 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12524 /* create a list of serial ports that will be mutable */
12525 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12526 {
12527 unconst(mSerialPorts[slot]).createObject();
12528 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12529 }
12530 /* create a list of parallel ports that will be mutable */
12531 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12532 {
12533 unconst(mParallelPorts[slot]).createObject();
12534 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12535 }
12536
12537 /* create another USB device filters object that will be mutable */
12538 unconst(mUSBDeviceFilters).createObject();
12539 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12540
12541 /* create a list of network adapters that will be mutable */
12542 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12543 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12544 {
12545 unconst(mNetworkAdapters[slot]).createObject();
12546 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12547 }
12548
12549 /* create another bandwidth control object that will be mutable */
12550 unconst(mBandwidthControl).createObject();
12551 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12552
12553#ifdef VBOX_WITH_UNATTENDED
12554 /* create another unattended object that will be mutable */
12555 unconst(mUnattended).createObject();
12556 mUnattended->init(this, aMachine->mUnattended);
12557#endif
12558
12559 /* default is to delete saved state on Saved -> PoweredOff transition */
12560 mRemoveSavedState = true;
12561
12562 /* Confirm a successful initialization when it's the case */
12563 autoInitSpan.setSucceeded();
12564
12565 miNATNetworksStarted = 0;
12566
12567 LogFlowThisFuncLeave();
12568 return rc;
12569}
12570
12571/**
12572 * Uninitializes this session object. If the reason is other than
12573 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12574 * or the client watcher code.
12575 *
12576 * @param aReason uninitialization reason
12577 *
12578 * @note Locks mParent + this object for writing.
12579 */
12580void SessionMachine::uninit(Uninit::Reason aReason)
12581{
12582 LogFlowThisFuncEnter();
12583 LogFlowThisFunc(("reason=%d\n", aReason));
12584
12585 /*
12586 * Strongly reference ourselves to prevent this object deletion after
12587 * mData->mSession.mMachine.setNull() below (which can release the last
12588 * reference and call the destructor). Important: this must be done before
12589 * accessing any members (and before AutoUninitSpan that does it as well).
12590 * This self reference will be released as the very last step on return.
12591 */
12592 ComObjPtr<SessionMachine> selfRef = this;
12593
12594 /* Enclose the state transition Ready->InUninit->NotReady */
12595 AutoUninitSpan autoUninitSpan(this);
12596 if (autoUninitSpan.uninitDone())
12597 {
12598 LogFlowThisFunc(("Already uninitialized\n"));
12599 LogFlowThisFuncLeave();
12600 return;
12601 }
12602
12603 if (autoUninitSpan.initFailed())
12604 {
12605 /* We've been called by init() because it's failed. It's not really
12606 * necessary (nor it's safe) to perform the regular uninit sequence
12607 * below, the following is enough.
12608 */
12609 LogFlowThisFunc(("Initialization failed.\n"));
12610 /* destroy the machine client token */
12611 if (mClientToken)
12612 {
12613 delete mClientToken;
12614 mClientToken = NULL;
12615 }
12616 uninitDataAndChildObjects();
12617 mData.free();
12618 unconst(mParent) = NULL;
12619 unconst(mPeer) = NULL;
12620 LogFlowThisFuncLeave();
12621 return;
12622 }
12623
12624 MachineState_T lastState;
12625 {
12626 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12627 lastState = mData->mMachineState;
12628 }
12629 NOREF(lastState);
12630
12631#ifdef VBOX_WITH_USB
12632 // release all captured USB devices, but do this before requesting the locks below
12633 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12634 {
12635 /* Console::captureUSBDevices() is called in the VM process only after
12636 * setting the machine state to Starting or Restoring.
12637 * Console::detachAllUSBDevices() will be called upon successful
12638 * termination. So, we need to release USB devices only if there was
12639 * an abnormal termination of a running VM.
12640 *
12641 * This is identical to SessionMachine::DetachAllUSBDevices except
12642 * for the aAbnormal argument. */
12643 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12644 AssertComRC(rc);
12645 NOREF(rc);
12646
12647 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12648 if (service)
12649 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12650 }
12651#endif /* VBOX_WITH_USB */
12652
12653 // we need to lock this object in uninit() because the lock is shared
12654 // with mPeer (as well as data we modify below). mParent lock is needed
12655 // by several calls to it.
12656 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12657
12658#ifdef VBOX_WITH_RESOURCE_USAGE_API
12659 /*
12660 * It is safe to call Machine::i_unregisterMetrics() here because
12661 * PerformanceCollector::samplerCallback no longer accesses guest methods
12662 * holding the lock.
12663 */
12664 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12665 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12666 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12667 if (mCollectorGuest)
12668 {
12669 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12670 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12671 mCollectorGuest = NULL;
12672 }
12673#endif
12674
12675 if (aReason == Uninit::Abnormal)
12676 {
12677 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12678
12679 /* reset the state to Aborted */
12680 if (mData->mMachineState != MachineState_Aborted)
12681 i_setMachineState(MachineState_Aborted);
12682 }
12683
12684 // any machine settings modified?
12685 if (mData->flModifications)
12686 {
12687 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12688 i_rollback(false /* aNotify */);
12689 }
12690
12691 mData->mSession.mPID = NIL_RTPROCESS;
12692
12693 if (aReason == Uninit::Unexpected)
12694 {
12695 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12696 * client watcher thread to update the set of machines that have open
12697 * sessions. */
12698 mParent->i_updateClientWatcher();
12699 }
12700
12701 /* uninitialize all remote controls */
12702 if (mData->mSession.mRemoteControls.size())
12703 {
12704 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12705 mData->mSession.mRemoteControls.size()));
12706
12707 Data::Session::RemoteControlList::iterator it =
12708 mData->mSession.mRemoteControls.begin();
12709 while (it != mData->mSession.mRemoteControls.end())
12710 {
12711 ComPtr<IInternalSessionControl> pControl = *it;
12712 mData->mSession.mRemoteControls.erase(it);
12713 multilock.release();
12714 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12715 HRESULT rc = pControl->Uninitialize();
12716 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12717 if (FAILED(rc))
12718 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12719 multilock.acquire();
12720 it = mData->mSession.mRemoteControls.begin();
12721 }
12722 mData->mSession.mRemoteControls.clear();
12723 }
12724
12725 /* Remove all references to the NAT network service. The service will stop
12726 * if all references (also from other VMs) are removed. */
12727 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12728 {
12729 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12730 {
12731 BOOL enabled;
12732 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12733 if ( FAILED(hrc)
12734 || !enabled)
12735 continue;
12736
12737 NetworkAttachmentType_T type;
12738 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12739 if ( SUCCEEDED(hrc)
12740 && type == NetworkAttachmentType_NATNetwork)
12741 {
12742 Bstr name;
12743 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12744 if (SUCCEEDED(hrc))
12745 {
12746 multilock.release();
12747 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12748 mUserData->s.strName.c_str(), name.raw()));
12749 mParent->i_natNetworkRefDec(name.raw());
12750 multilock.acquire();
12751 }
12752 }
12753 }
12754 }
12755
12756 /*
12757 * An expected uninitialization can come only from #i_checkForDeath().
12758 * Otherwise it means that something's gone really wrong (for example,
12759 * the Session implementation has released the VirtualBox reference
12760 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12761 * etc). However, it's also possible, that the client releases the IPC
12762 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12763 * but the VirtualBox release event comes first to the server process.
12764 * This case is practically possible, so we should not assert on an
12765 * unexpected uninit, just log a warning.
12766 */
12767
12768 if (aReason == Uninit::Unexpected)
12769 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12770
12771 if (aReason != Uninit::Normal)
12772 {
12773 mData->mSession.mDirectControl.setNull();
12774 }
12775 else
12776 {
12777 /* this must be null here (see #OnSessionEnd()) */
12778 Assert(mData->mSession.mDirectControl.isNull());
12779 Assert(mData->mSession.mState == SessionState_Unlocking);
12780 Assert(!mData->mSession.mProgress.isNull());
12781 }
12782 if (mData->mSession.mProgress)
12783 {
12784 if (aReason == Uninit::Normal)
12785 mData->mSession.mProgress->i_notifyComplete(S_OK);
12786 else
12787 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12788 COM_IIDOF(ISession),
12789 getComponentName(),
12790 tr("The VM session was aborted"));
12791 mData->mSession.mProgress.setNull();
12792 }
12793
12794 if (mConsoleTaskData.mProgress)
12795 {
12796 Assert(aReason == Uninit::Abnormal);
12797 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12798 COM_IIDOF(ISession),
12799 getComponentName(),
12800 tr("The VM session was aborted"));
12801 mConsoleTaskData.mProgress.setNull();
12802 }
12803
12804 /* remove the association between the peer machine and this session machine */
12805 Assert( (SessionMachine*)mData->mSession.mMachine == this
12806 || aReason == Uninit::Unexpected);
12807
12808 /* reset the rest of session data */
12809 mData->mSession.mLockType = LockType_Null;
12810 mData->mSession.mMachine.setNull();
12811 mData->mSession.mState = SessionState_Unlocked;
12812 mData->mSession.mName.setNull();
12813
12814 /* destroy the machine client token before leaving the exclusive lock */
12815 if (mClientToken)
12816 {
12817 delete mClientToken;
12818 mClientToken = NULL;
12819 }
12820
12821 /* fire an event */
12822 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12823
12824 uninitDataAndChildObjects();
12825
12826 /* free the essential data structure last */
12827 mData.free();
12828
12829 /* release the exclusive lock before setting the below two to NULL */
12830 multilock.release();
12831
12832 unconst(mParent) = NULL;
12833 unconst(mPeer) = NULL;
12834
12835 AuthLibUnload(&mAuthLibCtx);
12836
12837 LogFlowThisFuncLeave();
12838}
12839
12840// util::Lockable interface
12841////////////////////////////////////////////////////////////////////////////////
12842
12843/**
12844 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12845 * with the primary Machine instance (mPeer).
12846 */
12847RWLockHandle *SessionMachine::lockHandle() const
12848{
12849 AssertReturn(mPeer != NULL, NULL);
12850 return mPeer->lockHandle();
12851}
12852
12853// IInternalMachineControl methods
12854////////////////////////////////////////////////////////////////////////////////
12855
12856/**
12857 * Passes collected guest statistics to performance collector object
12858 */
12859HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12860 ULONG aCpuKernel, ULONG aCpuIdle,
12861 ULONG aMemTotal, ULONG aMemFree,
12862 ULONG aMemBalloon, ULONG aMemShared,
12863 ULONG aMemCache, ULONG aPageTotal,
12864 ULONG aAllocVMM, ULONG aFreeVMM,
12865 ULONG aBalloonedVMM, ULONG aSharedVMM,
12866 ULONG aVmNetRx, ULONG aVmNetTx)
12867{
12868#ifdef VBOX_WITH_RESOURCE_USAGE_API
12869 if (mCollectorGuest)
12870 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12871 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12872 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12873 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12874
12875 return S_OK;
12876#else
12877 NOREF(aValidStats);
12878 NOREF(aCpuUser);
12879 NOREF(aCpuKernel);
12880 NOREF(aCpuIdle);
12881 NOREF(aMemTotal);
12882 NOREF(aMemFree);
12883 NOREF(aMemBalloon);
12884 NOREF(aMemShared);
12885 NOREF(aMemCache);
12886 NOREF(aPageTotal);
12887 NOREF(aAllocVMM);
12888 NOREF(aFreeVMM);
12889 NOREF(aBalloonedVMM);
12890 NOREF(aSharedVMM);
12891 NOREF(aVmNetRx);
12892 NOREF(aVmNetTx);
12893 return E_NOTIMPL;
12894#endif
12895}
12896
12897////////////////////////////////////////////////////////////////////////////////
12898//
12899// SessionMachine task records
12900//
12901////////////////////////////////////////////////////////////////////////////////
12902
12903/**
12904 * Task record for saving the machine state.
12905 */
12906class SessionMachine::SaveStateTask
12907 : public Machine::Task
12908{
12909public:
12910 SaveStateTask(SessionMachine *m,
12911 Progress *p,
12912 const Utf8Str &t,
12913 Reason_T enmReason,
12914 const Utf8Str &strStateFilePath)
12915 : Task(m, p, t),
12916 m_enmReason(enmReason),
12917 m_strStateFilePath(strStateFilePath)
12918 {}
12919
12920private:
12921 void handler()
12922 {
12923 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12924 }
12925
12926 Reason_T m_enmReason;
12927 Utf8Str m_strStateFilePath;
12928
12929 friend class SessionMachine;
12930};
12931
12932/**
12933 * Task thread implementation for SessionMachine::SaveState(), called from
12934 * SessionMachine::taskHandler().
12935 *
12936 * @note Locks this object for writing.
12937 *
12938 * @param task
12939 * @return
12940 */
12941void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12942{
12943 LogFlowThisFuncEnter();
12944
12945 AutoCaller autoCaller(this);
12946 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12947 if (FAILED(autoCaller.rc()))
12948 {
12949 /* we might have been uninitialized because the session was accidentally
12950 * closed by the client, so don't assert */
12951 HRESULT rc = setError(E_FAIL,
12952 tr("The session has been accidentally closed"));
12953 task.m_pProgress->i_notifyComplete(rc);
12954 LogFlowThisFuncLeave();
12955 return;
12956 }
12957
12958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12959
12960 HRESULT rc = S_OK;
12961
12962 try
12963 {
12964 ComPtr<IInternalSessionControl> directControl;
12965 if (mData->mSession.mLockType == LockType_VM)
12966 directControl = mData->mSession.mDirectControl;
12967 if (directControl.isNull())
12968 throw setError(VBOX_E_INVALID_VM_STATE,
12969 tr("Trying to save state without a running VM"));
12970 alock.release();
12971 BOOL fSuspendedBySave;
12972 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12973 Assert(!fSuspendedBySave);
12974 alock.acquire();
12975
12976 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12977 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12978 throw E_FAIL);
12979
12980 if (SUCCEEDED(rc))
12981 {
12982 mSSData->strStateFilePath = task.m_strStateFilePath;
12983
12984 /* save all VM settings */
12985 rc = i_saveSettings(NULL);
12986 // no need to check whether VirtualBox.xml needs saving also since
12987 // we can't have a name change pending at this point
12988 }
12989 else
12990 {
12991 // On failure, set the state to the state we had at the beginning.
12992 i_setMachineState(task.m_machineStateBackup);
12993 i_updateMachineStateOnClient();
12994
12995 // Delete the saved state file (might have been already created).
12996 // No need to check whether this is shared with a snapshot here
12997 // because we certainly created a fresh saved state file here.
12998 RTFileDelete(task.m_strStateFilePath.c_str());
12999 }
13000 }
13001 catch (HRESULT aRC) { rc = aRC; }
13002
13003 task.m_pProgress->i_notifyComplete(rc);
13004
13005 LogFlowThisFuncLeave();
13006}
13007
13008/**
13009 * @note Locks this object for writing.
13010 */
13011HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13012{
13013 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13014}
13015
13016HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13017{
13018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13019
13020 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13021 if (FAILED(rc)) return rc;
13022
13023 if ( mData->mMachineState != MachineState_Running
13024 && mData->mMachineState != MachineState_Paused
13025 )
13026 return setError(VBOX_E_INVALID_VM_STATE,
13027 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13028 Global::stringifyMachineState(mData->mMachineState));
13029
13030 ComObjPtr<Progress> pProgress;
13031 pProgress.createObject();
13032 rc = pProgress->init(i_getVirtualBox(),
13033 static_cast<IMachine *>(this) /* aInitiator */,
13034 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13035 FALSE /* aCancelable */);
13036 if (FAILED(rc))
13037 return rc;
13038
13039 Utf8Str strStateFilePath;
13040 i_composeSavedStateFilename(strStateFilePath);
13041
13042 /* create and start the task on a separate thread (note that it will not
13043 * start working until we release alock) */
13044 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13045 rc = pTask->createThread();
13046 if (FAILED(rc))
13047 return rc;
13048
13049 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13050 i_setMachineState(MachineState_Saving);
13051 i_updateMachineStateOnClient();
13052
13053 pProgress.queryInterfaceTo(aProgress.asOutParam());
13054
13055 return S_OK;
13056}
13057
13058/**
13059 * @note Locks this object for writing.
13060 */
13061HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13062{
13063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13064
13065 HRESULT rc = i_checkStateDependency(MutableStateDep);
13066 if (FAILED(rc)) return rc;
13067
13068 if ( mData->mMachineState != MachineState_PoweredOff
13069 && mData->mMachineState != MachineState_Teleported
13070 && mData->mMachineState != MachineState_Aborted
13071 )
13072 return setError(VBOX_E_INVALID_VM_STATE,
13073 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13074 Global::stringifyMachineState(mData->mMachineState));
13075
13076 com::Utf8Str stateFilePathFull;
13077 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13078 if (RT_FAILURE(vrc))
13079 return setError(VBOX_E_FILE_ERROR,
13080 tr("Invalid saved state file path '%s' (%Rrc)"),
13081 aSavedStateFile.c_str(),
13082 vrc);
13083
13084 mSSData->strStateFilePath = stateFilePathFull;
13085
13086 /* The below i_setMachineState() will detect the state transition and will
13087 * update the settings file */
13088
13089 return i_setMachineState(MachineState_Saved);
13090}
13091
13092/**
13093 * @note Locks this object for writing.
13094 */
13095HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13096{
13097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13098
13099 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13100 if (FAILED(rc)) return rc;
13101
13102 if (mData->mMachineState != MachineState_Saved)
13103 return setError(VBOX_E_INVALID_VM_STATE,
13104 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13105 Global::stringifyMachineState(mData->mMachineState));
13106
13107 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13108
13109 /*
13110 * Saved -> PoweredOff transition will be detected in the SessionMachine
13111 * and properly handled.
13112 */
13113 rc = i_setMachineState(MachineState_PoweredOff);
13114 return rc;
13115}
13116
13117
13118/**
13119 * @note Locks the same as #i_setMachineState() does.
13120 */
13121HRESULT SessionMachine::updateState(MachineState_T aState)
13122{
13123 return i_setMachineState(aState);
13124}
13125
13126/**
13127 * @note Locks this object for writing.
13128 */
13129HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13130{
13131 IProgress* pProgress(aProgress);
13132
13133 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13134
13135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13136
13137 if (mData->mSession.mState != SessionState_Locked)
13138 return VBOX_E_INVALID_OBJECT_STATE;
13139
13140 if (!mData->mSession.mProgress.isNull())
13141 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13142
13143 /* If we didn't reference the NAT network service yet, add a reference to
13144 * force a start */
13145 if (miNATNetworksStarted < 1)
13146 {
13147 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13148 {
13149 BOOL enabled;
13150 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13151 if ( FAILED(hrc)
13152 || !enabled)
13153 continue;
13154
13155 NetworkAttachmentType_T type;
13156 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13157 if ( SUCCEEDED(hrc)
13158 && type == NetworkAttachmentType_NATNetwork)
13159 {
13160 Bstr name;
13161 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13162 if (SUCCEEDED(hrc))
13163 {
13164 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13165 mUserData->s.strName.c_str(), name.raw()));
13166 mPeer->lockHandle()->unlockWrite();
13167 mParent->i_natNetworkRefInc(name.raw());
13168#ifdef RT_LOCK_STRICT
13169 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13170#else
13171 mPeer->lockHandle()->lockWrite();
13172#endif
13173 }
13174 }
13175 }
13176 miNATNetworksStarted++;
13177 }
13178
13179 LogFlowThisFunc(("returns S_OK.\n"));
13180 return S_OK;
13181}
13182
13183/**
13184 * @note Locks this object for writing.
13185 */
13186HRESULT SessionMachine::endPowerUp(LONG aResult)
13187{
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 if (mData->mSession.mState != SessionState_Locked)
13191 return VBOX_E_INVALID_OBJECT_STATE;
13192
13193 /* Finalize the LaunchVMProcess progress object. */
13194 if (mData->mSession.mProgress)
13195 {
13196 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13197 mData->mSession.mProgress.setNull();
13198 }
13199
13200 if (SUCCEEDED((HRESULT)aResult))
13201 {
13202#ifdef VBOX_WITH_RESOURCE_USAGE_API
13203 /* The VM has been powered up successfully, so it makes sense
13204 * now to offer the performance metrics for a running machine
13205 * object. Doing it earlier wouldn't be safe. */
13206 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13207 mData->mSession.mPID);
13208#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13209 }
13210
13211 return S_OK;
13212}
13213
13214/**
13215 * @note Locks this object for writing.
13216 */
13217HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13218{
13219 LogFlowThisFuncEnter();
13220
13221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13222
13223 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13224 E_FAIL);
13225
13226 /* create a progress object to track operation completion */
13227 ComObjPtr<Progress> pProgress;
13228 pProgress.createObject();
13229 pProgress->init(i_getVirtualBox(),
13230 static_cast<IMachine *>(this) /* aInitiator */,
13231 Bstr(tr("Stopping the virtual machine")).raw(),
13232 FALSE /* aCancelable */);
13233
13234 /* fill in the console task data */
13235 mConsoleTaskData.mLastState = mData->mMachineState;
13236 mConsoleTaskData.mProgress = pProgress;
13237
13238 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13239 i_setMachineState(MachineState_Stopping);
13240
13241 pProgress.queryInterfaceTo(aProgress.asOutParam());
13242
13243 return S_OK;
13244}
13245
13246/**
13247 * @note Locks this object for writing.
13248 */
13249HRESULT SessionMachine::endPoweringDown(LONG aResult,
13250 const com::Utf8Str &aErrMsg)
13251{
13252 LogFlowThisFuncEnter();
13253
13254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13255
13256 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13257 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13258 && mConsoleTaskData.mLastState != MachineState_Null,
13259 E_FAIL);
13260
13261 /*
13262 * On failure, set the state to the state we had when BeginPoweringDown()
13263 * was called (this is expected by Console::PowerDown() and the associated
13264 * task). On success the VM process already changed the state to
13265 * MachineState_PoweredOff, so no need to do anything.
13266 */
13267 if (FAILED(aResult))
13268 i_setMachineState(mConsoleTaskData.mLastState);
13269
13270 /* notify the progress object about operation completion */
13271 Assert(mConsoleTaskData.mProgress);
13272 if (SUCCEEDED(aResult))
13273 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13274 else
13275 {
13276 if (aErrMsg.length())
13277 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13278 COM_IIDOF(ISession),
13279 getComponentName(),
13280 aErrMsg.c_str());
13281 else
13282 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13283 }
13284
13285 /* clear out the temporary saved state data */
13286 mConsoleTaskData.mLastState = MachineState_Null;
13287 mConsoleTaskData.mProgress.setNull();
13288
13289 LogFlowThisFuncLeave();
13290 return S_OK;
13291}
13292
13293
13294/**
13295 * Goes through the USB filters of the given machine to see if the given
13296 * device matches any filter or not.
13297 *
13298 * @note Locks the same as USBController::hasMatchingFilter() does.
13299 */
13300HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13301 BOOL *aMatched,
13302 ULONG *aMaskedInterfaces)
13303{
13304 LogFlowThisFunc(("\n"));
13305
13306#ifdef VBOX_WITH_USB
13307 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13308#else
13309 NOREF(aDevice);
13310 NOREF(aMaskedInterfaces);
13311 *aMatched = FALSE;
13312#endif
13313
13314 return S_OK;
13315}
13316
13317/**
13318 * @note Locks the same as Host::captureUSBDevice() does.
13319 */
13320HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13321{
13322 LogFlowThisFunc(("\n"));
13323
13324#ifdef VBOX_WITH_USB
13325 /* if captureDeviceForVM() fails, it must have set extended error info */
13326 clearError();
13327 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13328 if (FAILED(rc)) return rc;
13329
13330 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13331 AssertReturn(service, E_FAIL);
13332 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13333#else
13334 NOREF(aId);
13335 return E_NOTIMPL;
13336#endif
13337}
13338
13339/**
13340 * @note Locks the same as Host::detachUSBDevice() does.
13341 */
13342HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13343 BOOL aDone)
13344{
13345 LogFlowThisFunc(("\n"));
13346
13347#ifdef VBOX_WITH_USB
13348 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13349 AssertReturn(service, E_FAIL);
13350 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13351#else
13352 NOREF(aId);
13353 NOREF(aDone);
13354 return E_NOTIMPL;
13355#endif
13356}
13357
13358/**
13359 * Inserts all machine filters to the USB proxy service and then calls
13360 * Host::autoCaptureUSBDevices().
13361 *
13362 * Called by Console from the VM process upon VM startup.
13363 *
13364 * @note Locks what called methods lock.
13365 */
13366HRESULT SessionMachine::autoCaptureUSBDevices()
13367{
13368 LogFlowThisFunc(("\n"));
13369
13370#ifdef VBOX_WITH_USB
13371 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13372 AssertComRC(rc);
13373 NOREF(rc);
13374
13375 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13376 AssertReturn(service, E_FAIL);
13377 return service->autoCaptureDevicesForVM(this);
13378#else
13379 return S_OK;
13380#endif
13381}
13382
13383/**
13384 * Removes all machine filters from the USB proxy service and then calls
13385 * Host::detachAllUSBDevices().
13386 *
13387 * Called by Console from the VM process upon normal VM termination or by
13388 * SessionMachine::uninit() upon abnormal VM termination (from under the
13389 * Machine/SessionMachine lock).
13390 *
13391 * @note Locks what called methods lock.
13392 */
13393HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13394{
13395 LogFlowThisFunc(("\n"));
13396
13397#ifdef VBOX_WITH_USB
13398 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13399 AssertComRC(rc);
13400 NOREF(rc);
13401
13402 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13403 AssertReturn(service, E_FAIL);
13404 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13405#else
13406 NOREF(aDone);
13407 return S_OK;
13408#endif
13409}
13410
13411/**
13412 * @note Locks this object for writing.
13413 */
13414HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13415 ComPtr<IProgress> &aProgress)
13416{
13417 LogFlowThisFuncEnter();
13418
13419 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13420 /*
13421 * We don't assert below because it might happen that a non-direct session
13422 * informs us it is closed right after we've been uninitialized -- it's ok.
13423 */
13424
13425 /* get IInternalSessionControl interface */
13426 ComPtr<IInternalSessionControl> control(aSession);
13427
13428 ComAssertRet(!control.isNull(), E_INVALIDARG);
13429
13430 /* Creating a Progress object requires the VirtualBox lock, and
13431 * thus locking it here is required by the lock order rules. */
13432 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13433
13434 if (control == mData->mSession.mDirectControl)
13435 {
13436 /* The direct session is being normally closed by the client process
13437 * ----------------------------------------------------------------- */
13438
13439 /* go to the closing state (essential for all open*Session() calls and
13440 * for #i_checkForDeath()) */
13441 Assert(mData->mSession.mState == SessionState_Locked);
13442 mData->mSession.mState = SessionState_Unlocking;
13443
13444 /* set direct control to NULL to release the remote instance */
13445 mData->mSession.mDirectControl.setNull();
13446 LogFlowThisFunc(("Direct control is set to NULL\n"));
13447
13448 if (mData->mSession.mProgress)
13449 {
13450 /* finalize the progress, someone might wait if a frontend
13451 * closes the session before powering on the VM. */
13452 mData->mSession.mProgress->notifyComplete(E_FAIL,
13453 COM_IIDOF(ISession),
13454 getComponentName(),
13455 tr("The VM session was closed before any attempt to power it on"));
13456 mData->mSession.mProgress.setNull();
13457 }
13458
13459 /* Create the progress object the client will use to wait until
13460 * #i_checkForDeath() is called to uninitialize this session object after
13461 * it releases the IPC semaphore.
13462 * Note! Because we're "reusing" mProgress here, this must be a proxy
13463 * object just like for LaunchVMProcess. */
13464 Assert(mData->mSession.mProgress.isNull());
13465 ComObjPtr<ProgressProxy> progress;
13466 progress.createObject();
13467 ComPtr<IUnknown> pPeer(mPeer);
13468 progress->init(mParent, pPeer,
13469 Bstr(tr("Closing session")).raw(),
13470 FALSE /* aCancelable */);
13471 progress.queryInterfaceTo(aProgress.asOutParam());
13472 mData->mSession.mProgress = progress;
13473 }
13474 else
13475 {
13476 /* the remote session is being normally closed */
13477 Data::Session::RemoteControlList::iterator it =
13478 mData->mSession.mRemoteControls.begin();
13479 while (it != mData->mSession.mRemoteControls.end())
13480 {
13481 if (control == *it)
13482 break;
13483 ++it;
13484 }
13485 BOOL found = it != mData->mSession.mRemoteControls.end();
13486 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13487 E_INVALIDARG);
13488 // This MUST be erase(it), not remove(*it) as the latter triggers a
13489 // very nasty use after free due to the place where the value "lives".
13490 mData->mSession.mRemoteControls.erase(it);
13491 }
13492
13493 /* signal the client watcher thread, because the client is going away */
13494 mParent->i_updateClientWatcher();
13495
13496 LogFlowThisFuncLeave();
13497 return S_OK;
13498}
13499
13500HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13501 std::vector<com::Utf8Str> &aValues,
13502 std::vector<LONG64> &aTimestamps,
13503 std::vector<com::Utf8Str> &aFlags)
13504{
13505 LogFlowThisFunc(("\n"));
13506
13507#ifdef VBOX_WITH_GUEST_PROPS
13508 using namespace guestProp;
13509
13510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13511
13512 size_t cEntries = mHWData->mGuestProperties.size();
13513 aNames.resize(cEntries);
13514 aValues.resize(cEntries);
13515 aTimestamps.resize(cEntries);
13516 aFlags.resize(cEntries);
13517
13518 size_t i = 0;
13519 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13520 it != mHWData->mGuestProperties.end();
13521 ++it, ++i)
13522 {
13523 char szFlags[MAX_FLAGS_LEN + 1];
13524 aNames[i] = it->first;
13525 aValues[i] = it->second.strValue;
13526 aTimestamps[i] = it->second.mTimestamp;
13527
13528 /* If it is NULL, keep it NULL. */
13529 if (it->second.mFlags)
13530 {
13531 writeFlags(it->second.mFlags, szFlags);
13532 aFlags[i] = szFlags;
13533 }
13534 else
13535 aFlags[i] = "";
13536 }
13537 return S_OK;
13538#else
13539 ReturnComNotImplemented();
13540#endif
13541}
13542
13543HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13544 const com::Utf8Str &aValue,
13545 LONG64 aTimestamp,
13546 const com::Utf8Str &aFlags)
13547{
13548 LogFlowThisFunc(("\n"));
13549
13550#ifdef VBOX_WITH_GUEST_PROPS
13551 using namespace guestProp;
13552
13553 try
13554 {
13555 /*
13556 * Convert input up front.
13557 */
13558 uint32_t fFlags = NILFLAG;
13559 if (aFlags.length())
13560 {
13561 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13562 AssertRCReturn(vrc, E_INVALIDARG);
13563 }
13564
13565 /*
13566 * Now grab the object lock, validate the state and do the update.
13567 */
13568
13569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13570
13571 if (!Global::IsOnline(mData->mMachineState))
13572 {
13573 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13574 VBOX_E_INVALID_VM_STATE);
13575 }
13576
13577 i_setModified(IsModified_MachineData);
13578 mHWData.backup();
13579
13580 bool fDelete = !aValue.length();
13581 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13582 if (it != mHWData->mGuestProperties.end())
13583 {
13584 if (!fDelete)
13585 {
13586 it->second.strValue = aValue;
13587 it->second.mTimestamp = aTimestamp;
13588 it->second.mFlags = fFlags;
13589 }
13590 else
13591 mHWData->mGuestProperties.erase(it);
13592
13593 mData->mGuestPropertiesModified = TRUE;
13594 }
13595 else if (!fDelete)
13596 {
13597 HWData::GuestProperty prop;
13598 prop.strValue = aValue;
13599 prop.mTimestamp = aTimestamp;
13600 prop.mFlags = fFlags;
13601
13602 mHWData->mGuestProperties[aName] = prop;
13603 mData->mGuestPropertiesModified = TRUE;
13604 }
13605
13606 alock.release();
13607
13608 mParent->i_onGuestPropertyChange(mData->mUuid,
13609 Bstr(aName).raw(),
13610 Bstr(aValue).raw(),
13611 Bstr(aFlags).raw());
13612 }
13613 catch (...)
13614 {
13615 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13616 }
13617 return S_OK;
13618#else
13619 ReturnComNotImplemented();
13620#endif
13621}
13622
13623
13624HRESULT SessionMachine::lockMedia()
13625{
13626 AutoMultiWriteLock2 alock(this->lockHandle(),
13627 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13628
13629 AssertReturn( mData->mMachineState == MachineState_Starting
13630 || mData->mMachineState == MachineState_Restoring
13631 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13632
13633 clearError();
13634 alock.release();
13635 return i_lockMedia();
13636}
13637
13638HRESULT SessionMachine::unlockMedia()
13639{
13640 HRESULT hrc = i_unlockMedia();
13641 return hrc;
13642}
13643
13644HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13645 ComPtr<IMediumAttachment> &aNewAttachment)
13646{
13647 // request the host lock first, since might be calling Host methods for getting host drives;
13648 // next, protect the media tree all the while we're in here, as well as our member variables
13649 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13650 this->lockHandle(),
13651 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13652
13653 IMediumAttachment *iAttach = aAttachment;
13654 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13655
13656 Bstr ctrlName;
13657 LONG lPort;
13658 LONG lDevice;
13659 bool fTempEject;
13660 {
13661 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13662
13663 /* Need to query the details first, as the IMediumAttachment reference
13664 * might be to the original settings, which we are going to change. */
13665 ctrlName = pAttach->i_getControllerName();
13666 lPort = pAttach->i_getPort();
13667 lDevice = pAttach->i_getDevice();
13668 fTempEject = pAttach->i_getTempEject();
13669 }
13670
13671 if (!fTempEject)
13672 {
13673 /* Remember previously mounted medium. The medium before taking the
13674 * backup is not necessarily the same thing. */
13675 ComObjPtr<Medium> oldmedium;
13676 oldmedium = pAttach->i_getMedium();
13677
13678 i_setModified(IsModified_Storage);
13679 mMediaData.backup();
13680
13681 // The backup operation makes the pAttach reference point to the
13682 // old settings. Re-get the correct reference.
13683 pAttach = i_findAttachment(mMediaData->mAttachments,
13684 ctrlName.raw(),
13685 lPort,
13686 lDevice);
13687
13688 {
13689 AutoCaller autoAttachCaller(this);
13690 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13691
13692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13693 if (!oldmedium.isNull())
13694 oldmedium->i_removeBackReference(mData->mUuid);
13695
13696 pAttach->i_updateMedium(NULL);
13697 pAttach->i_updateEjected();
13698 }
13699
13700 i_setModified(IsModified_Storage);
13701 }
13702 else
13703 {
13704 {
13705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13706 pAttach->i_updateEjected();
13707 }
13708 }
13709
13710 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13711
13712 return S_OK;
13713}
13714
13715HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13716 com::Utf8Str &aResult)
13717{
13718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13719
13720 HRESULT hr = S_OK;
13721
13722 if (!mAuthLibCtx.hAuthLibrary)
13723 {
13724 /* Load the external authentication library. */
13725 Bstr authLibrary;
13726 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13727
13728 Utf8Str filename = authLibrary;
13729
13730 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13731 if (RT_FAILURE(rc))
13732 {
13733 hr = setError(E_FAIL,
13734 tr("Could not load the external authentication library '%s' (%Rrc)"),
13735 filename.c_str(), rc);
13736 }
13737 }
13738
13739 /* The auth library might need the machine lock. */
13740 alock.release();
13741
13742 if (FAILED(hr))
13743 return hr;
13744
13745 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13746 {
13747 enum VRDEAuthParams
13748 {
13749 parmUuid = 1,
13750 parmGuestJudgement,
13751 parmUser,
13752 parmPassword,
13753 parmDomain,
13754 parmClientId
13755 };
13756
13757 AuthResult result = AuthResultAccessDenied;
13758
13759 Guid uuid(aAuthParams[parmUuid]);
13760 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13761 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13762
13763 result = AuthLibAuthenticate(&mAuthLibCtx,
13764 uuid.raw(), guestJudgement,
13765 aAuthParams[parmUser].c_str(),
13766 aAuthParams[parmPassword].c_str(),
13767 aAuthParams[parmDomain].c_str(),
13768 u32ClientId);
13769
13770 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13771 size_t cbPassword = aAuthParams[parmPassword].length();
13772 if (cbPassword)
13773 {
13774 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13775 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13776 }
13777
13778 if (result == AuthResultAccessGranted)
13779 aResult = "granted";
13780 else
13781 aResult = "denied";
13782
13783 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13784 aAuthParams[parmUser].c_str(), aResult.c_str()));
13785 }
13786 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13787 {
13788 enum VRDEAuthDisconnectParams
13789 {
13790 parmUuid = 1,
13791 parmClientId
13792 };
13793
13794 Guid uuid(aAuthParams[parmUuid]);
13795 uint32_t u32ClientId = 0;
13796 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13797 }
13798 else
13799 {
13800 hr = E_INVALIDARG;
13801 }
13802
13803 return hr;
13804}
13805
13806// public methods only for internal purposes
13807/////////////////////////////////////////////////////////////////////////////
13808
13809#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13810/**
13811 * Called from the client watcher thread to check for expected or unexpected
13812 * death of the client process that has a direct session to this machine.
13813 *
13814 * On Win32 and on OS/2, this method is called only when we've got the
13815 * mutex (i.e. the client has either died or terminated normally) so it always
13816 * returns @c true (the client is terminated, the session machine is
13817 * uninitialized).
13818 *
13819 * On other platforms, the method returns @c true if the client process has
13820 * terminated normally or abnormally and the session machine was uninitialized,
13821 * and @c false if the client process is still alive.
13822 *
13823 * @note Locks this object for writing.
13824 */
13825bool SessionMachine::i_checkForDeath()
13826{
13827 Uninit::Reason reason;
13828 bool terminated = false;
13829
13830 /* Enclose autoCaller with a block because calling uninit() from under it
13831 * will deadlock. */
13832 {
13833 AutoCaller autoCaller(this);
13834 if (!autoCaller.isOk())
13835 {
13836 /* return true if not ready, to cause the client watcher to exclude
13837 * the corresponding session from watching */
13838 LogFlowThisFunc(("Already uninitialized!\n"));
13839 return true;
13840 }
13841
13842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13843
13844 /* Determine the reason of death: if the session state is Closing here,
13845 * everything is fine. Otherwise it means that the client did not call
13846 * OnSessionEnd() before it released the IPC semaphore. This may happen
13847 * either because the client process has abnormally terminated, or
13848 * because it simply forgot to call ISession::Close() before exiting. We
13849 * threat the latter also as an abnormal termination (see
13850 * Session::uninit() for details). */
13851 reason = mData->mSession.mState == SessionState_Unlocking ?
13852 Uninit::Normal :
13853 Uninit::Abnormal;
13854
13855 if (mClientToken)
13856 terminated = mClientToken->release();
13857 } /* AutoCaller block */
13858
13859 if (terminated)
13860 uninit(reason);
13861
13862 return terminated;
13863}
13864
13865void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 strTokenId.setNull();
13870
13871 AutoCaller autoCaller(this);
13872 AssertComRCReturnVoid(autoCaller.rc());
13873
13874 Assert(mClientToken);
13875 if (mClientToken)
13876 mClientToken->getId(strTokenId);
13877}
13878#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13879IToken *SessionMachine::i_getToken()
13880{
13881 LogFlowThisFunc(("\n"));
13882
13883 AutoCaller autoCaller(this);
13884 AssertComRCReturn(autoCaller.rc(), NULL);
13885
13886 Assert(mClientToken);
13887 if (mClientToken)
13888 return mClientToken->getToken();
13889 else
13890 return NULL;
13891}
13892#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13893
13894Machine::ClientToken *SessionMachine::i_getClientToken()
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), NULL);
13900
13901 return mClientToken;
13902}
13903
13904
13905/**
13906 * @note Locks this object for reading.
13907 */
13908HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13909{
13910 LogFlowThisFunc(("\n"));
13911
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13914
13915 ComPtr<IInternalSessionControl> directControl;
13916 {
13917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13918 if (mData->mSession.mLockType == LockType_VM)
13919 directControl = mData->mSession.mDirectControl;
13920 }
13921
13922 /* ignore notifications sent after #OnSessionEnd() is called */
13923 if (!directControl)
13924 return S_OK;
13925
13926 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13927}
13928
13929/**
13930 * @note Locks this object for reading.
13931 */
13932HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13933 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13934 IN_BSTR aGuestIp, LONG aGuestPort)
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 AutoCaller autoCaller(this);
13939 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13940
13941 ComPtr<IInternalSessionControl> directControl;
13942 {
13943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13944 if (mData->mSession.mLockType == LockType_VM)
13945 directControl = mData->mSession.mDirectControl;
13946 }
13947
13948 /* ignore notifications sent after #OnSessionEnd() is called */
13949 if (!directControl)
13950 return S_OK;
13951 /*
13952 * instead acting like callback we ask IVirtualBox deliver corresponding event
13953 */
13954
13955 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13956 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13957 return S_OK;
13958}
13959
13960/**
13961 * @note Locks this object for reading.
13962 */
13963HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967 AutoCaller autoCaller(this);
13968 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13969
13970 ComPtr<IInternalSessionControl> directControl;
13971 {
13972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13973 if (mData->mSession.mLockType == LockType_VM)
13974 directControl = mData->mSession.mDirectControl;
13975 }
13976
13977 /* ignore notifications sent after #OnSessionEnd() is called */
13978 if (!directControl)
13979 return S_OK;
13980
13981 return directControl->OnSerialPortChange(serialPort);
13982}
13983
13984/**
13985 * @note Locks this object for reading.
13986 */
13987HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13988{
13989 LogFlowThisFunc(("\n"));
13990
13991 AutoCaller autoCaller(this);
13992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13993
13994 ComPtr<IInternalSessionControl> directControl;
13995 {
13996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13997 if (mData->mSession.mLockType == LockType_VM)
13998 directControl = mData->mSession.mDirectControl;
13999 }
14000
14001 /* ignore notifications sent after #OnSessionEnd() is called */
14002 if (!directControl)
14003 return S_OK;
14004
14005 return directControl->OnParallelPortChange(parallelPort);
14006}
14007
14008/**
14009 * @note Locks this object for reading.
14010 */
14011HRESULT SessionMachine::i_onStorageControllerChange()
14012{
14013 LogFlowThisFunc(("\n"));
14014
14015 AutoCaller autoCaller(this);
14016 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14017
14018 ComPtr<IInternalSessionControl> directControl;
14019 {
14020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14021 if (mData->mSession.mLockType == LockType_VM)
14022 directControl = mData->mSession.mDirectControl;
14023 }
14024
14025 /* ignore notifications sent after #OnSessionEnd() is called */
14026 if (!directControl)
14027 return S_OK;
14028
14029 return directControl->OnStorageControllerChange();
14030}
14031
14032/**
14033 * @note Locks this object for reading.
14034 */
14035HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14036{
14037 LogFlowThisFunc(("\n"));
14038
14039 AutoCaller autoCaller(this);
14040 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14041
14042 ComPtr<IInternalSessionControl> directControl;
14043 {
14044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14045 if (mData->mSession.mLockType == LockType_VM)
14046 directControl = mData->mSession.mDirectControl;
14047 }
14048
14049 /* ignore notifications sent after #OnSessionEnd() is called */
14050 if (!directControl)
14051 return S_OK;
14052
14053 return directControl->OnMediumChange(aAttachment, aForce);
14054}
14055
14056/**
14057 * @note Locks this object for reading.
14058 */
14059HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14060{
14061 LogFlowThisFunc(("\n"));
14062
14063 AutoCaller autoCaller(this);
14064 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14065
14066 ComPtr<IInternalSessionControl> directControl;
14067 {
14068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14069 if (mData->mSession.mLockType == LockType_VM)
14070 directControl = mData->mSession.mDirectControl;
14071 }
14072
14073 /* ignore notifications sent after #OnSessionEnd() is called */
14074 if (!directControl)
14075 return S_OK;
14076
14077 return directControl->OnCPUChange(aCPU, aRemove);
14078}
14079
14080HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14081{
14082 LogFlowThisFunc(("\n"));
14083
14084 AutoCaller autoCaller(this);
14085 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14086
14087 ComPtr<IInternalSessionControl> directControl;
14088 {
14089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14090 if (mData->mSession.mLockType == LockType_VM)
14091 directControl = mData->mSession.mDirectControl;
14092 }
14093
14094 /* ignore notifications sent after #OnSessionEnd() is called */
14095 if (!directControl)
14096 return S_OK;
14097
14098 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14099}
14100
14101/**
14102 * @note Locks this object for reading.
14103 */
14104HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14105{
14106 LogFlowThisFunc(("\n"));
14107
14108 AutoCaller autoCaller(this);
14109 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14110
14111 ComPtr<IInternalSessionControl> directControl;
14112 {
14113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14114 if (mData->mSession.mLockType == LockType_VM)
14115 directControl = mData->mSession.mDirectControl;
14116 }
14117
14118 /* ignore notifications sent after #OnSessionEnd() is called */
14119 if (!directControl)
14120 return S_OK;
14121
14122 return directControl->OnVRDEServerChange(aRestart);
14123}
14124
14125/**
14126 * @note Locks this object for reading.
14127 */
14128HRESULT SessionMachine::i_onVideoCaptureChange()
14129{
14130 LogFlowThisFunc(("\n"));
14131
14132 AutoCaller autoCaller(this);
14133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14134
14135 ComPtr<IInternalSessionControl> directControl;
14136 {
14137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14138 if (mData->mSession.mLockType == LockType_VM)
14139 directControl = mData->mSession.mDirectControl;
14140 }
14141
14142 /* ignore notifications sent after #OnSessionEnd() is called */
14143 if (!directControl)
14144 return S_OK;
14145
14146 return directControl->OnVideoCaptureChange();
14147}
14148
14149/**
14150 * @note Locks this object for reading.
14151 */
14152HRESULT SessionMachine::i_onUSBControllerChange()
14153{
14154 LogFlowThisFunc(("\n"));
14155
14156 AutoCaller autoCaller(this);
14157 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14158
14159 ComPtr<IInternalSessionControl> directControl;
14160 {
14161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14162 if (mData->mSession.mLockType == LockType_VM)
14163 directControl = mData->mSession.mDirectControl;
14164 }
14165
14166 /* ignore notifications sent after #OnSessionEnd() is called */
14167 if (!directControl)
14168 return S_OK;
14169
14170 return directControl->OnUSBControllerChange();
14171}
14172
14173/**
14174 * @note Locks this object for reading.
14175 */
14176HRESULT SessionMachine::i_onSharedFolderChange()
14177{
14178 LogFlowThisFunc(("\n"));
14179
14180 AutoCaller autoCaller(this);
14181 AssertComRCReturnRC(autoCaller.rc());
14182
14183 ComPtr<IInternalSessionControl> directControl;
14184 {
14185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14186 if (mData->mSession.mLockType == LockType_VM)
14187 directControl = mData->mSession.mDirectControl;
14188 }
14189
14190 /* ignore notifications sent after #OnSessionEnd() is called */
14191 if (!directControl)
14192 return S_OK;
14193
14194 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14195}
14196
14197/**
14198 * @note Locks this object for reading.
14199 */
14200HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14201{
14202 LogFlowThisFunc(("\n"));
14203
14204 AutoCaller autoCaller(this);
14205 AssertComRCReturnRC(autoCaller.rc());
14206
14207 ComPtr<IInternalSessionControl> directControl;
14208 {
14209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14210 if (mData->mSession.mLockType == LockType_VM)
14211 directControl = mData->mSession.mDirectControl;
14212 }
14213
14214 /* ignore notifications sent after #OnSessionEnd() is called */
14215 if (!directControl)
14216 return S_OK;
14217
14218 return directControl->OnClipboardModeChange(aClipboardMode);
14219}
14220
14221/**
14222 * @note Locks this object for reading.
14223 */
14224HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14225{
14226 LogFlowThisFunc(("\n"));
14227
14228 AutoCaller autoCaller(this);
14229 AssertComRCReturnRC(autoCaller.rc());
14230
14231 ComPtr<IInternalSessionControl> directControl;
14232 {
14233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14234 if (mData->mSession.mLockType == LockType_VM)
14235 directControl = mData->mSession.mDirectControl;
14236 }
14237
14238 /* ignore notifications sent after #OnSessionEnd() is called */
14239 if (!directControl)
14240 return S_OK;
14241
14242 return directControl->OnDnDModeChange(aDnDMode);
14243}
14244
14245/**
14246 * @note Locks this object for reading.
14247 */
14248HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14249{
14250 LogFlowThisFunc(("\n"));
14251
14252 AutoCaller autoCaller(this);
14253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14254
14255 ComPtr<IInternalSessionControl> directControl;
14256 {
14257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14258 if (mData->mSession.mLockType == LockType_VM)
14259 directControl = mData->mSession.mDirectControl;
14260 }
14261
14262 /* ignore notifications sent after #OnSessionEnd() is called */
14263 if (!directControl)
14264 return S_OK;
14265
14266 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14267}
14268
14269/**
14270 * @note Locks this object for reading.
14271 */
14272HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14273{
14274 LogFlowThisFunc(("\n"));
14275
14276 AutoCaller autoCaller(this);
14277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14278
14279 ComPtr<IInternalSessionControl> directControl;
14280 {
14281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14282 if (mData->mSession.mLockType == LockType_VM)
14283 directControl = mData->mSession.mDirectControl;
14284 }
14285
14286 /* ignore notifications sent after #OnSessionEnd() is called */
14287 if (!directControl)
14288 return S_OK;
14289
14290 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14291}
14292
14293/**
14294 * Returns @c true if this machine's USB controller reports it has a matching
14295 * filter for the given USB device and @c false otherwise.
14296 *
14297 * @note locks this object for reading.
14298 */
14299bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14300{
14301 AutoCaller autoCaller(this);
14302 /* silently return if not ready -- this method may be called after the
14303 * direct machine session has been called */
14304 if (!autoCaller.isOk())
14305 return false;
14306
14307#ifdef VBOX_WITH_USB
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309
14310 switch (mData->mMachineState)
14311 {
14312 case MachineState_Starting:
14313 case MachineState_Restoring:
14314 case MachineState_TeleportingIn:
14315 case MachineState_Paused:
14316 case MachineState_Running:
14317 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14318 * elsewhere... */
14319 alock.release();
14320 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14321 default: break;
14322 }
14323#else
14324 NOREF(aDevice);
14325 NOREF(aMaskedIfs);
14326#endif
14327 return false;
14328}
14329
14330/**
14331 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14332 */
14333HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14334 IVirtualBoxErrorInfo *aError,
14335 ULONG aMaskedIfs,
14336 const com::Utf8Str &aCaptureFilename)
14337{
14338 LogFlowThisFunc(("\n"));
14339
14340 AutoCaller autoCaller(this);
14341
14342 /* This notification may happen after the machine object has been
14343 * uninitialized (the session was closed), so don't assert. */
14344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14345
14346 ComPtr<IInternalSessionControl> directControl;
14347 {
14348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14349 if (mData->mSession.mLockType == LockType_VM)
14350 directControl = mData->mSession.mDirectControl;
14351 }
14352
14353 /* fail on notifications sent after #OnSessionEnd() is called, it is
14354 * expected by the caller */
14355 if (!directControl)
14356 return E_FAIL;
14357
14358 /* No locks should be held at this point. */
14359 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14360 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14361
14362 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14363}
14364
14365/**
14366 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14367 */
14368HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14369 IVirtualBoxErrorInfo *aError)
14370{
14371 LogFlowThisFunc(("\n"));
14372
14373 AutoCaller autoCaller(this);
14374
14375 /* This notification may happen after the machine object has been
14376 * uninitialized (the session was closed), so don't assert. */
14377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14378
14379 ComPtr<IInternalSessionControl> directControl;
14380 {
14381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14382 if (mData->mSession.mLockType == LockType_VM)
14383 directControl = mData->mSession.mDirectControl;
14384 }
14385
14386 /* fail on notifications sent after #OnSessionEnd() is called, it is
14387 * expected by the caller */
14388 if (!directControl)
14389 return E_FAIL;
14390
14391 /* No locks should be held at this point. */
14392 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14393 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14394
14395 return directControl->OnUSBDeviceDetach(aId, aError);
14396}
14397
14398// protected methods
14399/////////////////////////////////////////////////////////////////////////////
14400
14401/**
14402 * Deletes the given file if it is no longer in use by either the current machine state
14403 * (if the machine is "saved") or any of the machine's snapshots.
14404 *
14405 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14406 * but is different for each SnapshotMachine. When calling this, the order of calling this
14407 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14408 * is therefore critical. I know, it's all rather messy.
14409 *
14410 * @param strStateFile
14411 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14412 * the test for whether the saved state file is in use.
14413 */
14414void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14415 Snapshot *pSnapshotToIgnore)
14416{
14417 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14418 if ( (strStateFile.isNotEmpty())
14419 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14420 )
14421 // ... and it must also not be shared with other snapshots
14422 if ( !mData->mFirstSnapshot
14423 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14424 // this checks the SnapshotMachine's state file paths
14425 )
14426 RTFileDelete(strStateFile.c_str());
14427}
14428
14429/**
14430 * Locks the attached media.
14431 *
14432 * All attached hard disks are locked for writing and DVD/floppy are locked for
14433 * reading. Parents of attached hard disks (if any) are locked for reading.
14434 *
14435 * This method also performs accessibility check of all media it locks: if some
14436 * media is inaccessible, the method will return a failure and a bunch of
14437 * extended error info objects per each inaccessible medium.
14438 *
14439 * Note that this method is atomic: if it returns a success, all media are
14440 * locked as described above; on failure no media is locked at all (all
14441 * succeeded individual locks will be undone).
14442 *
14443 * The caller is responsible for doing the necessary state sanity checks.
14444 *
14445 * The locks made by this method must be undone by calling #unlockMedia() when
14446 * no more needed.
14447 */
14448HRESULT SessionMachine::i_lockMedia()
14449{
14450 AutoCaller autoCaller(this);
14451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14452
14453 AutoMultiWriteLock2 alock(this->lockHandle(),
14454 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14455
14456 /* bail out if trying to lock things with already set up locking */
14457 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14458
14459 MultiResult mrc(S_OK);
14460
14461 /* Collect locking information for all medium objects attached to the VM. */
14462 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14463 it != mMediaData->mAttachments.end();
14464 ++it)
14465 {
14466 MediumAttachment* pAtt = *it;
14467 DeviceType_T devType = pAtt->i_getType();
14468 Medium *pMedium = pAtt->i_getMedium();
14469
14470 MediumLockList *pMediumLockList(new MediumLockList());
14471 // There can be attachments without a medium (floppy/dvd), and thus
14472 // it's impossible to create a medium lock list. It still makes sense
14473 // to have the empty medium lock list in the map in case a medium is
14474 // attached later.
14475 if (pMedium != NULL)
14476 {
14477 MediumType_T mediumType = pMedium->i_getType();
14478 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14479 || mediumType == MediumType_Shareable;
14480 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14481
14482 alock.release();
14483 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14484 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14485 false /* fMediumLockWriteAll */,
14486 NULL,
14487 *pMediumLockList);
14488 alock.acquire();
14489 if (FAILED(mrc))
14490 {
14491 delete pMediumLockList;
14492 mData->mSession.mLockedMedia.Clear();
14493 break;
14494 }
14495 }
14496
14497 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14498 if (FAILED(rc))
14499 {
14500 mData->mSession.mLockedMedia.Clear();
14501 mrc = setError(rc,
14502 tr("Collecting locking information for all attached media failed"));
14503 break;
14504 }
14505 }
14506
14507 if (SUCCEEDED(mrc))
14508 {
14509 /* Now lock all media. If this fails, nothing is locked. */
14510 alock.release();
14511 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14512 alock.acquire();
14513 if (FAILED(rc))
14514 {
14515 mrc = setError(rc,
14516 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14517 }
14518 }
14519
14520 return mrc;
14521}
14522
14523/**
14524 * Undoes the locks made by by #lockMedia().
14525 */
14526HRESULT SessionMachine::i_unlockMedia()
14527{
14528 AutoCaller autoCaller(this);
14529 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14530
14531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14532
14533 /* we may be holding important error info on the current thread;
14534 * preserve it */
14535 ErrorInfoKeeper eik;
14536
14537 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14538 AssertComRC(rc);
14539 return rc;
14540}
14541
14542/**
14543 * Helper to change the machine state (reimplementation).
14544 *
14545 * @note Locks this object for writing.
14546 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14547 * it can cause crashes in random places due to unexpectedly committing
14548 * the current settings. The caller is responsible for that. The call
14549 * to saveStateSettings is fine, because this method does not commit.
14550 */
14551HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14552{
14553 LogFlowThisFuncEnter();
14554 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14555
14556 AutoCaller autoCaller(this);
14557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14558
14559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14560
14561 MachineState_T oldMachineState = mData->mMachineState;
14562
14563 AssertMsgReturn(oldMachineState != aMachineState,
14564 ("oldMachineState=%s, aMachineState=%s\n",
14565 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14566 E_FAIL);
14567
14568 HRESULT rc = S_OK;
14569
14570 int stsFlags = 0;
14571 bool deleteSavedState = false;
14572
14573 /* detect some state transitions */
14574
14575 if ( ( oldMachineState == MachineState_Saved
14576 && aMachineState == MachineState_Restoring)
14577 || ( ( oldMachineState == MachineState_PoweredOff
14578 || oldMachineState == MachineState_Teleported
14579 || oldMachineState == MachineState_Aborted
14580 )
14581 && ( aMachineState == MachineState_TeleportingIn
14582 || aMachineState == MachineState_Starting
14583 )
14584 )
14585 )
14586 {
14587 /* The EMT thread is about to start */
14588
14589 /* Nothing to do here for now... */
14590
14591 /// @todo NEWMEDIA don't let mDVDDrive and other children
14592 /// change anything when in the Starting/Restoring state
14593 }
14594 else if ( ( oldMachineState == MachineState_Running
14595 || oldMachineState == MachineState_Paused
14596 || oldMachineState == MachineState_Teleporting
14597 || oldMachineState == MachineState_OnlineSnapshotting
14598 || oldMachineState == MachineState_LiveSnapshotting
14599 || oldMachineState == MachineState_Stuck
14600 || oldMachineState == MachineState_Starting
14601 || oldMachineState == MachineState_Stopping
14602 || oldMachineState == MachineState_Saving
14603 || oldMachineState == MachineState_Restoring
14604 || oldMachineState == MachineState_TeleportingPausedVM
14605 || oldMachineState == MachineState_TeleportingIn
14606 )
14607 && ( aMachineState == MachineState_PoweredOff
14608 || aMachineState == MachineState_Saved
14609 || aMachineState == MachineState_Teleported
14610 || aMachineState == MachineState_Aborted
14611 )
14612 )
14613 {
14614 /* The EMT thread has just stopped, unlock attached media. Note that as
14615 * opposed to locking that is done from Console, we do unlocking here
14616 * because the VM process may have aborted before having a chance to
14617 * properly unlock all media it locked. */
14618
14619 unlockMedia();
14620 }
14621
14622 if (oldMachineState == MachineState_Restoring)
14623 {
14624 if (aMachineState != MachineState_Saved)
14625 {
14626 /*
14627 * delete the saved state file once the machine has finished
14628 * restoring from it (note that Console sets the state from
14629 * Restoring to Saved if the VM couldn't restore successfully,
14630 * to give the user an ability to fix an error and retry --
14631 * we keep the saved state file in this case)
14632 */
14633 deleteSavedState = true;
14634 }
14635 }
14636 else if ( oldMachineState == MachineState_Saved
14637 && ( aMachineState == MachineState_PoweredOff
14638 || aMachineState == MachineState_Aborted
14639 || aMachineState == MachineState_Teleported
14640 )
14641 )
14642 {
14643 /*
14644 * delete the saved state after SessionMachine::ForgetSavedState() is called
14645 * or if the VM process (owning a direct VM session) crashed while the
14646 * VM was Saved
14647 */
14648
14649 /// @todo (dmik)
14650 // Not sure that deleting the saved state file just because of the
14651 // client death before it attempted to restore the VM is a good
14652 // thing. But when it crashes we need to go to the Aborted state
14653 // which cannot have the saved state file associated... The only
14654 // way to fix this is to make the Aborted condition not a VM state
14655 // but a bool flag: i.e., when a crash occurs, set it to true and
14656 // change the state to PoweredOff or Saved depending on the
14657 // saved state presence.
14658
14659 deleteSavedState = true;
14660 mData->mCurrentStateModified = TRUE;
14661 stsFlags |= SaveSTS_CurStateModified;
14662 }
14663
14664 if ( aMachineState == MachineState_Starting
14665 || aMachineState == MachineState_Restoring
14666 || aMachineState == MachineState_TeleportingIn
14667 )
14668 {
14669 /* set the current state modified flag to indicate that the current
14670 * state is no more identical to the state in the
14671 * current snapshot */
14672 if (!mData->mCurrentSnapshot.isNull())
14673 {
14674 mData->mCurrentStateModified = TRUE;
14675 stsFlags |= SaveSTS_CurStateModified;
14676 }
14677 }
14678
14679 if (deleteSavedState)
14680 {
14681 if (mRemoveSavedState)
14682 {
14683 Assert(!mSSData->strStateFilePath.isEmpty());
14684
14685 // it is safe to delete the saved state file if ...
14686 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14687 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14688 // ... none of the snapshots share the saved state file
14689 )
14690 RTFileDelete(mSSData->strStateFilePath.c_str());
14691 }
14692
14693 mSSData->strStateFilePath.setNull();
14694 stsFlags |= SaveSTS_StateFilePath;
14695 }
14696
14697 /* redirect to the underlying peer machine */
14698 mPeer->i_setMachineState(aMachineState);
14699
14700 if ( oldMachineState != MachineState_RestoringSnapshot
14701 && ( aMachineState == MachineState_PoweredOff
14702 || aMachineState == MachineState_Teleported
14703 || aMachineState == MachineState_Aborted
14704 || aMachineState == MachineState_Saved))
14705 {
14706 /* the machine has stopped execution
14707 * (or the saved state file was adopted) */
14708 stsFlags |= SaveSTS_StateTimeStamp;
14709 }
14710
14711 if ( ( oldMachineState == MachineState_PoweredOff
14712 || oldMachineState == MachineState_Aborted
14713 || oldMachineState == MachineState_Teleported
14714 )
14715 && aMachineState == MachineState_Saved)
14716 {
14717 /* the saved state file was adopted */
14718 Assert(!mSSData->strStateFilePath.isEmpty());
14719 stsFlags |= SaveSTS_StateFilePath;
14720 }
14721
14722#ifdef VBOX_WITH_GUEST_PROPS
14723 if ( aMachineState == MachineState_PoweredOff
14724 || aMachineState == MachineState_Aborted
14725 || aMachineState == MachineState_Teleported)
14726 {
14727 /* Make sure any transient guest properties get removed from the
14728 * property store on shutdown. */
14729 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14730
14731 /* remove it from the settings representation */
14732 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14733 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14734 it != llGuestProperties.end();
14735 /*nothing*/)
14736 {
14737 const settings::GuestProperty &prop = *it;
14738 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14739 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14740 {
14741 it = llGuestProperties.erase(it);
14742 fNeedsSaving = true;
14743 }
14744 else
14745 {
14746 ++it;
14747 }
14748 }
14749
14750 /* Additionally remove it from the HWData representation. Required to
14751 * keep everything in sync, as this is what the API keeps using. */
14752 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14753 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14754 it != llHWGuestProperties.end();
14755 /*nothing*/)
14756 {
14757 uint32_t fFlags = it->second.mFlags;
14758 if ( fFlags & guestProp::TRANSIENT
14759 || fFlags & guestProp::TRANSRESET)
14760 {
14761 /* iterator where we need to continue after the erase call
14762 * (C++03 is a fact still, and it doesn't return the iterator
14763 * which would allow continuing) */
14764 HWData::GuestPropertyMap::iterator it2 = it;
14765 ++it2;
14766 llHWGuestProperties.erase(it);
14767 it = it2;
14768 fNeedsSaving = true;
14769 }
14770 else
14771 {
14772 ++it;
14773 }
14774 }
14775
14776 if (fNeedsSaving)
14777 {
14778 mData->mCurrentStateModified = TRUE;
14779 stsFlags |= SaveSTS_CurStateModified;
14780 }
14781 }
14782#endif /* VBOX_WITH_GUEST_PROPS */
14783
14784 rc = i_saveStateSettings(stsFlags);
14785
14786 if ( ( oldMachineState != MachineState_PoweredOff
14787 && oldMachineState != MachineState_Aborted
14788 && oldMachineState != MachineState_Teleported
14789 )
14790 && ( aMachineState == MachineState_PoweredOff
14791 || aMachineState == MachineState_Aborted
14792 || aMachineState == MachineState_Teleported
14793 )
14794 )
14795 {
14796 /* we've been shut down for any reason */
14797 /* no special action so far */
14798 }
14799
14800 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14801 LogFlowThisFuncLeave();
14802 return rc;
14803}
14804
14805/**
14806 * Sends the current machine state value to the VM process.
14807 *
14808 * @note Locks this object for reading, then calls a client process.
14809 */
14810HRESULT SessionMachine::i_updateMachineStateOnClient()
14811{
14812 AutoCaller autoCaller(this);
14813 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14814
14815 ComPtr<IInternalSessionControl> directControl;
14816 {
14817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14818 AssertReturn(!!mData, E_FAIL);
14819 if (mData->mSession.mLockType == LockType_VM)
14820 directControl = mData->mSession.mDirectControl;
14821
14822 /* directControl may be already set to NULL here in #OnSessionEnd()
14823 * called too early by the direct session process while there is still
14824 * some operation (like deleting the snapshot) in progress. The client
14825 * process in this case is waiting inside Session::close() for the
14826 * "end session" process object to complete, while #uninit() called by
14827 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14828 * operation to complete. For now, we accept this inconsistent behavior
14829 * and simply do nothing here. */
14830
14831 if (mData->mSession.mState == SessionState_Unlocking)
14832 return S_OK;
14833 }
14834
14835 /* ignore notifications sent after #OnSessionEnd() is called */
14836 if (!directControl)
14837 return S_OK;
14838
14839 return directControl->UpdateMachineState(mData->mMachineState);
14840}
14841
14842
14843/*static*/
14844HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14845{
14846 va_list args;
14847 va_start(args, pcszMsg);
14848 HRESULT rc = setErrorInternal(aResultCode,
14849 getStaticClassIID(),
14850 getStaticComponentName(),
14851 Utf8Str(pcszMsg, args),
14852 false /* aWarning */,
14853 true /* aLogIt */);
14854 va_end(args);
14855 return rc;
14856}
14857
14858
14859HRESULT Machine::updateState(MachineState_T aState)
14860{
14861 NOREF(aState);
14862 ReturnComNotImplemented();
14863}
14864
14865HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14866{
14867 NOREF(aProgress);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::endPowerUp(LONG aResult)
14872{
14873 NOREF(aResult);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14878{
14879 NOREF(aProgress);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::endPoweringDown(LONG aResult,
14884 const com::Utf8Str &aErrMsg)
14885{
14886 NOREF(aResult);
14887 NOREF(aErrMsg);
14888 ReturnComNotImplemented();
14889}
14890
14891HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14892 BOOL *aMatched,
14893 ULONG *aMaskedInterfaces)
14894{
14895 NOREF(aDevice);
14896 NOREF(aMatched);
14897 NOREF(aMaskedInterfaces);
14898 ReturnComNotImplemented();
14899
14900}
14901
14902HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14903{
14904 NOREF(aId); NOREF(aCaptureFilename);
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14909 BOOL aDone)
14910{
14911 NOREF(aId);
14912 NOREF(aDone);
14913 ReturnComNotImplemented();
14914}
14915
14916HRESULT Machine::autoCaptureUSBDevices()
14917{
14918 ReturnComNotImplemented();
14919}
14920
14921HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14922{
14923 NOREF(aDone);
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14928 ComPtr<IProgress> &aProgress)
14929{
14930 NOREF(aSession);
14931 NOREF(aProgress);
14932 ReturnComNotImplemented();
14933}
14934
14935HRESULT Machine::finishOnlineMergeMedium()
14936{
14937 ReturnComNotImplemented();
14938}
14939
14940HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14941 std::vector<com::Utf8Str> &aValues,
14942 std::vector<LONG64> &aTimestamps,
14943 std::vector<com::Utf8Str> &aFlags)
14944{
14945 NOREF(aNames);
14946 NOREF(aValues);
14947 NOREF(aTimestamps);
14948 NOREF(aFlags);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14953 const com::Utf8Str &aValue,
14954 LONG64 aTimestamp,
14955 const com::Utf8Str &aFlags)
14956{
14957 NOREF(aName);
14958 NOREF(aValue);
14959 NOREF(aTimestamp);
14960 NOREF(aFlags);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::lockMedia()
14965{
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::unlockMedia()
14970{
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14975 ComPtr<IMediumAttachment> &aNewAttachment)
14976{
14977 NOREF(aAttachment);
14978 NOREF(aNewAttachment);
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14983 ULONG aCpuUser,
14984 ULONG aCpuKernel,
14985 ULONG aCpuIdle,
14986 ULONG aMemTotal,
14987 ULONG aMemFree,
14988 ULONG aMemBalloon,
14989 ULONG aMemShared,
14990 ULONG aMemCache,
14991 ULONG aPagedTotal,
14992 ULONG aMemAllocTotal,
14993 ULONG aMemFreeTotal,
14994 ULONG aMemBalloonTotal,
14995 ULONG aMemSharedTotal,
14996 ULONG aVmNetRx,
14997 ULONG aVmNetTx)
14998{
14999 NOREF(aValidStats);
15000 NOREF(aCpuUser);
15001 NOREF(aCpuKernel);
15002 NOREF(aCpuIdle);
15003 NOREF(aMemTotal);
15004 NOREF(aMemFree);
15005 NOREF(aMemBalloon);
15006 NOREF(aMemShared);
15007 NOREF(aMemCache);
15008 NOREF(aPagedTotal);
15009 NOREF(aMemAllocTotal);
15010 NOREF(aMemFreeTotal);
15011 NOREF(aMemBalloonTotal);
15012 NOREF(aMemSharedTotal);
15013 NOREF(aVmNetRx);
15014 NOREF(aVmNetTx);
15015 ReturnComNotImplemented();
15016}
15017
15018HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15019 com::Utf8Str &aResult)
15020{
15021 NOREF(aAuthParams);
15022 NOREF(aResult);
15023 ReturnComNotImplemented();
15024}
15025
15026HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15027{
15028 NOREF(aFlags);
15029 ReturnComNotImplemented();
15030}
15031
15032/* This isn't handled entirely by the wrapper generator yet. */
15033#ifdef VBOX_WITH_XPCOM
15034NS_DECL_CLASSINFO(SessionMachine)
15035NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15036
15037NS_DECL_CLASSINFO(SnapshotMachine)
15038NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15039#endif
Note: See TracBrowser for help on using the repository browser.

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