VirtualBox

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

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

Unattended,Machine: Create Unattended instance on-demand from the IMachine getter. This eliminates Unattended::initWithUnattended.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 519.2 KB
Line 
1/* $Id: MachineImpl.cpp 67730 2017-06-30 12:42:20Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
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 class
232/////////////////////////////////////////////////////////////////////////////
233
234// constructor / destructor
235/////////////////////////////////////////////////////////////////////////////
236
237Machine::Machine() :
238#ifdef VBOX_WITH_RESOURCE_USAGE_API
239 mCollectorGuest(NULL),
240#endif
241 mPeer(NULL),
242 mParent(NULL),
243 mSerialPorts(),
244 mParallelPorts(),
245 uRegistryNeedsSaving(0)
246{}
247
248Machine::~Machine()
249{}
250
251HRESULT Machine::FinalConstruct()
252{
253 LogFlowThisFunc(("\n"));
254 return BaseFinalConstruct();
255}
256
257void Machine::FinalRelease()
258{
259 LogFlowThisFunc(("\n"));
260 uninit();
261 BaseFinalRelease();
262}
263
264/**
265 * Initializes a new machine instance; this init() variant creates a new, empty machine.
266 * This gets called from VirtualBox::CreateMachine().
267 *
268 * @param aParent Associated parent object
269 * @param strConfigFile Local file system path to the VM settings file (can
270 * be relative to the VirtualBox config directory).
271 * @param strName name for the machine
272 * @param llGroups list of groups for the machine
273 * @param aOsType OS Type of this machine or NULL.
274 * @param aId UUID for the new machine.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
277 * scheme (includes the UUID).
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 GuestOSType *aOsType,
286 const Guid &aId,
287 bool fForceOverwrite,
288 bool fDirectoryIncludesUUID)
289{
290 LogFlowThisFuncEnter();
291 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
292
293 /* Enclose the state transition NotReady->InInit->Ready */
294 AutoInitSpan autoInitSpan(this);
295 AssertReturn(autoInitSpan.isOk(), E_FAIL);
296
297 HRESULT rc = initImpl(aParent, strConfigFile);
298 if (FAILED(rc)) return rc;
299
300 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
301 if (FAILED(rc)) return rc;
302
303 if (SUCCEEDED(rc))
304 {
305 // create an empty machine config
306 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
307
308 rc = initDataAndChildObjects();
309 }
310
311 if (SUCCEEDED(rc))
312 {
313 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
314 mData->mAccessible = TRUE;
315
316 unconst(mData->mUuid) = aId;
317
318 mUserData->s.strName = strName;
319
320 mUserData->s.llGroups = llGroups;
321
322 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
323 // the "name sync" flag determines whether the machine directory gets renamed along
324 // with the machine file; say so if the settings file name is the same as the
325 // settings file parent directory (machine directory)
326 mUserData->s.fNameSync = i_isInOwnDir();
327
328 // initialize the default snapshots folder
329 rc = COMSETTER(SnapshotFolder)(NULL);
330 AssertComRC(rc);
331
332 if (aOsType)
333 {
334 /* Store OS type */
335 mUserData->s.strOsType = aOsType->i_id();
336
337 /* Apply BIOS defaults */
338 mBIOSSettings->i_applyDefaults(aOsType);
339
340 /* Let the OS type select 64-bit ness. */
341 mHWData->mLongMode = aOsType->i_is64Bit()
342 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
343
344 /* Let the OS type enable the X2APIC */
345 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
346 }
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Apply parallel port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
358 mParallelPorts[slot]->i_applyDefaults();
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param strConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param aParent Associated parent object.
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->i_getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = i_loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 i_allowStateModification();
565
566 /* commit all changes made during the initialization */
567 i_commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 /* Ignore all errors from unregistering, they would destroy
579- * the more interesting error information we already have,
580- * pinpointing the issue with the VM config. */
581 ErrorInfoKeeper eik;
582
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->i_unregisterMachineMedia(i_getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent The VirtualBox object.
604 * @param strConfigFile Settings file.
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it has a
831 * VirtualBox caller for the duration of the
832 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 Log1WarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from i_registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator
1048 it = mUserData->s.llGroups.begin();
1049 it != mUserData->s.llGroups.end();
1050 ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComObjPtr<GuestOSType> pGuestOSType;
1088 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1089 pGuestOSType);
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Utf8Str osTypeId = pGuestOSType->i_id();
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 aParavirtDebug = mHWData->mParavirtDebug;
1223 return S_OK;
1224}
1225
1226HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1227{
1228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1229
1230 HRESULT rc = i_checkStateDependency(MutableStateDep);
1231 if (FAILED(rc)) return rc;
1232
1233 /** @todo Parse/validate options? */
1234 if (aParavirtDebug != mHWData->mParavirtDebug)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtDebug = aParavirtDebug;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249
1250 return S_OK;
1251}
1252
1253HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1254{
1255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 HRESULT rc = i_checkStateDependency(MutableStateDep);
1258 if (FAILED(rc)) return rc;
1259
1260 if (aParavirtProvider != mHWData->mParavirtProvider)
1261 {
1262 i_setModified(IsModified_MachineData);
1263 mHWData.backup();
1264 mHWData->mParavirtProvider = aParavirtProvider;
1265 }
1266
1267 return S_OK;
1268}
1269
1270HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1271{
1272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 *aParavirtProvider = mHWData->mParavirtProvider;
1275 switch (mHWData->mParavirtProvider)
1276 {
1277 case ParavirtProvider_None:
1278 case ParavirtProvider_HyperV:
1279 case ParavirtProvider_KVM:
1280 case ParavirtProvider_Minimal:
1281 break;
1282
1283 /* Resolve dynamic provider types to the effective types. */
1284 default:
1285 {
1286 ComObjPtr<GuestOSType> pGuestOSType;
1287 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1288 pGuestOSType);
1289 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1290
1291 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1292 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1293
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_Legacy:
1297 {
1298 if (fOsXGuest)
1299 *aParavirtProvider = ParavirtProvider_Minimal;
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304
1305 case ParavirtProvider_Default:
1306 {
1307 if (fOsXGuest)
1308 *aParavirtProvider = ParavirtProvider_Minimal;
1309 else if ( mUserData->s.strOsType == "Windows10"
1310 || mUserData->s.strOsType == "Windows10_64"
1311 || mUserData->s.strOsType == "Windows81"
1312 || mUserData->s.strOsType == "Windows81_64"
1313 || mUserData->s.strOsType == "Windows8"
1314 || mUserData->s.strOsType == "Windows8_64"
1315 || mUserData->s.strOsType == "Windows7"
1316 || mUserData->s.strOsType == "Windows7_64"
1317 || mUserData->s.strOsType == "WindowsVista"
1318 || mUserData->s.strOsType == "WindowsVista_64"
1319 || mUserData->s.strOsType == "Windows2012"
1320 || mUserData->s.strOsType == "Windows2012_64"
1321 || mUserData->s.strOsType == "Windows2008"
1322 || mUserData->s.strOsType == "Windows2008_64")
1323 {
1324 *aParavirtProvider = ParavirtProvider_HyperV;
1325 }
1326 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1327 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1328 || mUserData->s.strOsType == "Linux"
1329 || mUserData->s.strOsType == "Linux_64"
1330 || mUserData->s.strOsType == "ArchLinux"
1331 || mUserData->s.strOsType == "ArchLinux_64"
1332 || mUserData->s.strOsType == "Debian"
1333 || mUserData->s.strOsType == "Debian_64"
1334 || mUserData->s.strOsType == "Fedora"
1335 || mUserData->s.strOsType == "Fedora_64"
1336 || mUserData->s.strOsType == "Gentoo"
1337 || mUserData->s.strOsType == "Gentoo_64"
1338 || mUserData->s.strOsType == "Mandriva"
1339 || mUserData->s.strOsType == "Mandriva_64"
1340 || mUserData->s.strOsType == "OpenSUSE"
1341 || mUserData->s.strOsType == "OpenSUSE_64"
1342 || mUserData->s.strOsType == "Oracle"
1343 || mUserData->s.strOsType == "Oracle_64"
1344 || mUserData->s.strOsType == "RedHat"
1345 || mUserData->s.strOsType == "RedHat_64"
1346 || mUserData->s.strOsType == "Turbolinux"
1347 || mUserData->s.strOsType == "Turbolinux_64"
1348 || mUserData->s.strOsType == "Ubuntu"
1349 || mUserData->s.strOsType == "Ubuntu_64"
1350 || mUserData->s.strOsType == "Xandros"
1351 || mUserData->s.strOsType == "Xandros_64")
1352 {
1353 *aParavirtProvider = ParavirtProvider_KVM;
1354 }
1355 else
1356 *aParavirtProvider = ParavirtProvider_None;
1357 break;
1358 }
1359
1360 default: AssertFailedBreak(); /* Shut up MSC. */
1361 }
1362 break;
1363 }
1364 }
1365
1366 Assert( *aParavirtProvider == ParavirtProvider_None
1367 || *aParavirtProvider == ParavirtProvider_Minimal
1368 || *aParavirtProvider == ParavirtProvider_HyperV
1369 || *aParavirtProvider == ParavirtProvider_KVM);
1370 return S_OK;
1371}
1372
1373HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1374{
1375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1376
1377 aHardwareVersion = mHWData->mHWVersion;
1378
1379 return S_OK;
1380}
1381
1382HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1383{
1384 /* check known version */
1385 Utf8Str hwVersion = aHardwareVersion;
1386 if ( hwVersion.compare("1") != 0
1387 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1388 return setError(E_INVALIDARG,
1389 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1390
1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 HRESULT rc = i_checkStateDependency(MutableStateDep);
1394 if (FAILED(rc)) return rc;
1395
1396 i_setModified(IsModified_MachineData);
1397 mHWData.backup();
1398 mHWData->mHWVersion = aHardwareVersion;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1404{
1405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 if (!mHWData->mHardwareUUID.isZero())
1408 aHardwareUUID = mHWData->mHardwareUUID;
1409 else
1410 aHardwareUUID = mData->mUuid;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1416{
1417 if (!aHardwareUUID.isValid())
1418 return E_INVALIDARG;
1419
1420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 HRESULT rc = i_checkStateDependency(MutableStateDep);
1423 if (FAILED(rc)) return rc;
1424
1425 i_setModified(IsModified_MachineData);
1426 mHWData.backup();
1427 if (aHardwareUUID == mData->mUuid)
1428 mHWData->mHardwareUUID.clear();
1429 else
1430 mHWData->mHardwareUUID = aHardwareUUID;
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 *aMemorySize = mHWData->mMemorySize;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setMemorySize(ULONG aMemorySize)
1445{
1446 /* check RAM limits */
1447 if ( aMemorySize < MM_RAM_MIN_IN_MB
1448 || aMemorySize > MM_RAM_MAX_IN_MB
1449 )
1450 return setError(E_INVALIDARG,
1451 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1452 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 HRESULT rc = i_checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 i_setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mMemorySize = aMemorySize;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1467{
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 *aCPUCount = mHWData->mCPUCount;
1471
1472 return S_OK;
1473}
1474
1475HRESULT Machine::setCPUCount(ULONG aCPUCount)
1476{
1477 /* check CPU limits */
1478 if ( aCPUCount < SchemaDefs::MinCPUCount
1479 || aCPUCount > SchemaDefs::MaxCPUCount
1480 )
1481 return setError(E_INVALIDARG,
1482 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1483 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1488 if (mHWData->mCPUHotPlugEnabled)
1489 {
1490 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1491 {
1492 if (mHWData->mCPUAttached[idx])
1493 return setError(E_INVALIDARG,
1494 tr("There is still a CPU attached to socket %lu."
1495 "Detach the CPU before removing the socket"),
1496 aCPUCount, idx+1);
1497 }
1498 }
1499
1500 HRESULT rc = i_checkStateDependency(MutableStateDep);
1501 if (FAILED(rc)) return rc;
1502
1503 i_setModified(IsModified_MachineData);
1504 mHWData.backup();
1505 mHWData->mCPUCount = aCPUCount;
1506
1507 return S_OK;
1508}
1509
1510HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1511{
1512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1513
1514 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1515
1516 return S_OK;
1517}
1518
1519HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1520{
1521 HRESULT rc = S_OK;
1522
1523 /* check throttle limits */
1524 if ( aCPUExecutionCap < 1
1525 || aCPUExecutionCap > 100
1526 )
1527 return setError(E_INVALIDARG,
1528 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1529 aCPUExecutionCap, 1, 100);
1530
1531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 alock.release();
1534 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1535 alock.acquire();
1536 if (FAILED(rc)) return rc;
1537
1538 i_setModified(IsModified_MachineData);
1539 mHWData.backup();
1540 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1541
1542 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1543 if (Global::IsOnline(mData->mMachineState))
1544 i_saveSettings(NULL);
1545
1546 return S_OK;
1547}
1548
1549HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1550{
1551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1554
1555 return S_OK;
1556}
1557
1558HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1559{
1560 HRESULT rc = S_OK;
1561
1562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 rc = i_checkStateDependency(MutableStateDep);
1565 if (FAILED(rc)) return rc;
1566
1567 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1568 {
1569 if (aCPUHotPlugEnabled)
1570 {
1571 i_setModified(IsModified_MachineData);
1572 mHWData.backup();
1573
1574 /* Add the amount of CPUs currently attached */
1575 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1576 mHWData->mCPUAttached[i] = true;
1577 }
1578 else
1579 {
1580 /*
1581 * We can disable hotplug only if the amount of maximum CPUs is equal
1582 * to the amount of attached CPUs
1583 */
1584 unsigned cCpusAttached = 0;
1585 unsigned iHighestId = 0;
1586
1587 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1588 {
1589 if (mHWData->mCPUAttached[i])
1590 {
1591 cCpusAttached++;
1592 iHighestId = i;
1593 }
1594 }
1595
1596 if ( (cCpusAttached != mHWData->mCPUCount)
1597 || (iHighestId >= mHWData->mCPUCount))
1598 return setError(E_INVALIDARG,
1599 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1600
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 }
1604 }
1605
1606 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1607
1608 return rc;
1609}
1610
1611HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1621{
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1625 if (SUCCEEDED(hrc))
1626 {
1627 i_setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1630 }
1631 return hrc;
1632}
1633
1634HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637 aCPUProfile = mHWData->mCpuProfile;
1638 return S_OK;
1639}
1640
1641HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1642{
1643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1644 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1645 if (SUCCEEDED(hrc))
1646 {
1647 i_setModified(IsModified_MachineData);
1648 mHWData.backup();
1649 /* Empty equals 'host'. */
1650 if (aCPUProfile.isNotEmpty())
1651 mHWData->mCpuProfile = aCPUProfile;
1652 else
1653 mHWData->mCpuProfile = "host";
1654 }
1655 return hrc;
1656}
1657
1658HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1659{
1660#ifdef VBOX_WITH_USB_CARDREADER
1661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(aEmulatedUSBCardReaderEnabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1673{
1674#ifdef VBOX_WITH_USB_CARDREADER
1675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1678 if (FAILED(rc)) return rc;
1679
1680 i_setModified(IsModified_MachineData);
1681 mHWData.backup();
1682 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1683
1684 return S_OK;
1685#else
1686 NOREF(aEmulatedUSBCardReaderEnabled);
1687 return E_NOTIMPL;
1688#endif
1689}
1690
1691HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 *aHPETEnabled = mHWData->mHPETEnabled;
1696
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1701{
1702 HRESULT rc = S_OK;
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 rc = i_checkStateDependency(MutableStateDep);
1707 if (FAILED(rc)) return rc;
1708
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711
1712 mHWData->mHPETEnabled = aHPETEnabled;
1713
1714 return rc;
1715}
1716
1717HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1718{
1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1726{
1727 HRESULT rc = S_OK;
1728
1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 i_setModified(IsModified_MachineData);
1732 mHWData.backup();
1733 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1734
1735 alock.release();
1736 rc = i_onVideoCaptureChange();
1737 alock.acquire();
1738 if (FAILED(rc))
1739 {
1740 /*
1741 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1742 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1743 * determine if it should start or stop capturing. Therefore we need to manually
1744 * undo change.
1745 */
1746 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1747 return rc;
1748 }
1749
1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1751 if (Global::IsOnline(mData->mMachineState))
1752 i_saveSettings(NULL);
1753
1754 return rc;
1755}
1756
1757HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1758{
1759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1760 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1761 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1762 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1763 return S_OK;
1764}
1765
1766HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1767{
1768 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1769 bool fChanged = false;
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1774 {
1775 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1776 {
1777 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1778 fChanged = true;
1779 }
1780 }
1781 if (fChanged)
1782 {
1783 alock.release();
1784 HRESULT rc = i_onVideoCaptureChange();
1785 alock.acquire();
1786 if (FAILED(rc)) return rc;
1787 i_setModified(IsModified_MachineData);
1788
1789 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1790 if (Global::IsOnline(mData->mMachineState))
1791 i_saveSettings(NULL);
1792 }
1793
1794 return S_OK;
1795}
1796
1797HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1798{
1799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1800 if (mHWData->mVideoCaptureFile.isEmpty())
1801 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1802 else
1803 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1808{
1809 Utf8Str strFile(aVideoCaptureFile);
1810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1811
1812 if ( Global::IsOnline(mData->mMachineState)
1813 && mHWData->mVideoCaptureEnabled)
1814 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1815
1816 if (!RTPathStartsWithRoot(strFile.c_str()))
1817 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1818
1819 if (!strFile.isEmpty())
1820 {
1821 Utf8Str defaultFile;
1822 i_getDefaultVideoCaptureFile(defaultFile);
1823 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1824 strFile.setNull();
1825 }
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mVideoCaptureFile = strFile;
1830
1831 return S_OK;
1832}
1833
1834HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1838 return S_OK;
1839}
1840
1841HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1842{
1843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 if ( Global::IsOnline(mData->mMachineState)
1846 && mHWData->mVideoCaptureEnabled)
1847 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1852
1853 return S_OK;
1854}
1855
1856HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1857{
1858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1859 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1860 return S_OK;
1861}
1862
1863HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1864{
1865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 if ( Global::IsOnline(mData->mMachineState)
1868 && mHWData->mVideoCaptureEnabled)
1869 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1870
1871 i_setModified(IsModified_MachineData);
1872 mHWData.backup();
1873 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1879{
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1882 return S_OK;
1883}
1884
1885HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1886{
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 if ( Global::IsOnline(mData->mMachineState)
1890 && mHWData->mVideoCaptureEnabled)
1891 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1892
1893 i_setModified(IsModified_MachineData);
1894 mHWData.backup();
1895 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1896
1897 return S_OK;
1898}
1899
1900HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1901{
1902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1903 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1904 return S_OK;
1905}
1906
1907HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1908{
1909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
1911 if ( Global::IsOnline(mData->mMachineState)
1912 && mHWData->mVideoCaptureEnabled)
1913 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1914
1915 i_setModified(IsModified_MachineData);
1916 mHWData.backup();
1917 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1918
1919 return S_OK;
1920}
1921
1922HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1930{
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 if ( Global::IsOnline(mData->mMachineState)
1934 && mHWData->mVideoCaptureEnabled)
1935 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1936
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1940
1941 return S_OK;
1942}
1943
1944HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1945{
1946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1947 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1948 return S_OK;
1949}
1950
1951HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1952{
1953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 if ( Global::IsOnline(mData->mMachineState)
1956 && mHWData->mVideoCaptureEnabled)
1957 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1958
1959 i_setModified(IsModified_MachineData);
1960 mHWData.backup();
1961 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1962
1963 return S_OK;
1964}
1965
1966HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1967{
1968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1971 return S_OK;
1972}
1973
1974HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1975{
1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 if ( Global::IsOnline(mData->mMachineState)
1979 && mHWData->mVideoCaptureEnabled)
1980 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1981
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1999{
2000 switch (aGraphicsControllerType)
2001 {
2002 case GraphicsControllerType_Null:
2003 case GraphicsControllerType_VBoxVGA:
2004#ifdef VBOX_WITH_VMSVGA
2005 case GraphicsControllerType_VMSVGA:
2006#endif
2007 break;
2008 default:
2009 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2010 }
2011
2012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 HRESULT rc = i_checkStateDependency(MutableStateDep);
2015 if (FAILED(rc)) return rc;
2016
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2020
2021 return S_OK;
2022}
2023
2024HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2025{
2026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2027
2028 *aVRAMSize = mHWData->mVRAMSize;
2029
2030 return S_OK;
2031}
2032
2033HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2034{
2035 /* check VRAM limits */
2036 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2037 return setError(E_INVALIDARG,
2038 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2039 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2040
2041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 HRESULT rc = i_checkStateDependency(MutableStateDep);
2044 if (FAILED(rc)) return rc;
2045
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mVRAMSize = aVRAMSize;
2049
2050 return S_OK;
2051}
2052
2053/** @todo this method should not be public */
2054HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2055{
2056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2059
2060 return S_OK;
2061}
2062
2063/**
2064 * Set the memory balloon size.
2065 *
2066 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2067 * we have to make sure that we never call IGuest from here.
2068 */
2069HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2070{
2071 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2072#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2073 /* check limits */
2074 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2075 return setError(E_INVALIDARG,
2076 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2077 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 i_setModified(IsModified_MachineData);
2082 mHWData.backup();
2083 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2084
2085 return S_OK;
2086#else
2087 NOREF(aMemoryBalloonSize);
2088 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2089#endif
2090}
2091
2092HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2093{
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2097 return S_OK;
2098}
2099
2100HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2101{
2102#ifdef VBOX_WITH_PAGE_SHARING
2103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2104
2105 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2109 return S_OK;
2110#else
2111 NOREF(aPageFusionEnabled);
2112 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2113#endif
2114}
2115
2116HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2117{
2118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2126{
2127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 HRESULT rc = i_checkStateDependency(MutableStateDep);
2130 if (FAILED(rc)) return rc;
2131
2132 /** @todo check validity! */
2133
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2137
2138 return S_OK;
2139}
2140
2141
2142HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2143{
2144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2147
2148 return S_OK;
2149}
2150
2151HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2152{
2153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 HRESULT rc = i_checkStateDependency(MutableStateDep);
2156 if (FAILED(rc)) return rc;
2157
2158 /** @todo check validity! */
2159 i_setModified(IsModified_MachineData);
2160 mHWData.backup();
2161 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2167{
2168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 *aMonitorCount = mHWData->mMonitorCount;
2171
2172 return S_OK;
2173}
2174
2175HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2176{
2177 /* make sure monitor count is a sensible number */
2178 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2179 return setError(E_INVALIDARG,
2180 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2181 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2182
2183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 HRESULT rc = i_checkStateDependency(MutableStateDep);
2186 if (FAILED(rc)) return rc;
2187
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 mHWData->mMonitorCount = aMonitorCount;
2191
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2196{
2197 /* mBIOSSettings is constant during life time, no need to lock */
2198 aBIOSSettings = mBIOSSettings;
2199
2200 return S_OK;
2201}
2202
2203HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2204{
2205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 switch (aProperty)
2208 {
2209 case CPUPropertyType_PAE:
2210 *aValue = mHWData->mPAEEnabled;
2211 break;
2212
2213 case CPUPropertyType_LongMode:
2214 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2215 *aValue = TRUE;
2216 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2217 *aValue = FALSE;
2218#if HC_ARCH_BITS == 64
2219 else
2220 *aValue = TRUE;
2221#else
2222 else
2223 {
2224 *aValue = FALSE;
2225
2226 ComObjPtr<GuestOSType> pGuestOSType;
2227 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2228 pGuestOSType);
2229 if (SUCCEEDED(hrc2))
2230 {
2231 if (pGuestOSType->i_is64Bit())
2232 {
2233 ComObjPtr<Host> pHost = mParent->i_host();
2234 alock.release();
2235
2236 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2237 if (FAILED(hrc2))
2238 *aValue = FALSE;
2239 }
2240 }
2241 }
2242#endif
2243 break;
2244
2245 case CPUPropertyType_TripleFaultReset:
2246 *aValue = mHWData->mTripleFaultReset;
2247 break;
2248
2249 case CPUPropertyType_APIC:
2250 *aValue = mHWData->mAPIC;
2251 break;
2252
2253 case CPUPropertyType_X2APIC:
2254 *aValue = mHWData->mX2APIC;
2255 break;
2256
2257 default:
2258 return E_INVALIDARG;
2259 }
2260 return S_OK;
2261}
2262
2263HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2264{
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 HRESULT rc = i_checkStateDependency(MutableStateDep);
2268 if (FAILED(rc)) return rc;
2269
2270 switch (aProperty)
2271 {
2272 case CPUPropertyType_PAE:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mPAEEnabled = !!aValue;
2276 break;
2277
2278 case CPUPropertyType_LongMode:
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2282 break;
2283
2284 case CPUPropertyType_TripleFaultReset:
2285 i_setModified(IsModified_MachineData);
2286 mHWData.backup();
2287 mHWData->mTripleFaultReset = !!aValue;
2288 break;
2289
2290 case CPUPropertyType_APIC:
2291 if (mHWData->mX2APIC)
2292 aValue = TRUE;
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mAPIC = !!aValue;
2296 break;
2297
2298 case CPUPropertyType_X2APIC:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mX2APIC = !!aValue;
2302 if (aValue)
2303 mHWData->mAPIC = !!aValue;
2304 break;
2305
2306 default:
2307 return E_INVALIDARG;
2308 }
2309 return S_OK;
2310}
2311
2312HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2313{
2314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2315
2316 switch(aId)
2317 {
2318 case 0x0:
2319 case 0x1:
2320 case 0x2:
2321 case 0x3:
2322 case 0x4:
2323 case 0x5:
2324 case 0x6:
2325 case 0x7:
2326 case 0x8:
2327 case 0x9:
2328 case 0xA:
2329 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2330 return E_INVALIDARG;
2331
2332 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2333 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2334 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2335 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2336 break;
2337
2338 case 0x80000000:
2339 case 0x80000001:
2340 case 0x80000002:
2341 case 0x80000003:
2342 case 0x80000004:
2343 case 0x80000005:
2344 case 0x80000006:
2345 case 0x80000007:
2346 case 0x80000008:
2347 case 0x80000009:
2348 case 0x8000000A:
2349 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2350 return E_INVALIDARG;
2351
2352 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2353 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2354 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2355 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2356 break;
2357
2358 default:
2359 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2360 }
2361 return S_OK;
2362}
2363
2364
2365HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2366{
2367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 HRESULT rc = i_checkStateDependency(MutableStateDep);
2370 if (FAILED(rc)) return rc;
2371
2372 switch(aId)
2373 {
2374 case 0x0:
2375 case 0x1:
2376 case 0x2:
2377 case 0x3:
2378 case 0x4:
2379 case 0x5:
2380 case 0x6:
2381 case 0x7:
2382 case 0x8:
2383 case 0x9:
2384 case 0xA:
2385 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2386 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2390 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2391 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2392 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2393 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2394 break;
2395
2396 case 0x80000000:
2397 case 0x80000001:
2398 case 0x80000002:
2399 case 0x80000003:
2400 case 0x80000004:
2401 case 0x80000005:
2402 case 0x80000006:
2403 case 0x80000007:
2404 case 0x80000008:
2405 case 0x80000009:
2406 case 0x8000000A:
2407 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2408 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2409 i_setModified(IsModified_MachineData);
2410 mHWData.backup();
2411 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2412 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2413 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2414 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2416 break;
2417
2418 default:
2419 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2420 }
2421 return S_OK;
2422}
2423
2424HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2425{
2426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 HRESULT rc = i_checkStateDependency(MutableStateDep);
2429 if (FAILED(rc)) return rc;
2430
2431 switch(aId)
2432 {
2433 case 0x0:
2434 case 0x1:
2435 case 0x2:
2436 case 0x3:
2437 case 0x4:
2438 case 0x5:
2439 case 0x6:
2440 case 0x7:
2441 case 0x8:
2442 case 0x9:
2443 case 0xA:
2444 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2445 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2446 i_setModified(IsModified_MachineData);
2447 mHWData.backup();
2448 /* Invalidate leaf. */
2449 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2450 break;
2451
2452 case 0x80000000:
2453 case 0x80000001:
2454 case 0x80000002:
2455 case 0x80000003:
2456 case 0x80000004:
2457 case 0x80000005:
2458 case 0x80000006:
2459 case 0x80000007:
2460 case 0x80000008:
2461 case 0x80000009:
2462 case 0x8000000A:
2463 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2464 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2465 i_setModified(IsModified_MachineData);
2466 mHWData.backup();
2467 /* Invalidate leaf. */
2468 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2469 break;
2470
2471 default:
2472 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2473 }
2474 return S_OK;
2475}
2476
2477HRESULT Machine::removeAllCPUIDLeaves()
2478{
2479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2480
2481 HRESULT rc = i_checkStateDependency(MutableStateDep);
2482 if (FAILED(rc)) return rc;
2483
2484 i_setModified(IsModified_MachineData);
2485 mHWData.backup();
2486
2487 /* Invalidate all standard leafs. */
2488 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2489 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2490
2491 /* Invalidate all extended leafs. */
2492 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2493 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2494
2495 return S_OK;
2496}
2497HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2498{
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 switch(aProperty)
2502 {
2503 case HWVirtExPropertyType_Enabled:
2504 *aValue = mHWData->mHWVirtExEnabled;
2505 break;
2506
2507 case HWVirtExPropertyType_VPID:
2508 *aValue = mHWData->mHWVirtExVPIDEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_NestedPaging:
2512 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_UnrestrictedExecution:
2516 *aValue = mHWData->mHWVirtExUXEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_LargePages:
2520 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2521#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2522 *aValue = FALSE;
2523#endif
2524 break;
2525
2526 case HWVirtExPropertyType_Force:
2527 *aValue = mHWData->mHWVirtExForceEnabled;
2528 break;
2529
2530 default:
2531 return E_INVALIDARG;
2532 }
2533 return S_OK;
2534}
2535
2536HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2537{
2538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2539
2540 HRESULT rc = i_checkStateDependency(MutableStateDep);
2541 if (FAILED(rc)) return rc;
2542
2543 switch(aProperty)
2544 {
2545 case HWVirtExPropertyType_Enabled:
2546 i_setModified(IsModified_MachineData);
2547 mHWData.backup();
2548 mHWData->mHWVirtExEnabled = !!aValue;
2549 break;
2550
2551 case HWVirtExPropertyType_VPID:
2552 i_setModified(IsModified_MachineData);
2553 mHWData.backup();
2554 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2555 break;
2556
2557 case HWVirtExPropertyType_NestedPaging:
2558 i_setModified(IsModified_MachineData);
2559 mHWData.backup();
2560 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2561 break;
2562
2563 case HWVirtExPropertyType_UnrestrictedExecution:
2564 i_setModified(IsModified_MachineData);
2565 mHWData.backup();
2566 mHWData->mHWVirtExUXEnabled = !!aValue;
2567 break;
2568
2569 case HWVirtExPropertyType_LargePages:
2570 i_setModified(IsModified_MachineData);
2571 mHWData.backup();
2572 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2573 break;
2574
2575 case HWVirtExPropertyType_Force:
2576 i_setModified(IsModified_MachineData);
2577 mHWData.backup();
2578 mHWData->mHWVirtExForceEnabled = !!aValue;
2579 break;
2580
2581 default:
2582 return E_INVALIDARG;
2583 }
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2598{
2599 /** @todo (r=dmik):
2600 * 1. Allow to change the name of the snapshot folder containing snapshots
2601 * 2. Rename the folder on disk instead of just changing the property
2602 * value (to be smart and not to leave garbage). Note that it cannot be
2603 * done here because the change may be rolled back. Thus, the right
2604 * place is #saveSettings().
2605 */
2606
2607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 HRESULT rc = i_checkStateDependency(MutableStateDep);
2610 if (FAILED(rc)) return rc;
2611
2612 if (!mData->mCurrentSnapshot.isNull())
2613 return setError(E_FAIL,
2614 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2615
2616 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2617
2618 if (strSnapshotFolder.isEmpty())
2619 strSnapshotFolder = "Snapshots";
2620 int vrc = i_calculateFullPath(strSnapshotFolder,
2621 strSnapshotFolder);
2622 if (RT_FAILURE(vrc))
2623 return setError(E_FAIL,
2624 tr("Invalid snapshot folder '%s' (%Rrc)"),
2625 strSnapshotFolder.c_str(), vrc);
2626
2627 i_setModified(IsModified_MachineData);
2628 mUserData.backup();
2629
2630 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2631
2632 return S_OK;
2633}
2634
2635HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2636{
2637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2638
2639 aMediumAttachments.resize(mMediumAttachments->size());
2640 size_t i = 0;
2641 for (MediumAttachmentList::const_iterator
2642 it = mMediumAttachments->begin();
2643 it != mMediumAttachments->end();
2644 ++it, ++i)
2645 aMediumAttachments[i] = *it;
2646
2647 return S_OK;
2648}
2649
2650HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2651{
2652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2653
2654 Assert(!!mVRDEServer);
2655
2656 aVRDEServer = mVRDEServer;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2662{
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 aAudioAdapter = mAudioAdapter;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2671{
2672#ifdef VBOX_WITH_VUSB
2673 clearError();
2674 MultiResult rc(S_OK);
2675
2676# ifdef VBOX_WITH_USB
2677 rc = mParent->i_host()->i_checkUSBProxyService();
2678 if (FAILED(rc)) return rc;
2679# endif
2680
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aUSBControllers.resize(mUSBControllers->size());
2684 size_t i = 0;
2685 for (USBControllerList::const_iterator
2686 it = mUSBControllers->begin();
2687 it != mUSBControllers->end();
2688 ++it, ++i)
2689 aUSBControllers[i] = *it;
2690
2691 return S_OK;
2692#else
2693 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2694 * extended error info to indicate that USB is simply not available
2695 * (w/o treating it as a failure), for example, as in OSE */
2696 NOREF(aUSBControllers);
2697 ReturnComNotImplemented();
2698#endif /* VBOX_WITH_VUSB */
2699}
2700
2701HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2702{
2703#ifdef VBOX_WITH_VUSB
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->i_host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 aUSBDeviceFilters = mUSBDeviceFilters;
2715 return rc;
2716#else
2717 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2718 * extended error info to indicate that USB is simply not available
2719 * (w/o treating it as a failure), for example, as in OSE */
2720 NOREF(aUSBDeviceFilters);
2721 ReturnComNotImplemented();
2722#endif /* VBOX_WITH_VUSB */
2723}
2724
2725HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2726{
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 aSettingsFilePath = mData->m_strConfigFileFull;
2730
2731 return S_OK;
2732}
2733
2734HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2735{
2736 RT_NOREF(aSettingsFilePath);
2737 ReturnComNotImplemented();
2738}
2739
2740HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2745 if (FAILED(rc)) return rc;
2746
2747 if (!mData->pMachineConfigFile->fileExists())
2748 // this is a new machine, and no config file exists yet:
2749 *aSettingsModified = TRUE;
2750 else
2751 *aSettingsModified = (mData->flModifications != 0);
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 *aSessionState = mData->mSession.mState;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 aSessionName = mData->mSession.mName;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 *aSessionPID = mData->mSession.mPID;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getState(MachineState_T *aState)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aState = mData->mMachineState;
2788 Assert(mData->mMachineState != MachineState_Null);
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2794{
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 aStateFilePath = mSSData->strStateFilePath;
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 i_getLogFolder(aLogFolder);
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2821{
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 aCurrentSnapshot = mData->mCurrentSnapshot;
2825
2826 return S_OK;
2827}
2828
2829HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2830{
2831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2834 ? 0
2835 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2836
2837 return S_OK;
2838}
2839
2840HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2841{
2842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2843
2844 /* Note: for machines with no snapshots, we always return FALSE
2845 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2846 * reasons :) */
2847
2848 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2849 ? FALSE
2850 : mData->mCurrentStateModified;
2851
2852 return S_OK;
2853}
2854
2855HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2856{
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 aSharedFolders.resize(mHWData->mSharedFolders.size());
2860 size_t i = 0;
2861 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2862 it = mHWData->mSharedFolders.begin();
2863 it != mHWData->mSharedFolders.end();
2864 ++it, ++i)
2865 aSharedFolders[i] = *it;
2866
2867 return S_OK;
2868}
2869
2870HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2871{
2872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 *aClipboardMode = mHWData->mClipboardMode;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2880{
2881 HRESULT rc = S_OK;
2882
2883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2884
2885 alock.release();
2886 rc = i_onClipboardModeChange(aClipboardMode);
2887 alock.acquire();
2888 if (FAILED(rc)) return rc;
2889
2890 i_setModified(IsModified_MachineData);
2891 mHWData.backup();
2892 mHWData->mClipboardMode = aClipboardMode;
2893
2894 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2895 if (Global::IsOnline(mData->mMachineState))
2896 i_saveSettings(NULL);
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 *aDnDMode = mHWData->mDnDMode;
2906
2907 return S_OK;
2908}
2909
2910HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2911{
2912 HRESULT rc = S_OK;
2913
2914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 alock.release();
2917 rc = i_onDnDModeChange(aDnDMode);
2918
2919 alock.acquire();
2920 if (FAILED(rc)) return rc;
2921
2922 i_setModified(IsModified_MachineData);
2923 mHWData.backup();
2924 mHWData->mDnDMode = aDnDMode;
2925
2926 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2927 if (Global::IsOnline(mData->mMachineState))
2928 i_saveSettings(NULL);
2929
2930 return S_OK;
2931}
2932
2933HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2934{
2935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2936
2937 aStorageControllers.resize(mStorageControllers->size());
2938 size_t i = 0;
2939 for (StorageControllerList::const_iterator
2940 it = mStorageControllers->begin();
2941 it != mStorageControllers->end();
2942 ++it, ++i)
2943 aStorageControllers[i] = *it;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2949{
2950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 *aEnabled = mUserData->s.fTeleporterEnabled;
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2958{
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 /* Only allow it to be set to true when PoweredOff or Aborted.
2962 (Clearing it is always permitted.) */
2963 if ( aTeleporterEnabled
2964 && mData->mRegistered
2965 && ( !i_isSessionMachine()
2966 || ( mData->mMachineState != MachineState_PoweredOff
2967 && mData->mMachineState != MachineState_Teleported
2968 && mData->mMachineState != MachineState_Aborted
2969 )
2970 )
2971 )
2972 return setError(VBOX_E_INVALID_VM_STATE,
2973 tr("The machine is not powered off (state is %s)"),
2974 Global::stringifyMachineState(mData->mMachineState));
2975
2976 i_setModified(IsModified_MachineData);
2977 mUserData.backup();
2978 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2984{
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2993{
2994 if (aTeleporterPort >= _64K)
2995 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2996
2997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2998
2999 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3000 if (FAILED(rc)) return rc;
3001
3002 i_setModified(IsModified_MachineData);
3003 mUserData.backup();
3004 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3005
3006 return S_OK;
3007}
3008
3009HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3010{
3011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3014
3015 return S_OK;
3016}
3017
3018HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3019{
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3023 if (FAILED(rc)) return rc;
3024
3025 i_setModified(IsModified_MachineData);
3026 mUserData.backup();
3027 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3028
3029 return S_OK;
3030}
3031
3032HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3033{
3034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3035 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3036
3037 return S_OK;
3038}
3039
3040HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3041{
3042 /*
3043 * Hash the password first.
3044 */
3045 com::Utf8Str aT = aTeleporterPassword;
3046
3047 if (!aT.isEmpty())
3048 {
3049 if (VBoxIsPasswordHashed(&aT))
3050 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3051 VBoxHashPassword(&aT);
3052 }
3053
3054 /*
3055 * Do the update.
3056 */
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3059 if (SUCCEEDED(hrc))
3060 {
3061 i_setModified(IsModified_MachineData);
3062 mUserData.backup();
3063 mUserData->s.strTeleporterPassword = aT;
3064 }
3065
3066 return hrc;
3067}
3068
3069HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3070{
3071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3072
3073 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3074 return S_OK;
3075}
3076
3077HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3078{
3079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 /** @todo deal with running state change. */
3082 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3083 if (FAILED(rc)) return rc;
3084
3085 i_setModified(IsModified_MachineData);
3086 mUserData.backup();
3087 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3088 return S_OK;
3089}
3090
3091HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3092{
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3096 return S_OK;
3097}
3098
3099HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3100{
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 /** @todo deal with running state change. */
3104 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3105 if (FAILED(rc)) return rc;
3106
3107 i_setModified(IsModified_MachineData);
3108 mUserData.backup();
3109 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3110 return S_OK;
3111}
3112
3113HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3114{
3115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 /** @todo deal with running state change. */
3126 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3127 if (FAILED(rc)) return rc;
3128
3129 i_setModified(IsModified_MachineData);
3130 mUserData.backup();
3131 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3140
3141 return S_OK;
3142}
3143
3144HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3145{
3146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3147
3148 /** @todo deal with running state change. */
3149 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3150 if (FAILED(rc)) return rc;
3151
3152 i_setModified(IsModified_MachineData);
3153 mUserData.backup();
3154 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3155
3156 return S_OK;
3157}
3158
3159HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3160{
3161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3164 return S_OK;
3165}
3166
3167HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3168{
3169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3170
3171 /** @todo deal with running state change. */
3172 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3173 if (FAILED(rc)) return rc;
3174
3175 i_setModified(IsModified_MachineData);
3176 mUserData.backup();
3177 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3178 return S_OK;
3179}
3180
3181HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3182{
3183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3186
3187 return S_OK;
3188}
3189
3190HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3191{
3192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3193
3194 /* Only allow it to be set to true when PoweredOff or Aborted.
3195 (Clearing it is always permitted.) */
3196 if ( aRTCUseUTC
3197 && mData->mRegistered
3198 && ( !i_isSessionMachine()
3199 || ( mData->mMachineState != MachineState_PoweredOff
3200 && mData->mMachineState != MachineState_Teleported
3201 && mData->mMachineState != MachineState_Aborted
3202 )
3203 )
3204 )
3205 return setError(VBOX_E_INVALID_VM_STATE,
3206 tr("The machine is not powered off (state is %s)"),
3207 Global::stringifyMachineState(mData->mMachineState));
3208
3209 i_setModified(IsModified_MachineData);
3210 mUserData.backup();
3211 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3212
3213 return S_OK;
3214}
3215
3216HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3217{
3218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3221
3222 return S_OK;
3223}
3224
3225HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3226{
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 HRESULT rc = i_checkStateDependency(MutableStateDep);
3230 if (FAILED(rc)) return rc;
3231
3232 i_setModified(IsModified_MachineData);
3233 mHWData.backup();
3234 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3240{
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243 *aIOCacheSize = mHWData->mIOCacheSize;
3244
3245 return S_OK;
3246}
3247
3248HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3249{
3250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3251
3252 HRESULT rc = i_checkStateDependency(MutableStateDep);
3253 if (FAILED(rc)) return rc;
3254
3255 i_setModified(IsModified_MachineData);
3256 mHWData.backup();
3257 mHWData->mIOCacheSize = aIOCacheSize;
3258
3259 return S_OK;
3260}
3261
3262
3263/**
3264 * @note Locks objects!
3265 */
3266HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3267 LockType_T aLockType)
3268{
3269 /* check the session state */
3270 SessionState_T state;
3271 HRESULT rc = aSession->COMGETTER(State)(&state);
3272 if (FAILED(rc)) return rc;
3273
3274 if (state != SessionState_Unlocked)
3275 return setError(VBOX_E_INVALID_OBJECT_STATE,
3276 tr("The given session is busy"));
3277
3278 // get the client's IInternalSessionControl interface
3279 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3280 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3281 E_INVALIDARG);
3282
3283 // session name (only used in some code paths)
3284 Utf8Str strSessionName;
3285
3286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3287
3288 if (!mData->mRegistered)
3289 return setError(E_UNEXPECTED,
3290 tr("The machine '%s' is not registered"),
3291 mUserData->s.strName.c_str());
3292
3293 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3294
3295 SessionState_T oldState = mData->mSession.mState;
3296 /* Hack: in case the session is closing and there is a progress object
3297 * which allows waiting for the session to be closed, take the opportunity
3298 * and do a limited wait (max. 1 second). This helps a lot when the system
3299 * is busy and thus session closing can take a little while. */
3300 if ( mData->mSession.mState == SessionState_Unlocking
3301 && mData->mSession.mProgress)
3302 {
3303 alock.release();
3304 mData->mSession.mProgress->WaitForCompletion(1000);
3305 alock.acquire();
3306 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3307 }
3308
3309 // try again now
3310 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3311 // (i.e. session machine exists)
3312 && (aLockType == LockType_Shared) // caller wants a shared link to the
3313 // existing session that holds the write lock:
3314 )
3315 {
3316 // OK, share the session... we are now dealing with three processes:
3317 // 1) VBoxSVC (where this code runs);
3318 // 2) process C: the caller's client process (who wants a shared session);
3319 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3320
3321 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3322 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3323 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3324 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3325 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3326
3327 /*
3328 * Release the lock before calling the client process. It's safe here
3329 * since the only thing to do after we get the lock again is to add
3330 * the remote control to the list (which doesn't directly influence
3331 * anything).
3332 */
3333 alock.release();
3334
3335 // get the console of the session holding the write lock (this is a remote call)
3336 ComPtr<IConsole> pConsoleW;
3337 if (mData->mSession.mLockType == LockType_VM)
3338 {
3339 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3340 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3341 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3342 if (FAILED(rc))
3343 // the failure may occur w/o any error info (from RPC), so provide one
3344 return setError(VBOX_E_VM_ERROR,
3345 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3346 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3347 }
3348
3349 // share the session machine and W's console with the caller's session
3350 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3351 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3352 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3353
3354 if (FAILED(rc))
3355 // the failure may occur w/o any error info (from RPC), so provide one
3356 return setError(VBOX_E_VM_ERROR,
3357 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3358 alock.acquire();
3359
3360 // need to revalidate the state after acquiring the lock again
3361 if (mData->mSession.mState != SessionState_Locked)
3362 {
3363 pSessionControl->Uninitialize();
3364 return setError(VBOX_E_INVALID_SESSION_STATE,
3365 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3366 mUserData->s.strName.c_str());
3367 }
3368
3369 // add the caller's session to the list
3370 mData->mSession.mRemoteControls.push_back(pSessionControl);
3371 }
3372 else if ( mData->mSession.mState == SessionState_Locked
3373 || mData->mSession.mState == SessionState_Unlocking
3374 )
3375 {
3376 // sharing not permitted, or machine still unlocking:
3377 return setError(VBOX_E_INVALID_OBJECT_STATE,
3378 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3379 mUserData->s.strName.c_str());
3380 }
3381 else
3382 {
3383 // machine is not locked: then write-lock the machine (create the session machine)
3384
3385 // must not be busy
3386 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3387
3388 // get the caller's session PID
3389 RTPROCESS pid = NIL_RTPROCESS;
3390 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3391 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3392 Assert(pid != NIL_RTPROCESS);
3393
3394 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3395
3396 if (fLaunchingVMProcess)
3397 {
3398 if (mData->mSession.mPID == NIL_RTPROCESS)
3399 {
3400 // two or more clients racing for a lock, the one which set the
3401 // session state to Spawning will win, the others will get an
3402 // error as we can't decide here if waiting a little would help
3403 // (only for shared locks this would avoid an error)
3404 return setError(VBOX_E_INVALID_OBJECT_STATE,
3405 tr("The machine '%s' already has a lock request pending"),
3406 mUserData->s.strName.c_str());
3407 }
3408
3409 // this machine is awaiting for a spawning session to be opened:
3410 // then the calling process must be the one that got started by
3411 // LaunchVMProcess()
3412
3413 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3414 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3415
3416#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3417 /* Hardened windows builds spawns three processes when a VM is
3418 launched, the 3rd one is the one that will end up here. */
3419 RTPROCESS ppid;
3420 int rc = RTProcQueryParent(pid, &ppid);
3421 if (RT_SUCCESS(rc))
3422 rc = RTProcQueryParent(ppid, &ppid);
3423 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3424 || rc == VERR_ACCESS_DENIED)
3425 {
3426 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3427 mData->mSession.mPID = pid;
3428 }
3429#endif
3430
3431 if (mData->mSession.mPID != pid)
3432 return setError(E_ACCESSDENIED,
3433 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3434 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3435 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3436 }
3437
3438 // create the mutable SessionMachine from the current machine
3439 ComObjPtr<SessionMachine> sessionMachine;
3440 sessionMachine.createObject();
3441 rc = sessionMachine->init(this);
3442 AssertComRC(rc);
3443
3444 /* NOTE: doing return from this function after this point but
3445 * before the end is forbidden since it may call SessionMachine::uninit()
3446 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3447 * lock while still holding the Machine lock in alock so that a deadlock
3448 * is possible due to the wrong lock order. */
3449
3450 if (SUCCEEDED(rc))
3451 {
3452 /*
3453 * Set the session state to Spawning to protect against subsequent
3454 * attempts to open a session and to unregister the machine after
3455 * we release the lock.
3456 */
3457 SessionState_T origState = mData->mSession.mState;
3458 mData->mSession.mState = SessionState_Spawning;
3459
3460#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3461 /* Get the client token ID to be passed to the client process */
3462 Utf8Str strTokenId;
3463 sessionMachine->i_getTokenId(strTokenId);
3464 Assert(!strTokenId.isEmpty());
3465#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3466 /* Get the client token to be passed to the client process */
3467 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3468 /* The token is now "owned" by pToken, fix refcount */
3469 if (!pToken.isNull())
3470 pToken->Release();
3471#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3472
3473 /*
3474 * Release the lock before calling the client process -- it will call
3475 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3476 * because the state is Spawning, so that LaunchVMProcess() and
3477 * LockMachine() calls will fail. This method, called before we
3478 * acquire the lock again, will fail because of the wrong PID.
3479 *
3480 * Note that mData->mSession.mRemoteControls accessed outside
3481 * the lock may not be modified when state is Spawning, so it's safe.
3482 */
3483 alock.release();
3484
3485 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3486#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3487 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3488#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3489 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3490 /* Now the token is owned by the client process. */
3491 pToken.setNull();
3492#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3493 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3494
3495 /* The failure may occur w/o any error info (from RPC), so provide one */
3496 if (FAILED(rc))
3497 setError(VBOX_E_VM_ERROR,
3498 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3499
3500 // get session name, either to remember or to compare against
3501 // the already known session name.
3502 {
3503 Bstr bstrSessionName;
3504 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3505 if (SUCCEEDED(rc2))
3506 strSessionName = bstrSessionName;
3507 }
3508
3509 if ( SUCCEEDED(rc)
3510 && fLaunchingVMProcess
3511 )
3512 {
3513 /* complete the remote session initialization */
3514
3515 /* get the console from the direct session */
3516 ComPtr<IConsole> console;
3517 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3518 ComAssertComRC(rc);
3519
3520 if (SUCCEEDED(rc) && !console)
3521 {
3522 ComAssert(!!console);
3523 rc = E_FAIL;
3524 }
3525
3526 /* assign machine & console to the remote session */
3527 if (SUCCEEDED(rc))
3528 {
3529 /*
3530 * after LaunchVMProcess(), the first and the only
3531 * entry in remoteControls is that remote session
3532 */
3533 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3534 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3535 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3536
3537 /* The failure may occur w/o any error info (from RPC), so provide one */
3538 if (FAILED(rc))
3539 setError(VBOX_E_VM_ERROR,
3540 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3541 }
3542
3543 if (FAILED(rc))
3544 pSessionControl->Uninitialize();
3545 }
3546
3547 /* acquire the lock again */
3548 alock.acquire();
3549
3550 /* Restore the session state */
3551 mData->mSession.mState = origState;
3552 }
3553
3554 // finalize spawning anyway (this is why we don't return on errors above)
3555 if (fLaunchingVMProcess)
3556 {
3557 Assert(mData->mSession.mName == strSessionName);
3558 /* Note that the progress object is finalized later */
3559 /** @todo Consider checking mData->mSession.mProgress for cancellation
3560 * around here. */
3561
3562 /* We don't reset mSession.mPID here because it is necessary for
3563 * SessionMachine::uninit() to reap the child process later. */
3564
3565 if (FAILED(rc))
3566 {
3567 /* Close the remote session, remove the remote control from the list
3568 * and reset session state to Closed (@note keep the code in sync
3569 * with the relevant part in checkForSpawnFailure()). */
3570
3571 Assert(mData->mSession.mRemoteControls.size() == 1);
3572 if (mData->mSession.mRemoteControls.size() == 1)
3573 {
3574 ErrorInfoKeeper eik;
3575 mData->mSession.mRemoteControls.front()->Uninitialize();
3576 }
3577
3578 mData->mSession.mRemoteControls.clear();
3579 mData->mSession.mState = SessionState_Unlocked;
3580 }
3581 }
3582 else
3583 {
3584 /* memorize PID of the directly opened session */
3585 if (SUCCEEDED(rc))
3586 mData->mSession.mPID = pid;
3587 }
3588
3589 if (SUCCEEDED(rc))
3590 {
3591 mData->mSession.mLockType = aLockType;
3592 /* memorize the direct session control and cache IUnknown for it */
3593 mData->mSession.mDirectControl = pSessionControl;
3594 mData->mSession.mState = SessionState_Locked;
3595 if (!fLaunchingVMProcess)
3596 mData->mSession.mName = strSessionName;
3597 /* associate the SessionMachine with this Machine */
3598 mData->mSession.mMachine = sessionMachine;
3599
3600 /* request an IUnknown pointer early from the remote party for later
3601 * identity checks (it will be internally cached within mDirectControl
3602 * at least on XPCOM) */
3603 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3604 NOREF(unk);
3605 }
3606
3607 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3608 * would break the lock order */
3609 alock.release();
3610
3611 /* uninitialize the created session machine on failure */
3612 if (FAILED(rc))
3613 sessionMachine->uninit();
3614 }
3615
3616 if (SUCCEEDED(rc))
3617 {
3618 /*
3619 * tell the client watcher thread to update the set of
3620 * machines that have open sessions
3621 */
3622 mParent->i_updateClientWatcher();
3623
3624 if (oldState != SessionState_Locked)
3625 /* fire an event */
3626 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3627 }
3628
3629 return rc;
3630}
3631
3632/**
3633 * @note Locks objects!
3634 */
3635HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3636 const com::Utf8Str &aName,
3637 const com::Utf8Str &aEnvironment,
3638 ComPtr<IProgress> &aProgress)
3639{
3640 Utf8Str strFrontend(aName);
3641 /* "emergencystop" doesn't need the session, so skip the checks/interface
3642 * retrieval. This code doesn't quite fit in here, but introducing a
3643 * special API method would be even more effort, and would require explicit
3644 * support by every API client. It's better to hide the feature a bit. */
3645 if (strFrontend != "emergencystop")
3646 CheckComArgNotNull(aSession);
3647
3648 HRESULT rc = S_OK;
3649 if (strFrontend.isEmpty())
3650 {
3651 Bstr bstrFrontend;
3652 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3653 if (FAILED(rc))
3654 return rc;
3655 strFrontend = bstrFrontend;
3656 if (strFrontend.isEmpty())
3657 {
3658 ComPtr<ISystemProperties> systemProperties;
3659 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3660 if (FAILED(rc))
3661 return rc;
3662 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3663 if (FAILED(rc))
3664 return rc;
3665 strFrontend = bstrFrontend;
3666 }
3667 /* paranoia - emergencystop is not a valid default */
3668 if (strFrontend == "emergencystop")
3669 strFrontend = Utf8Str::Empty;
3670 }
3671 /* default frontend: Qt GUI */
3672 if (strFrontend.isEmpty())
3673 strFrontend = "GUI/Qt";
3674
3675 if (strFrontend != "emergencystop")
3676 {
3677 /* check the session state */
3678 SessionState_T state;
3679 rc = aSession->COMGETTER(State)(&state);
3680 if (FAILED(rc))
3681 return rc;
3682
3683 if (state != SessionState_Unlocked)
3684 return setError(VBOX_E_INVALID_OBJECT_STATE,
3685 tr("The given session is busy"));
3686
3687 /* get the IInternalSessionControl interface */
3688 ComPtr<IInternalSessionControl> control(aSession);
3689 ComAssertMsgRet(!control.isNull(),
3690 ("No IInternalSessionControl interface"),
3691 E_INVALIDARG);
3692
3693 /* get the teleporter enable state for the progress object init. */
3694 BOOL fTeleporterEnabled;
3695 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3696 if (FAILED(rc))
3697 return rc;
3698
3699 /* create a progress object */
3700 ComObjPtr<ProgressProxy> progress;
3701 progress.createObject();
3702 rc = progress->init(mParent,
3703 static_cast<IMachine*>(this),
3704 Bstr(tr("Starting VM")).raw(),
3705 TRUE /* aCancelable */,
3706 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3707 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3708 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3709 2 /* uFirstOperationWeight */,
3710 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3711
3712 if (SUCCEEDED(rc))
3713 {
3714 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3715 if (SUCCEEDED(rc))
3716 {
3717 aProgress = progress;
3718
3719 /* signal the client watcher thread */
3720 mParent->i_updateClientWatcher();
3721
3722 /* fire an event */
3723 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3724 }
3725 }
3726 }
3727 else
3728 {
3729 /* no progress object - either instant success or failure */
3730 aProgress = NULL;
3731
3732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3733
3734 if (mData->mSession.mState != SessionState_Locked)
3735 return setError(VBOX_E_INVALID_OBJECT_STATE,
3736 tr("The machine '%s' is not locked by a session"),
3737 mUserData->s.strName.c_str());
3738
3739 /* must have a VM process associated - do not kill normal API clients
3740 * with an open session */
3741 if (!Global::IsOnline(mData->mMachineState))
3742 return setError(VBOX_E_INVALID_OBJECT_STATE,
3743 tr("The machine '%s' does not have a VM process"),
3744 mUserData->s.strName.c_str());
3745
3746 /* forcibly terminate the VM process */
3747 if (mData->mSession.mPID != NIL_RTPROCESS)
3748 RTProcTerminate(mData->mSession.mPID);
3749
3750 /* signal the client watcher thread, as most likely the client has
3751 * been terminated */
3752 mParent->i_updateClientWatcher();
3753 }
3754
3755 return rc;
3756}
3757
3758HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3759{
3760 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3761 return setError(E_INVALIDARG,
3762 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3763 aPosition, SchemaDefs::MaxBootPosition);
3764
3765 if (aDevice == DeviceType_USB)
3766 return setError(E_NOTIMPL,
3767 tr("Booting from USB device is currently not supported"));
3768
3769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3770
3771 HRESULT rc = i_checkStateDependency(MutableStateDep);
3772 if (FAILED(rc)) return rc;
3773
3774 i_setModified(IsModified_MachineData);
3775 mHWData.backup();
3776 mHWData->mBootOrder[aPosition - 1] = aDevice;
3777
3778 return S_OK;
3779}
3780
3781HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3782{
3783 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3784 return setError(E_INVALIDARG,
3785 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3786 aPosition, SchemaDefs::MaxBootPosition);
3787
3788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3789
3790 *aDevice = mHWData->mBootOrder[aPosition - 1];
3791
3792 return S_OK;
3793}
3794
3795HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3796 LONG aControllerPort,
3797 LONG aDevice,
3798 DeviceType_T aType,
3799 const ComPtr<IMedium> &aMedium)
3800{
3801 IMedium *aM = aMedium;
3802 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3803 aName.c_str(), aControllerPort, aDevice, aType, aM));
3804
3805 // request the host lock first, since might be calling Host methods for getting host drives;
3806 // next, protect the media tree all the while we're in here, as well as our member variables
3807 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3808 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3809
3810 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3811 if (FAILED(rc)) return rc;
3812
3813 /// @todo NEWMEDIA implicit machine registration
3814 if (!mData->mRegistered)
3815 return setError(VBOX_E_INVALID_OBJECT_STATE,
3816 tr("Cannot attach storage devices to an unregistered machine"));
3817
3818 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3819
3820 /* Check for an existing controller. */
3821 ComObjPtr<StorageController> ctl;
3822 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3823 if (FAILED(rc)) return rc;
3824
3825 StorageControllerType_T ctrlType;
3826 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3827 if (FAILED(rc))
3828 return setError(E_FAIL,
3829 tr("Could not get type of controller '%s'"),
3830 aName.c_str());
3831
3832 bool fSilent = false;
3833 Utf8Str strReconfig;
3834
3835 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3836 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3837 if ( mData->mMachineState == MachineState_Paused
3838 && strReconfig == "1")
3839 fSilent = true;
3840
3841 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3842 bool fHotplug = false;
3843 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3844 fHotplug = true;
3845
3846 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3847 return setError(VBOX_E_INVALID_VM_STATE,
3848 tr("Controller '%s' does not support hotplugging"),
3849 aName.c_str());
3850
3851 // check that the port and device are not out of range
3852 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3853 if (FAILED(rc)) return rc;
3854
3855 /* check if the device slot is already busy */
3856 MediumAttachment *pAttachTemp;
3857 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3858 aName,
3859 aControllerPort,
3860 aDevice)))
3861 {
3862 Medium *pMedium = pAttachTemp->i_getMedium();
3863 if (pMedium)
3864 {
3865 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3866 return setError(VBOX_E_OBJECT_IN_USE,
3867 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3868 pMedium->i_getLocationFull().c_str(),
3869 aControllerPort,
3870 aDevice,
3871 aName.c_str());
3872 }
3873 else
3874 return setError(VBOX_E_OBJECT_IN_USE,
3875 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3876 aControllerPort, aDevice, aName.c_str());
3877 }
3878
3879 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3880 if (aMedium && medium.isNull())
3881 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3882
3883 AutoCaller mediumCaller(medium);
3884 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3885
3886 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3887
3888 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3889 && !medium.isNull()
3890 )
3891 return setError(VBOX_E_OBJECT_IN_USE,
3892 tr("Medium '%s' is already attached to this virtual machine"),
3893 medium->i_getLocationFull().c_str());
3894
3895 if (!medium.isNull())
3896 {
3897 MediumType_T mtype = medium->i_getType();
3898 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3899 // For DVDs it's not written to the config file, so needs no global config
3900 // version bump. For floppies it's a new attribute "type", which is ignored
3901 // by older VirtualBox version, so needs no global config version bump either.
3902 // For hard disks this type is not accepted.
3903 if (mtype == MediumType_MultiAttach)
3904 {
3905 // This type is new with VirtualBox 4.0 and therefore requires settings
3906 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3907 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3908 // two reasons: The medium type is a property of the media registry tree, which
3909 // can reside in the global config file (for pre-4.0 media); we would therefore
3910 // possibly need to bump the global config version. We don't want to do that though
3911 // because that might make downgrading to pre-4.0 impossible.
3912 // As a result, we can only use these two new types if the medium is NOT in the
3913 // global registry:
3914 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3915 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3916 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3917 )
3918 return setError(VBOX_E_INVALID_OBJECT_STATE,
3919 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3920 "to machines that were created with VirtualBox 4.0 or later"),
3921 medium->i_getLocationFull().c_str());
3922 }
3923 }
3924
3925 bool fIndirect = false;
3926 if (!medium.isNull())
3927 fIndirect = medium->i_isReadOnly();
3928 bool associate = true;
3929
3930 do
3931 {
3932 if ( aType == DeviceType_HardDisk
3933 && mMediumAttachments.isBackedUp())
3934 {
3935 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3936
3937 /* check if the medium was attached to the VM before we started
3938 * changing attachments in which case the attachment just needs to
3939 * be restored */
3940 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3941 {
3942 AssertReturn(!fIndirect, E_FAIL);
3943
3944 /* see if it's the same bus/channel/device */
3945 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3946 {
3947 /* the simplest case: restore the whole attachment
3948 * and return, nothing else to do */
3949 mMediumAttachments->push_back(pAttachTemp);
3950
3951 /* Reattach the medium to the VM. */
3952 if (fHotplug || fSilent)
3953 {
3954 mediumLock.release();
3955 treeLock.release();
3956 alock.release();
3957
3958 MediumLockList *pMediumLockList(new MediumLockList());
3959
3960 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3961 medium /* pToLockWrite */,
3962 false /* fMediumLockWriteAll */,
3963 NULL,
3964 *pMediumLockList);
3965 alock.acquire();
3966 if (FAILED(rc))
3967 delete pMediumLockList;
3968 else
3969 {
3970 mData->mSession.mLockedMedia.Unlock();
3971 alock.release();
3972 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3973 mData->mSession.mLockedMedia.Lock();
3974 alock.acquire();
3975 }
3976 alock.release();
3977
3978 if (SUCCEEDED(rc))
3979 {
3980 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3981 /* Remove lock list in case of error. */
3982 if (FAILED(rc))
3983 {
3984 mData->mSession.mLockedMedia.Unlock();
3985 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3986 mData->mSession.mLockedMedia.Lock();
3987 }
3988 }
3989 }
3990
3991 return S_OK;
3992 }
3993
3994 /* bus/channel/device differ; we need a new attachment object,
3995 * but don't try to associate it again */
3996 associate = false;
3997 break;
3998 }
3999 }
4000
4001 /* go further only if the attachment is to be indirect */
4002 if (!fIndirect)
4003 break;
4004
4005 /* perform the so called smart attachment logic for indirect
4006 * attachments. Note that smart attachment is only applicable to base
4007 * hard disks. */
4008
4009 if (medium->i_getParent().isNull())
4010 {
4011 /* first, investigate the backup copy of the current hard disk
4012 * attachments to make it possible to re-attach existing diffs to
4013 * another device slot w/o losing their contents */
4014 if (mMediumAttachments.isBackedUp())
4015 {
4016 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4017
4018 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4019 uint32_t foundLevel = 0;
4020
4021 for (MediumAttachmentList::const_iterator
4022 it = oldAtts.begin();
4023 it != oldAtts.end();
4024 ++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(*mMediumAttachments.data(),
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(aName, aControllerPort, aDevice))
4047 {
4048 /* the simplest case: restore the whole attachment
4049 * and return, nothing else to do */
4050 mMediumAttachments->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 MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4134
4135 MediumAttachment *pAttachFound = NULL;
4136 uint32_t foundLevel = 0;
4137
4138 for (MediumAttachmentList::const_iterator
4139 it = snapAtts.begin();
4140 it != snapAtts.end();
4141 ++it)
4142 {
4143 MediumAttachment *pAttach = *it;
4144 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4145 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4146 if (pMedium.isNull())
4147 continue;
4148
4149 uint32_t level = 0;
4150 if (pMedium->i_getBase(&level) == medium)
4151 {
4152 /* matched device, channel and bus (i.e. attached to the
4153 * same place) will win and immediately stop the search;
4154 * otherwise the attachment that has the youngest
4155 * descendant of medium will be used
4156 */
4157 if ( pAttach->i_getDevice() == aDevice
4158 && pAttach->i_getPort() == aControllerPort
4159 && pAttach->i_getControllerName() == aName
4160 )
4161 {
4162 pAttachFound = pAttach;
4163 break;
4164 }
4165 else if ( !pAttachFound
4166 || level > foundLevel /* prefer younger */
4167 )
4168 {
4169 pAttachFound = pAttach;
4170 foundLevel = level;
4171 }
4172 }
4173 }
4174
4175 if (pAttachFound)
4176 {
4177 base = pAttachFound->i_getMedium();
4178 break;
4179 }
4180
4181 snap = snap->i_getParent();
4182 }
4183
4184 /* re-lock medium tree and the medium, as we need it below */
4185 treeLock.acquire();
4186 mediumLock.acquire();
4187
4188 /* found a suitable diff, use it as a base */
4189 if (!base.isNull())
4190 {
4191 medium = base;
4192 mediumCaller.attach(medium);
4193 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4194 mediumLock.attach(medium);
4195 }
4196 }
4197
4198 Utf8Str strFullSnapshotFolder;
4199 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4200
4201 ComObjPtr<Medium> diff;
4202 diff.createObject();
4203 // store this diff in the same registry as the parent
4204 Guid uuidRegistryParent;
4205 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4206 {
4207 // parent image has no registry: this can happen if we're attaching a new immutable
4208 // image that has not yet been attached (medium then points to the base and we're
4209 // creating the diff image for the immutable, and the parent is not yet registered);
4210 // put the parent in the machine registry then
4211 mediumLock.release();
4212 treeLock.release();
4213 alock.release();
4214 i_addMediumToRegistry(medium);
4215 alock.acquire();
4216 treeLock.acquire();
4217 mediumLock.acquire();
4218 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4219 }
4220 rc = diff->init(mParent,
4221 medium->i_getPreferredDiffFormat(),
4222 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4223 uuidRegistryParent,
4224 DeviceType_HardDisk);
4225 if (FAILED(rc)) return rc;
4226
4227 /* Apply the normal locking logic to the entire chain. */
4228 MediumLockList *pMediumLockList(new MediumLockList());
4229 mediumLock.release();
4230 treeLock.release();
4231 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4232 diff /* pToLockWrite */,
4233 false /* fMediumLockWriteAll */,
4234 medium,
4235 *pMediumLockList);
4236 treeLock.acquire();
4237 mediumLock.acquire();
4238 if (SUCCEEDED(rc))
4239 {
4240 mediumLock.release();
4241 treeLock.release();
4242 rc = pMediumLockList->Lock();
4243 treeLock.acquire();
4244 mediumLock.acquire();
4245 if (FAILED(rc))
4246 setError(rc,
4247 tr("Could not lock medium when creating diff '%s'"),
4248 diff->i_getLocationFull().c_str());
4249 else
4250 {
4251 /* will release the lock before the potentially lengthy
4252 * operation, so protect with the special state */
4253 MachineState_T oldState = mData->mMachineState;
4254 i_setMachineState(MachineState_SettingUp);
4255
4256 mediumLock.release();
4257 treeLock.release();
4258 alock.release();
4259
4260 rc = medium->i_createDiffStorage(diff,
4261 medium->i_getPreferredDiffVariant(),
4262 pMediumLockList,
4263 NULL /* aProgress */,
4264 true /* aWait */);
4265
4266 alock.acquire();
4267 treeLock.acquire();
4268 mediumLock.acquire();
4269
4270 i_setMachineState(oldState);
4271 }
4272 }
4273
4274 /* Unlock the media and free the associated memory. */
4275 delete pMediumLockList;
4276
4277 if (FAILED(rc)) return rc;
4278
4279 /* use the created diff for the actual attachment */
4280 medium = diff;
4281 mediumCaller.attach(medium);
4282 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4283 mediumLock.attach(medium);
4284 }
4285 while (0);
4286
4287 ComObjPtr<MediumAttachment> attachment;
4288 attachment.createObject();
4289 rc = attachment->init(this,
4290 medium,
4291 aName,
4292 aControllerPort,
4293 aDevice,
4294 aType,
4295 fIndirect,
4296 false /* fPassthrough */,
4297 false /* fTempEject */,
4298 false /* fNonRotational */,
4299 false /* fDiscard */,
4300 fHotplug /* fHotPluggable */,
4301 Utf8Str::Empty);
4302 if (FAILED(rc)) return rc;
4303
4304 if (associate && !medium.isNull())
4305 {
4306 // as the last step, associate the medium to the VM
4307 rc = medium->i_addBackReference(mData->mUuid);
4308 // here we can fail because of Deleting, or being in process of creating a Diff
4309 if (FAILED(rc)) return rc;
4310
4311 mediumLock.release();
4312 treeLock.release();
4313 alock.release();
4314 i_addMediumToRegistry(medium);
4315 alock.acquire();
4316 treeLock.acquire();
4317 mediumLock.acquire();
4318 }
4319
4320 /* success: finally remember the attachment */
4321 i_setModified(IsModified_Storage);
4322 mMediumAttachments.backup();
4323 mMediumAttachments->push_back(attachment);
4324
4325 mediumLock.release();
4326 treeLock.release();
4327 alock.release();
4328
4329 if (fHotplug || fSilent)
4330 {
4331 if (!medium.isNull())
4332 {
4333 MediumLockList *pMediumLockList(new MediumLockList());
4334
4335 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4336 medium /* pToLockWrite */,
4337 false /* fMediumLockWriteAll */,
4338 NULL,
4339 *pMediumLockList);
4340 alock.acquire();
4341 if (FAILED(rc))
4342 delete pMediumLockList;
4343 else
4344 {
4345 mData->mSession.mLockedMedia.Unlock();
4346 alock.release();
4347 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4348 mData->mSession.mLockedMedia.Lock();
4349 alock.acquire();
4350 }
4351 alock.release();
4352 }
4353
4354 if (SUCCEEDED(rc))
4355 {
4356 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4357 /* Remove lock list in case of error. */
4358 if (FAILED(rc))
4359 {
4360 mData->mSession.mLockedMedia.Unlock();
4361 mData->mSession.mLockedMedia.Remove(attachment);
4362 mData->mSession.mLockedMedia.Lock();
4363 }
4364 }
4365 }
4366
4367 /* Save modified registries, but skip this machine as it's the caller's
4368 * job to save its settings like all other settings changes. */
4369 mParent->i_unmarkRegistryModified(i_getId());
4370 mParent->i_saveModifiedRegistries();
4371
4372 return rc;
4373}
4374
4375HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4376 LONG aDevice)
4377{
4378 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4379 aName.c_str(), aControllerPort, aDevice));
4380
4381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4382
4383 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4384 if (FAILED(rc)) return rc;
4385
4386 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4387
4388 /* Check for an existing controller. */
4389 ComObjPtr<StorageController> ctl;
4390 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4391 if (FAILED(rc)) return rc;
4392
4393 StorageControllerType_T ctrlType;
4394 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4395 if (FAILED(rc))
4396 return setError(E_FAIL,
4397 tr("Could not get type of controller '%s'"),
4398 aName.c_str());
4399
4400 bool fSilent = false;
4401 Utf8Str strReconfig;
4402
4403 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4404 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4405 if ( mData->mMachineState == MachineState_Paused
4406 && strReconfig == "1")
4407 fSilent = true;
4408
4409 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4410 bool fHotplug = false;
4411 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4412 fHotplug = true;
4413
4414 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4415 return setError(VBOX_E_INVALID_VM_STATE,
4416 tr("Controller '%s' does not support hotplugging"),
4417 aName.c_str());
4418
4419 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4420 aName,
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4426 aDevice, aControllerPort, aName.c_str());
4427
4428 if (fHotplug && !pAttach->i_getHotPluggable())
4429 return setError(VBOX_E_NOT_SUPPORTED,
4430 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4431 aDevice, aControllerPort, aName.c_str());
4432
4433 /*
4434 * The VM has to detach the device before we delete any implicit diffs.
4435 * If this fails we can roll back without loosing data.
4436 */
4437 if (fHotplug || fSilent)
4438 {
4439 alock.release();
4440 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4441 alock.acquire();
4442 }
4443 if (FAILED(rc)) return rc;
4444
4445 /* If we are here everything went well and we can delete the implicit now. */
4446 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4447
4448 alock.release();
4449
4450 /* Save modified registries, but skip this machine as it's the caller's
4451 * job to save its settings like all other settings changes. */
4452 mParent->i_unmarkRegistryModified(i_getId());
4453 mParent->i_saveModifiedRegistries();
4454
4455 return rc;
4456}
4457
4458HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4459 LONG aDevice, BOOL aPassthrough)
4460{
4461 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4462 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4463
4464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4465
4466 HRESULT rc = i_checkStateDependency(MutableStateDep);
4467 if (FAILED(rc)) return rc;
4468
4469 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4470
4471 if (Global::IsOnlineOrTransient(mData->mMachineState))
4472 return setError(VBOX_E_INVALID_VM_STATE,
4473 tr("Invalid machine state: %s"),
4474 Global::stringifyMachineState(mData->mMachineState));
4475
4476 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4477 aName,
4478 aControllerPort,
4479 aDevice);
4480 if (!pAttach)
4481 return setError(VBOX_E_OBJECT_NOT_FOUND,
4482 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4483 aDevice, aControllerPort, aName.c_str());
4484
4485
4486 i_setModified(IsModified_Storage);
4487 mMediumAttachments.backup();
4488
4489 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4490
4491 if (pAttach->i_getType() != DeviceType_DVD)
4492 return setError(E_INVALIDARG,
4493 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4494 aDevice, aControllerPort, aName.c_str());
4495 pAttach->i_updatePassthrough(!!aPassthrough);
4496
4497 return S_OK;
4498}
4499
4500HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4501 LONG aDevice, BOOL aTemporaryEject)
4502{
4503
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4510 if (FAILED(rc)) return rc;
4511
4512 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4513 aName,
4514 aControllerPort,
4515 aDevice);
4516 if (!pAttach)
4517 return setError(VBOX_E_OBJECT_NOT_FOUND,
4518 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4519 aDevice, aControllerPort, aName.c_str());
4520
4521
4522 i_setModified(IsModified_Storage);
4523 mMediumAttachments.backup();
4524
4525 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4526
4527 if (pAttach->i_getType() != DeviceType_DVD)
4528 return setError(E_INVALIDARG,
4529 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4530 aDevice, aControllerPort, aName.c_str());
4531 pAttach->i_updateTempEject(!!aTemporaryEject);
4532
4533 return S_OK;
4534}
4535
4536HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4537 LONG aDevice, BOOL aNonRotational)
4538{
4539
4540 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4541 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4542
4543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4544
4545 HRESULT rc = i_checkStateDependency(MutableStateDep);
4546 if (FAILED(rc)) return rc;
4547
4548 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4549
4550 if (Global::IsOnlineOrTransient(mData->mMachineState))
4551 return setError(VBOX_E_INVALID_VM_STATE,
4552 tr("Invalid machine state: %s"),
4553 Global::stringifyMachineState(mData->mMachineState));
4554
4555 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4556 aName,
4557 aControllerPort,
4558 aDevice);
4559 if (!pAttach)
4560 return setError(VBOX_E_OBJECT_NOT_FOUND,
4561 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4562 aDevice, aControllerPort, aName.c_str());
4563
4564
4565 i_setModified(IsModified_Storage);
4566 mMediumAttachments.backup();
4567
4568 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4569
4570 if (pAttach->i_getType() != DeviceType_HardDisk)
4571 return setError(E_INVALIDARG,
4572 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"),
4573 aDevice, aControllerPort, aName.c_str());
4574 pAttach->i_updateNonRotational(!!aNonRotational);
4575
4576 return S_OK;
4577}
4578
4579HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4580 LONG aDevice, BOOL aDiscard)
4581{
4582
4583 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4584 aName.c_str(), aControllerPort, aDevice, aDiscard));
4585
4586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4587
4588 HRESULT rc = i_checkStateDependency(MutableStateDep);
4589 if (FAILED(rc)) return rc;
4590
4591 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4592
4593 if (Global::IsOnlineOrTransient(mData->mMachineState))
4594 return setError(VBOX_E_INVALID_VM_STATE,
4595 tr("Invalid machine state: %s"),
4596 Global::stringifyMachineState(mData->mMachineState));
4597
4598 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4599 aName,
4600 aControllerPort,
4601 aDevice);
4602 if (!pAttach)
4603 return setError(VBOX_E_OBJECT_NOT_FOUND,
4604 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4605 aDevice, aControllerPort, aName.c_str());
4606
4607
4608 i_setModified(IsModified_Storage);
4609 mMediumAttachments.backup();
4610
4611 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4612
4613 if (pAttach->i_getType() != DeviceType_HardDisk)
4614 return setError(E_INVALIDARG,
4615 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"),
4616 aDevice, aControllerPort, aName.c_str());
4617 pAttach->i_updateDiscard(!!aDiscard);
4618
4619 return S_OK;
4620}
4621
4622HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4623 LONG aDevice, BOOL aHotPluggable)
4624{
4625 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4626 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4627
4628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4629
4630 HRESULT rc = i_checkStateDependency(MutableStateDep);
4631 if (FAILED(rc)) return rc;
4632
4633 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4634
4635 if (Global::IsOnlineOrTransient(mData->mMachineState))
4636 return setError(VBOX_E_INVALID_VM_STATE,
4637 tr("Invalid machine state: %s"),
4638 Global::stringifyMachineState(mData->mMachineState));
4639
4640 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4641 aName,
4642 aControllerPort,
4643 aDevice);
4644 if (!pAttach)
4645 return setError(VBOX_E_OBJECT_NOT_FOUND,
4646 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4647 aDevice, aControllerPort, aName.c_str());
4648
4649 /* Check for an existing controller. */
4650 ComObjPtr<StorageController> ctl;
4651 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4652 if (FAILED(rc)) return rc;
4653
4654 StorageControllerType_T ctrlType;
4655 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4656 if (FAILED(rc))
4657 return setError(E_FAIL,
4658 tr("Could not get type of controller '%s'"),
4659 aName.c_str());
4660
4661 if (!i_isControllerHotplugCapable(ctrlType))
4662 return setError(VBOX_E_NOT_SUPPORTED,
4663 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4664 aName.c_str());
4665
4666 i_setModified(IsModified_Storage);
4667 mMediumAttachments.backup();
4668
4669 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4670
4671 if (pAttach->i_getType() == DeviceType_Floppy)
4672 return setError(E_INVALIDARG,
4673 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"),
4674 aDevice, aControllerPort, aName.c_str());
4675 pAttach->i_updateHotPluggable(!!aHotPluggable);
4676
4677 return S_OK;
4678}
4679
4680HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4681 LONG aDevice)
4682{
4683 int rc = S_OK;
4684 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4685 aName.c_str(), aControllerPort, aDevice));
4686
4687 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4688
4689 return rc;
4690}
4691
4692HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4693 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4694{
4695 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4696 aName.c_str(), aControllerPort, aDevice));
4697
4698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4699
4700 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4701 if (FAILED(rc)) return rc;
4702
4703 if (Global::IsOnlineOrTransient(mData->mMachineState))
4704 return setError(VBOX_E_INVALID_VM_STATE,
4705 tr("Invalid machine state: %s"),
4706 Global::stringifyMachineState(mData->mMachineState));
4707
4708 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4709 aName,
4710 aControllerPort,
4711 aDevice);
4712 if (!pAttach)
4713 return setError(VBOX_E_OBJECT_NOT_FOUND,
4714 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4715 aDevice, aControllerPort, aName.c_str());
4716
4717
4718 i_setModified(IsModified_Storage);
4719 mMediumAttachments.backup();
4720
4721 IBandwidthGroup *iB = aBandwidthGroup;
4722 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4723 if (aBandwidthGroup && group.isNull())
4724 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4725
4726 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4727
4728 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4729 if (strBandwidthGroupOld.isNotEmpty())
4730 {
4731 /* Get the bandwidth group object and release it - this must not fail. */
4732 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4733 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4734 Assert(SUCCEEDED(rc));
4735
4736 pBandwidthGroupOld->i_release();
4737 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4738 }
4739
4740 if (!group.isNull())
4741 {
4742 group->i_reference();
4743 pAttach->i_updateBandwidthGroup(group->i_getName());
4744 }
4745
4746 return S_OK;
4747}
4748
4749HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4750 LONG aControllerPort,
4751 LONG aDevice,
4752 DeviceType_T aType)
4753{
4754 HRESULT rc = S_OK;
4755
4756 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4757 aName.c_str(), aControllerPort, aDevice, aType));
4758
4759 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4760
4761 return rc;
4762}
4763
4764
4765HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4766 LONG aControllerPort,
4767 LONG aDevice,
4768 BOOL aForce)
4769{
4770 int rc = S_OK;
4771 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4772 aName.c_str(), aControllerPort, aForce));
4773
4774 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4775
4776 return rc;
4777}
4778
4779HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4780 LONG aControllerPort,
4781 LONG aDevice,
4782 const ComPtr<IMedium> &aMedium,
4783 BOOL aForce)
4784{
4785 int rc = S_OK;
4786 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4787 aName.c_str(), aControllerPort, aDevice, aForce));
4788
4789 // request the host lock first, since might be calling Host methods for getting host drives;
4790 // next, protect the media tree all the while we're in here, as well as our member variables
4791 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4792 this->lockHandle(),
4793 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4794
4795 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4796 aName,
4797 aControllerPort,
4798 aDevice);
4799 if (pAttach.isNull())
4800 return setError(VBOX_E_OBJECT_NOT_FOUND,
4801 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4802 aDevice, aControllerPort, aName.c_str());
4803
4804 /* Remember previously mounted medium. The medium before taking the
4805 * backup is not necessarily the same thing. */
4806 ComObjPtr<Medium> oldmedium;
4807 oldmedium = pAttach->i_getMedium();
4808
4809 IMedium *iM = aMedium;
4810 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4811 if (aMedium && pMedium.isNull())
4812 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4813
4814 AutoCaller mediumCaller(pMedium);
4815 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4816
4817 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4818 if (pMedium)
4819 {
4820 DeviceType_T mediumType = pAttach->i_getType();
4821 switch (mediumType)
4822 {
4823 case DeviceType_DVD:
4824 case DeviceType_Floppy:
4825 break;
4826
4827 default:
4828 return setError(VBOX_E_INVALID_OBJECT_STATE,
4829 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4830 aControllerPort,
4831 aDevice,
4832 aName.c_str());
4833 }
4834 }
4835
4836 i_setModified(IsModified_Storage);
4837 mMediumAttachments.backup();
4838
4839 {
4840 // The backup operation makes the pAttach reference point to the
4841 // old settings. Re-get the correct reference.
4842 pAttach = i_findAttachment(*mMediumAttachments.data(),
4843 aName,
4844 aControllerPort,
4845 aDevice);
4846 if (!oldmedium.isNull())
4847 oldmedium->i_removeBackReference(mData->mUuid);
4848 if (!pMedium.isNull())
4849 {
4850 pMedium->i_addBackReference(mData->mUuid);
4851
4852 mediumLock.release();
4853 multiLock.release();
4854 i_addMediumToRegistry(pMedium);
4855 multiLock.acquire();
4856 mediumLock.acquire();
4857 }
4858
4859 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4860 pAttach->i_updateMedium(pMedium);
4861 }
4862
4863 i_setModified(IsModified_Storage);
4864
4865 mediumLock.release();
4866 multiLock.release();
4867 rc = i_onMediumChange(pAttach, aForce);
4868 multiLock.acquire();
4869 mediumLock.acquire();
4870
4871 /* On error roll back this change only. */
4872 if (FAILED(rc))
4873 {
4874 if (!pMedium.isNull())
4875 pMedium->i_removeBackReference(mData->mUuid);
4876 pAttach = i_findAttachment(*mMediumAttachments.data(),
4877 aName,
4878 aControllerPort,
4879 aDevice);
4880 /* If the attachment is gone in the meantime, bail out. */
4881 if (pAttach.isNull())
4882 return rc;
4883 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4884 if (!oldmedium.isNull())
4885 oldmedium->i_addBackReference(mData->mUuid);
4886 pAttach->i_updateMedium(oldmedium);
4887 }
4888
4889 mediumLock.release();
4890 multiLock.release();
4891
4892 /* Save modified registries, but skip this machine as it's the caller's
4893 * job to save its settings like all other settings changes. */
4894 mParent->i_unmarkRegistryModified(i_getId());
4895 mParent->i_saveModifiedRegistries();
4896
4897 return rc;
4898}
4899HRESULT Machine::getMedium(const com::Utf8Str &aName,
4900 LONG aControllerPort,
4901 LONG aDevice,
4902 ComPtr<IMedium> &aMedium)
4903{
4904 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4905 aName.c_str(), aControllerPort, aDevice));
4906
4907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4908
4909 aMedium = NULL;
4910
4911 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4912 aName,
4913 aControllerPort,
4914 aDevice);
4915 if (pAttach.isNull())
4916 return setError(VBOX_E_OBJECT_NOT_FOUND,
4917 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4918 aDevice, aControllerPort, aName.c_str());
4919
4920 aMedium = pAttach->i_getMedium();
4921
4922 return S_OK;
4923}
4924
4925HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4926{
4927
4928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4929
4930 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4931
4932 return S_OK;
4933}
4934
4935HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4936{
4937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4938
4939 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4940
4941 return S_OK;
4942}
4943
4944HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4945{
4946 /* Do not assert if slot is out of range, just return the advertised
4947 status. testdriver/vbox.py triggers this in logVmInfo. */
4948 if (aSlot >= mNetworkAdapters.size())
4949 return setError(E_INVALIDARG,
4950 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4951 aSlot, mNetworkAdapters.size());
4952
4953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4954
4955 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4956
4957 return S_OK;
4958}
4959
4960HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4961{
4962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4963
4964 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4965 size_t i = 0;
4966 for (settings::StringsMap::const_iterator
4967 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4968 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4969 ++it, ++i)
4970 aKeys[i] = it->first;
4971
4972 return S_OK;
4973}
4974
4975 /**
4976 * @note Locks this object for reading.
4977 */
4978HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4979 com::Utf8Str &aValue)
4980{
4981 /* start with nothing found */
4982 aValue = "";
4983
4984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4985
4986 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4987 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4988 // found:
4989 aValue = it->second; // source is a Utf8Str
4990
4991 /* return the result to caller (may be empty) */
4992 return S_OK;
4993}
4994
4995 /**
4996 * @note Locks mParent for writing + this object for writing.
4997 */
4998HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4999{
5000 Utf8Str strOldValue; // empty
5001
5002 // locking note: we only hold the read lock briefly to look up the old value,
5003 // then release it and call the onExtraCanChange callbacks. There is a small
5004 // chance of a race insofar as the callback might be called twice if two callers
5005 // change the same key at the same time, but that's a much better solution
5006 // than the deadlock we had here before. The actual changing of the extradata
5007 // is then performed under the write lock and race-free.
5008
5009 // look up the old value first; if nothing has changed then we need not do anything
5010 {
5011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5012
5013 // For snapshots don't even think about allowing changes, extradata
5014 // is global for a machine, so there is nothing snapshot specific.
5015 if (i_isSnapshotMachine())
5016 return setError(VBOX_E_INVALID_VM_STATE,
5017 tr("Cannot set extradata for a snapshot"));
5018
5019 // check if the right IMachine instance is used
5020 if (mData->mRegistered && !i_isSessionMachine())
5021 return setError(VBOX_E_INVALID_VM_STATE,
5022 tr("Cannot set extradata for an immutable machine"));
5023
5024 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5025 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5026 strOldValue = it->second;
5027 }
5028
5029 bool fChanged;
5030 if ((fChanged = (strOldValue != aValue)))
5031 {
5032 // ask for permission from all listeners outside the locks;
5033 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5034 // lock to copy the list of callbacks to invoke
5035 Bstr error;
5036 Bstr bstrValue(aValue);
5037
5038 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5039 {
5040 const char *sep = error.isEmpty() ? "" : ": ";
5041 CBSTR err = error.raw();
5042 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5043 return setError(E_ACCESSDENIED,
5044 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5045 aKey.c_str(),
5046 aValue.c_str(),
5047 sep,
5048 err);
5049 }
5050
5051 // data is changing and change not vetoed: then write it out under the lock
5052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5053
5054 if (aValue.isEmpty())
5055 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5056 else
5057 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5058 // creates a new key if needed
5059
5060 bool fNeedsGlobalSaveSettings = false;
5061 // This saving of settings is tricky: there is no "old state" for the
5062 // extradata items at all (unlike all other settings), so the old/new
5063 // settings comparison would give a wrong result!
5064 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5065
5066 if (fNeedsGlobalSaveSettings)
5067 {
5068 // save the global settings; for that we should hold only the VirtualBox lock
5069 alock.release();
5070 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5071 mParent->i_saveSettings();
5072 }
5073 }
5074
5075 // fire notification outside the lock
5076 if (fChanged)
5077 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5078
5079 return S_OK;
5080}
5081
5082HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5083{
5084 aProgress = NULL;
5085 NOREF(aSettingsFilePath);
5086 ReturnComNotImplemented();
5087}
5088
5089HRESULT Machine::saveSettings()
5090{
5091 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5092
5093 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5094 if (FAILED(rc)) return rc;
5095
5096 /* the settings file path may never be null */
5097 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5098
5099 /* save all VM data excluding snapshots */
5100 bool fNeedsGlobalSaveSettings = false;
5101 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5102 mlock.release();
5103
5104 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5105 {
5106 // save the global settings; for that we should hold only the VirtualBox lock
5107 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5108 rc = mParent->i_saveSettings();
5109 }
5110
5111 return rc;
5112}
5113
5114
5115HRESULT Machine::discardSettings()
5116{
5117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5118
5119 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5120 if (FAILED(rc)) return rc;
5121
5122 /*
5123 * during this rollback, the session will be notified if data has
5124 * been actually changed
5125 */
5126 i_rollback(true /* aNotify */);
5127
5128 return S_OK;
5129}
5130
5131/** @note Locks objects! */
5132HRESULT Machine::unregister(AutoCaller &autoCaller,
5133 CleanupMode_T aCleanupMode,
5134 std::vector<ComPtr<IMedium> > &aMedia)
5135{
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 Guid id(i_getId());
5139
5140 if (mData->mSession.mState != SessionState_Unlocked)
5141 return setError(VBOX_E_INVALID_OBJECT_STATE,
5142 tr("Cannot unregister the machine '%s' while it is locked"),
5143 mUserData->s.strName.c_str());
5144
5145 // wait for state dependents to drop to zero
5146 i_ensureNoStateDependencies();
5147
5148 if (!mData->mAccessible)
5149 {
5150 // inaccessible maschines can only be unregistered; uninitialize ourselves
5151 // here because currently there may be no unregistered that are inaccessible
5152 // (this state combination is not supported). Note releasing the caller and
5153 // leaving the lock before calling uninit()
5154 alock.release();
5155 autoCaller.release();
5156
5157 uninit();
5158
5159 mParent->i_unregisterMachine(this, id);
5160 // calls VirtualBox::i_saveSettings()
5161
5162 return S_OK;
5163 }
5164
5165 HRESULT rc = S_OK;
5166
5167 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5168 // discard saved state
5169 if (mData->mMachineState == MachineState_Saved)
5170 {
5171 // add the saved state file to the list of files the caller should delete
5172 Assert(!mSSData->strStateFilePath.isEmpty());
5173 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5174
5175 mSSData->strStateFilePath.setNull();
5176
5177 // unconditionally set the machine state to powered off, we now
5178 // know no session has locked the machine
5179 mData->mMachineState = MachineState_PoweredOff;
5180 }
5181
5182 size_t cSnapshots = 0;
5183 if (mData->mFirstSnapshot)
5184 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5185 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5186 // fail now before we start detaching media
5187 return setError(VBOX_E_INVALID_OBJECT_STATE,
5188 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5189 mUserData->s.strName.c_str(), cSnapshots);
5190
5191 // This list collects the medium objects from all medium attachments
5192 // which we will detach from the machine and its snapshots, in a specific
5193 // order which allows for closing all media without getting "media in use"
5194 // errors, simply by going through the list from the front to the back:
5195 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5196 // and must be closed before the parent media from the snapshots, or closing the parents
5197 // will fail because they still have children);
5198 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5199 // the root ("first") snapshot of the machine.
5200 MediaList llMedia;
5201
5202 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5203 && mMediumAttachments->size()
5204 )
5205 {
5206 // we have media attachments: detach them all and add the Medium objects to our list
5207 if (aCleanupMode != CleanupMode_UnregisterOnly)
5208 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5209 else
5210 return setError(VBOX_E_INVALID_OBJECT_STATE,
5211 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5212 mUserData->s.strName.c_str(), mMediumAttachments->size());
5213 }
5214
5215 if (cSnapshots)
5216 {
5217 // add the media from the medium attachments of the snapshots to llMedia
5218 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5219 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5220 // into the children first
5221
5222 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5223 MachineState_T oldState = mData->mMachineState;
5224 mData->mMachineState = MachineState_DeletingSnapshot;
5225
5226 // make a copy of the first snapshot so the refcount does not drop to 0
5227 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5228 // because of the AutoCaller voodoo)
5229 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5230
5231 // GO!
5232 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5233
5234 mData->mMachineState = oldState;
5235 }
5236
5237 if (FAILED(rc))
5238 {
5239 i_rollbackMedia();
5240 return rc;
5241 }
5242
5243 // commit all the media changes made above
5244 i_commitMedia();
5245
5246 mData->mRegistered = false;
5247
5248 // machine lock no longer needed
5249 alock.release();
5250
5251 // return media to caller
5252 aMedia.resize(llMedia.size());
5253 size_t i = 0;
5254 for (MediaList::const_iterator
5255 it = llMedia.begin();
5256 it != llMedia.end();
5257 ++it, ++i)
5258 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5259
5260 mParent->i_unregisterMachine(this, id);
5261 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5262
5263 return S_OK;
5264}
5265
5266/**
5267 * Task record for deleting a machine config.
5268 */
5269class Machine::DeleteConfigTask
5270 : public Machine::Task
5271{
5272public:
5273 DeleteConfigTask(Machine *m,
5274 Progress *p,
5275 const Utf8Str &t,
5276 const RTCList<ComPtr<IMedium> > &llMediums,
5277 const StringsList &llFilesToDelete)
5278 : Task(m, p, t),
5279 m_llMediums(llMediums),
5280 m_llFilesToDelete(llFilesToDelete)
5281 {}
5282
5283private:
5284 void handler()
5285 {
5286 try
5287 {
5288 m_pMachine->i_deleteConfigHandler(*this);
5289 }
5290 catch (...)
5291 {
5292 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5293 }
5294 }
5295
5296 RTCList<ComPtr<IMedium> > m_llMediums;
5297 StringsList m_llFilesToDelete;
5298
5299 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5300};
5301
5302/**
5303 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5304 * SessionMachine::taskHandler().
5305 *
5306 * @note Locks this object for writing.
5307 *
5308 * @param task
5309 * @return
5310 */
5311void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5312{
5313 LogFlowThisFuncEnter();
5314
5315 AutoCaller autoCaller(this);
5316 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5317 if (FAILED(autoCaller.rc()))
5318 {
5319 /* we might have been uninitialized because the session was accidentally
5320 * closed by the client, so don't assert */
5321 HRESULT rc = setError(E_FAIL,
5322 tr("The session has been accidentally closed"));
5323 task.m_pProgress->i_notifyComplete(rc);
5324 LogFlowThisFuncLeave();
5325 return;
5326 }
5327
5328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5329
5330 HRESULT rc = S_OK;
5331
5332 try
5333 {
5334 ULONG uLogHistoryCount = 3;
5335 ComPtr<ISystemProperties> systemProperties;
5336 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5337 if (FAILED(rc)) throw rc;
5338
5339 if (!systemProperties.isNull())
5340 {
5341 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5342 if (FAILED(rc)) throw rc;
5343 }
5344
5345 MachineState_T oldState = mData->mMachineState;
5346 i_setMachineState(MachineState_SettingUp);
5347 alock.release();
5348 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5349 {
5350 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5351 {
5352 AutoCaller mac(pMedium);
5353 if (FAILED(mac.rc())) throw mac.rc();
5354 Utf8Str strLocation = pMedium->i_getLocationFull();
5355 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5356 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5357 if (FAILED(rc)) throw rc;
5358 }
5359 if (pMedium->i_isMediumFormatFile())
5360 {
5361 ComPtr<IProgress> pProgress2;
5362 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5363 if (FAILED(rc)) throw rc;
5364 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5365 if (FAILED(rc)) throw rc;
5366 /* Check the result of the asynchronous process. */
5367 LONG iRc;
5368 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5369 if (FAILED(rc)) throw rc;
5370 /* If the thread of the progress object has an error, then
5371 * retrieve the error info from there, or it'll be lost. */
5372 if (FAILED(iRc))
5373 throw setError(ProgressErrorInfo(pProgress2));
5374 }
5375
5376 /* Close the medium, deliberately without checking the return
5377 * code, and without leaving any trace in the error info, as
5378 * a failure here is a very minor issue, which shouldn't happen
5379 * as above we even managed to delete the medium. */
5380 {
5381 ErrorInfoKeeper eik;
5382 pMedium->Close();
5383 }
5384 }
5385 i_setMachineState(oldState);
5386 alock.acquire();
5387
5388 // delete the files pushed on the task list by Machine::Delete()
5389 // (this includes saved states of the machine and snapshots and
5390 // medium storage files from the IMedium list passed in, and the
5391 // machine XML file)
5392 for (StringsList::const_iterator
5393 it = task.m_llFilesToDelete.begin();
5394 it != task.m_llFilesToDelete.end();
5395 ++it)
5396 {
5397 const Utf8Str &strFile = *it;
5398 LogFunc(("Deleting file %s\n", strFile.c_str()));
5399 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5400 if (FAILED(rc)) throw rc;
5401
5402 int vrc = RTFileDelete(strFile.c_str());
5403 if (RT_FAILURE(vrc))
5404 throw setError(VBOX_E_IPRT_ERROR,
5405 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5406 }
5407
5408 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5409 if (FAILED(rc)) throw rc;
5410
5411 /* delete the settings only when the file actually exists */
5412 if (mData->pMachineConfigFile->fileExists())
5413 {
5414 /* Delete any backup or uncommitted XML files. Ignore failures.
5415 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5416 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5417 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5418 RTFileDelete(otherXml.c_str());
5419 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5420 RTFileDelete(otherXml.c_str());
5421
5422 /* delete the Logs folder, nothing important should be left
5423 * there (we don't check for errors because the user might have
5424 * some private files there that we don't want to delete) */
5425 Utf8Str logFolder;
5426 getLogFolder(logFolder);
5427 Assert(logFolder.length());
5428 if (RTDirExists(logFolder.c_str()))
5429 {
5430 /* Delete all VBox.log[.N] files from the Logs folder
5431 * (this must be in sync with the rotation logic in
5432 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5433 * files that may have been created by the GUI. */
5434 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5435 logFolder.c_str(), RTPATH_DELIMITER);
5436 RTFileDelete(log.c_str());
5437 log = Utf8StrFmt("%s%cVBox.png",
5438 logFolder.c_str(), RTPATH_DELIMITER);
5439 RTFileDelete(log.c_str());
5440 for (int i = uLogHistoryCount; i > 0; i--)
5441 {
5442 log = Utf8StrFmt("%s%cVBox.log.%d",
5443 logFolder.c_str(), RTPATH_DELIMITER, i);
5444 RTFileDelete(log.c_str());
5445 log = Utf8StrFmt("%s%cVBox.png.%d",
5446 logFolder.c_str(), RTPATH_DELIMITER, i);
5447 RTFileDelete(log.c_str());
5448 }
5449#if defined(RT_OS_WINDOWS)
5450 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5451 RTFileDelete(log.c_str());
5452 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5453 RTFileDelete(log.c_str());
5454#endif
5455
5456 RTDirRemove(logFolder.c_str());
5457 }
5458
5459 /* delete the Snapshots folder, nothing important should be left
5460 * there (we don't check for errors because the user might have
5461 * some private files there that we don't want to delete) */
5462 Utf8Str strFullSnapshotFolder;
5463 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5464 Assert(!strFullSnapshotFolder.isEmpty());
5465 if (RTDirExists(strFullSnapshotFolder.c_str()))
5466 RTDirRemove(strFullSnapshotFolder.c_str());
5467
5468 // delete the directory that contains the settings file, but only
5469 // if it matches the VM name
5470 Utf8Str settingsDir;
5471 if (i_isInOwnDir(&settingsDir))
5472 RTDirRemove(settingsDir.c_str());
5473 }
5474
5475 alock.release();
5476
5477 mParent->i_saveModifiedRegistries();
5478 }
5479 catch (HRESULT aRC) { rc = aRC; }
5480
5481 task.m_pProgress->i_notifyComplete(rc);
5482
5483 LogFlowThisFuncLeave();
5484}
5485
5486HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5487{
5488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5489
5490 HRESULT rc = i_checkStateDependency(MutableStateDep);
5491 if (FAILED(rc)) return rc;
5492
5493 if (mData->mRegistered)
5494 return setError(VBOX_E_INVALID_VM_STATE,
5495 tr("Cannot delete settings of a registered machine"));
5496
5497 // collect files to delete
5498 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5499 if (mData->pMachineConfigFile->fileExists())
5500 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5501
5502 RTCList<ComPtr<IMedium> > llMediums;
5503 for (size_t i = 0; i < aMedia.size(); ++i)
5504 {
5505 IMedium *pIMedium(aMedia[i]);
5506 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5507 if (pMedium.isNull())
5508 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5509 SafeArray<BSTR> ids;
5510 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5511 if (FAILED(rc)) return rc;
5512 /* At this point the medium should not have any back references
5513 * anymore. If it has it is attached to another VM and *must* not
5514 * deleted. */
5515 if (ids.size() < 1)
5516 llMediums.append(pMedium);
5517 }
5518
5519 ComObjPtr<Progress> pProgress;
5520 pProgress.createObject();
5521 rc = pProgress->init(i_getVirtualBox(),
5522 static_cast<IMachine*>(this) /* aInitiator */,
5523 tr("Deleting files"),
5524 true /* fCancellable */,
5525 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5526 tr("Collecting file inventory"));
5527 if (FAILED(rc))
5528 return rc;
5529
5530 /* create and start the task on a separate thread (note that it will not
5531 * start working until we release alock) */
5532 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5533 rc = pTask->createThread();
5534 if (FAILED(rc))
5535 return rc;
5536
5537 pProgress.queryInterfaceTo(aProgress.asOutParam());
5538
5539 LogFlowFuncLeave();
5540
5541 return S_OK;
5542}
5543
5544HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5545{
5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5547
5548 ComObjPtr<Snapshot> pSnapshot;
5549 HRESULT rc;
5550
5551 if (aNameOrId.isEmpty())
5552 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5553 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5554 else
5555 {
5556 Guid uuid(aNameOrId);
5557 if (uuid.isValid())
5558 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5559 else
5560 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5561 }
5562 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5563
5564 return rc;
5565}
5566
5567HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5568{
5569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5570
5571 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5572 if (FAILED(rc)) return rc;
5573
5574 ComObjPtr<SharedFolder> sharedFolder;
5575 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5576 if (SUCCEEDED(rc))
5577 return setError(VBOX_E_OBJECT_IN_USE,
5578 tr("Shared folder named '%s' already exists"),
5579 aName.c_str());
5580
5581 sharedFolder.createObject();
5582 rc = sharedFolder->init(i_getMachine(),
5583 aName,
5584 aHostPath,
5585 !!aWritable,
5586 !!aAutomount,
5587 true /* fFailOnError */);
5588 if (FAILED(rc)) return rc;
5589
5590 i_setModified(IsModified_SharedFolders);
5591 mHWData.backup();
5592 mHWData->mSharedFolders.push_back(sharedFolder);
5593
5594 /* inform the direct session if any */
5595 alock.release();
5596 i_onSharedFolderChange();
5597
5598 return S_OK;
5599}
5600
5601HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5602{
5603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5604
5605 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5606 if (FAILED(rc)) return rc;
5607
5608 ComObjPtr<SharedFolder> sharedFolder;
5609 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5610 if (FAILED(rc)) return rc;
5611
5612 i_setModified(IsModified_SharedFolders);
5613 mHWData.backup();
5614 mHWData->mSharedFolders.remove(sharedFolder);
5615
5616 /* inform the direct session if any */
5617 alock.release();
5618 i_onSharedFolderChange();
5619
5620 return S_OK;
5621}
5622
5623HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5624{
5625 /* start with No */
5626 *aCanShow = FALSE;
5627
5628 ComPtr<IInternalSessionControl> directControl;
5629 {
5630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5631
5632 if (mData->mSession.mState != SessionState_Locked)
5633 return setError(VBOX_E_INVALID_VM_STATE,
5634 tr("Machine is not locked for session (session state: %s)"),
5635 Global::stringifySessionState(mData->mSession.mState));
5636
5637 if (mData->mSession.mLockType == LockType_VM)
5638 directControl = mData->mSession.mDirectControl;
5639 }
5640
5641 /* ignore calls made after #OnSessionEnd() is called */
5642 if (!directControl)
5643 return S_OK;
5644
5645 LONG64 dummy;
5646 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5647}
5648
5649HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5650{
5651 ComPtr<IInternalSessionControl> directControl;
5652 {
5653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5654
5655 if (mData->mSession.mState != SessionState_Locked)
5656 return setError(E_FAIL,
5657 tr("Machine is not locked for session (session state: %s)"),
5658 Global::stringifySessionState(mData->mSession.mState));
5659
5660 if (mData->mSession.mLockType == LockType_VM)
5661 directControl = mData->mSession.mDirectControl;
5662 }
5663
5664 /* ignore calls made after #OnSessionEnd() is called */
5665 if (!directControl)
5666 return S_OK;
5667
5668 BOOL dummy;
5669 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5670}
5671
5672#ifdef VBOX_WITH_GUEST_PROPS
5673/**
5674 * Look up a guest property in VBoxSVC's internal structures.
5675 */
5676HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5677 com::Utf8Str &aValue,
5678 LONG64 *aTimestamp,
5679 com::Utf8Str &aFlags) const
5680{
5681 using namespace guestProp;
5682
5683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5684 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5685
5686 if (it != mHWData->mGuestProperties.end())
5687 {
5688 char szFlags[MAX_FLAGS_LEN + 1];
5689 aValue = it->second.strValue;
5690 *aTimestamp = it->second.mTimestamp;
5691 writeFlags(it->second.mFlags, szFlags);
5692 aFlags = Utf8Str(szFlags);
5693 }
5694
5695 return S_OK;
5696}
5697
5698/**
5699 * Query the VM that a guest property belongs to for the property.
5700 * @returns E_ACCESSDENIED if the VM process is not available or not
5701 * currently handling queries and the lookup should then be done in
5702 * VBoxSVC.
5703 */
5704HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5705 com::Utf8Str &aValue,
5706 LONG64 *aTimestamp,
5707 com::Utf8Str &aFlags) const
5708{
5709 HRESULT rc = S_OK;
5710 BSTR bValue = NULL;
5711 BSTR bFlags = NULL;
5712
5713 ComPtr<IInternalSessionControl> directControl;
5714 {
5715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5716 if (mData->mSession.mLockType == LockType_VM)
5717 directControl = mData->mSession.mDirectControl;
5718 }
5719
5720 /* ignore calls made after #OnSessionEnd() is called */
5721 if (!directControl)
5722 rc = E_ACCESSDENIED;
5723 else
5724 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5725 0 /* accessMode */,
5726 &bValue, aTimestamp, &bFlags);
5727
5728 aValue = bValue;
5729 aFlags = bFlags;
5730
5731 return rc;
5732}
5733#endif // VBOX_WITH_GUEST_PROPS
5734
5735HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5736 com::Utf8Str &aValue,
5737 LONG64 *aTimestamp,
5738 com::Utf8Str &aFlags)
5739{
5740#ifndef VBOX_WITH_GUEST_PROPS
5741 ReturnComNotImplemented();
5742#else // VBOX_WITH_GUEST_PROPS
5743
5744 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5745
5746 if (rc == E_ACCESSDENIED)
5747 /* The VM is not running or the service is not (yet) accessible */
5748 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5749 return rc;
5750#endif // VBOX_WITH_GUEST_PROPS
5751}
5752
5753HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5754{
5755 LONG64 dummyTimestamp;
5756 com::Utf8Str dummyFlags;
5757 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5758 return rc;
5759
5760}
5761HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5762{
5763 com::Utf8Str dummyFlags;
5764 com::Utf8Str dummyValue;
5765 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5766 return rc;
5767}
5768
5769#ifdef VBOX_WITH_GUEST_PROPS
5770/**
5771 * Set a guest property in VBoxSVC's internal structures.
5772 */
5773HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5774 const com::Utf8Str &aFlags, bool fDelete)
5775{
5776 using namespace guestProp;
5777
5778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5779 HRESULT rc = S_OK;
5780
5781 rc = i_checkStateDependency(MutableOrSavedStateDep);
5782 if (FAILED(rc)) return rc;
5783
5784 try
5785 {
5786 uint32_t fFlags = NILFLAG;
5787 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5788 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5789
5790 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5791 if (it == mHWData->mGuestProperties.end())
5792 {
5793 if (!fDelete)
5794 {
5795 i_setModified(IsModified_MachineData);
5796 mHWData.backupEx();
5797
5798 RTTIMESPEC time;
5799 HWData::GuestProperty prop;
5800 prop.strValue = Bstr(aValue).raw();
5801 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5802 prop.mFlags = fFlags;
5803 mHWData->mGuestProperties[aName] = prop;
5804 }
5805 }
5806 else
5807 {
5808 if (it->second.mFlags & (RDONLYHOST))
5809 {
5810 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5811 }
5812 else
5813 {
5814 i_setModified(IsModified_MachineData);
5815 mHWData.backupEx();
5816
5817 /* The backupEx() operation invalidates our iterator,
5818 * so get a new one. */
5819 it = mHWData->mGuestProperties.find(aName);
5820 Assert(it != mHWData->mGuestProperties.end());
5821
5822 if (!fDelete)
5823 {
5824 RTTIMESPEC time;
5825 it->second.strValue = aValue;
5826 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5827 it->second.mFlags = fFlags;
5828 }
5829 else
5830 mHWData->mGuestProperties.erase(it);
5831 }
5832 }
5833
5834 if (SUCCEEDED(rc))
5835 {
5836 alock.release();
5837
5838 mParent->i_onGuestPropertyChange(mData->mUuid,
5839 Bstr(aName).raw(),
5840 Bstr(aValue).raw(),
5841 Bstr(aFlags).raw());
5842 }
5843 }
5844 catch (std::bad_alloc &)
5845 {
5846 rc = E_OUTOFMEMORY;
5847 }
5848
5849 return rc;
5850}
5851
5852/**
5853 * Set a property on the VM that that property belongs to.
5854 * @returns E_ACCESSDENIED if the VM process is not available or not
5855 * currently handling queries and the setting should then be done in
5856 * VBoxSVC.
5857 */
5858HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5859 const com::Utf8Str &aFlags, bool fDelete)
5860{
5861 HRESULT rc;
5862
5863 try
5864 {
5865 ComPtr<IInternalSessionControl> directControl;
5866 {
5867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5868 if (mData->mSession.mLockType == LockType_VM)
5869 directControl = mData->mSession.mDirectControl;
5870 }
5871
5872 BSTR dummy = NULL; /* will not be changed (setter) */
5873 LONG64 dummy64;
5874 if (!directControl)
5875 rc = E_ACCESSDENIED;
5876 else
5877 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5878 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5879 fDelete? 2: 1 /* accessMode */,
5880 &dummy, &dummy64, &dummy);
5881 }
5882 catch (std::bad_alloc &)
5883 {
5884 rc = E_OUTOFMEMORY;
5885 }
5886
5887 return rc;
5888}
5889#endif // VBOX_WITH_GUEST_PROPS
5890
5891HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5892 const com::Utf8Str &aFlags)
5893{
5894#ifndef VBOX_WITH_GUEST_PROPS
5895 ReturnComNotImplemented();
5896#else // VBOX_WITH_GUEST_PROPS
5897 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5898 if (rc == E_ACCESSDENIED)
5899 /* The VM is not running or the service is not (yet) accessible */
5900 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5901 return rc;
5902#endif // VBOX_WITH_GUEST_PROPS
5903}
5904
5905HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5906{
5907 return setGuestProperty(aProperty, aValue, "");
5908}
5909
5910HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5911{
5912#ifndef VBOX_WITH_GUEST_PROPS
5913 ReturnComNotImplemented();
5914#else // VBOX_WITH_GUEST_PROPS
5915 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5916 if (rc == E_ACCESSDENIED)
5917 /* The VM is not running or the service is not (yet) accessible */
5918 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5919 return rc;
5920#endif // VBOX_WITH_GUEST_PROPS
5921}
5922
5923#ifdef VBOX_WITH_GUEST_PROPS
5924/**
5925 * Enumerate the guest properties in VBoxSVC's internal structures.
5926 */
5927HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5928 std::vector<com::Utf8Str> &aNames,
5929 std::vector<com::Utf8Str> &aValues,
5930 std::vector<LONG64> &aTimestamps,
5931 std::vector<com::Utf8Str> &aFlags)
5932{
5933 using namespace guestProp;
5934
5935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5936 Utf8Str strPatterns(aPatterns);
5937
5938 HWData::GuestPropertyMap propMap;
5939
5940 /*
5941 * Look for matching patterns and build up a list.
5942 */
5943 for (HWData::GuestPropertyMap::const_iterator
5944 it = mHWData->mGuestProperties.begin();
5945 it != mHWData->mGuestProperties.end();
5946 ++it)
5947 {
5948 if ( strPatterns.isEmpty()
5949 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5950 RTSTR_MAX,
5951 it->first.c_str(),
5952 RTSTR_MAX,
5953 NULL)
5954 )
5955 propMap.insert(*it);
5956 }
5957
5958 alock.release();
5959
5960 /*
5961 * And build up the arrays for returning the property information.
5962 */
5963 size_t cEntries = propMap.size();
5964
5965 aNames.resize(cEntries);
5966 aValues.resize(cEntries);
5967 aTimestamps.resize(cEntries);
5968 aFlags.resize(cEntries);
5969
5970 char szFlags[MAX_FLAGS_LEN + 1];
5971 size_t i = 0;
5972 for (HWData::GuestPropertyMap::const_iterator
5973 it = propMap.begin();
5974 it != propMap.end();
5975 ++it, ++i)
5976 {
5977 aNames[i] = it->first;
5978 aValues[i] = it->second.strValue;
5979 aTimestamps[i] = it->second.mTimestamp;
5980 writeFlags(it->second.mFlags, szFlags);
5981 aFlags[i] = Utf8Str(szFlags);
5982 }
5983
5984 return S_OK;
5985}
5986
5987/**
5988 * Enumerate the properties managed by a VM.
5989 * @returns E_ACCESSDENIED if the VM process is not available or not
5990 * currently handling queries and the setting should then be done in
5991 * VBoxSVC.
5992 */
5993HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5994 std::vector<com::Utf8Str> &aNames,
5995 std::vector<com::Utf8Str> &aValues,
5996 std::vector<LONG64> &aTimestamps,
5997 std::vector<com::Utf8Str> &aFlags)
5998{
5999 HRESULT rc;
6000 ComPtr<IInternalSessionControl> directControl;
6001 {
6002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6003 if (mData->mSession.mLockType == LockType_VM)
6004 directControl = mData->mSession.mDirectControl;
6005 }
6006
6007 com::SafeArray<BSTR> bNames;
6008 com::SafeArray<BSTR> bValues;
6009 com::SafeArray<LONG64> bTimestamps;
6010 com::SafeArray<BSTR> bFlags;
6011
6012 if (!directControl)
6013 rc = E_ACCESSDENIED;
6014 else
6015 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6016 ComSafeArrayAsOutParam(bNames),
6017 ComSafeArrayAsOutParam(bValues),
6018 ComSafeArrayAsOutParam(bTimestamps),
6019 ComSafeArrayAsOutParam(bFlags));
6020 size_t i;
6021 aNames.resize(bNames.size());
6022 for (i = 0; i < bNames.size(); ++i)
6023 aNames[i] = Utf8Str(bNames[i]);
6024 aValues.resize(bValues.size());
6025 for (i = 0; i < bValues.size(); ++i)
6026 aValues[i] = Utf8Str(bValues[i]);
6027 aTimestamps.resize(bTimestamps.size());
6028 for (i = 0; i < bTimestamps.size(); ++i)
6029 aTimestamps[i] = bTimestamps[i];
6030 aFlags.resize(bFlags.size());
6031 for (i = 0; i < bFlags.size(); ++i)
6032 aFlags[i] = Utf8Str(bFlags[i]);
6033
6034 return rc;
6035}
6036#endif // VBOX_WITH_GUEST_PROPS
6037HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6038 std::vector<com::Utf8Str> &aNames,
6039 std::vector<com::Utf8Str> &aValues,
6040 std::vector<LONG64> &aTimestamps,
6041 std::vector<com::Utf8Str> &aFlags)
6042{
6043#ifndef VBOX_WITH_GUEST_PROPS
6044 ReturnComNotImplemented();
6045#else // VBOX_WITH_GUEST_PROPS
6046
6047 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6048
6049 if (rc == E_ACCESSDENIED)
6050 /* The VM is not running or the service is not (yet) accessible */
6051 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6052 return rc;
6053#endif // VBOX_WITH_GUEST_PROPS
6054}
6055
6056HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6057 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6058{
6059 MediumAttachmentList atts;
6060
6061 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6062 if (FAILED(rc)) return rc;
6063
6064 aMediumAttachments.resize(atts.size());
6065 size_t i = 0;
6066 for (MediumAttachmentList::const_iterator
6067 it = atts.begin();
6068 it != atts.end();
6069 ++it, ++i)
6070 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6071
6072 return S_OK;
6073}
6074
6075HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6076 LONG aControllerPort,
6077 LONG aDevice,
6078 ComPtr<IMediumAttachment> &aAttachment)
6079{
6080 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6081 aName.c_str(), aControllerPort, aDevice));
6082
6083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6084
6085 aAttachment = NULL;
6086
6087 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6088 aName,
6089 aControllerPort,
6090 aDevice);
6091 if (pAttach.isNull())
6092 return setError(VBOX_E_OBJECT_NOT_FOUND,
6093 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6094 aDevice, aControllerPort, aName.c_str());
6095
6096 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6097
6098 return S_OK;
6099}
6100
6101
6102HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6103 StorageBus_T aConnectionType,
6104 ComPtr<IStorageController> &aController)
6105{
6106 if ( (aConnectionType <= StorageBus_Null)
6107 || (aConnectionType > StorageBus_PCIe))
6108 return setError(E_INVALIDARG,
6109 tr("Invalid connection type: %d"),
6110 aConnectionType);
6111
6112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6113
6114 HRESULT rc = i_checkStateDependency(MutableStateDep);
6115 if (FAILED(rc)) return rc;
6116
6117 /* try to find one with the name first. */
6118 ComObjPtr<StorageController> ctrl;
6119
6120 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6121 if (SUCCEEDED(rc))
6122 return setError(VBOX_E_OBJECT_IN_USE,
6123 tr("Storage controller named '%s' already exists"),
6124 aName.c_str());
6125
6126 ctrl.createObject();
6127
6128 /* get a new instance number for the storage controller */
6129 ULONG ulInstance = 0;
6130 bool fBootable = true;
6131 for (StorageControllerList::const_iterator
6132 it = mStorageControllers->begin();
6133 it != mStorageControllers->end();
6134 ++it)
6135 {
6136 if ((*it)->i_getStorageBus() == aConnectionType)
6137 {
6138 ULONG ulCurInst = (*it)->i_getInstance();
6139
6140 if (ulCurInst >= ulInstance)
6141 ulInstance = ulCurInst + 1;
6142
6143 /* Only one controller of each type can be marked as bootable. */
6144 if ((*it)->i_getBootable())
6145 fBootable = false;
6146 }
6147 }
6148
6149 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6150 if (FAILED(rc)) return rc;
6151
6152 i_setModified(IsModified_Storage);
6153 mStorageControllers.backup();
6154 mStorageControllers->push_back(ctrl);
6155
6156 ctrl.queryInterfaceTo(aController.asOutParam());
6157
6158 /* inform the direct session if any */
6159 alock.release();
6160 i_onStorageControllerChange();
6161
6162 return S_OK;
6163}
6164
6165HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6166 ComPtr<IStorageController> &aStorageController)
6167{
6168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6169
6170 ComObjPtr<StorageController> ctrl;
6171
6172 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6173 if (SUCCEEDED(rc))
6174 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6175
6176 return rc;
6177}
6178
6179HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6180 ULONG aInstance,
6181 ComPtr<IStorageController> &aStorageController)
6182{
6183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6184
6185 for (StorageControllerList::const_iterator
6186 it = mStorageControllers->begin();
6187 it != mStorageControllers->end();
6188 ++it)
6189 {
6190 if ( (*it)->i_getStorageBus() == aConnectionType
6191 && (*it)->i_getInstance() == aInstance)
6192 {
6193 (*it).queryInterfaceTo(aStorageController.asOutParam());
6194 return S_OK;
6195 }
6196 }
6197
6198 return setError(VBOX_E_OBJECT_NOT_FOUND,
6199 tr("Could not find a storage controller with instance number '%lu'"),
6200 aInstance);
6201}
6202
6203HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6204{
6205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 HRESULT rc = i_checkStateDependency(MutableStateDep);
6208 if (FAILED(rc)) return rc;
6209
6210 ComObjPtr<StorageController> ctrl;
6211
6212 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6213 if (SUCCEEDED(rc))
6214 {
6215 /* Ensure that only one controller of each type is marked as bootable. */
6216 if (aBootable == TRUE)
6217 {
6218 for (StorageControllerList::const_iterator
6219 it = mStorageControllers->begin();
6220 it != mStorageControllers->end();
6221 ++it)
6222 {
6223 ComObjPtr<StorageController> aCtrl = (*it);
6224
6225 if ( (aCtrl->i_getName() != aName)
6226 && aCtrl->i_getBootable() == TRUE
6227 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6228 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6229 {
6230 aCtrl->i_setBootable(FALSE);
6231 break;
6232 }
6233 }
6234 }
6235
6236 if (SUCCEEDED(rc))
6237 {
6238 ctrl->i_setBootable(aBootable);
6239 i_setModified(IsModified_Storage);
6240 }
6241 }
6242
6243 if (SUCCEEDED(rc))
6244 {
6245 /* inform the direct session if any */
6246 alock.release();
6247 i_onStorageControllerChange();
6248 }
6249
6250 return rc;
6251}
6252
6253HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6254{
6255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6256
6257 HRESULT rc = i_checkStateDependency(MutableStateDep);
6258 if (FAILED(rc)) return rc;
6259
6260 ComObjPtr<StorageController> ctrl;
6261 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6262 if (FAILED(rc)) return rc;
6263
6264 {
6265 /* find all attached devices to the appropriate storage controller and detach them all */
6266 // make a temporary list because detachDevice invalidates iterators into
6267 // mMediumAttachments
6268 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6269
6270 for (MediumAttachmentList::const_iterator
6271 it = llAttachments2.begin();
6272 it != llAttachments2.end();
6273 ++it)
6274 {
6275 MediumAttachment *pAttachTemp = *it;
6276
6277 AutoCaller localAutoCaller(pAttachTemp);
6278 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6279
6280 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6281
6282 if (pAttachTemp->i_getControllerName() == aName)
6283 {
6284 rc = i_detachDevice(pAttachTemp, alock, NULL);
6285 if (FAILED(rc)) return rc;
6286 }
6287 }
6288 }
6289
6290 /* We can remove it now. */
6291 i_setModified(IsModified_Storage);
6292 mStorageControllers.backup();
6293
6294 ctrl->i_unshare();
6295
6296 mStorageControllers->remove(ctrl);
6297
6298 /* inform the direct session if any */
6299 alock.release();
6300 i_onStorageControllerChange();
6301
6302 return S_OK;
6303}
6304
6305HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6306 ComPtr<IUSBController> &aController)
6307{
6308 if ( (aType <= USBControllerType_Null)
6309 || (aType >= USBControllerType_Last))
6310 return setError(E_INVALIDARG,
6311 tr("Invalid USB controller type: %d"),
6312 aType);
6313
6314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6315
6316 HRESULT rc = i_checkStateDependency(MutableStateDep);
6317 if (FAILED(rc)) return rc;
6318
6319 /* try to find one with the same type first. */
6320 ComObjPtr<USBController> ctrl;
6321
6322 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6323 if (SUCCEEDED(rc))
6324 return setError(VBOX_E_OBJECT_IN_USE,
6325 tr("USB controller named '%s' already exists"),
6326 aName.c_str());
6327
6328 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6329 ULONG maxInstances;
6330 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6331 if (FAILED(rc))
6332 return rc;
6333
6334 ULONG cInstances = i_getUSBControllerCountByType(aType);
6335 if (cInstances >= maxInstances)
6336 return setError(E_INVALIDARG,
6337 tr("Too many USB controllers of this type"));
6338
6339 ctrl.createObject();
6340
6341 rc = ctrl->init(this, aName, aType);
6342 if (FAILED(rc)) return rc;
6343
6344 i_setModified(IsModified_USB);
6345 mUSBControllers.backup();
6346 mUSBControllers->push_back(ctrl);
6347
6348 ctrl.queryInterfaceTo(aController.asOutParam());
6349
6350 /* inform the direct session if any */
6351 alock.release();
6352 i_onUSBControllerChange();
6353
6354 return S_OK;
6355}
6356
6357HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6358{
6359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6360
6361 ComObjPtr<USBController> ctrl;
6362
6363 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6364 if (SUCCEEDED(rc))
6365 ctrl.queryInterfaceTo(aController.asOutParam());
6366
6367 return rc;
6368}
6369
6370HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6371 ULONG *aControllers)
6372{
6373 if ( (aType <= USBControllerType_Null)
6374 || (aType >= USBControllerType_Last))
6375 return setError(E_INVALIDARG,
6376 tr("Invalid USB controller type: %d"),
6377 aType);
6378
6379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6380
6381 ComObjPtr<USBController> ctrl;
6382
6383 *aControllers = i_getUSBControllerCountByType(aType);
6384
6385 return S_OK;
6386}
6387
6388HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6389{
6390
6391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6392
6393 HRESULT rc = i_checkStateDependency(MutableStateDep);
6394 if (FAILED(rc)) return rc;
6395
6396 ComObjPtr<USBController> ctrl;
6397 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6398 if (FAILED(rc)) return rc;
6399
6400 i_setModified(IsModified_USB);
6401 mUSBControllers.backup();
6402
6403 ctrl->i_unshare();
6404
6405 mUSBControllers->remove(ctrl);
6406
6407 /* inform the direct session if any */
6408 alock.release();
6409 i_onUSBControllerChange();
6410
6411 return S_OK;
6412}
6413
6414HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6415 ULONG *aOriginX,
6416 ULONG *aOriginY,
6417 ULONG *aWidth,
6418 ULONG *aHeight,
6419 BOOL *aEnabled)
6420{
6421 uint32_t u32OriginX= 0;
6422 uint32_t u32OriginY= 0;
6423 uint32_t u32Width = 0;
6424 uint32_t u32Height = 0;
6425 uint16_t u16Flags = 0;
6426
6427 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6428 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6429 if (RT_FAILURE(vrc))
6430 {
6431#ifdef RT_OS_WINDOWS
6432 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6433 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6434 * So just assign fEnable to TRUE again.
6435 * The right fix would be to change GUI API wrappers to make sure that parameters
6436 * are changed only if API succeeds.
6437 */
6438 *aEnabled = TRUE;
6439#endif
6440 return setError(VBOX_E_IPRT_ERROR,
6441 tr("Saved guest size is not available (%Rrc)"),
6442 vrc);
6443 }
6444
6445 *aOriginX = u32OriginX;
6446 *aOriginY = u32OriginY;
6447 *aWidth = u32Width;
6448 *aHeight = u32Height;
6449 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6450
6451 return S_OK;
6452}
6453
6454HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6455 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6456{
6457 if (aScreenId != 0)
6458 return E_NOTIMPL;
6459
6460 if ( aBitmapFormat != BitmapFormat_BGR0
6461 && aBitmapFormat != BitmapFormat_BGRA
6462 && aBitmapFormat != BitmapFormat_RGBA
6463 && aBitmapFormat != BitmapFormat_PNG)
6464 return setError(E_NOTIMPL,
6465 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6466
6467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6468
6469 uint8_t *pu8Data = NULL;
6470 uint32_t cbData = 0;
6471 uint32_t u32Width = 0;
6472 uint32_t u32Height = 0;
6473
6474 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6475
6476 if (RT_FAILURE(vrc))
6477 return setError(VBOX_E_IPRT_ERROR,
6478 tr("Saved thumbnail data is not available (%Rrc)"),
6479 vrc);
6480
6481 HRESULT hr = S_OK;
6482
6483 *aWidth = u32Width;
6484 *aHeight = u32Height;
6485
6486 if (cbData > 0)
6487 {
6488 /* Convert pixels to the format expected by the API caller. */
6489 if (aBitmapFormat == BitmapFormat_BGR0)
6490 {
6491 /* [0] B, [1] G, [2] R, [3] 0. */
6492 aData.resize(cbData);
6493 memcpy(&aData.front(), pu8Data, cbData);
6494 }
6495 else if (aBitmapFormat == BitmapFormat_BGRA)
6496 {
6497 /* [0] B, [1] G, [2] R, [3] A. */
6498 aData.resize(cbData);
6499 for (uint32_t i = 0; i < cbData; i += 4)
6500 {
6501 aData[i] = pu8Data[i];
6502 aData[i + 1] = pu8Data[i + 1];
6503 aData[i + 2] = pu8Data[i + 2];
6504 aData[i + 3] = 0xff;
6505 }
6506 }
6507 else if (aBitmapFormat == BitmapFormat_RGBA)
6508 {
6509 /* [0] R, [1] G, [2] B, [3] A. */
6510 aData.resize(cbData);
6511 for (uint32_t i = 0; i < cbData; i += 4)
6512 {
6513 aData[i] = pu8Data[i + 2];
6514 aData[i + 1] = pu8Data[i + 1];
6515 aData[i + 2] = pu8Data[i];
6516 aData[i + 3] = 0xff;
6517 }
6518 }
6519 else if (aBitmapFormat == BitmapFormat_PNG)
6520 {
6521 uint8_t *pu8PNG = NULL;
6522 uint32_t cbPNG = 0;
6523 uint32_t cxPNG = 0;
6524 uint32_t cyPNG = 0;
6525
6526 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6527
6528 if (RT_SUCCESS(vrc))
6529 {
6530 aData.resize(cbPNG);
6531 if (cbPNG)
6532 memcpy(&aData.front(), pu8PNG, cbPNG);
6533 }
6534 else
6535 hr = setError(VBOX_E_IPRT_ERROR,
6536 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6537 vrc);
6538
6539 RTMemFree(pu8PNG);
6540 }
6541 }
6542
6543 freeSavedDisplayScreenshot(pu8Data);
6544
6545 return hr;
6546}
6547
6548HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6549 ULONG *aWidth,
6550 ULONG *aHeight,
6551 std::vector<BitmapFormat_T> &aBitmapFormats)
6552{
6553 if (aScreenId != 0)
6554 return E_NOTIMPL;
6555
6556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 uint8_t *pu8Data = NULL;
6559 uint32_t cbData = 0;
6560 uint32_t u32Width = 0;
6561 uint32_t u32Height = 0;
6562
6563 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6564
6565 if (RT_FAILURE(vrc))
6566 return setError(VBOX_E_IPRT_ERROR,
6567 tr("Saved screenshot data is not available (%Rrc)"),
6568 vrc);
6569
6570 *aWidth = u32Width;
6571 *aHeight = u32Height;
6572 aBitmapFormats.resize(1);
6573 aBitmapFormats[0] = BitmapFormat_PNG;
6574
6575 freeSavedDisplayScreenshot(pu8Data);
6576
6577 return S_OK;
6578}
6579
6580HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6581 BitmapFormat_T aBitmapFormat,
6582 ULONG *aWidth,
6583 ULONG *aHeight,
6584 std::vector<BYTE> &aData)
6585{
6586 if (aScreenId != 0)
6587 return E_NOTIMPL;
6588
6589 if (aBitmapFormat != BitmapFormat_PNG)
6590 return E_NOTIMPL;
6591
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 uint8_t *pu8Data = NULL;
6595 uint32_t cbData = 0;
6596 uint32_t u32Width = 0;
6597 uint32_t u32Height = 0;
6598
6599 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6600
6601 if (RT_FAILURE(vrc))
6602 return setError(VBOX_E_IPRT_ERROR,
6603 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6604 vrc);
6605
6606 *aWidth = u32Width;
6607 *aHeight = u32Height;
6608
6609 aData.resize(cbData);
6610 if (cbData)
6611 memcpy(&aData.front(), pu8Data, cbData);
6612
6613 freeSavedDisplayScreenshot(pu8Data);
6614
6615 return S_OK;
6616}
6617
6618HRESULT Machine::hotPlugCPU(ULONG aCpu)
6619{
6620 HRESULT rc = S_OK;
6621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6622
6623 if (!mHWData->mCPUHotPlugEnabled)
6624 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6625
6626 if (aCpu >= mHWData->mCPUCount)
6627 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6628
6629 if (mHWData->mCPUAttached[aCpu])
6630 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6631
6632 alock.release();
6633 rc = i_onCPUChange(aCpu, false);
6634 alock.acquire();
6635 if (FAILED(rc)) return rc;
6636
6637 i_setModified(IsModified_MachineData);
6638 mHWData.backup();
6639 mHWData->mCPUAttached[aCpu] = true;
6640
6641 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6642 if (Global::IsOnline(mData->mMachineState))
6643 i_saveSettings(NULL);
6644
6645 return S_OK;
6646}
6647
6648HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6649{
6650 HRESULT rc = S_OK;
6651
6652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6653
6654 if (!mHWData->mCPUHotPlugEnabled)
6655 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6656
6657 if (aCpu >= SchemaDefs::MaxCPUCount)
6658 return setError(E_INVALIDARG,
6659 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6660 SchemaDefs::MaxCPUCount);
6661
6662 if (!mHWData->mCPUAttached[aCpu])
6663 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6664
6665 /* CPU 0 can't be detached */
6666 if (aCpu == 0)
6667 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6668
6669 alock.release();
6670 rc = i_onCPUChange(aCpu, true);
6671 alock.acquire();
6672 if (FAILED(rc)) return rc;
6673
6674 i_setModified(IsModified_MachineData);
6675 mHWData.backup();
6676 mHWData->mCPUAttached[aCpu] = false;
6677
6678 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6679 if (Global::IsOnline(mData->mMachineState))
6680 i_saveSettings(NULL);
6681
6682 return S_OK;
6683}
6684
6685HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6686{
6687 *aAttached = false;
6688
6689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6690
6691 /* If hotplug is enabled the CPU is always enabled. */
6692 if (!mHWData->mCPUHotPlugEnabled)
6693 {
6694 if (aCpu < mHWData->mCPUCount)
6695 *aAttached = true;
6696 }
6697 else
6698 {
6699 if (aCpu < SchemaDefs::MaxCPUCount)
6700 *aAttached = mHWData->mCPUAttached[aCpu];
6701 }
6702
6703 return S_OK;
6704}
6705
6706HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6707{
6708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6709
6710 Utf8Str log = i_getLogFilename(aIdx);
6711 if (!RTFileExists(log.c_str()))
6712 log.setNull();
6713 aFilename = log;
6714
6715 return S_OK;
6716}
6717
6718HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6719{
6720 if (aSize < 0)
6721 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6722
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 HRESULT rc = S_OK;
6726 Utf8Str log = i_getLogFilename(aIdx);
6727
6728 /* do not unnecessarily hold the lock while doing something which does
6729 * not need the lock and potentially takes a long time. */
6730 alock.release();
6731
6732 /* Limit the chunk size to 32K for now, as that gives better performance
6733 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6734 * One byte expands to approx. 25 bytes of breathtaking XML. */
6735 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6736 aData.resize(cbData);
6737
6738 RTFILE LogFile;
6739 int vrc = RTFileOpen(&LogFile, log.c_str(),
6740 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6741 if (RT_SUCCESS(vrc))
6742 {
6743 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6744 if (RT_SUCCESS(vrc))
6745 aData.resize(cbData);
6746 else
6747 rc = setError(VBOX_E_IPRT_ERROR,
6748 tr("Could not read log file '%s' (%Rrc)"),
6749 log.c_str(), vrc);
6750 RTFileClose(LogFile);
6751 }
6752 else
6753 rc = setError(VBOX_E_IPRT_ERROR,
6754 tr("Could not open log file '%s' (%Rrc)"),
6755 log.c_str(), vrc);
6756
6757 if (FAILED(rc))
6758 aData.resize(0);
6759
6760 return rc;
6761}
6762
6763
6764/**
6765 * Currently this method doesn't attach device to the running VM,
6766 * just makes sure it's plugged on next VM start.
6767 */
6768HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6769{
6770 // lock scope
6771 {
6772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6773
6774 HRESULT rc = i_checkStateDependency(MutableStateDep);
6775 if (FAILED(rc)) return rc;
6776
6777 ChipsetType_T aChipset = ChipsetType_PIIX3;
6778 COMGETTER(ChipsetType)(&aChipset);
6779
6780 if (aChipset != ChipsetType_ICH9)
6781 {
6782 return setError(E_INVALIDARG,
6783 tr("Host PCI attachment only supported with ICH9 chipset"));
6784 }
6785
6786 // check if device with this host PCI address already attached
6787 for (HWData::PCIDeviceAssignmentList::const_iterator
6788 it = mHWData->mPCIDeviceAssignments.begin();
6789 it != mHWData->mPCIDeviceAssignments.end();
6790 ++it)
6791 {
6792 LONG iHostAddress = -1;
6793 ComPtr<PCIDeviceAttachment> pAttach;
6794 pAttach = *it;
6795 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6796 if (iHostAddress == aHostAddress)
6797 return setError(E_INVALIDARG,
6798 tr("Device with host PCI address already attached to this VM"));
6799 }
6800
6801 ComObjPtr<PCIDeviceAttachment> pda;
6802 char name[32];
6803
6804 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6805 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6806 pda.createObject();
6807 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6808 i_setModified(IsModified_MachineData);
6809 mHWData.backup();
6810 mHWData->mPCIDeviceAssignments.push_back(pda);
6811 }
6812
6813 return S_OK;
6814}
6815
6816/**
6817 * Currently this method doesn't detach device from the running VM,
6818 * just makes sure it's not plugged on next VM start.
6819 */
6820HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6821{
6822 ComObjPtr<PCIDeviceAttachment> pAttach;
6823 bool fRemoved = false;
6824 HRESULT rc;
6825
6826 // lock scope
6827 {
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 rc = i_checkStateDependency(MutableStateDep);
6831 if (FAILED(rc)) return rc;
6832
6833 for (HWData::PCIDeviceAssignmentList::const_iterator
6834 it = mHWData->mPCIDeviceAssignments.begin();
6835 it != mHWData->mPCIDeviceAssignments.end();
6836 ++it)
6837 {
6838 LONG iHostAddress = -1;
6839 pAttach = *it;
6840 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6841 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6842 {
6843 i_setModified(IsModified_MachineData);
6844 mHWData.backup();
6845 mHWData->mPCIDeviceAssignments.remove(pAttach);
6846 fRemoved = true;
6847 break;
6848 }
6849 }
6850 }
6851
6852
6853 /* Fire event outside of the lock */
6854 if (fRemoved)
6855 {
6856 Assert(!pAttach.isNull());
6857 ComPtr<IEventSource> es;
6858 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6859 Assert(SUCCEEDED(rc));
6860 Bstr mid;
6861 rc = this->COMGETTER(Id)(mid.asOutParam());
6862 Assert(SUCCEEDED(rc));
6863 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6864 }
6865
6866 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6867 tr("No host PCI device %08x attached"),
6868 aHostAddress
6869 );
6870}
6871
6872HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6873{
6874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6875
6876 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6877 size_t i = 0;
6878 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6879 it = mHWData->mPCIDeviceAssignments.begin();
6880 it != mHWData->mPCIDeviceAssignments.end();
6881 ++it, ++i)
6882 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6883
6884 return S_OK;
6885}
6886
6887HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6888{
6889 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6890
6891 return S_OK;
6892}
6893
6894HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6895{
6896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6897
6898 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6899
6900 return S_OK;
6901}
6902
6903HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6904{
6905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6906 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6907 if (SUCCEEDED(hrc))
6908 {
6909 hrc = mHWData.backupEx();
6910 if (SUCCEEDED(hrc))
6911 {
6912 i_setModified(IsModified_MachineData);
6913 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6914 }
6915 }
6916 return hrc;
6917}
6918
6919HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6920{
6921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6922 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6923 return S_OK;
6924}
6925
6926HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6927{
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mHWData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6936 if (SUCCEEDED(hrc))
6937 i_setModified(IsModified_MachineData);
6938 }
6939 }
6940 return hrc;
6941}
6942
6943HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6944{
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6948
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6956 if (SUCCEEDED(hrc))
6957 {
6958 hrc = mHWData.backupEx();
6959 if (SUCCEEDED(hrc))
6960 {
6961 i_setModified(IsModified_MachineData);
6962 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6963 }
6964 }
6965 return hrc;
6966}
6967
6968HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6969{
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971
6972 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6973
6974 return S_OK;
6975}
6976
6977HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6978{
6979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6982 if ( SUCCEEDED(hrc)
6983 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6984 {
6985 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6986 int vrc;
6987
6988 if (aAutostartEnabled)
6989 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6990 else
6991 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6992
6993 if (RT_SUCCESS(vrc))
6994 {
6995 hrc = mHWData.backupEx();
6996 if (SUCCEEDED(hrc))
6997 {
6998 i_setModified(IsModified_MachineData);
6999 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7000 }
7001 }
7002 else if (vrc == VERR_NOT_SUPPORTED)
7003 hrc = setError(VBOX_E_NOT_SUPPORTED,
7004 tr("The VM autostart feature is not supported on this platform"));
7005 else if (vrc == VERR_PATH_NOT_FOUND)
7006 hrc = setError(E_FAIL,
7007 tr("The path to the autostart database is not set"));
7008 else
7009 hrc = setError(E_UNEXPECTED,
7010 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7011 aAutostartEnabled ? "Adding" : "Removing",
7012 mUserData->s.strName.c_str(), vrc);
7013 }
7014 return hrc;
7015}
7016
7017HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7018{
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020
7021 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7022
7023 return S_OK;
7024}
7025
7026HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7027{
7028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7029 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7030 if (SUCCEEDED(hrc))
7031 {
7032 hrc = mHWData.backupEx();
7033 if (SUCCEEDED(hrc))
7034 {
7035 i_setModified(IsModified_MachineData);
7036 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7037 }
7038 }
7039 return hrc;
7040}
7041
7042HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7043{
7044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7045
7046 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7047
7048 return S_OK;
7049}
7050
7051HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7052{
7053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7054 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7055 if ( SUCCEEDED(hrc)
7056 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7057 {
7058 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7059 int vrc;
7060
7061 if (aAutostopType != AutostopType_Disabled)
7062 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7063 else
7064 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7065
7066 if (RT_SUCCESS(vrc))
7067 {
7068 hrc = mHWData.backupEx();
7069 if (SUCCEEDED(hrc))
7070 {
7071 i_setModified(IsModified_MachineData);
7072 mHWData->mAutostart.enmAutostopType = aAutostopType;
7073 }
7074 }
7075 else if (vrc == VERR_NOT_SUPPORTED)
7076 hrc = setError(VBOX_E_NOT_SUPPORTED,
7077 tr("The VM autostop feature is not supported on this platform"));
7078 else if (vrc == VERR_PATH_NOT_FOUND)
7079 hrc = setError(E_FAIL,
7080 tr("The path to the autostart database is not set"));
7081 else
7082 hrc = setError(E_UNEXPECTED,
7083 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7084 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7085 mUserData->s.strName.c_str(), vrc);
7086 }
7087 return hrc;
7088}
7089
7090HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7091{
7092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7093
7094 aDefaultFrontend = mHWData->mDefaultFrontend;
7095
7096 return S_OK;
7097}
7098
7099HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7100{
7101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7102 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7103 if (SUCCEEDED(hrc))
7104 {
7105 hrc = mHWData.backupEx();
7106 if (SUCCEEDED(hrc))
7107 {
7108 i_setModified(IsModified_MachineData);
7109 mHWData->mDefaultFrontend = aDefaultFrontend;
7110 }
7111 }
7112 return hrc;
7113}
7114
7115HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7116{
7117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7118 size_t cbIcon = mUserData->s.ovIcon.size();
7119 aIcon.resize(cbIcon);
7120 if (cbIcon)
7121 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7122 return S_OK;
7123}
7124
7125HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7126{
7127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7128 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7129 if (SUCCEEDED(hrc))
7130 {
7131 i_setModified(IsModified_MachineData);
7132 mUserData.backup();
7133 size_t cbIcon = aIcon.size();
7134 mUserData->s.ovIcon.resize(cbIcon);
7135 if (cbIcon)
7136 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7137 }
7138 return hrc;
7139}
7140
7141HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7142{
7143#ifdef VBOX_WITH_USB
7144 *aUSBProxyAvailable = true;
7145#else
7146 *aUSBProxyAvailable = false;
7147#endif
7148 return S_OK;
7149}
7150
7151HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7152{
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 aVMProcessPriority = mUserData->s.strVMPriority;
7156
7157 return S_OK;
7158}
7159
7160HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7161{
7162 RT_NOREF(aVMProcessPriority);
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7165 if (SUCCEEDED(hrc))
7166 {
7167 /** @todo r=klaus: currently this is marked as not implemented, as
7168 * the code for setting the priority of the process is not there
7169 * (neither when starting the VM nor at runtime). */
7170 ReturnComNotImplemented();
7171#if 0
7172 hrc = mUserData.backupEx();
7173 if (SUCCEEDED(hrc))
7174 {
7175 i_setModified(IsModified_MachineData);
7176 mUserData->s.strVMPriority = aVMProcessPriority;
7177 }
7178#endif
7179 }
7180 return hrc;
7181}
7182
7183HRESULT Machine::getUnattended(ComPtr<IUnattended> &aUnattended)
7184{
7185#ifdef VBOX_WITH_UNATTENDED
7186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7187 if (mUnattended.isNotNull())
7188 aUnattended = mUnattended;
7189 else
7190 {
7191 /* Do on-demand creation. */
7192 alock.release();
7193 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
7194 if (mUnattended.isNull())
7195 {
7196 unconst(mUnattended).createObject();
7197 HRESULT hrc = mUnattended->init(this);
7198 if (FAILED(hrc))
7199 {
7200 mUnattended->uninit();
7201 unconst(mUnattended).setNull();
7202 return hrc;
7203 }
7204 }
7205 aUnattended = mUnattended;
7206 }
7207 return S_OK;
7208#else
7209 NOREF(aUnattended);
7210 return E_NOTIMPL;
7211#endif
7212}
7213
7214HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7215 ComPtr<IProgress> &aProgress)
7216{
7217 ComObjPtr<Progress> pP;
7218 Progress *ppP = pP;
7219 IProgress *iP = static_cast<IProgress *>(ppP);
7220 IProgress **pProgress = &iP;
7221
7222 IMachine *pTarget = aTarget;
7223
7224 /* Convert the options. */
7225 RTCList<CloneOptions_T> optList;
7226 if (aOptions.size())
7227 for (size_t i = 0; i < aOptions.size(); ++i)
7228 optList.append(aOptions[i]);
7229
7230 if (optList.contains(CloneOptions_Link))
7231 {
7232 if (!i_isSnapshotMachine())
7233 return setError(E_INVALIDARG,
7234 tr("Linked clone can only be created from a snapshot"));
7235 if (aMode != CloneMode_MachineState)
7236 return setError(E_INVALIDARG,
7237 tr("Linked clone can only be created for a single machine state"));
7238 }
7239 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7240
7241 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7242
7243 HRESULT rc = pWorker->start(pProgress);
7244
7245 pP = static_cast<Progress *>(*pProgress);
7246 pP.queryInterfaceTo(aProgress.asOutParam());
7247
7248 return rc;
7249
7250}
7251
7252HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7253{
7254 NOREF(aProgress);
7255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 // This check should always fail.
7258 HRESULT rc = i_checkStateDependency(MutableStateDep);
7259 if (FAILED(rc)) return rc;
7260
7261 AssertFailedReturn(E_NOTIMPL);
7262}
7263
7264HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7265{
7266 NOREF(aSavedStateFile);
7267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7268
7269 // This check should always fail.
7270 HRESULT rc = i_checkStateDependency(MutableStateDep);
7271 if (FAILED(rc)) return rc;
7272
7273 AssertFailedReturn(E_NOTIMPL);
7274}
7275
7276HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7277{
7278 NOREF(aFRemoveFile);
7279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 // This check should always fail.
7282 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7283 if (FAILED(rc)) return rc;
7284
7285 AssertFailedReturn(E_NOTIMPL);
7286}
7287
7288// public methods for internal purposes
7289/////////////////////////////////////////////////////////////////////////////
7290
7291/**
7292 * Adds the given IsModified_* flag to the dirty flags of the machine.
7293 * This must be called either during i_loadSettings or under the machine write lock.
7294 * @param fl Flag
7295 * @param fAllowStateModification If state modifications are allowed.
7296 */
7297void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7298{
7299 mData->flModifications |= fl;
7300 if (fAllowStateModification && i_isStateModificationAllowed())
7301 mData->mCurrentStateModified = true;
7302}
7303
7304/**
7305 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7306 * care of the write locking.
7307 *
7308 * @param fModification The flag to add.
7309 * @param fAllowStateModification If state modifications are allowed.
7310 */
7311void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7312{
7313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7314 i_setModified(fModification, fAllowStateModification);
7315}
7316
7317/**
7318 * Saves the registry entry of this machine to the given configuration node.
7319 *
7320 * @param data Machine registry data.
7321 *
7322 * @note locks this object for reading.
7323 */
7324HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7325{
7326 AutoLimitedCaller autoCaller(this);
7327 AssertComRCReturnRC(autoCaller.rc());
7328
7329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7330
7331 data.uuid = mData->mUuid;
7332 data.strSettingsFile = mData->m_strConfigFile;
7333
7334 return S_OK;
7335}
7336
7337/**
7338 * Calculates the absolute path of the given path taking the directory of the
7339 * machine settings file as the current directory.
7340 *
7341 * @param strPath Path to calculate the absolute path for.
7342 * @param aResult Where to put the result (used only on success, can be the
7343 * same Utf8Str instance as passed in @a aPath).
7344 * @return IPRT result.
7345 *
7346 * @note Locks this object for reading.
7347 */
7348int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7349{
7350 AutoCaller autoCaller(this);
7351 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7352
7353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7354
7355 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7356
7357 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7358
7359 strSettingsDir.stripFilename();
7360 char folder[RTPATH_MAX];
7361 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7362 if (RT_SUCCESS(vrc))
7363 aResult = folder;
7364
7365 return vrc;
7366}
7367
7368/**
7369 * Copies strSource to strTarget, making it relative to the machine folder
7370 * if it is a subdirectory thereof, or simply copying it otherwise.
7371 *
7372 * @param strSource Path to evaluate and copy.
7373 * @param strTarget Buffer to receive target path.
7374 *
7375 * @note Locks this object for reading.
7376 */
7377void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7378 Utf8Str &strTarget)
7379{
7380 AutoCaller autoCaller(this);
7381 AssertComRCReturn(autoCaller.rc(), (void)0);
7382
7383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7384
7385 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7386 // use strTarget as a temporary buffer to hold the machine settings dir
7387 strTarget = mData->m_strConfigFileFull;
7388 strTarget.stripFilename();
7389 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7390 {
7391 // is relative: then append what's left
7392 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7393 // for empty paths (only possible for subdirs) use "." to avoid
7394 // triggering default settings for not present config attributes.
7395 if (strTarget.isEmpty())
7396 strTarget = ".";
7397 }
7398 else
7399 // is not relative: then overwrite
7400 strTarget = strSource;
7401}
7402
7403/**
7404 * Returns the full path to the machine's log folder in the
7405 * \a aLogFolder argument.
7406 */
7407void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7408{
7409 AutoCaller autoCaller(this);
7410 AssertComRCReturnVoid(autoCaller.rc());
7411
7412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7413
7414 char szTmp[RTPATH_MAX];
7415 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7416 if (RT_SUCCESS(vrc))
7417 {
7418 if (szTmp[0] && !mUserData.isNull())
7419 {
7420 char szTmp2[RTPATH_MAX];
7421 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7422 if (RT_SUCCESS(vrc))
7423 aLogFolder = Utf8StrFmt("%s%c%s",
7424 szTmp2,
7425 RTPATH_DELIMITER,
7426 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7427 }
7428 else
7429 vrc = VERR_PATH_IS_RELATIVE;
7430 }
7431
7432 if (RT_FAILURE(vrc))
7433 {
7434 // fallback if VBOX_USER_LOGHOME is not set or invalid
7435 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7436 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7437 aLogFolder.append(RTPATH_DELIMITER);
7438 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7439 }
7440}
7441
7442/**
7443 * Returns the full path to the machine's log file for an given index.
7444 */
7445Utf8Str Machine::i_getLogFilename(ULONG idx)
7446{
7447 Utf8Str logFolder;
7448 getLogFolder(logFolder);
7449 Assert(logFolder.length());
7450
7451 Utf8Str log;
7452 if (idx == 0)
7453 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7454#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7455 else if (idx == 1)
7456 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7457 else
7458 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7459#else
7460 else
7461 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7462#endif
7463 return log;
7464}
7465
7466/**
7467 * Returns the full path to the machine's hardened log file.
7468 */
7469Utf8Str Machine::i_getHardeningLogFilename(void)
7470{
7471 Utf8Str strFilename;
7472 getLogFolder(strFilename);
7473 Assert(strFilename.length());
7474 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7475 return strFilename;
7476}
7477
7478
7479/**
7480 * Composes a unique saved state filename based on the current system time. The filename is
7481 * granular to the second so this will work so long as no more than one snapshot is taken on
7482 * a machine per second.
7483 *
7484 * Before version 4.1, we used this formula for saved state files:
7485 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7486 * which no longer works because saved state files can now be shared between the saved state of the
7487 * "saved" machine and an online snapshot, and the following would cause problems:
7488 * 1) save machine
7489 * 2) create online snapshot from that machine state --> reusing saved state file
7490 * 3) save machine again --> filename would be reused, breaking the online snapshot
7491 *
7492 * So instead we now use a timestamp.
7493 *
7494 * @param strStateFilePath
7495 */
7496
7497void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7498{
7499 AutoCaller autoCaller(this);
7500 AssertComRCReturnVoid(autoCaller.rc());
7501
7502 {
7503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7504 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7505 }
7506
7507 RTTIMESPEC ts;
7508 RTTimeNow(&ts);
7509 RTTIME time;
7510 RTTimeExplode(&time, &ts);
7511
7512 strStateFilePath += RTPATH_DELIMITER;
7513 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7514 time.i32Year, time.u8Month, time.u8MonthDay,
7515 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7516}
7517
7518/**
7519 * Returns the full path to the default video capture file.
7520 */
7521void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7522{
7523 AutoCaller autoCaller(this);
7524 AssertComRCReturnVoid(autoCaller.rc());
7525
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527
7528 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7529 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7530 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7531}
7532
7533/**
7534 * Returns whether at least one USB controller is present for the VM.
7535 */
7536bool Machine::i_isUSBControllerPresent()
7537{
7538 AutoCaller autoCaller(this);
7539 AssertComRCReturn(autoCaller.rc(), false);
7540
7541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7542
7543 return (mUSBControllers->size() > 0);
7544}
7545
7546/**
7547 * @note Locks this object for writing, calls the client process
7548 * (inside the lock).
7549 */
7550HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7551 const Utf8Str &strFrontend,
7552 const Utf8Str &strEnvironment,
7553 ProgressProxy *aProgress)
7554{
7555 LogFlowThisFuncEnter();
7556
7557 AssertReturn(aControl, E_FAIL);
7558 AssertReturn(aProgress, E_FAIL);
7559 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7560
7561 AutoCaller autoCaller(this);
7562 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7563
7564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7565
7566 if (!mData->mRegistered)
7567 return setError(E_UNEXPECTED,
7568 tr("The machine '%s' is not registered"),
7569 mUserData->s.strName.c_str());
7570
7571 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7572
7573 /* The process started when launching a VM with separate UI/VM processes is always
7574 * the UI process, i.e. needs special handling as it won't claim the session. */
7575 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7576
7577 if (fSeparate)
7578 {
7579 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7580 return setError(VBOX_E_INVALID_OBJECT_STATE,
7581 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7582 mUserData->s.strName.c_str());
7583 }
7584 else
7585 {
7586 if ( mData->mSession.mState == SessionState_Locked
7587 || mData->mSession.mState == SessionState_Spawning
7588 || mData->mSession.mState == SessionState_Unlocking)
7589 return setError(VBOX_E_INVALID_OBJECT_STATE,
7590 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7591 mUserData->s.strName.c_str());
7592
7593 /* may not be busy */
7594 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7595 }
7596
7597 /* get the path to the executable */
7598 char szPath[RTPATH_MAX];
7599 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7600 size_t cchBufLeft = strlen(szPath);
7601 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7602 szPath[cchBufLeft] = 0;
7603 char *pszNamePart = szPath + cchBufLeft;
7604 cchBufLeft = sizeof(szPath) - cchBufLeft;
7605
7606 int vrc = VINF_SUCCESS;
7607 RTPROCESS pid = NIL_RTPROCESS;
7608
7609 RTENV env = RTENV_DEFAULT;
7610
7611 if (!strEnvironment.isEmpty())
7612 {
7613 char *newEnvStr = NULL;
7614
7615 do
7616 {
7617 /* clone the current environment */
7618 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7619 AssertRCBreakStmt(vrc2, vrc = vrc2);
7620
7621 newEnvStr = RTStrDup(strEnvironment.c_str());
7622 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7623
7624 /* put new variables to the environment
7625 * (ignore empty variable names here since RTEnv API
7626 * intentionally doesn't do that) */
7627 char *var = newEnvStr;
7628 for (char *p = newEnvStr; *p; ++p)
7629 {
7630 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7631 {
7632 *p = '\0';
7633 if (*var)
7634 {
7635 char *val = strchr(var, '=');
7636 if (val)
7637 {
7638 *val++ = '\0';
7639 vrc2 = RTEnvSetEx(env, var, val);
7640 }
7641 else
7642 vrc2 = RTEnvUnsetEx(env, var);
7643 if (RT_FAILURE(vrc2))
7644 break;
7645 }
7646 var = p + 1;
7647 }
7648 }
7649 if (RT_SUCCESS(vrc2) && *var)
7650 vrc2 = RTEnvPutEx(env, var);
7651
7652 AssertRCBreakStmt(vrc2, vrc = vrc2);
7653 }
7654 while (0);
7655
7656 if (newEnvStr != NULL)
7657 RTStrFree(newEnvStr);
7658 }
7659
7660 /* Hardening logging */
7661#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7662 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7663 {
7664 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7665 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7666 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7667 {
7668 Utf8Str strStartupLogDir = strHardeningLogFile;
7669 strStartupLogDir.stripFilename();
7670 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7671 file without stripping the file. */
7672 }
7673 strSupHardeningLogArg.append(strHardeningLogFile);
7674
7675 /* Remove legacy log filename to avoid confusion. */
7676 Utf8Str strOldStartupLogFile;
7677 getLogFolder(strOldStartupLogFile);
7678 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7679 RTFileDelete(strOldStartupLogFile.c_str());
7680 }
7681 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7682#else
7683 const char *pszSupHardeningLogArg = NULL;
7684#endif
7685
7686 Utf8Str strCanonicalName;
7687
7688#ifdef VBOX_WITH_QTGUI
7689 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7690 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7691 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7692 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7693 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7694 {
7695 strCanonicalName = "GUI/Qt";
7696# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7697 /* Modify the base path so that we don't need to use ".." below. */
7698 RTPathStripTrailingSlash(szPath);
7699 RTPathStripFilename(szPath);
7700 cchBufLeft = strlen(szPath);
7701 pszNamePart = szPath + cchBufLeft;
7702 cchBufLeft = sizeof(szPath) - cchBufLeft;
7703
7704# define OSX_APP_NAME "VirtualBoxVM"
7705# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7706
7707 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7708 if ( strAppOverride.contains(".")
7709 || strAppOverride.contains("/")
7710 || strAppOverride.contains("\\")
7711 || strAppOverride.contains(":"))
7712 strAppOverride.setNull();
7713 Utf8Str strAppPath;
7714 if (!strAppOverride.isEmpty())
7715 {
7716 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7717 Utf8Str strFullPath(szPath);
7718 strFullPath.append(strAppPath);
7719 /* there is a race, but people using this deserve the failure */
7720 if (!RTFileExists(strFullPath.c_str()))
7721 strAppOverride.setNull();
7722 }
7723 if (strAppOverride.isEmpty())
7724 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7725 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7726 strcpy(pszNamePart, strAppPath.c_str());
7727# else
7728 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7729 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7730 strcpy(pszNamePart, s_szVirtualBox_exe);
7731# endif
7732
7733 Utf8Str idStr = mData->mUuid.toString();
7734 const char *apszArgs[] =
7735 {
7736 szPath,
7737 "--comment", mUserData->s.strName.c_str(),
7738 "--startvm", idStr.c_str(),
7739 "--no-startvm-errormsgbox",
7740 NULL, /* For "--separate". */
7741 NULL, /* For "--sup-startup-log". */
7742 NULL
7743 };
7744 unsigned iArg = 6;
7745 if (fSeparate)
7746 apszArgs[iArg++] = "--separate";
7747 apszArgs[iArg++] = pszSupHardeningLogArg;
7748
7749 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7750 }
7751#else /* !VBOX_WITH_QTGUI */
7752 if (0)
7753 ;
7754#endif /* VBOX_WITH_QTGUI */
7755
7756 else
7757
7758#ifdef VBOX_WITH_VBOXSDL
7759 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7760 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7761 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7762 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7763 {
7764 strCanonicalName = "GUI/SDL";
7765 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7766 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7767 strcpy(pszNamePart, s_szVBoxSDL_exe);
7768
7769 Utf8Str idStr = mData->mUuid.toString();
7770 const char *apszArgs[] =
7771 {
7772 szPath,
7773 "--comment", mUserData->s.strName.c_str(),
7774 "--startvm", idStr.c_str(),
7775 NULL, /* For "--separate". */
7776 NULL, /* For "--sup-startup-log". */
7777 NULL
7778 };
7779 unsigned iArg = 5;
7780 if (fSeparate)
7781 apszArgs[iArg++] = "--separate";
7782 apszArgs[iArg++] = pszSupHardeningLogArg;
7783
7784 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7785 }
7786#else /* !VBOX_WITH_VBOXSDL */
7787 if (0)
7788 ;
7789#endif /* !VBOX_WITH_VBOXSDL */
7790
7791 else
7792
7793#ifdef VBOX_WITH_HEADLESS
7794 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7795 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7796 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7797 )
7798 {
7799 strCanonicalName = "headless";
7800 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7801 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7802 * and a VM works even if the server has not been installed.
7803 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7804 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7805 * differently in 4.0 and 3.x.
7806 */
7807 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7808 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7809 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7810
7811 Utf8Str idStr = mData->mUuid.toString();
7812 const char *apszArgs[] =
7813 {
7814 szPath,
7815 "--comment", mUserData->s.strName.c_str(),
7816 "--startvm", idStr.c_str(),
7817 "--vrde", "config",
7818 NULL, /* For "--capture". */
7819 NULL, /* For "--sup-startup-log". */
7820 NULL
7821 };
7822 unsigned iArg = 7;
7823 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7824 apszArgs[iArg++] = "--capture";
7825 apszArgs[iArg++] = pszSupHardeningLogArg;
7826
7827# ifdef RT_OS_WINDOWS
7828 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7829# else
7830 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7831# endif
7832 }
7833#else /* !VBOX_WITH_HEADLESS */
7834 if (0)
7835 ;
7836#endif /* !VBOX_WITH_HEADLESS */
7837 else
7838 {
7839 RTEnvDestroy(env);
7840 return setError(E_INVALIDARG,
7841 tr("Invalid frontend name: '%s'"),
7842 strFrontend.c_str());
7843 }
7844
7845 RTEnvDestroy(env);
7846
7847 if (RT_FAILURE(vrc))
7848 return setError(VBOX_E_IPRT_ERROR,
7849 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7850 mUserData->s.strName.c_str(), vrc);
7851
7852 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7853
7854 if (!fSeparate)
7855 {
7856 /*
7857 * Note that we don't release the lock here before calling the client,
7858 * because it doesn't need to call us back if called with a NULL argument.
7859 * Releasing the lock here is dangerous because we didn't prepare the
7860 * launch data yet, but the client we've just started may happen to be
7861 * too fast and call LockMachine() that will fail (because of PID, etc.),
7862 * so that the Machine will never get out of the Spawning session state.
7863 */
7864
7865 /* inform the session that it will be a remote one */
7866 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7867#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7868 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7869#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7870 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7871#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7872 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7873
7874 if (FAILED(rc))
7875 {
7876 /* restore the session state */
7877 mData->mSession.mState = SessionState_Unlocked;
7878 alock.release();
7879 mParent->i_addProcessToReap(pid);
7880 /* The failure may occur w/o any error info (from RPC), so provide one */
7881 return setError(VBOX_E_VM_ERROR,
7882 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7883 }
7884
7885 /* attach launch data to the machine */
7886 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7887 mData->mSession.mRemoteControls.push_back(aControl);
7888 mData->mSession.mProgress = aProgress;
7889 mData->mSession.mPID = pid;
7890 mData->mSession.mState = SessionState_Spawning;
7891 Assert(strCanonicalName.isNotEmpty());
7892 mData->mSession.mName = strCanonicalName;
7893 }
7894 else
7895 {
7896 /* For separate UI process we declare the launch as completed instantly, as the
7897 * actual headless VM start may or may not come. No point in remembering anything
7898 * yet, as what matters for us is when the headless VM gets started. */
7899 aProgress->i_notifyComplete(S_OK);
7900 }
7901
7902 alock.release();
7903 mParent->i_addProcessToReap(pid);
7904
7905 LogFlowThisFuncLeave();
7906 return S_OK;
7907}
7908
7909/**
7910 * Returns @c true if the given session machine instance has an open direct
7911 * session (and optionally also for direct sessions which are closing) and
7912 * returns the session control machine instance if so.
7913 *
7914 * Note that when the method returns @c false, the arguments remain unchanged.
7915 *
7916 * @param aMachine Session machine object.
7917 * @param aControl Direct session control object (optional).
7918 * @param aRequireVM If true then only allow VM sessions.
7919 * @param aAllowClosing If true then additionally a session which is currently
7920 * being closed will also be allowed.
7921 *
7922 * @note locks this object for reading.
7923 */
7924bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7925 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7926 bool aRequireVM /*= false*/,
7927 bool aAllowClosing /*= false*/)
7928{
7929 AutoLimitedCaller autoCaller(this);
7930 AssertComRCReturn(autoCaller.rc(), false);
7931
7932 /* just return false for inaccessible machines */
7933 if (getObjectState().getState() != ObjectState::Ready)
7934 return false;
7935
7936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7937
7938 if ( ( mData->mSession.mState == SessionState_Locked
7939 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7940 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7941 )
7942 {
7943 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7944
7945 aMachine = mData->mSession.mMachine;
7946
7947 if (aControl != NULL)
7948 *aControl = mData->mSession.mDirectControl;
7949
7950 return true;
7951 }
7952
7953 return false;
7954}
7955
7956/**
7957 * Returns @c true if the given machine has an spawning direct session.
7958 *
7959 * @note locks this object for reading.
7960 */
7961bool Machine::i_isSessionSpawning()
7962{
7963 AutoLimitedCaller autoCaller(this);
7964 AssertComRCReturn(autoCaller.rc(), false);
7965
7966 /* just return false for inaccessible machines */
7967 if (getObjectState().getState() != ObjectState::Ready)
7968 return false;
7969
7970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7971
7972 if (mData->mSession.mState == SessionState_Spawning)
7973 return true;
7974
7975 return false;
7976}
7977
7978/**
7979 * Called from the client watcher thread to check for unexpected client process
7980 * death during Session_Spawning state (e.g. before it successfully opened a
7981 * direct session).
7982 *
7983 * On Win32 and on OS/2, this method is called only when we've got the
7984 * direct client's process termination notification, so it always returns @c
7985 * true.
7986 *
7987 * On other platforms, this method returns @c true if the client process is
7988 * terminated and @c false if it's still alive.
7989 *
7990 * @note Locks this object for writing.
7991 */
7992bool Machine::i_checkForSpawnFailure()
7993{
7994 AutoCaller autoCaller(this);
7995 if (!autoCaller.isOk())
7996 {
7997 /* nothing to do */
7998 LogFlowThisFunc(("Already uninitialized!\n"));
7999 return true;
8000 }
8001
8002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8003
8004 if (mData->mSession.mState != SessionState_Spawning)
8005 {
8006 /* nothing to do */
8007 LogFlowThisFunc(("Not spawning any more!\n"));
8008 return true;
8009 }
8010
8011 HRESULT rc = S_OK;
8012
8013 /* PID not yet initialized, skip check. */
8014 if (mData->mSession.mPID == NIL_RTPROCESS)
8015 return false;
8016
8017 RTPROCSTATUS status;
8018 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8019
8020 if (vrc != VERR_PROCESS_RUNNING)
8021 {
8022 Utf8Str strExtraInfo;
8023
8024#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8025 /* If the startup logfile exists and is of non-zero length, tell the
8026 user to look there for more details to encourage them to attach it
8027 when reporting startup issues. */
8028 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8029 uint64_t cbStartupLogFile = 0;
8030 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8031 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8032 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8033#endif
8034
8035 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8036 rc = setError(E_FAIL,
8037 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8038 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8039 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8040 rc = setError(E_FAIL,
8041 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8042 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8043 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8044 rc = setError(E_FAIL,
8045 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8046 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8047 else
8048 rc = setError(E_FAIL,
8049 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8050 i_getName().c_str(), vrc, strExtraInfo.c_str());
8051 }
8052
8053 if (FAILED(rc))
8054 {
8055 /* Close the remote session, remove the remote control from the list
8056 * and reset session state to Closed (@note keep the code in sync with
8057 * the relevant part in LockMachine()). */
8058
8059 Assert(mData->mSession.mRemoteControls.size() == 1);
8060 if (mData->mSession.mRemoteControls.size() == 1)
8061 {
8062 ErrorInfoKeeper eik;
8063 mData->mSession.mRemoteControls.front()->Uninitialize();
8064 }
8065
8066 mData->mSession.mRemoteControls.clear();
8067 mData->mSession.mState = SessionState_Unlocked;
8068
8069 /* finalize the progress after setting the state */
8070 if (!mData->mSession.mProgress.isNull())
8071 {
8072 mData->mSession.mProgress->notifyComplete(rc);
8073 mData->mSession.mProgress.setNull();
8074 }
8075
8076 mData->mSession.mPID = NIL_RTPROCESS;
8077
8078 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8079 return true;
8080 }
8081
8082 return false;
8083}
8084
8085/**
8086 * Checks whether the machine can be registered. If so, commits and saves
8087 * all settings.
8088 *
8089 * @note Must be called from mParent's write lock. Locks this object and
8090 * children for writing.
8091 */
8092HRESULT Machine::i_prepareRegister()
8093{
8094 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8095
8096 AutoLimitedCaller autoCaller(this);
8097 AssertComRCReturnRC(autoCaller.rc());
8098
8099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8100
8101 /* wait for state dependents to drop to zero */
8102 i_ensureNoStateDependencies();
8103
8104 if (!mData->mAccessible)
8105 return setError(VBOX_E_INVALID_OBJECT_STATE,
8106 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8107 mUserData->s.strName.c_str(),
8108 mData->mUuid.toString().c_str());
8109
8110 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8111
8112 if (mData->mRegistered)
8113 return setError(VBOX_E_INVALID_OBJECT_STATE,
8114 tr("The machine '%s' with UUID {%s} is already registered"),
8115 mUserData->s.strName.c_str(),
8116 mData->mUuid.toString().c_str());
8117
8118 HRESULT rc = S_OK;
8119
8120 // Ensure the settings are saved. If we are going to be registered and
8121 // no config file exists yet, create it by calling i_saveSettings() too.
8122 if ( (mData->flModifications)
8123 || (!mData->pMachineConfigFile->fileExists())
8124 )
8125 {
8126 rc = i_saveSettings(NULL);
8127 // no need to check whether VirtualBox.xml needs saving too since
8128 // we can't have a machine XML file rename pending
8129 if (FAILED(rc)) return rc;
8130 }
8131
8132 /* more config checking goes here */
8133
8134 if (SUCCEEDED(rc))
8135 {
8136 /* we may have had implicit modifications we want to fix on success */
8137 i_commit();
8138
8139 mData->mRegistered = true;
8140 }
8141 else
8142 {
8143 /* we may have had implicit modifications we want to cancel on failure*/
8144 i_rollback(false /* aNotify */);
8145 }
8146
8147 return rc;
8148}
8149
8150/**
8151 * Increases the number of objects dependent on the machine state or on the
8152 * registered state. Guarantees that these two states will not change at least
8153 * until #i_releaseStateDependency() is called.
8154 *
8155 * Depending on the @a aDepType value, additional state checks may be made.
8156 * These checks will set extended error info on failure. See
8157 * #i_checkStateDependency() for more info.
8158 *
8159 * If this method returns a failure, the dependency is not added and the caller
8160 * is not allowed to rely on any particular machine state or registration state
8161 * value and may return the failed result code to the upper level.
8162 *
8163 * @param aDepType Dependency type to add.
8164 * @param aState Current machine state (NULL if not interested).
8165 * @param aRegistered Current registered state (NULL if not interested).
8166 *
8167 * @note Locks this object for writing.
8168 */
8169HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8170 MachineState_T *aState /* = NULL */,
8171 BOOL *aRegistered /* = NULL */)
8172{
8173 AutoCaller autoCaller(this);
8174 AssertComRCReturnRC(autoCaller.rc());
8175
8176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8177
8178 HRESULT rc = i_checkStateDependency(aDepType);
8179 if (FAILED(rc)) return rc;
8180
8181 {
8182 if (mData->mMachineStateChangePending != 0)
8183 {
8184 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8185 * drop to zero so don't add more. It may make sense to wait a bit
8186 * and retry before reporting an error (since the pending state
8187 * transition should be really quick) but let's just assert for
8188 * now to see if it ever happens on practice. */
8189
8190 AssertFailed();
8191
8192 return setError(E_ACCESSDENIED,
8193 tr("Machine state change is in progress. Please retry the operation later."));
8194 }
8195
8196 ++mData->mMachineStateDeps;
8197 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8198 }
8199
8200 if (aState)
8201 *aState = mData->mMachineState;
8202 if (aRegistered)
8203 *aRegistered = mData->mRegistered;
8204
8205 return S_OK;
8206}
8207
8208/**
8209 * Decreases the number of objects dependent on the machine state.
8210 * Must always complete the #i_addStateDependency() call after the state
8211 * dependency is no more necessary.
8212 */
8213void Machine::i_releaseStateDependency()
8214{
8215 AutoCaller autoCaller(this);
8216 AssertComRCReturnVoid(autoCaller.rc());
8217
8218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8219
8220 /* releaseStateDependency() w/o addStateDependency()? */
8221 AssertReturnVoid(mData->mMachineStateDeps != 0);
8222 -- mData->mMachineStateDeps;
8223
8224 if (mData->mMachineStateDeps == 0)
8225 {
8226 /* inform i_ensureNoStateDependencies() that there are no more deps */
8227 if (mData->mMachineStateChangePending != 0)
8228 {
8229 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8230 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8231 }
8232 }
8233}
8234
8235Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8236{
8237 /* start with nothing found */
8238 Utf8Str strResult("");
8239
8240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8241
8242 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8243 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8244 // found:
8245 strResult = it->second; // source is a Utf8Str
8246
8247 return strResult;
8248}
8249
8250// protected methods
8251/////////////////////////////////////////////////////////////////////////////
8252
8253/**
8254 * Performs machine state checks based on the @a aDepType value. If a check
8255 * fails, this method will set extended error info, otherwise it will return
8256 * S_OK. It is supposed, that on failure, the caller will immediately return
8257 * the return value of this method to the upper level.
8258 *
8259 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8260 *
8261 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8262 * current state of this machine object allows to change settings of the
8263 * machine (i.e. the machine is not registered, or registered but not running
8264 * and not saved). It is useful to call this method from Machine setters
8265 * before performing any change.
8266 *
8267 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8268 * as for MutableStateDep except that if the machine is saved, S_OK is also
8269 * returned. This is useful in setters which allow changing machine
8270 * properties when it is in the saved state.
8271 *
8272 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8273 * if the current state of this machine object allows to change runtime
8274 * changeable settings of the machine (i.e. the machine is not registered, or
8275 * registered but either running or not running and not saved). It is useful
8276 * to call this method from Machine setters before performing any changes to
8277 * runtime changeable settings.
8278 *
8279 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8280 * the same as for MutableOrRunningStateDep except that if the machine is
8281 * saved, S_OK is also returned. This is useful in setters which allow
8282 * changing runtime and saved state changeable machine properties.
8283 *
8284 * @param aDepType Dependency type to check.
8285 *
8286 * @note Non Machine based classes should use #i_addStateDependency() and
8287 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8288 * template.
8289 *
8290 * @note This method must be called from under this object's read or write
8291 * lock.
8292 */
8293HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8294{
8295 switch (aDepType)
8296 {
8297 case AnyStateDep:
8298 {
8299 break;
8300 }
8301 case MutableStateDep:
8302 {
8303 if ( mData->mRegistered
8304 && ( !i_isSessionMachine()
8305 || ( mData->mMachineState != MachineState_Aborted
8306 && mData->mMachineState != MachineState_Teleported
8307 && mData->mMachineState != MachineState_PoweredOff
8308 )
8309 )
8310 )
8311 return setError(VBOX_E_INVALID_VM_STATE,
8312 tr("The machine is not mutable (state is %s)"),
8313 Global::stringifyMachineState(mData->mMachineState));
8314 break;
8315 }
8316 case MutableOrSavedStateDep:
8317 {
8318 if ( mData->mRegistered
8319 && ( !i_isSessionMachine()
8320 || ( mData->mMachineState != MachineState_Aborted
8321 && mData->mMachineState != MachineState_Teleported
8322 && mData->mMachineState != MachineState_Saved
8323 && mData->mMachineState != MachineState_PoweredOff
8324 )
8325 )
8326 )
8327 return setError(VBOX_E_INVALID_VM_STATE,
8328 tr("The machine is not mutable or saved (state is %s)"),
8329 Global::stringifyMachineState(mData->mMachineState));
8330 break;
8331 }
8332 case MutableOrRunningStateDep:
8333 {
8334 if ( mData->mRegistered
8335 && ( !i_isSessionMachine()
8336 || ( mData->mMachineState != MachineState_Aborted
8337 && mData->mMachineState != MachineState_Teleported
8338 && mData->mMachineState != MachineState_PoweredOff
8339 && !Global::IsOnline(mData->mMachineState)
8340 )
8341 )
8342 )
8343 return setError(VBOX_E_INVALID_VM_STATE,
8344 tr("The machine is not mutable or running (state is %s)"),
8345 Global::stringifyMachineState(mData->mMachineState));
8346 break;
8347 }
8348 case MutableOrSavedOrRunningStateDep:
8349 {
8350 if ( mData->mRegistered
8351 && ( !i_isSessionMachine()
8352 || ( mData->mMachineState != MachineState_Aborted
8353 && mData->mMachineState != MachineState_Teleported
8354 && mData->mMachineState != MachineState_Saved
8355 && mData->mMachineState != MachineState_PoweredOff
8356 && !Global::IsOnline(mData->mMachineState)
8357 )
8358 )
8359 )
8360 return setError(VBOX_E_INVALID_VM_STATE,
8361 tr("The machine is not mutable, saved or running (state is %s)"),
8362 Global::stringifyMachineState(mData->mMachineState));
8363 break;
8364 }
8365 }
8366
8367 return S_OK;
8368}
8369
8370/**
8371 * Helper to initialize all associated child objects and allocate data
8372 * structures.
8373 *
8374 * This method must be called as a part of the object's initialization procedure
8375 * (usually done in the #init() method).
8376 *
8377 * @note Must be called only from #init() or from #i_registeredInit().
8378 */
8379HRESULT Machine::initDataAndChildObjects()
8380{
8381 AutoCaller autoCaller(this);
8382 AssertComRCReturnRC(autoCaller.rc());
8383 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8384 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8385
8386 AssertReturn(!mData->mAccessible, E_FAIL);
8387
8388 /* allocate data structures */
8389 mSSData.allocate();
8390 mUserData.allocate();
8391 mHWData.allocate();
8392 mMediumAttachments.allocate();
8393 mStorageControllers.allocate();
8394 mUSBControllers.allocate();
8395
8396 /* initialize mOSTypeId */
8397 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8398
8399/** @todo r=bird: init() methods never fails, right? Why don't we make them
8400 * return void then! */
8401
8402 /* create associated BIOS settings object */
8403 unconst(mBIOSSettings).createObject();
8404 mBIOSSettings->init(this);
8405
8406 /* create an associated VRDE object (default is disabled) */
8407 unconst(mVRDEServer).createObject();
8408 mVRDEServer->init(this);
8409
8410 /* create associated serial port objects */
8411 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8412 {
8413 unconst(mSerialPorts[slot]).createObject();
8414 mSerialPorts[slot]->init(this, slot);
8415 }
8416
8417 /* create associated parallel port objects */
8418 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8419 {
8420 unconst(mParallelPorts[slot]).createObject();
8421 mParallelPorts[slot]->init(this, slot);
8422 }
8423
8424 /* create the audio adapter object (always present, default is disabled) */
8425 unconst(mAudioAdapter).createObject();
8426 mAudioAdapter->init(this);
8427
8428 /* create the USB device filters object (always present) */
8429 unconst(mUSBDeviceFilters).createObject();
8430 mUSBDeviceFilters->init(this);
8431
8432 /* create associated network adapter objects */
8433 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8434 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8435 {
8436 unconst(mNetworkAdapters[slot]).createObject();
8437 mNetworkAdapters[slot]->init(this, slot);
8438 }
8439
8440 /* create the bandwidth control */
8441 unconst(mBandwidthControl).createObject();
8442 mBandwidthControl->init(this);
8443
8444#ifdef VBOX_WITH_UNATTENDED
8445 Assert(mUnattended.isNull()); /* Created on-demand. */
8446#endif
8447
8448 return S_OK;
8449}
8450
8451/**
8452 * Helper to uninitialize all associated child objects and to free all data
8453 * structures.
8454 *
8455 * This method must be called as a part of the object's uninitialization
8456 * procedure (usually done in the #uninit() method).
8457 *
8458 * @note Must be called only from #uninit() or from #i_registeredInit().
8459 */
8460void Machine::uninitDataAndChildObjects()
8461{
8462 AutoCaller autoCaller(this);
8463 AssertComRCReturnVoid(autoCaller.rc());
8464 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8465 || getObjectState().getState() == ObjectState::Limited);
8466
8467 /* tell all our other child objects we've been uninitialized */
8468 if (mBandwidthControl)
8469 {
8470 mBandwidthControl->uninit();
8471 unconst(mBandwidthControl).setNull();
8472 }
8473
8474 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8475 {
8476 if (mNetworkAdapters[slot])
8477 {
8478 mNetworkAdapters[slot]->uninit();
8479 unconst(mNetworkAdapters[slot]).setNull();
8480 }
8481 }
8482
8483 if (mUSBDeviceFilters)
8484 {
8485 mUSBDeviceFilters->uninit();
8486 unconst(mUSBDeviceFilters).setNull();
8487 }
8488
8489 if (mAudioAdapter)
8490 {
8491 mAudioAdapter->uninit();
8492 unconst(mAudioAdapter).setNull();
8493 }
8494
8495 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8496 {
8497 if (mParallelPorts[slot])
8498 {
8499 mParallelPorts[slot]->uninit();
8500 unconst(mParallelPorts[slot]).setNull();
8501 }
8502 }
8503
8504 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8505 {
8506 if (mSerialPorts[slot])
8507 {
8508 mSerialPorts[slot]->uninit();
8509 unconst(mSerialPorts[slot]).setNull();
8510 }
8511 }
8512
8513 if (mVRDEServer)
8514 {
8515 mVRDEServer->uninit();
8516 unconst(mVRDEServer).setNull();
8517 }
8518
8519 if (mBIOSSettings)
8520 {
8521 mBIOSSettings->uninit();
8522 unconst(mBIOSSettings).setNull();
8523 }
8524
8525#ifdef VBOX_WITH_UNATTENDED
8526 if (mUnattended)
8527 {
8528 mUnattended->uninit();
8529 unconst(mUnattended).setNull();
8530 }
8531#endif
8532
8533 /* Deassociate media (only when a real Machine or a SnapshotMachine
8534 * instance is uninitialized; SessionMachine instances refer to real
8535 * Machine media). This is necessary for a clean re-initialization of
8536 * the VM after successfully re-checking the accessibility state. Note
8537 * that in case of normal Machine or SnapshotMachine uninitialization (as
8538 * a result of unregistering or deleting the snapshot), outdated media
8539 * attachments will already be uninitialized and deleted, so this
8540 * code will not affect them. */
8541 if ( !mMediumAttachments.isNull()
8542 && !i_isSessionMachine()
8543 )
8544 {
8545 for (MediumAttachmentList::const_iterator
8546 it = mMediumAttachments->begin();
8547 it != mMediumAttachments->end();
8548 ++it)
8549 {
8550 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8551 if (pMedium.isNull())
8552 continue;
8553 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8554 AssertComRC(rc);
8555 }
8556 }
8557
8558 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8559 {
8560 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8561 if (mData->mFirstSnapshot)
8562 {
8563 // snapshots tree is protected by machine write lock; strictly
8564 // this isn't necessary here since we're deleting the entire
8565 // machine, but otherwise we assert in Snapshot::uninit()
8566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8567 mData->mFirstSnapshot->uninit();
8568 mData->mFirstSnapshot.setNull();
8569 }
8570
8571 mData->mCurrentSnapshot.setNull();
8572 }
8573
8574 /* free data structures (the essential mData structure is not freed here
8575 * since it may be still in use) */
8576 mMediumAttachments.free();
8577 mStorageControllers.free();
8578 mUSBControllers.free();
8579 mHWData.free();
8580 mUserData.free();
8581 mSSData.free();
8582}
8583
8584/**
8585 * Returns a pointer to the Machine object for this machine that acts like a
8586 * parent for complex machine data objects such as shared folders, etc.
8587 *
8588 * For primary Machine objects and for SnapshotMachine objects, returns this
8589 * object's pointer itself. For SessionMachine objects, returns the peer
8590 * (primary) machine pointer.
8591 */
8592Machine *Machine::i_getMachine()
8593{
8594 if (i_isSessionMachine())
8595 return (Machine*)mPeer;
8596 return this;
8597}
8598
8599/**
8600 * Makes sure that there are no machine state dependents. If necessary, waits
8601 * for the number of dependents to drop to zero.
8602 *
8603 * Make sure this method is called from under this object's write lock to
8604 * guarantee that no new dependents may be added when this method returns
8605 * control to the caller.
8606 *
8607 * @note Locks this object for writing. The lock will be released while waiting
8608 * (if necessary).
8609 *
8610 * @warning To be used only in methods that change the machine state!
8611 */
8612void Machine::i_ensureNoStateDependencies()
8613{
8614 AssertReturnVoid(isWriteLockOnCurrentThread());
8615
8616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8617
8618 /* Wait for all state dependents if necessary */
8619 if (mData->mMachineStateDeps != 0)
8620 {
8621 /* lazy semaphore creation */
8622 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8623 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8624
8625 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8626 mData->mMachineStateDeps));
8627
8628 ++mData->mMachineStateChangePending;
8629
8630 /* reset the semaphore before waiting, the last dependent will signal
8631 * it */
8632 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8633
8634 alock.release();
8635
8636 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8637
8638 alock.acquire();
8639
8640 -- mData->mMachineStateChangePending;
8641 }
8642}
8643
8644/**
8645 * Changes the machine state and informs callbacks.
8646 *
8647 * This method is not intended to fail so it either returns S_OK or asserts (and
8648 * returns a failure).
8649 *
8650 * @note Locks this object for writing.
8651 */
8652HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8653{
8654 LogFlowThisFuncEnter();
8655 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8656 Assert(aMachineState != MachineState_Null);
8657
8658 AutoCaller autoCaller(this);
8659 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8660
8661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8662
8663 /* wait for state dependents to drop to zero */
8664 i_ensureNoStateDependencies();
8665
8666 MachineState_T const enmOldState = mData->mMachineState;
8667 if (enmOldState != aMachineState)
8668 {
8669 mData->mMachineState = aMachineState;
8670 RTTimeNow(&mData->mLastStateChange);
8671
8672#ifdef VBOX_WITH_DTRACE_R3_MAIN
8673 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8674#endif
8675 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8676 }
8677
8678 LogFlowThisFuncLeave();
8679 return S_OK;
8680}
8681
8682/**
8683 * Searches for a shared folder with the given logical name
8684 * in the collection of shared folders.
8685 *
8686 * @param aName logical name of the shared folder
8687 * @param aSharedFolder where to return the found object
8688 * @param aSetError whether to set the error info if the folder is
8689 * not found
8690 * @return
8691 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8692 *
8693 * @note
8694 * must be called from under the object's lock!
8695 */
8696HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8697 ComObjPtr<SharedFolder> &aSharedFolder,
8698 bool aSetError /* = false */)
8699{
8700 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8701 for (HWData::SharedFolderList::const_iterator
8702 it = mHWData->mSharedFolders.begin();
8703 it != mHWData->mSharedFolders.end();
8704 ++it)
8705 {
8706 SharedFolder *pSF = *it;
8707 AutoCaller autoCaller(pSF);
8708 if (pSF->i_getName() == aName)
8709 {
8710 aSharedFolder = pSF;
8711 rc = S_OK;
8712 break;
8713 }
8714 }
8715
8716 if (aSetError && FAILED(rc))
8717 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8718
8719 return rc;
8720}
8721
8722/**
8723 * Initializes all machine instance data from the given settings structures
8724 * from XML. The exception is the machine UUID which needs special handling
8725 * depending on the caller's use case, so the caller needs to set that herself.
8726 *
8727 * This gets called in several contexts during machine initialization:
8728 *
8729 * -- When machine XML exists on disk already and needs to be loaded into memory,
8730 * for example, from #i_registeredInit() to load all registered machines on
8731 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8732 * attached to the machine should be part of some media registry already.
8733 *
8734 * -- During OVF import, when a machine config has been constructed from an
8735 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8736 * ensure that the media listed as attachments in the config (which have
8737 * been imported from the OVF) receive the correct registry ID.
8738 *
8739 * -- During VM cloning.
8740 *
8741 * @param config Machine settings from XML.
8742 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8743 * for each attached medium in the config.
8744 * @return
8745 */
8746HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8747 const Guid *puuidRegistry)
8748{
8749 // copy name, description, OS type, teleporter, UTC etc.
8750 mUserData->s = config.machineUserData;
8751
8752 // look up the object by Id to check it is valid
8753 ComObjPtr<GuestOSType> pGuestOSType;
8754 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8755 pGuestOSType);
8756 if (FAILED(rc)) return rc;
8757 mUserData->s.strOsType = pGuestOSType->i_id();
8758
8759 // stateFile (optional)
8760 if (config.strStateFile.isEmpty())
8761 mSSData->strStateFilePath.setNull();
8762 else
8763 {
8764 Utf8Str stateFilePathFull(config.strStateFile);
8765 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8766 if (RT_FAILURE(vrc))
8767 return setError(E_FAIL,
8768 tr("Invalid saved state file path '%s' (%Rrc)"),
8769 config.strStateFile.c_str(),
8770 vrc);
8771 mSSData->strStateFilePath = stateFilePathFull;
8772 }
8773
8774 // snapshot folder needs special processing so set it again
8775 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8776 if (FAILED(rc)) return rc;
8777
8778 /* Copy the extra data items (config may or may not be the same as
8779 * mData->pMachineConfigFile) if necessary. When loading the XML files
8780 * from disk they are the same, but not for OVF import. */
8781 if (mData->pMachineConfigFile != &config)
8782 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8783
8784 /* currentStateModified (optional, default is true) */
8785 mData->mCurrentStateModified = config.fCurrentStateModified;
8786
8787 mData->mLastStateChange = config.timeLastStateChange;
8788
8789 /*
8790 * note: all mUserData members must be assigned prior this point because
8791 * we need to commit changes in order to let mUserData be shared by all
8792 * snapshot machine instances.
8793 */
8794 mUserData.commitCopy();
8795
8796 // machine registry, if present (must be loaded before snapshots)
8797 if (config.canHaveOwnMediaRegistry())
8798 {
8799 // determine machine folder
8800 Utf8Str strMachineFolder = i_getSettingsFileFull();
8801 strMachineFolder.stripFilename();
8802 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8803 config.mediaRegistry,
8804 strMachineFolder);
8805 if (FAILED(rc)) return rc;
8806 }
8807
8808 /* Snapshot node (optional) */
8809 size_t cRootSnapshots;
8810 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8811 {
8812 // there must be only one root snapshot
8813 Assert(cRootSnapshots == 1);
8814
8815 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8816
8817 rc = i_loadSnapshot(snap,
8818 config.uuidCurrentSnapshot,
8819 NULL); // no parent == first snapshot
8820 if (FAILED(rc)) return rc;
8821 }
8822
8823 // hardware data
8824 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8825 if (FAILED(rc)) return rc;
8826
8827 /*
8828 * NOTE: the assignment below must be the last thing to do,
8829 * otherwise it will be not possible to change the settings
8830 * somewhere in the code above because all setters will be
8831 * blocked by i_checkStateDependency(MutableStateDep).
8832 */
8833
8834 /* set the machine state to Aborted or Saved when appropriate */
8835 if (config.fAborted)
8836 {
8837 mSSData->strStateFilePath.setNull();
8838
8839 /* no need to use i_setMachineState() during init() */
8840 mData->mMachineState = MachineState_Aborted;
8841 }
8842 else if (!mSSData->strStateFilePath.isEmpty())
8843 {
8844 /* no need to use i_setMachineState() during init() */
8845 mData->mMachineState = MachineState_Saved;
8846 }
8847
8848 // after loading settings, we are no longer different from the XML on disk
8849 mData->flModifications = 0;
8850
8851 return S_OK;
8852}
8853
8854/**
8855 * Recursively loads all snapshots starting from the given.
8856 *
8857 * @param data snapshot settings.
8858 * @param aCurSnapshotId Current snapshot ID from the settings file.
8859 * @param aParentSnapshot Parent snapshot.
8860 */
8861HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8862 const Guid &aCurSnapshotId,
8863 Snapshot *aParentSnapshot)
8864{
8865 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8866 AssertReturn(!i_isSessionMachine(), E_FAIL);
8867
8868 HRESULT rc = S_OK;
8869
8870 Utf8Str strStateFile;
8871 if (!data.strStateFile.isEmpty())
8872 {
8873 /* optional */
8874 strStateFile = data.strStateFile;
8875 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8876 if (RT_FAILURE(vrc))
8877 return setError(E_FAIL,
8878 tr("Invalid saved state file path '%s' (%Rrc)"),
8879 strStateFile.c_str(),
8880 vrc);
8881 }
8882
8883 /* create a snapshot machine object */
8884 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8885 pSnapshotMachine.createObject();
8886 rc = pSnapshotMachine->initFromSettings(this,
8887 data.hardware,
8888 &data.debugging,
8889 &data.autostart,
8890 data.uuid.ref(),
8891 strStateFile);
8892 if (FAILED(rc)) return rc;
8893
8894 /* create a snapshot object */
8895 ComObjPtr<Snapshot> pSnapshot;
8896 pSnapshot.createObject();
8897 /* initialize the snapshot */
8898 rc = pSnapshot->init(mParent, // VirtualBox object
8899 data.uuid,
8900 data.strName,
8901 data.strDescription,
8902 data.timestamp,
8903 pSnapshotMachine,
8904 aParentSnapshot);
8905 if (FAILED(rc)) return rc;
8906
8907 /* memorize the first snapshot if necessary */
8908 if (!mData->mFirstSnapshot)
8909 mData->mFirstSnapshot = pSnapshot;
8910
8911 /* memorize the current snapshot when appropriate */
8912 if ( !mData->mCurrentSnapshot
8913 && pSnapshot->i_getId() == aCurSnapshotId
8914 )
8915 mData->mCurrentSnapshot = pSnapshot;
8916
8917 // now create the children
8918 for (settings::SnapshotsList::const_iterator
8919 it = data.llChildSnapshots.begin();
8920 it != data.llChildSnapshots.end();
8921 ++it)
8922 {
8923 const settings::Snapshot &childData = *it;
8924 // recurse
8925 rc = i_loadSnapshot(childData,
8926 aCurSnapshotId,
8927 pSnapshot); // parent = the one we created above
8928 if (FAILED(rc)) return rc;
8929 }
8930
8931 return rc;
8932}
8933
8934/**
8935 * Loads settings into mHWData.
8936 *
8937 * @param puuidRegistry Registry ID.
8938 * @param puuidSnapshot Snapshot ID
8939 * @param data Reference to the hardware settings.
8940 * @param pDbg Pointer to the debugging settings.
8941 * @param pAutostart Pointer to the autostart settings.
8942 */
8943HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8944 const Guid *puuidSnapshot,
8945 const settings::Hardware &data,
8946 const settings::Debugging *pDbg,
8947 const settings::Autostart *pAutostart)
8948{
8949 AssertReturn(!i_isSessionMachine(), E_FAIL);
8950
8951 HRESULT rc = S_OK;
8952
8953 try
8954 {
8955 ComObjPtr<GuestOSType> pGuestOSType;
8956 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8957 pGuestOSType);
8958 if (FAILED(rc))
8959 return rc;
8960
8961 /* The hardware version attribute (optional). */
8962 mHWData->mHWVersion = data.strVersion;
8963 mHWData->mHardwareUUID = data.uuid;
8964
8965 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8966 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8967 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8968 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8969 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8970 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8971 mHWData->mPAEEnabled = data.fPAE;
8972 mHWData->mLongMode = data.enmLongMode;
8973 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8974 mHWData->mAPIC = data.fAPIC;
8975 mHWData->mX2APIC = data.fX2APIC;
8976 mHWData->mCPUCount = data.cCPUs;
8977 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8978 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8979 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8980 mHWData->mCpuProfile = data.strCpuProfile;
8981
8982 // cpu
8983 if (mHWData->mCPUHotPlugEnabled)
8984 {
8985 for (settings::CpuList::const_iterator
8986 it = data.llCpus.begin();
8987 it != data.llCpus.end();
8988 ++it)
8989 {
8990 const settings::Cpu &cpu = *it;
8991
8992 mHWData->mCPUAttached[cpu.ulId] = true;
8993 }
8994 }
8995
8996 // cpuid leafs
8997 for (settings::CpuIdLeafsList::const_iterator
8998 it = data.llCpuIdLeafs.begin();
8999 it != data.llCpuIdLeafs.end();
9000 ++it)
9001 {
9002 const settings::CpuIdLeaf &leaf = *it;
9003
9004 switch (leaf.ulId)
9005 {
9006 case 0x0:
9007 case 0x1:
9008 case 0x2:
9009 case 0x3:
9010 case 0x4:
9011 case 0x5:
9012 case 0x6:
9013 case 0x7:
9014 case 0x8:
9015 case 0x9:
9016 case 0xA:
9017 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9018 break;
9019
9020 case 0x80000000:
9021 case 0x80000001:
9022 case 0x80000002:
9023 case 0x80000003:
9024 case 0x80000004:
9025 case 0x80000005:
9026 case 0x80000006:
9027 case 0x80000007:
9028 case 0x80000008:
9029 case 0x80000009:
9030 case 0x8000000A:
9031 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9032 break;
9033
9034 default:
9035 /* just ignore */
9036 break;
9037 }
9038 }
9039
9040 mHWData->mMemorySize = data.ulMemorySizeMB;
9041 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9042
9043 // boot order
9044 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9045 {
9046 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9047 if (it == data.mapBootOrder.end())
9048 mHWData->mBootOrder[i] = DeviceType_Null;
9049 else
9050 mHWData->mBootOrder[i] = it->second;
9051 }
9052
9053 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9054 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9055 mHWData->mMonitorCount = data.cMonitors;
9056 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9057 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9058 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9059 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9060 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9061 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9062 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9063 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9064 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9065 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9066 if (!data.strVideoCaptureFile.isEmpty())
9067 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9068 else
9069 mHWData->mVideoCaptureFile.setNull();
9070 mHWData->mFirmwareType = data.firmwareType;
9071 mHWData->mPointingHIDType = data.pointingHIDType;
9072 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9073 mHWData->mChipsetType = data.chipsetType;
9074 mHWData->mParavirtProvider = data.paravirtProvider;
9075 mHWData->mParavirtDebug = data.strParavirtDebug;
9076 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9077 mHWData->mHPETEnabled = data.fHPETEnabled;
9078
9079 /* VRDEServer */
9080 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9081 if (FAILED(rc)) return rc;
9082
9083 /* BIOS */
9084 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9085 if (FAILED(rc)) return rc;
9086
9087 // Bandwidth control (must come before network adapters)
9088 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9089 if (FAILED(rc)) return rc;
9090
9091 /* Shared folders */
9092 for (settings::USBControllerList::const_iterator
9093 it = data.usbSettings.llUSBControllers.begin();
9094 it != data.usbSettings.llUSBControllers.end();
9095 ++it)
9096 {
9097 const settings::USBController &settingsCtrl = *it;
9098 ComObjPtr<USBController> newCtrl;
9099
9100 newCtrl.createObject();
9101 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9102 mUSBControllers->push_back(newCtrl);
9103 }
9104
9105 /* USB device filters */
9106 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9107 if (FAILED(rc)) return rc;
9108
9109 // network adapters (establish array size first and apply defaults, to
9110 // ensure reading the same settings as we saved, since the list skips
9111 // adapters having defaults)
9112 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9113 size_t oldCount = mNetworkAdapters.size();
9114 if (newCount > oldCount)
9115 {
9116 mNetworkAdapters.resize(newCount);
9117 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9118 {
9119 unconst(mNetworkAdapters[slot]).createObject();
9120 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9121 }
9122 }
9123 else if (newCount < oldCount)
9124 mNetworkAdapters.resize(newCount);
9125 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9126 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9127 for (settings::NetworkAdaptersList::const_iterator
9128 it = data.llNetworkAdapters.begin();
9129 it != data.llNetworkAdapters.end();
9130 ++it)
9131 {
9132 const settings::NetworkAdapter &nic = *it;
9133
9134 /* slot uniqueness is guaranteed by XML Schema */
9135 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9136 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9137 if (FAILED(rc)) return rc;
9138 }
9139
9140 // serial ports (establish defaults first, to ensure reading the same
9141 // settings as we saved, since the list skips ports having defaults)
9142 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9143 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9144 for (settings::SerialPortsList::const_iterator
9145 it = data.llSerialPorts.begin();
9146 it != data.llSerialPorts.end();
9147 ++it)
9148 {
9149 const settings::SerialPort &s = *it;
9150
9151 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9152 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9153 if (FAILED(rc)) return rc;
9154 }
9155
9156 // parallel ports (establish defaults first, to ensure reading the same
9157 // settings as we saved, since the list skips ports having defaults)
9158 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9159 mParallelPorts[i]->i_applyDefaults();
9160 for (settings::ParallelPortsList::const_iterator
9161 it = data.llParallelPorts.begin();
9162 it != data.llParallelPorts.end();
9163 ++it)
9164 {
9165 const settings::ParallelPort &p = *it;
9166
9167 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9168 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9169 if (FAILED(rc)) return rc;
9170 }
9171
9172 /* AudioAdapter */
9173 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9174 if (FAILED(rc)) return rc;
9175
9176 /* storage controllers */
9177 rc = i_loadStorageControllers(data.storage,
9178 puuidRegistry,
9179 puuidSnapshot);
9180 if (FAILED(rc)) return rc;
9181
9182 /* Shared folders */
9183 for (settings::SharedFoldersList::const_iterator
9184 it = data.llSharedFolders.begin();
9185 it != data.llSharedFolders.end();
9186 ++it)
9187 {
9188 const settings::SharedFolder &sf = *it;
9189
9190 ComObjPtr<SharedFolder> sharedFolder;
9191 /* Check for double entries. Not allowed! */
9192 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9193 if (SUCCEEDED(rc))
9194 return setError(VBOX_E_OBJECT_IN_USE,
9195 tr("Shared folder named '%s' already exists"),
9196 sf.strName.c_str());
9197
9198 /* Create the new shared folder. Don't break on error. This will be
9199 * reported when the machine starts. */
9200 sharedFolder.createObject();
9201 rc = sharedFolder->init(i_getMachine(),
9202 sf.strName,
9203 sf.strHostPath,
9204 RT_BOOL(sf.fWritable),
9205 RT_BOOL(sf.fAutoMount),
9206 false /* fFailOnError */);
9207 if (FAILED(rc)) return rc;
9208 mHWData->mSharedFolders.push_back(sharedFolder);
9209 }
9210
9211 // Clipboard
9212 mHWData->mClipboardMode = data.clipboardMode;
9213
9214 // drag'n'drop
9215 mHWData->mDnDMode = data.dndMode;
9216
9217 // guest settings
9218 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9219
9220 // IO settings
9221 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9222 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9223
9224 // Host PCI devices
9225 for (settings::HostPCIDeviceAttachmentList::const_iterator
9226 it = data.pciAttachments.begin();
9227 it != data.pciAttachments.end();
9228 ++it)
9229 {
9230 const settings::HostPCIDeviceAttachment &hpda = *it;
9231 ComObjPtr<PCIDeviceAttachment> pda;
9232
9233 pda.createObject();
9234 pda->i_loadSettings(this, hpda);
9235 mHWData->mPCIDeviceAssignments.push_back(pda);
9236 }
9237
9238 /*
9239 * (The following isn't really real hardware, but it lives in HWData
9240 * for reasons of convenience.)
9241 */
9242
9243#ifdef VBOX_WITH_GUEST_PROPS
9244 /* Guest properties (optional) */
9245
9246 /* Only load transient guest properties for configs which have saved
9247 * state, because there shouldn't be any for powered off VMs. The same
9248 * logic applies for snapshots, as offline snapshots shouldn't have
9249 * any such properties. They confuse the code in various places.
9250 * Note: can't rely on the machine state, as it isn't set yet. */
9251 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9252 /* apologies for the hacky unconst() usage, but this needs hacking
9253 * actually inconsistent settings into consistency, otherwise there
9254 * will be some corner cases where the inconsistency survives
9255 * surprisingly long without getting fixed, especially for snapshots
9256 * as there are no config changes. */
9257 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9258 for (settings::GuestPropertiesList::iterator
9259 it = llGuestProperties.begin();
9260 it != llGuestProperties.end();
9261 /*nothing*/)
9262 {
9263 const settings::GuestProperty &prop = *it;
9264 uint32_t fFlags = guestProp::NILFLAG;
9265 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9266 if ( fSkipTransientGuestProperties
9267 && ( fFlags & guestProp::TRANSIENT
9268 || fFlags & guestProp::TRANSRESET))
9269 {
9270 it = llGuestProperties.erase(it);
9271 continue;
9272 }
9273 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9274 mHWData->mGuestProperties[prop.strName] = property;
9275 ++it;
9276 }
9277#endif /* VBOX_WITH_GUEST_PROPS defined */
9278
9279 rc = i_loadDebugging(pDbg);
9280 if (FAILED(rc))
9281 return rc;
9282
9283 mHWData->mAutostart = *pAutostart;
9284
9285 /* default frontend */
9286 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9287 }
9288 catch (std::bad_alloc &)
9289 {
9290 return E_OUTOFMEMORY;
9291 }
9292
9293 AssertComRC(rc);
9294 return rc;
9295}
9296
9297/**
9298 * Called from i_loadHardware() to load the debugging settings of the
9299 * machine.
9300 *
9301 * @param pDbg Pointer to the settings.
9302 */
9303HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9304{
9305 mHWData->mDebugging = *pDbg;
9306 /* no more processing currently required, this will probably change. */
9307 return S_OK;
9308}
9309
9310/**
9311 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9312 *
9313 * @param data storage settings.
9314 * @param puuidRegistry media registry ID to set media to or NULL;
9315 * see Machine::i_loadMachineDataFromSettings()
9316 * @param puuidSnapshot snapshot ID
9317 * @return
9318 */
9319HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9320 const Guid *puuidRegistry,
9321 const Guid *puuidSnapshot)
9322{
9323 AssertReturn(!i_isSessionMachine(), E_FAIL);
9324
9325 HRESULT rc = S_OK;
9326
9327 for (settings::StorageControllersList::const_iterator
9328 it = data.llStorageControllers.begin();
9329 it != data.llStorageControllers.end();
9330 ++it)
9331 {
9332 const settings::StorageController &ctlData = *it;
9333
9334 ComObjPtr<StorageController> pCtl;
9335 /* Try to find one with the name first. */
9336 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9337 if (SUCCEEDED(rc))
9338 return setError(VBOX_E_OBJECT_IN_USE,
9339 tr("Storage controller named '%s' already exists"),
9340 ctlData.strName.c_str());
9341
9342 pCtl.createObject();
9343 rc = pCtl->init(this,
9344 ctlData.strName,
9345 ctlData.storageBus,
9346 ctlData.ulInstance,
9347 ctlData.fBootable);
9348 if (FAILED(rc)) return rc;
9349
9350 mStorageControllers->push_back(pCtl);
9351
9352 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9353 if (FAILED(rc)) return rc;
9354
9355 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9356 if (FAILED(rc)) return rc;
9357
9358 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9359 if (FAILED(rc)) return rc;
9360
9361 /* Load the attached devices now. */
9362 rc = i_loadStorageDevices(pCtl,
9363 ctlData,
9364 puuidRegistry,
9365 puuidSnapshot);
9366 if (FAILED(rc)) return rc;
9367 }
9368
9369 return S_OK;
9370}
9371
9372/**
9373 * Called from i_loadStorageControllers for a controller's devices.
9374 *
9375 * @param aStorageController
9376 * @param data
9377 * @param puuidRegistry media registry ID to set media to or NULL; see
9378 * Machine::i_loadMachineDataFromSettings()
9379 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9380 * @return
9381 */
9382HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9383 const settings::StorageController &data,
9384 const Guid *puuidRegistry,
9385 const Guid *puuidSnapshot)
9386{
9387 HRESULT rc = S_OK;
9388
9389 /* paranoia: detect duplicate attachments */
9390 for (settings::AttachedDevicesList::const_iterator
9391 it = data.llAttachedDevices.begin();
9392 it != data.llAttachedDevices.end();
9393 ++it)
9394 {
9395 const settings::AttachedDevice &ad = *it;
9396
9397 for (settings::AttachedDevicesList::const_iterator it2 = it;
9398 it2 != data.llAttachedDevices.end();
9399 ++it2)
9400 {
9401 if (it == it2)
9402 continue;
9403
9404 const settings::AttachedDevice &ad2 = *it2;
9405
9406 if ( ad.lPort == ad2.lPort
9407 && ad.lDevice == ad2.lDevice)
9408 {
9409 return setError(E_FAIL,
9410 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9411 aStorageController->i_getName().c_str(),
9412 ad.lPort,
9413 ad.lDevice,
9414 mUserData->s.strName.c_str());
9415 }
9416 }
9417 }
9418
9419 for (settings::AttachedDevicesList::const_iterator
9420 it = data.llAttachedDevices.begin();
9421 it != data.llAttachedDevices.end();
9422 ++it)
9423 {
9424 const settings::AttachedDevice &dev = *it;
9425 ComObjPtr<Medium> medium;
9426
9427 switch (dev.deviceType)
9428 {
9429 case DeviceType_Floppy:
9430 case DeviceType_DVD:
9431 if (dev.strHostDriveSrc.isNotEmpty())
9432 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9433 false /* fRefresh */, medium);
9434 else
9435 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9436 dev.uuid,
9437 false /* fRefresh */,
9438 false /* aSetError */,
9439 medium);
9440 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9441 // This is not an error. The host drive or UUID might have vanished, so just go
9442 // ahead without this removeable medium attachment
9443 rc = S_OK;
9444 break;
9445
9446 case DeviceType_HardDisk:
9447 {
9448 /* find a hard disk by UUID */
9449 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9450 if (FAILED(rc))
9451 {
9452 if (i_isSnapshotMachine())
9453 {
9454 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9455 // so the user knows that the bad disk is in a snapshot somewhere
9456 com::ErrorInfo info;
9457 return setError(E_FAIL,
9458 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9459 puuidSnapshot->raw(),
9460 info.getText().raw());
9461 }
9462 else
9463 return rc;
9464 }
9465
9466 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9467
9468 if (medium->i_getType() == MediumType_Immutable)
9469 {
9470 if (i_isSnapshotMachine())
9471 return setError(E_FAIL,
9472 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9473 "of the virtual machine '%s' ('%s')"),
9474 medium->i_getLocationFull().c_str(),
9475 dev.uuid.raw(),
9476 puuidSnapshot->raw(),
9477 mUserData->s.strName.c_str(),
9478 mData->m_strConfigFileFull.c_str());
9479
9480 return setError(E_FAIL,
9481 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9482 medium->i_getLocationFull().c_str(),
9483 dev.uuid.raw(),
9484 mUserData->s.strName.c_str(),
9485 mData->m_strConfigFileFull.c_str());
9486 }
9487
9488 if (medium->i_getType() == MediumType_MultiAttach)
9489 {
9490 if (i_isSnapshotMachine())
9491 return setError(E_FAIL,
9492 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9493 "of the virtual machine '%s' ('%s')"),
9494 medium->i_getLocationFull().c_str(),
9495 dev.uuid.raw(),
9496 puuidSnapshot->raw(),
9497 mUserData->s.strName.c_str(),
9498 mData->m_strConfigFileFull.c_str());
9499
9500 return setError(E_FAIL,
9501 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9502 medium->i_getLocationFull().c_str(),
9503 dev.uuid.raw(),
9504 mUserData->s.strName.c_str(),
9505 mData->m_strConfigFileFull.c_str());
9506 }
9507
9508 if ( !i_isSnapshotMachine()
9509 && medium->i_getChildren().size() != 0
9510 )
9511 return setError(E_FAIL,
9512 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9513 "because it has %d differencing child hard disks"),
9514 medium->i_getLocationFull().c_str(),
9515 dev.uuid.raw(),
9516 mUserData->s.strName.c_str(),
9517 mData->m_strConfigFileFull.c_str(),
9518 medium->i_getChildren().size());
9519
9520 if (i_findAttachment(*mMediumAttachments.data(),
9521 medium))
9522 return setError(E_FAIL,
9523 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9524 medium->i_getLocationFull().c_str(),
9525 dev.uuid.raw(),
9526 mUserData->s.strName.c_str(),
9527 mData->m_strConfigFileFull.c_str());
9528
9529 break;
9530 }
9531
9532 default:
9533 return setError(E_FAIL,
9534 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9535 medium->i_getLocationFull().c_str(),
9536 mUserData->s.strName.c_str(),
9537 mData->m_strConfigFileFull.c_str());
9538 }
9539
9540 if (FAILED(rc))
9541 break;
9542
9543 /* Bandwidth groups are loaded at this point. */
9544 ComObjPtr<BandwidthGroup> pBwGroup;
9545
9546 if (!dev.strBwGroup.isEmpty())
9547 {
9548 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9549 if (FAILED(rc))
9550 return setError(E_FAIL,
9551 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9552 medium->i_getLocationFull().c_str(),
9553 dev.strBwGroup.c_str(),
9554 mUserData->s.strName.c_str(),
9555 mData->m_strConfigFileFull.c_str());
9556 pBwGroup->i_reference();
9557 }
9558
9559 const Utf8Str controllerName = aStorageController->i_getName();
9560 ComObjPtr<MediumAttachment> pAttachment;
9561 pAttachment.createObject();
9562 rc = pAttachment->init(this,
9563 medium,
9564 controllerName,
9565 dev.lPort,
9566 dev.lDevice,
9567 dev.deviceType,
9568 false,
9569 dev.fPassThrough,
9570 dev.fTempEject,
9571 dev.fNonRotational,
9572 dev.fDiscard,
9573 dev.fHotPluggable,
9574 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9575 if (FAILED(rc)) break;
9576
9577 /* associate the medium with this machine and snapshot */
9578 if (!medium.isNull())
9579 {
9580 AutoCaller medCaller(medium);
9581 if (FAILED(medCaller.rc())) return medCaller.rc();
9582 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9583
9584 if (i_isSnapshotMachine())
9585 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9586 else
9587 rc = medium->i_addBackReference(mData->mUuid);
9588 /* If the medium->addBackReference fails it sets an appropriate
9589 * error message, so no need to do any guesswork here. */
9590
9591 if (puuidRegistry)
9592 // caller wants registry ID to be set on all attached media (OVF import case)
9593 medium->i_addRegistry(*puuidRegistry);
9594 }
9595
9596 if (FAILED(rc))
9597 break;
9598
9599 /* back up mMediumAttachments to let registeredInit() properly rollback
9600 * on failure (= limited accessibility) */
9601 i_setModified(IsModified_Storage);
9602 mMediumAttachments.backup();
9603 mMediumAttachments->push_back(pAttachment);
9604 }
9605
9606 return rc;
9607}
9608
9609/**
9610 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9611 *
9612 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9613 * @param aSnapshot where to return the found snapshot
9614 * @param aSetError true to set extended error info on failure
9615 */
9616HRESULT Machine::i_findSnapshotById(const Guid &aId,
9617 ComObjPtr<Snapshot> &aSnapshot,
9618 bool aSetError /* = false */)
9619{
9620 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9621
9622 if (!mData->mFirstSnapshot)
9623 {
9624 if (aSetError)
9625 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9626 return E_FAIL;
9627 }
9628
9629 if (aId.isZero())
9630 aSnapshot = mData->mFirstSnapshot;
9631 else
9632 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9633
9634 if (!aSnapshot)
9635 {
9636 if (aSetError)
9637 return setError(E_FAIL,
9638 tr("Could not find a snapshot with UUID {%s}"),
9639 aId.toString().c_str());
9640 return E_FAIL;
9641 }
9642
9643 return S_OK;
9644}
9645
9646/**
9647 * Returns the snapshot with the given name or fails of no such snapshot.
9648 *
9649 * @param strName snapshot name to find
9650 * @param aSnapshot where to return the found snapshot
9651 * @param aSetError true to set extended error info on failure
9652 */
9653HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9654 ComObjPtr<Snapshot> &aSnapshot,
9655 bool aSetError /* = false */)
9656{
9657 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9658
9659 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9660
9661 if (!mData->mFirstSnapshot)
9662 {
9663 if (aSetError)
9664 return setError(VBOX_E_OBJECT_NOT_FOUND,
9665 tr("This machine does not have any snapshots"));
9666 return VBOX_E_OBJECT_NOT_FOUND;
9667 }
9668
9669 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9670
9671 if (!aSnapshot)
9672 {
9673 if (aSetError)
9674 return setError(VBOX_E_OBJECT_NOT_FOUND,
9675 tr("Could not find a snapshot named '%s'"), strName.c_str());
9676 return VBOX_E_OBJECT_NOT_FOUND;
9677 }
9678
9679 return S_OK;
9680}
9681
9682/**
9683 * Returns a storage controller object with the given name.
9684 *
9685 * @param aName storage controller name to find
9686 * @param aStorageController where to return the found storage controller
9687 * @param aSetError true to set extended error info on failure
9688 */
9689HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9690 ComObjPtr<StorageController> &aStorageController,
9691 bool aSetError /* = false */)
9692{
9693 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9694
9695 for (StorageControllerList::const_iterator
9696 it = mStorageControllers->begin();
9697 it != mStorageControllers->end();
9698 ++it)
9699 {
9700 if ((*it)->i_getName() == aName)
9701 {
9702 aStorageController = (*it);
9703 return S_OK;
9704 }
9705 }
9706
9707 if (aSetError)
9708 return setError(VBOX_E_OBJECT_NOT_FOUND,
9709 tr("Could not find a storage controller named '%s'"),
9710 aName.c_str());
9711 return VBOX_E_OBJECT_NOT_FOUND;
9712}
9713
9714/**
9715 * Returns a USB controller object with the given name.
9716 *
9717 * @param aName USB controller name to find
9718 * @param aUSBController where to return the found USB controller
9719 * @param aSetError true to set extended error info on failure
9720 */
9721HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9722 ComObjPtr<USBController> &aUSBController,
9723 bool aSetError /* = false */)
9724{
9725 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9726
9727 for (USBControllerList::const_iterator
9728 it = mUSBControllers->begin();
9729 it != mUSBControllers->end();
9730 ++it)
9731 {
9732 if ((*it)->i_getName() == aName)
9733 {
9734 aUSBController = (*it);
9735 return S_OK;
9736 }
9737 }
9738
9739 if (aSetError)
9740 return setError(VBOX_E_OBJECT_NOT_FOUND,
9741 tr("Could not find a storage controller named '%s'"),
9742 aName.c_str());
9743 return VBOX_E_OBJECT_NOT_FOUND;
9744}
9745
9746/**
9747 * Returns the number of USB controller instance of the given type.
9748 *
9749 * @param enmType USB controller type.
9750 */
9751ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9752{
9753 ULONG cCtrls = 0;
9754
9755 for (USBControllerList::const_iterator
9756 it = mUSBControllers->begin();
9757 it != mUSBControllers->end();
9758 ++it)
9759 {
9760 if ((*it)->i_getControllerType() == enmType)
9761 cCtrls++;
9762 }
9763
9764 return cCtrls;
9765}
9766
9767HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9768 MediumAttachmentList &atts)
9769{
9770 AutoCaller autoCaller(this);
9771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9772
9773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9774
9775 for (MediumAttachmentList::const_iterator
9776 it = mMediumAttachments->begin();
9777 it != mMediumAttachments->end();
9778 ++it)
9779 {
9780 const ComObjPtr<MediumAttachment> &pAtt = *it;
9781 // should never happen, but deal with NULL pointers in the list.
9782 AssertContinue(!pAtt.isNull());
9783
9784 // getControllerName() needs caller+read lock
9785 AutoCaller autoAttCaller(pAtt);
9786 if (FAILED(autoAttCaller.rc()))
9787 {
9788 atts.clear();
9789 return autoAttCaller.rc();
9790 }
9791 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9792
9793 if (pAtt->i_getControllerName() == aName)
9794 atts.push_back(pAtt);
9795 }
9796
9797 return S_OK;
9798}
9799
9800
9801/**
9802 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9803 * file if the machine name was changed and about creating a new settings file
9804 * if this is a new machine.
9805 *
9806 * @note Must be never called directly but only from #saveSettings().
9807 */
9808HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9809{
9810 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9811
9812 HRESULT rc = S_OK;
9813
9814 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9815
9816 /// @todo need to handle primary group change, too
9817
9818 /* attempt to rename the settings file if machine name is changed */
9819 if ( mUserData->s.fNameSync
9820 && mUserData.isBackedUp()
9821 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9822 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9823 )
9824 {
9825 bool dirRenamed = false;
9826 bool fileRenamed = false;
9827
9828 Utf8Str configFile, newConfigFile;
9829 Utf8Str configFilePrev, newConfigFilePrev;
9830 Utf8Str configDir, newConfigDir;
9831
9832 do
9833 {
9834 int vrc = VINF_SUCCESS;
9835
9836 Utf8Str name = mUserData.backedUpData()->s.strName;
9837 Utf8Str newName = mUserData->s.strName;
9838 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9839 if (group == "/")
9840 group.setNull();
9841 Utf8Str newGroup = mUserData->s.llGroups.front();
9842 if (newGroup == "/")
9843 newGroup.setNull();
9844
9845 configFile = mData->m_strConfigFileFull;
9846
9847 /* first, rename the directory if it matches the group and machine name */
9848 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9849 group.c_str(), RTPATH_DELIMITER, name.c_str());
9850 /** @todo hack, make somehow use of ComposeMachineFilename */
9851 if (mUserData->s.fDirectoryIncludesUUID)
9852 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9853 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9854 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9855 /** @todo hack, make somehow use of ComposeMachineFilename */
9856 if (mUserData->s.fDirectoryIncludesUUID)
9857 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9858 configDir = configFile;
9859 configDir.stripFilename();
9860 newConfigDir = configDir;
9861 if ( configDir.length() >= groupPlusName.length()
9862 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9863 groupPlusName.c_str()))
9864 {
9865 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9866 Utf8Str newConfigBaseDir(newConfigDir);
9867 newConfigDir.append(newGroupPlusName);
9868 /* consistency: use \ if appropriate on the platform */
9869 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9870 /* new dir and old dir cannot be equal here because of 'if'
9871 * above and because name != newName */
9872 Assert(configDir != newConfigDir);
9873 if (!fSettingsFileIsNew)
9874 {
9875 /* perform real rename only if the machine is not new */
9876 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9877 if ( vrc == VERR_FILE_NOT_FOUND
9878 || vrc == VERR_PATH_NOT_FOUND)
9879 {
9880 /* create the parent directory, then retry renaming */
9881 Utf8Str parent(newConfigDir);
9882 parent.stripFilename();
9883 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9884 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9885 }
9886 if (RT_FAILURE(vrc))
9887 {
9888 rc = setError(E_FAIL,
9889 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9890 configDir.c_str(),
9891 newConfigDir.c_str(),
9892 vrc);
9893 break;
9894 }
9895 /* delete subdirectories which are no longer needed */
9896 Utf8Str dir(configDir);
9897 dir.stripFilename();
9898 while (dir != newConfigBaseDir && dir != ".")
9899 {
9900 vrc = RTDirRemove(dir.c_str());
9901 if (RT_FAILURE(vrc))
9902 break;
9903 dir.stripFilename();
9904 }
9905 dirRenamed = true;
9906 }
9907 }
9908
9909 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9910 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9911
9912 /* then try to rename the settings file itself */
9913 if (newConfigFile != configFile)
9914 {
9915 /* get the path to old settings file in renamed directory */
9916 configFile = Utf8StrFmt("%s%c%s",
9917 newConfigDir.c_str(),
9918 RTPATH_DELIMITER,
9919 RTPathFilename(configFile.c_str()));
9920 if (!fSettingsFileIsNew)
9921 {
9922 /* perform real rename only if the machine is not new */
9923 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9924 if (RT_FAILURE(vrc))
9925 {
9926 rc = setError(E_FAIL,
9927 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9928 configFile.c_str(),
9929 newConfigFile.c_str(),
9930 vrc);
9931 break;
9932 }
9933 fileRenamed = true;
9934 configFilePrev = configFile;
9935 configFilePrev += "-prev";
9936 newConfigFilePrev = newConfigFile;
9937 newConfigFilePrev += "-prev";
9938 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9939 }
9940 }
9941
9942 // update m_strConfigFileFull amd mConfigFile
9943 mData->m_strConfigFileFull = newConfigFile;
9944 // compute the relative path too
9945 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9946
9947 // store the old and new so that VirtualBox::i_saveSettings() can update
9948 // the media registry
9949 if ( mData->mRegistered
9950 && (configDir != newConfigDir || configFile != newConfigFile))
9951 {
9952 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9953
9954 if (pfNeedsGlobalSaveSettings)
9955 *pfNeedsGlobalSaveSettings = true;
9956 }
9957
9958 // in the saved state file path, replace the old directory with the new directory
9959 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9960 {
9961 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9962 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9963 }
9964
9965 // and do the same thing for the saved state file paths of all the online snapshots
9966 if (mData->mFirstSnapshot)
9967 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9968 newConfigDir.c_str());
9969 }
9970 while (0);
9971
9972 if (FAILED(rc))
9973 {
9974 /* silently try to rename everything back */
9975 if (fileRenamed)
9976 {
9977 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9978 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9979 }
9980 if (dirRenamed)
9981 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9982 }
9983
9984 if (FAILED(rc)) return rc;
9985 }
9986
9987 if (fSettingsFileIsNew)
9988 {
9989 /* create a virgin config file */
9990 int vrc = VINF_SUCCESS;
9991
9992 /* ensure the settings directory exists */
9993 Utf8Str path(mData->m_strConfigFileFull);
9994 path.stripFilename();
9995 if (!RTDirExists(path.c_str()))
9996 {
9997 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9998 if (RT_FAILURE(vrc))
9999 {
10000 return setError(E_FAIL,
10001 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10002 path.c_str(),
10003 vrc);
10004 }
10005 }
10006
10007 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10008 path = Utf8Str(mData->m_strConfigFileFull);
10009 RTFILE f = NIL_RTFILE;
10010 vrc = RTFileOpen(&f, path.c_str(),
10011 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10012 if (RT_FAILURE(vrc))
10013 return setError(E_FAIL,
10014 tr("Could not create the settings file '%s' (%Rrc)"),
10015 path.c_str(),
10016 vrc);
10017 RTFileClose(f);
10018 }
10019
10020 return rc;
10021}
10022
10023/**
10024 * Saves and commits machine data, user data and hardware data.
10025 *
10026 * Note that on failure, the data remains uncommitted.
10027 *
10028 * @a aFlags may combine the following flags:
10029 *
10030 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10031 * Used when saving settings after an operation that makes them 100%
10032 * correspond to the settings from the current snapshot.
10033 * - SaveS_Force: settings will be saved without doing a deep compare of the
10034 * settings structures. This is used when this is called because snapshots
10035 * have changed to avoid the overhead of the deep compare.
10036 *
10037 * @note Must be called from under this object's write lock. Locks children for
10038 * writing.
10039 *
10040 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10041 * initialized to false and that will be set to true by this function if
10042 * the caller must invoke VirtualBox::i_saveSettings() because the global
10043 * settings have changed. This will happen if a machine rename has been
10044 * saved and the global machine and media registries will therefore need
10045 * updating.
10046 * @param aFlags Flags.
10047 */
10048HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10049 int aFlags /*= 0*/)
10050{
10051 LogFlowThisFuncEnter();
10052
10053 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10054
10055 /* make sure child objects are unable to modify the settings while we are
10056 * saving them */
10057 i_ensureNoStateDependencies();
10058
10059 AssertReturn(!i_isSnapshotMachine(),
10060 E_FAIL);
10061
10062 HRESULT rc = S_OK;
10063 bool fNeedsWrite = false;
10064
10065 /* First, prepare to save settings. It will care about renaming the
10066 * settings directory and file if the machine name was changed and about
10067 * creating a new settings file if this is a new machine. */
10068 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10069 if (FAILED(rc)) return rc;
10070
10071 // keep a pointer to the current settings structures
10072 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10073 settings::MachineConfigFile *pNewConfig = NULL;
10074
10075 try
10076 {
10077 // make a fresh one to have everyone write stuff into
10078 pNewConfig = new settings::MachineConfigFile(NULL);
10079 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10080
10081 // now go and copy all the settings data from COM to the settings structures
10082 // (this calls i_saveSettings() on all the COM objects in the machine)
10083 i_copyMachineDataToSettings(*pNewConfig);
10084
10085 if (aFlags & SaveS_ResetCurStateModified)
10086 {
10087 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10088 mData->mCurrentStateModified = FALSE;
10089 fNeedsWrite = true; // always, no need to compare
10090 }
10091 else if (aFlags & SaveS_Force)
10092 {
10093 fNeedsWrite = true; // always, no need to compare
10094 }
10095 else
10096 {
10097 if (!mData->mCurrentStateModified)
10098 {
10099 // do a deep compare of the settings that we just saved with the settings
10100 // previously stored in the config file; this invokes MachineConfigFile::operator==
10101 // which does a deep compare of all the settings, which is expensive but less expensive
10102 // than writing out XML in vain
10103 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10104
10105 // could still be modified if any settings changed
10106 mData->mCurrentStateModified = fAnySettingsChanged;
10107
10108 fNeedsWrite = fAnySettingsChanged;
10109 }
10110 else
10111 fNeedsWrite = true;
10112 }
10113
10114 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10115
10116 if (fNeedsWrite)
10117 // now spit it all out!
10118 pNewConfig->write(mData->m_strConfigFileFull);
10119
10120 mData->pMachineConfigFile = pNewConfig;
10121 delete pOldConfig;
10122 i_commit();
10123
10124 // after saving settings, we are no longer different from the XML on disk
10125 mData->flModifications = 0;
10126 }
10127 catch (HRESULT err)
10128 {
10129 // we assume that error info is set by the thrower
10130 rc = err;
10131
10132 // restore old config
10133 delete pNewConfig;
10134 mData->pMachineConfigFile = pOldConfig;
10135 }
10136 catch (...)
10137 {
10138 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10139 }
10140
10141 if (fNeedsWrite)
10142 {
10143 /* Fire the data change event, even on failure (since we've already
10144 * committed all data). This is done only for SessionMachines because
10145 * mutable Machine instances are always not registered (i.e. private
10146 * to the client process that creates them) and thus don't need to
10147 * inform callbacks. */
10148 if (i_isSessionMachine())
10149 mParent->i_onMachineDataChange(mData->mUuid);
10150 }
10151
10152 LogFlowThisFunc(("rc=%08X\n", rc));
10153 LogFlowThisFuncLeave();
10154 return rc;
10155}
10156
10157/**
10158 * Implementation for saving the machine settings into the given
10159 * settings::MachineConfigFile instance. This copies machine extradata
10160 * from the previous machine config file in the instance data, if any.
10161 *
10162 * This gets called from two locations:
10163 *
10164 * -- Machine::i_saveSettings(), during the regular XML writing;
10165 *
10166 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10167 * exported to OVF and we write the VirtualBox proprietary XML
10168 * into a <vbox:Machine> tag.
10169 *
10170 * This routine fills all the fields in there, including snapshots, *except*
10171 * for the following:
10172 *
10173 * -- fCurrentStateModified. There is some special logic associated with that.
10174 *
10175 * The caller can then call MachineConfigFile::write() or do something else
10176 * with it.
10177 *
10178 * Caller must hold the machine lock!
10179 *
10180 * This throws XML errors and HRESULT, so the caller must have a catch block!
10181 */
10182void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10183{
10184 // deep copy extradata, being extra careful with self assignment (the STL
10185 // map assignment on Mac OS X clang based Xcode isn't checking)
10186 if (&config != mData->pMachineConfigFile)
10187 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10188
10189 config.uuid = mData->mUuid;
10190
10191 // copy name, description, OS type, teleport, UTC etc.
10192 config.machineUserData = mUserData->s;
10193
10194 if ( mData->mMachineState == MachineState_Saved
10195 || mData->mMachineState == MachineState_Restoring
10196 // when doing certain snapshot operations we may or may not have
10197 // a saved state in the current state, so keep everything as is
10198 || ( ( mData->mMachineState == MachineState_Snapshotting
10199 || mData->mMachineState == MachineState_DeletingSnapshot
10200 || mData->mMachineState == MachineState_RestoringSnapshot)
10201 && (!mSSData->strStateFilePath.isEmpty())
10202 )
10203 )
10204 {
10205 Assert(!mSSData->strStateFilePath.isEmpty());
10206 /* try to make the file name relative to the settings file dir */
10207 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10208 }
10209 else
10210 {
10211 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10212 config.strStateFile.setNull();
10213 }
10214
10215 if (mData->mCurrentSnapshot)
10216 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10217 else
10218 config.uuidCurrentSnapshot.clear();
10219
10220 config.timeLastStateChange = mData->mLastStateChange;
10221 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10222 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10223
10224 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10225 if (FAILED(rc)) throw rc;
10226
10227 // save machine's media registry if this is VirtualBox 4.0 or later
10228 if (config.canHaveOwnMediaRegistry())
10229 {
10230 // determine machine folder
10231 Utf8Str strMachineFolder = i_getSettingsFileFull();
10232 strMachineFolder.stripFilename();
10233 mParent->i_saveMediaRegistry(config.mediaRegistry,
10234 i_getId(), // only media with registry ID == machine UUID
10235 strMachineFolder);
10236 // this throws HRESULT
10237 }
10238
10239 // save snapshots
10240 rc = i_saveAllSnapshots(config);
10241 if (FAILED(rc)) throw rc;
10242}
10243
10244/**
10245 * Saves all snapshots of the machine into the given machine config file. Called
10246 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10247 * @param config
10248 * @return
10249 */
10250HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10251{
10252 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10253
10254 HRESULT rc = S_OK;
10255
10256 try
10257 {
10258 config.llFirstSnapshot.clear();
10259
10260 if (mData->mFirstSnapshot)
10261 {
10262 // the settings use a list for "the first snapshot"
10263 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10264
10265 // get reference to the snapshot on the list and work on that
10266 // element straight in the list to avoid excessive copying later
10267 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10268 if (FAILED(rc)) throw rc;
10269 }
10270
10271// if (mType == IsSessionMachine)
10272// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10273
10274 }
10275 catch (HRESULT err)
10276 {
10277 /* we assume that error info is set by the thrower */
10278 rc = err;
10279 }
10280 catch (...)
10281 {
10282 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10283 }
10284
10285 return rc;
10286}
10287
10288/**
10289 * Saves the VM hardware configuration. It is assumed that the
10290 * given node is empty.
10291 *
10292 * @param data Reference to the settings object for the hardware config.
10293 * @param pDbg Pointer to the settings object for the debugging config
10294 * which happens to live in mHWData.
10295 * @param pAutostart Pointer to the settings object for the autostart config
10296 * which happens to live in mHWData.
10297 */
10298HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10299 settings::Autostart *pAutostart)
10300{
10301 HRESULT rc = S_OK;
10302
10303 try
10304 {
10305 /* The hardware version attribute (optional).
10306 Automatically upgrade from 1 to current default hardware version
10307 when there is no saved state. (ugly!) */
10308 if ( mHWData->mHWVersion == "1"
10309 && mSSData->strStateFilePath.isEmpty()
10310 )
10311 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10312
10313 data.strVersion = mHWData->mHWVersion;
10314 data.uuid = mHWData->mHardwareUUID;
10315
10316 // CPU
10317 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10318 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10319 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10320 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10321 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10322 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10323 data.fPAE = !!mHWData->mPAEEnabled;
10324 data.enmLongMode = mHWData->mLongMode;
10325 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10326 data.fAPIC = !!mHWData->mAPIC;
10327 data.fX2APIC = !!mHWData->mX2APIC;
10328 data.cCPUs = mHWData->mCPUCount;
10329 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10330 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10331 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10332 data.strCpuProfile = mHWData->mCpuProfile;
10333
10334 data.llCpus.clear();
10335 if (data.fCpuHotPlug)
10336 {
10337 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10338 {
10339 if (mHWData->mCPUAttached[idx])
10340 {
10341 settings::Cpu cpu;
10342 cpu.ulId = idx;
10343 data.llCpus.push_back(cpu);
10344 }
10345 }
10346 }
10347
10348 /* Standard and Extended CPUID leafs. */
10349 data.llCpuIdLeafs.clear();
10350 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10351 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10352 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10353 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10354 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10355 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10356
10357 // memory
10358 data.ulMemorySizeMB = mHWData->mMemorySize;
10359 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10360
10361 // firmware
10362 data.firmwareType = mHWData->mFirmwareType;
10363
10364 // HID
10365 data.pointingHIDType = mHWData->mPointingHIDType;
10366 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10367
10368 // chipset
10369 data.chipsetType = mHWData->mChipsetType;
10370
10371 // paravirt
10372 data.paravirtProvider = mHWData->mParavirtProvider;
10373 data.strParavirtDebug = mHWData->mParavirtDebug;
10374
10375 // emulated USB card reader
10376 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10377
10378 // HPET
10379 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10380
10381 // boot order
10382 data.mapBootOrder.clear();
10383 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10384 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10385
10386 // display
10387 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10388 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10389 data.cMonitors = mHWData->mMonitorCount;
10390 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10391 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10392 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10393 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10394 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10395 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10396 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10397 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10398 {
10399 if (mHWData->maVideoCaptureScreens[i])
10400 ASMBitSet(&data.u64VideoCaptureScreens, i);
10401 else
10402 ASMBitClear(&data.u64VideoCaptureScreens, i);
10403 }
10404 /* store relative video capture file if possible */
10405 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10406
10407 /* VRDEServer settings (optional) */
10408 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10409 if (FAILED(rc)) throw rc;
10410
10411 /* BIOS (required) */
10412 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10413 if (FAILED(rc)) throw rc;
10414
10415 /* USB Controller (required) */
10416 data.usbSettings.llUSBControllers.clear();
10417 for (USBControllerList::const_iterator
10418 it = mUSBControllers->begin();
10419 it != mUSBControllers->end();
10420 ++it)
10421 {
10422 ComObjPtr<USBController> ctrl = *it;
10423 settings::USBController settingsCtrl;
10424
10425 settingsCtrl.strName = ctrl->i_getName();
10426 settingsCtrl.enmType = ctrl->i_getControllerType();
10427
10428 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10429 }
10430
10431 /* USB device filters (required) */
10432 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10433 if (FAILED(rc)) throw rc;
10434
10435 /* Network adapters (required) */
10436 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10437 data.llNetworkAdapters.clear();
10438 /* Write out only the nominal number of network adapters for this
10439 * chipset type. Since Machine::commit() hasn't been called there
10440 * may be extra NIC settings in the vector. */
10441 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10442 {
10443 settings::NetworkAdapter nic;
10444 nic.ulSlot = (uint32_t)slot;
10445 /* paranoia check... must not be NULL, but must not crash either. */
10446 if (mNetworkAdapters[slot])
10447 {
10448 if (mNetworkAdapters[slot]->i_hasDefaults())
10449 continue;
10450
10451 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10452 if (FAILED(rc)) throw rc;
10453
10454 data.llNetworkAdapters.push_back(nic);
10455 }
10456 }
10457
10458 /* Serial ports */
10459 data.llSerialPorts.clear();
10460 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10461 {
10462 if (mSerialPorts[slot]->i_hasDefaults())
10463 continue;
10464
10465 settings::SerialPort s;
10466 s.ulSlot = slot;
10467 rc = mSerialPorts[slot]->i_saveSettings(s);
10468 if (FAILED(rc)) return rc;
10469
10470 data.llSerialPorts.push_back(s);
10471 }
10472
10473 /* Parallel ports */
10474 data.llParallelPorts.clear();
10475 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10476 {
10477 if (mParallelPorts[slot]->i_hasDefaults())
10478 continue;
10479
10480 settings::ParallelPort p;
10481 p.ulSlot = slot;
10482 rc = mParallelPorts[slot]->i_saveSettings(p);
10483 if (FAILED(rc)) return rc;
10484
10485 data.llParallelPorts.push_back(p);
10486 }
10487
10488 /* Audio adapter */
10489 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10490 if (FAILED(rc)) return rc;
10491
10492 rc = i_saveStorageControllers(data.storage);
10493 if (FAILED(rc)) return rc;
10494
10495 /* Shared folders */
10496 data.llSharedFolders.clear();
10497 for (HWData::SharedFolderList::const_iterator
10498 it = mHWData->mSharedFolders.begin();
10499 it != mHWData->mSharedFolders.end();
10500 ++it)
10501 {
10502 SharedFolder *pSF = *it;
10503 AutoCaller sfCaller(pSF);
10504 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10505 settings::SharedFolder sf;
10506 sf.strName = pSF->i_getName();
10507 sf.strHostPath = pSF->i_getHostPath();
10508 sf.fWritable = !!pSF->i_isWritable();
10509 sf.fAutoMount = !!pSF->i_isAutoMounted();
10510
10511 data.llSharedFolders.push_back(sf);
10512 }
10513
10514 // clipboard
10515 data.clipboardMode = mHWData->mClipboardMode;
10516
10517 // drag'n'drop
10518 data.dndMode = mHWData->mDnDMode;
10519
10520 /* Guest */
10521 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10522
10523 // IO settings
10524 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10525 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10526
10527 /* BandwidthControl (required) */
10528 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10529 if (FAILED(rc)) throw rc;
10530
10531 /* Host PCI devices */
10532 data.pciAttachments.clear();
10533 for (HWData::PCIDeviceAssignmentList::const_iterator
10534 it = mHWData->mPCIDeviceAssignments.begin();
10535 it != mHWData->mPCIDeviceAssignments.end();
10536 ++it)
10537 {
10538 ComObjPtr<PCIDeviceAttachment> pda = *it;
10539 settings::HostPCIDeviceAttachment hpda;
10540
10541 rc = pda->i_saveSettings(hpda);
10542 if (FAILED(rc)) throw rc;
10543
10544 data.pciAttachments.push_back(hpda);
10545 }
10546
10547 // guest properties
10548 data.llGuestProperties.clear();
10549#ifdef VBOX_WITH_GUEST_PROPS
10550 for (HWData::GuestPropertyMap::const_iterator
10551 it = mHWData->mGuestProperties.begin();
10552 it != mHWData->mGuestProperties.end();
10553 ++it)
10554 {
10555 HWData::GuestProperty property = it->second;
10556
10557 /* Remove transient guest properties at shutdown unless we
10558 * are saving state. Note that restoring snapshot intentionally
10559 * keeps them, they will be removed if appropriate once the final
10560 * machine state is set (as crashes etc. need to work). */
10561 if ( ( mData->mMachineState == MachineState_PoweredOff
10562 || mData->mMachineState == MachineState_Aborted
10563 || mData->mMachineState == MachineState_Teleported)
10564 && ( property.mFlags & guestProp::TRANSIENT
10565 || property.mFlags & guestProp::TRANSRESET))
10566 continue;
10567 settings::GuestProperty prop;
10568 prop.strName = it->first;
10569 prop.strValue = property.strValue;
10570 prop.timestamp = property.mTimestamp;
10571 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10572 guestProp::writeFlags(property.mFlags, szFlags);
10573 prop.strFlags = szFlags;
10574
10575 data.llGuestProperties.push_back(prop);
10576 }
10577
10578 /* I presume this doesn't require a backup(). */
10579 mData->mGuestPropertiesModified = FALSE;
10580#endif /* VBOX_WITH_GUEST_PROPS defined */
10581
10582 *pDbg = mHWData->mDebugging;
10583 *pAutostart = mHWData->mAutostart;
10584
10585 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10586 }
10587 catch (std::bad_alloc &)
10588 {
10589 return E_OUTOFMEMORY;
10590 }
10591
10592 AssertComRC(rc);
10593 return rc;
10594}
10595
10596/**
10597 * Saves the storage controller configuration.
10598 *
10599 * @param data storage settings.
10600 */
10601HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10602{
10603 data.llStorageControllers.clear();
10604
10605 for (StorageControllerList::const_iterator
10606 it = mStorageControllers->begin();
10607 it != mStorageControllers->end();
10608 ++it)
10609 {
10610 HRESULT rc;
10611 ComObjPtr<StorageController> pCtl = *it;
10612
10613 settings::StorageController ctl;
10614 ctl.strName = pCtl->i_getName();
10615 ctl.controllerType = pCtl->i_getControllerType();
10616 ctl.storageBus = pCtl->i_getStorageBus();
10617 ctl.ulInstance = pCtl->i_getInstance();
10618 ctl.fBootable = pCtl->i_getBootable();
10619
10620 /* Save the port count. */
10621 ULONG portCount;
10622 rc = pCtl->COMGETTER(PortCount)(&portCount);
10623 ComAssertComRCRet(rc, rc);
10624 ctl.ulPortCount = portCount;
10625
10626 /* Save fUseHostIOCache */
10627 BOOL fUseHostIOCache;
10628 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10629 ComAssertComRCRet(rc, rc);
10630 ctl.fUseHostIOCache = !!fUseHostIOCache;
10631
10632 /* save the devices now. */
10633 rc = i_saveStorageDevices(pCtl, ctl);
10634 ComAssertComRCRet(rc, rc);
10635
10636 data.llStorageControllers.push_back(ctl);
10637 }
10638
10639 return S_OK;
10640}
10641
10642/**
10643 * Saves the hard disk configuration.
10644 */
10645HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10646 settings::StorageController &data)
10647{
10648 MediumAttachmentList atts;
10649
10650 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10651 if (FAILED(rc)) return rc;
10652
10653 data.llAttachedDevices.clear();
10654 for (MediumAttachmentList::const_iterator
10655 it = atts.begin();
10656 it != atts.end();
10657 ++it)
10658 {
10659 settings::AttachedDevice dev;
10660 IMediumAttachment *iA = *it;
10661 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10662 Medium *pMedium = pAttach->i_getMedium();
10663
10664 dev.deviceType = pAttach->i_getType();
10665 dev.lPort = pAttach->i_getPort();
10666 dev.lDevice = pAttach->i_getDevice();
10667 dev.fPassThrough = pAttach->i_getPassthrough();
10668 dev.fHotPluggable = pAttach->i_getHotPluggable();
10669 if (pMedium)
10670 {
10671 if (pMedium->i_isHostDrive())
10672 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10673 else
10674 dev.uuid = pMedium->i_getId();
10675 dev.fTempEject = pAttach->i_getTempEject();
10676 dev.fNonRotational = pAttach->i_getNonRotational();
10677 dev.fDiscard = pAttach->i_getDiscard();
10678 }
10679
10680 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10681
10682 data.llAttachedDevices.push_back(dev);
10683 }
10684
10685 return S_OK;
10686}
10687
10688/**
10689 * Saves machine state settings as defined by aFlags
10690 * (SaveSTS_* values).
10691 *
10692 * @param aFlags Combination of SaveSTS_* flags.
10693 *
10694 * @note Locks objects for writing.
10695 */
10696HRESULT Machine::i_saveStateSettings(int aFlags)
10697{
10698 if (aFlags == 0)
10699 return S_OK;
10700
10701 AutoCaller autoCaller(this);
10702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10703
10704 /* This object's write lock is also necessary to serialize file access
10705 * (prevent concurrent reads and writes) */
10706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10707
10708 HRESULT rc = S_OK;
10709
10710 Assert(mData->pMachineConfigFile);
10711
10712 try
10713 {
10714 if (aFlags & SaveSTS_CurStateModified)
10715 mData->pMachineConfigFile->fCurrentStateModified = true;
10716
10717 if (aFlags & SaveSTS_StateFilePath)
10718 {
10719 if (!mSSData->strStateFilePath.isEmpty())
10720 /* try to make the file name relative to the settings file dir */
10721 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10722 else
10723 mData->pMachineConfigFile->strStateFile.setNull();
10724 }
10725
10726 if (aFlags & SaveSTS_StateTimeStamp)
10727 {
10728 Assert( mData->mMachineState != MachineState_Aborted
10729 || mSSData->strStateFilePath.isEmpty());
10730
10731 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10732
10733 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10734/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10735 }
10736
10737 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10738 }
10739 catch (...)
10740 {
10741 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10742 }
10743
10744 return rc;
10745}
10746
10747/**
10748 * Ensures that the given medium is added to a media registry. If this machine
10749 * was created with 4.0 or later, then the machine registry is used. Otherwise
10750 * the global VirtualBox media registry is used.
10751 *
10752 * Caller must NOT hold machine lock, media tree or any medium locks!
10753 *
10754 * @param pMedium
10755 */
10756void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10757{
10758 /* Paranoia checks: do not hold machine or media tree locks. */
10759 AssertReturnVoid(!isWriteLockOnCurrentThread());
10760 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10761
10762 ComObjPtr<Medium> pBase;
10763 {
10764 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10765 pBase = pMedium->i_getBase();
10766 }
10767
10768 /* Paranoia checks: do not hold medium locks. */
10769 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10770 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10771
10772 // decide which medium registry to use now that the medium is attached:
10773 Guid uuid;
10774 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10775 // machine XML is VirtualBox 4.0 or higher:
10776 uuid = i_getId(); // machine UUID
10777 else
10778 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10779
10780 if (pMedium->i_addRegistry(uuid))
10781 mParent->i_markRegistryModified(uuid);
10782
10783 /* For more complex hard disk structures it can happen that the base
10784 * medium isn't yet associated with any medium registry. Do that now. */
10785 if (pMedium != pBase)
10786 {
10787 /* Tree lock needed by Medium::addRegistry when recursing. */
10788 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10789 if (pBase->i_addRegistryRecursive(uuid))
10790 {
10791 treeLock.release();
10792 mParent->i_markRegistryModified(uuid);
10793 }
10794 }
10795}
10796
10797/**
10798 * Creates differencing hard disks for all normal hard disks attached to this
10799 * machine and a new set of attachments to refer to created disks.
10800 *
10801 * Used when taking a snapshot or when deleting the current state. Gets called
10802 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10803 *
10804 * This method assumes that mMediumAttachments contains the original hard disk
10805 * attachments it needs to create diffs for. On success, these attachments will
10806 * be replaced with the created diffs.
10807 *
10808 * Attachments with non-normal hard disks are left as is.
10809 *
10810 * If @a aOnline is @c false then the original hard disks that require implicit
10811 * diffs will be locked for reading. Otherwise it is assumed that they are
10812 * already locked for writing (when the VM was started). Note that in the latter
10813 * case it is responsibility of the caller to lock the newly created diffs for
10814 * writing if this method succeeds.
10815 *
10816 * @param aProgress Progress object to run (must contain at least as
10817 * many operations left as the number of hard disks
10818 * attached).
10819 * @param aWeight Weight of this operation.
10820 * @param aOnline Whether the VM was online prior to this operation.
10821 *
10822 * @note The progress object is not marked as completed, neither on success nor
10823 * on failure. This is a responsibility of the caller.
10824 *
10825 * @note Locks this object and the media tree for writing.
10826 */
10827HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10828 ULONG aWeight,
10829 bool aOnline)
10830{
10831 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10832
10833 AutoCaller autoCaller(this);
10834 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10835
10836 AutoMultiWriteLock2 alock(this->lockHandle(),
10837 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10838
10839 /* must be in a protective state because we release the lock below */
10840 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10841 || mData->mMachineState == MachineState_OnlineSnapshotting
10842 || mData->mMachineState == MachineState_LiveSnapshotting
10843 || mData->mMachineState == MachineState_RestoringSnapshot
10844 || mData->mMachineState == MachineState_DeletingSnapshot
10845 , E_FAIL);
10846
10847 HRESULT rc = S_OK;
10848
10849 // use appropriate locked media map (online or offline)
10850 MediumLockListMap lockedMediaOffline;
10851 MediumLockListMap *lockedMediaMap;
10852 if (aOnline)
10853 lockedMediaMap = &mData->mSession.mLockedMedia;
10854 else
10855 lockedMediaMap = &lockedMediaOffline;
10856
10857 try
10858 {
10859 if (!aOnline)
10860 {
10861 /* lock all attached hard disks early to detect "in use"
10862 * situations before creating actual diffs */
10863 for (MediumAttachmentList::const_iterator
10864 it = mMediumAttachments->begin();
10865 it != mMediumAttachments->end();
10866 ++it)
10867 {
10868 MediumAttachment *pAtt = *it;
10869 if (pAtt->i_getType() == DeviceType_HardDisk)
10870 {
10871 Medium *pMedium = pAtt->i_getMedium();
10872 Assert(pMedium);
10873
10874 MediumLockList *pMediumLockList(new MediumLockList());
10875 alock.release();
10876 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10877 NULL /* pToLockWrite */,
10878 false /* fMediumLockWriteAll */,
10879 NULL,
10880 *pMediumLockList);
10881 alock.acquire();
10882 if (FAILED(rc))
10883 {
10884 delete pMediumLockList;
10885 throw rc;
10886 }
10887 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10888 if (FAILED(rc))
10889 {
10890 throw setError(rc,
10891 tr("Collecting locking information for all attached media failed"));
10892 }
10893 }
10894 }
10895
10896 /* Now lock all media. If this fails, nothing is locked. */
10897 alock.release();
10898 rc = lockedMediaMap->Lock();
10899 alock.acquire();
10900 if (FAILED(rc))
10901 {
10902 throw setError(rc,
10903 tr("Locking of attached media failed"));
10904 }
10905 }
10906
10907 /* remember the current list (note that we don't use backup() since
10908 * mMediumAttachments may be already backed up) */
10909 MediumAttachmentList atts = *mMediumAttachments.data();
10910
10911 /* start from scratch */
10912 mMediumAttachments->clear();
10913
10914 /* go through remembered attachments and create diffs for normal hard
10915 * disks and attach them */
10916 for (MediumAttachmentList::const_iterator
10917 it = atts.begin();
10918 it != atts.end();
10919 ++it)
10920 {
10921 MediumAttachment *pAtt = *it;
10922
10923 DeviceType_T devType = pAtt->i_getType();
10924 Medium *pMedium = pAtt->i_getMedium();
10925
10926 if ( devType != DeviceType_HardDisk
10927 || pMedium == NULL
10928 || pMedium->i_getType() != MediumType_Normal)
10929 {
10930 /* copy the attachment as is */
10931
10932 /** @todo the progress object created in SessionMachine::TakeSnaphot
10933 * only expects operations for hard disks. Later other
10934 * device types need to show up in the progress as well. */
10935 if (devType == DeviceType_HardDisk)
10936 {
10937 if (pMedium == NULL)
10938 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10939 aWeight); // weight
10940 else
10941 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10942 pMedium->i_getBase()->i_getName().c_str()).raw(),
10943 aWeight); // weight
10944 }
10945
10946 mMediumAttachments->push_back(pAtt);
10947 continue;
10948 }
10949
10950 /* need a diff */
10951 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10952 pMedium->i_getBase()->i_getName().c_str()).raw(),
10953 aWeight); // weight
10954
10955 Utf8Str strFullSnapshotFolder;
10956 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10957
10958 ComObjPtr<Medium> diff;
10959 diff.createObject();
10960 // store the diff in the same registry as the parent
10961 // (this cannot fail here because we can't create implicit diffs for
10962 // unregistered images)
10963 Guid uuidRegistryParent;
10964 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10965 Assert(fInRegistry); NOREF(fInRegistry);
10966 rc = diff->init(mParent,
10967 pMedium->i_getPreferredDiffFormat(),
10968 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10969 uuidRegistryParent,
10970 DeviceType_HardDisk);
10971 if (FAILED(rc)) throw rc;
10972
10973 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10974 * the push_back? Looks like we're going to release medium with the
10975 * wrong kind of lock (general issue with if we fail anywhere at all)
10976 * and an orphaned VDI in the snapshots folder. */
10977
10978 /* update the appropriate lock list */
10979 MediumLockList *pMediumLockList;
10980 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10981 AssertComRCThrowRC(rc);
10982 if (aOnline)
10983 {
10984 alock.release();
10985 /* The currently attached medium will be read-only, change
10986 * the lock type to read. */
10987 rc = pMediumLockList->Update(pMedium, false);
10988 alock.acquire();
10989 AssertComRCThrowRC(rc);
10990 }
10991
10992 /* release the locks before the potentially lengthy operation */
10993 alock.release();
10994 rc = pMedium->i_createDiffStorage(diff,
10995 pMedium->i_getPreferredDiffVariant(),
10996 pMediumLockList,
10997 NULL /* aProgress */,
10998 true /* aWait */);
10999 alock.acquire();
11000 if (FAILED(rc)) throw rc;
11001
11002 /* actual lock list update is done in Machine::i_commitMedia */
11003
11004 rc = diff->i_addBackReference(mData->mUuid);
11005 AssertComRCThrowRC(rc);
11006
11007 /* add a new attachment */
11008 ComObjPtr<MediumAttachment> attachment;
11009 attachment.createObject();
11010 rc = attachment->init(this,
11011 diff,
11012 pAtt->i_getControllerName(),
11013 pAtt->i_getPort(),
11014 pAtt->i_getDevice(),
11015 DeviceType_HardDisk,
11016 true /* aImplicit */,
11017 false /* aPassthrough */,
11018 false /* aTempEject */,
11019 pAtt->i_getNonRotational(),
11020 pAtt->i_getDiscard(),
11021 pAtt->i_getHotPluggable(),
11022 pAtt->i_getBandwidthGroup());
11023 if (FAILED(rc)) throw rc;
11024
11025 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11026 AssertComRCThrowRC(rc);
11027 mMediumAttachments->push_back(attachment);
11028 }
11029 }
11030 catch (HRESULT aRC) { rc = aRC; }
11031
11032 /* unlock all hard disks we locked when there is no VM */
11033 if (!aOnline)
11034 {
11035 ErrorInfoKeeper eik;
11036
11037 HRESULT rc1 = lockedMediaMap->Clear();
11038 AssertComRC(rc1);
11039 }
11040
11041 return rc;
11042}
11043
11044/**
11045 * Deletes implicit differencing hard disks created either by
11046 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11047 * mMediumAttachments.
11048 *
11049 * Note that to delete hard disks created by #attachDevice() this method is
11050 * called from #i_rollbackMedia() when the changes are rolled back.
11051 *
11052 * @note Locks this object and the media tree for writing.
11053 */
11054HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11055{
11056 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11057
11058 AutoCaller autoCaller(this);
11059 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11060
11061 AutoMultiWriteLock2 alock(this->lockHandle(),
11062 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11063
11064 /* We absolutely must have backed up state. */
11065 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11066
11067 /* Check if there are any implicitly created diff images. */
11068 bool fImplicitDiffs = false;
11069 for (MediumAttachmentList::const_iterator
11070 it = mMediumAttachments->begin();
11071 it != mMediumAttachments->end();
11072 ++it)
11073 {
11074 const ComObjPtr<MediumAttachment> &pAtt = *it;
11075 if (pAtt->i_isImplicit())
11076 {
11077 fImplicitDiffs = true;
11078 break;
11079 }
11080 }
11081 /* If there is nothing to do, leave early. This saves lots of image locking
11082 * effort. It also avoids a MachineStateChanged event without real reason.
11083 * This is important e.g. when loading a VM config, because there should be
11084 * no events. Otherwise API clients can become thoroughly confused for
11085 * inaccessible VMs (the code for loading VM configs uses this method for
11086 * cleanup if the config makes no sense), as they take such events as an
11087 * indication that the VM is alive, and they would force the VM config to
11088 * be reread, leading to an endless loop. */
11089 if (!fImplicitDiffs)
11090 return S_OK;
11091
11092 HRESULT rc = S_OK;
11093 MachineState_T oldState = mData->mMachineState;
11094
11095 /* will release the lock before the potentially lengthy operation,
11096 * so protect with the special state (unless already protected) */
11097 if ( oldState != MachineState_Snapshotting
11098 && oldState != MachineState_OnlineSnapshotting
11099 && oldState != MachineState_LiveSnapshotting
11100 && oldState != MachineState_RestoringSnapshot
11101 && oldState != MachineState_DeletingSnapshot
11102 && oldState != MachineState_DeletingSnapshotOnline
11103 && oldState != MachineState_DeletingSnapshotPaused
11104 )
11105 i_setMachineState(MachineState_SettingUp);
11106
11107 // use appropriate locked media map (online or offline)
11108 MediumLockListMap lockedMediaOffline;
11109 MediumLockListMap *lockedMediaMap;
11110 if (aOnline)
11111 lockedMediaMap = &mData->mSession.mLockedMedia;
11112 else
11113 lockedMediaMap = &lockedMediaOffline;
11114
11115 try
11116 {
11117 if (!aOnline)
11118 {
11119 /* lock all attached hard disks early to detect "in use"
11120 * situations before deleting actual diffs */
11121 for (MediumAttachmentList::const_iterator
11122 it = mMediumAttachments->begin();
11123 it != mMediumAttachments->end();
11124 ++it)
11125 {
11126 MediumAttachment *pAtt = *it;
11127 if (pAtt->i_getType() == DeviceType_HardDisk)
11128 {
11129 Medium *pMedium = pAtt->i_getMedium();
11130 Assert(pMedium);
11131
11132 MediumLockList *pMediumLockList(new MediumLockList());
11133 alock.release();
11134 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11135 NULL /* pToLockWrite */,
11136 false /* fMediumLockWriteAll */,
11137 NULL,
11138 *pMediumLockList);
11139 alock.acquire();
11140
11141 if (FAILED(rc))
11142 {
11143 delete pMediumLockList;
11144 throw rc;
11145 }
11146
11147 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11148 if (FAILED(rc))
11149 throw rc;
11150 }
11151 }
11152
11153 if (FAILED(rc))
11154 throw rc;
11155 } // end of offline
11156
11157 /* Lock lists are now up to date and include implicitly created media */
11158
11159 /* Go through remembered attachments and delete all implicitly created
11160 * diffs and fix up the attachment information */
11161 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11162 MediumAttachmentList implicitAtts;
11163 for (MediumAttachmentList::const_iterator
11164 it = mMediumAttachments->begin();
11165 it != mMediumAttachments->end();
11166 ++it)
11167 {
11168 ComObjPtr<MediumAttachment> pAtt = *it;
11169 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11170 if (pMedium.isNull())
11171 continue;
11172
11173 // Implicit attachments go on the list for deletion and back references are removed.
11174 if (pAtt->i_isImplicit())
11175 {
11176 /* Deassociate and mark for deletion */
11177 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11178 rc = pMedium->i_removeBackReference(mData->mUuid);
11179 if (FAILED(rc))
11180 throw rc;
11181 implicitAtts.push_back(pAtt);
11182 continue;
11183 }
11184
11185 /* Was this medium attached before? */
11186 if (!i_findAttachment(oldAtts, pMedium))
11187 {
11188 /* no: de-associate */
11189 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11190 rc = pMedium->i_removeBackReference(mData->mUuid);
11191 if (FAILED(rc))
11192 throw rc;
11193 continue;
11194 }
11195 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11196 }
11197
11198 /* If there are implicit attachments to delete, throw away the lock
11199 * map contents (which will unlock all media) since the medium
11200 * attachments will be rolled back. Below we need to completely
11201 * recreate the lock map anyway since it is infinitely complex to
11202 * do this incrementally (would need reconstructing each attachment
11203 * change, which would be extremely hairy). */
11204 if (implicitAtts.size() != 0)
11205 {
11206 ErrorInfoKeeper eik;
11207
11208 HRESULT rc1 = lockedMediaMap->Clear();
11209 AssertComRC(rc1);
11210 }
11211
11212 /* rollback hard disk changes */
11213 mMediumAttachments.rollback();
11214
11215 MultiResult mrc(S_OK);
11216
11217 // Delete unused implicit diffs.
11218 if (implicitAtts.size() != 0)
11219 {
11220 alock.release();
11221
11222 for (MediumAttachmentList::const_iterator
11223 it = implicitAtts.begin();
11224 it != implicitAtts.end();
11225 ++it)
11226 {
11227 // Remove medium associated with this attachment.
11228 ComObjPtr<MediumAttachment> pAtt = *it;
11229 Assert(pAtt);
11230 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11231 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11232 Assert(pMedium);
11233
11234 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11235 // continue on delete failure, just collect error messages
11236 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11237 pMedium->i_getLocationFull().c_str() ));
11238 mrc = rc;
11239 }
11240 // Clear the list of deleted implicit attachments now, while not
11241 // holding the lock, as it will ultimately trigger Medium::uninit()
11242 // calls which assume that the media tree lock isn't held.
11243 implicitAtts.clear();
11244
11245 alock.acquire();
11246
11247 /* if there is a VM recreate media lock map as mentioned above,
11248 * otherwise it is a waste of time and we leave things unlocked */
11249 if (aOnline)
11250 {
11251 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11252 /* must never be NULL, but better safe than sorry */
11253 if (!pMachine.isNull())
11254 {
11255 alock.release();
11256 rc = mData->mSession.mMachine->i_lockMedia();
11257 alock.acquire();
11258 if (FAILED(rc))
11259 throw rc;
11260 }
11261 }
11262 }
11263 }
11264 catch (HRESULT aRC) {rc = aRC;}
11265
11266 if (mData->mMachineState == MachineState_SettingUp)
11267 i_setMachineState(oldState);
11268
11269 /* unlock all hard disks we locked when there is no VM */
11270 if (!aOnline)
11271 {
11272 ErrorInfoKeeper eik;
11273
11274 HRESULT rc1 = lockedMediaMap->Clear();
11275 AssertComRC(rc1);
11276 }
11277
11278 return rc;
11279}
11280
11281
11282/**
11283 * Looks through the given list of media attachments for one with the given parameters
11284 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11285 * can be searched as well if needed.
11286 *
11287 * @param ll
11288 * @param aControllerName
11289 * @param aControllerPort
11290 * @param aDevice
11291 * @return
11292 */
11293MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11294 const Utf8Str &aControllerName,
11295 LONG aControllerPort,
11296 LONG aDevice)
11297{
11298 for (MediumAttachmentList::const_iterator
11299 it = ll.begin();
11300 it != ll.end();
11301 ++it)
11302 {
11303 MediumAttachment *pAttach = *it;
11304 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11305 return pAttach;
11306 }
11307
11308 return NULL;
11309}
11310
11311/**
11312 * Looks through the given list of media attachments for one with the given parameters
11313 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11314 * can be searched as well if needed.
11315 *
11316 * @param ll
11317 * @param pMedium
11318 * @return
11319 */
11320MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11321 ComObjPtr<Medium> pMedium)
11322{
11323 for (MediumAttachmentList::const_iterator
11324 it = ll.begin();
11325 it != ll.end();
11326 ++it)
11327 {
11328 MediumAttachment *pAttach = *it;
11329 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11330 if (pMediumThis == pMedium)
11331 return pAttach;
11332 }
11333
11334 return NULL;
11335}
11336
11337/**
11338 * Looks through the given list of media attachments for one with the given parameters
11339 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11340 * can be searched as well if needed.
11341 *
11342 * @param ll
11343 * @param id
11344 * @return
11345 */
11346MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11347 Guid &id)
11348{
11349 for (MediumAttachmentList::const_iterator
11350 it = ll.begin();
11351 it != ll.end();
11352 ++it)
11353 {
11354 MediumAttachment *pAttach = *it;
11355 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11356 if (pMediumThis->i_getId() == id)
11357 return pAttach;
11358 }
11359
11360 return NULL;
11361}
11362
11363/**
11364 * Main implementation for Machine::DetachDevice. This also gets called
11365 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11366 *
11367 * @param pAttach Medium attachment to detach.
11368 * @param writeLock Machine write lock which the caller must have locked once.
11369 * This may be released temporarily in here.
11370 * @param pSnapshot If NULL, then the detachment is for the current machine.
11371 * Otherwise this is for a SnapshotMachine, and this must be
11372 * its snapshot.
11373 * @return
11374 */
11375HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11376 AutoWriteLock &writeLock,
11377 Snapshot *pSnapshot)
11378{
11379 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11380 DeviceType_T mediumType = pAttach->i_getType();
11381
11382 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11383
11384 if (pAttach->i_isImplicit())
11385 {
11386 /* attempt to implicitly delete the implicitly created diff */
11387
11388 /// @todo move the implicit flag from MediumAttachment to Medium
11389 /// and forbid any hard disk operation when it is implicit. Or maybe
11390 /// a special media state for it to make it even more simple.
11391
11392 Assert(mMediumAttachments.isBackedUp());
11393
11394 /* will release the lock before the potentially lengthy operation, so
11395 * protect with the special state */
11396 MachineState_T oldState = mData->mMachineState;
11397 i_setMachineState(MachineState_SettingUp);
11398
11399 writeLock.release();
11400
11401 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11402 true /*aWait*/);
11403
11404 writeLock.acquire();
11405
11406 i_setMachineState(oldState);
11407
11408 if (FAILED(rc)) return rc;
11409 }
11410
11411 i_setModified(IsModified_Storage);
11412 mMediumAttachments.backup();
11413 mMediumAttachments->remove(pAttach);
11414
11415 if (!oldmedium.isNull())
11416 {
11417 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11418 if (pSnapshot)
11419 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11420 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11421 else if (mediumType != DeviceType_HardDisk)
11422 oldmedium->i_removeBackReference(mData->mUuid);
11423 }
11424
11425 return S_OK;
11426}
11427
11428/**
11429 * Goes thru all media of the given list and
11430 *
11431 * 1) calls i_detachDevice() on each of them for this machine and
11432 * 2) adds all Medium objects found in the process to the given list,
11433 * depending on cleanupMode.
11434 *
11435 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11436 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11437 * media to the list.
11438 *
11439 * This gets called from Machine::Unregister, both for the actual Machine and
11440 * the SnapshotMachine objects that might be found in the snapshots.
11441 *
11442 * Requires caller and locking. The machine lock must be passed in because it
11443 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11444 *
11445 * @param writeLock Machine lock from top-level caller; this gets passed to
11446 * i_detachDevice.
11447 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11448 * object if called for a SnapshotMachine.
11449 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11450 * added to llMedia; if Full, then all media get added;
11451 * otherwise no media get added.
11452 * @param llMedia Caller's list to receive Medium objects which got detached so
11453 * caller can close() them, depending on cleanupMode.
11454 * @return
11455 */
11456HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11457 Snapshot *pSnapshot,
11458 CleanupMode_T cleanupMode,
11459 MediaList &llMedia)
11460{
11461 Assert(isWriteLockOnCurrentThread());
11462
11463 HRESULT rc;
11464
11465 // make a temporary list because i_detachDevice invalidates iterators into
11466 // mMediumAttachments
11467 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11468
11469 for (MediumAttachmentList::iterator
11470 it = llAttachments2.begin();
11471 it != llAttachments2.end();
11472 ++it)
11473 {
11474 ComObjPtr<MediumAttachment> &pAttach = *it;
11475 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11476
11477 if (!pMedium.isNull())
11478 {
11479 AutoCaller mac(pMedium);
11480 if (FAILED(mac.rc())) return mac.rc();
11481 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11482 DeviceType_T devType = pMedium->i_getDeviceType();
11483 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11484 && devType == DeviceType_HardDisk)
11485 || (cleanupMode == CleanupMode_Full)
11486 )
11487 {
11488 llMedia.push_back(pMedium);
11489 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11490 /* Not allowed to keep this lock as below we need the parent
11491 * medium lock, and the lock order is parent to child. */
11492 lock.release();
11493 /*
11494 * Search for medias which are not attached to any machine, but
11495 * in the chain to an attached disk. Mediums are only consided
11496 * if they are:
11497 * - have only one child
11498 * - no references to any machines
11499 * - are of normal medium type
11500 */
11501 while (!pParent.isNull())
11502 {
11503 AutoCaller mac1(pParent);
11504 if (FAILED(mac1.rc())) return mac1.rc();
11505 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11506 if (pParent->i_getChildren().size() == 1)
11507 {
11508 if ( pParent->i_getMachineBackRefCount() == 0
11509 && pParent->i_getType() == MediumType_Normal
11510 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11511 llMedia.push_back(pParent);
11512 }
11513 else
11514 break;
11515 pParent = pParent->i_getParent();
11516 }
11517 }
11518 }
11519
11520 // real machine: then we need to use the proper method
11521 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11522
11523 if (FAILED(rc))
11524 return rc;
11525 }
11526
11527 return S_OK;
11528}
11529
11530/**
11531 * Perform deferred hard disk detachments.
11532 *
11533 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11534 * changed (not backed up).
11535 *
11536 * If @a aOnline is @c true then this method will also unlock the old hard
11537 * disks for which the new implicit diffs were created and will lock these new
11538 * diffs for writing.
11539 *
11540 * @param aOnline Whether the VM was online prior to this operation.
11541 *
11542 * @note Locks this object for writing!
11543 */
11544void Machine::i_commitMedia(bool aOnline /*= false*/)
11545{
11546 AutoCaller autoCaller(this);
11547 AssertComRCReturnVoid(autoCaller.rc());
11548
11549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11550
11551 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11552
11553 HRESULT rc = S_OK;
11554
11555 /* no attach/detach operations -- nothing to do */
11556 if (!mMediumAttachments.isBackedUp())
11557 return;
11558
11559 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11560 bool fMediaNeedsLocking = false;
11561
11562 /* enumerate new attachments */
11563 for (MediumAttachmentList::const_iterator
11564 it = mMediumAttachments->begin();
11565 it != mMediumAttachments->end();
11566 ++it)
11567 {
11568 MediumAttachment *pAttach = *it;
11569
11570 pAttach->i_commit();
11571
11572 Medium *pMedium = pAttach->i_getMedium();
11573 bool fImplicit = pAttach->i_isImplicit();
11574
11575 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11576 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11577 fImplicit));
11578
11579 /** @todo convert all this Machine-based voodoo to MediumAttachment
11580 * based commit logic. */
11581 if (fImplicit)
11582 {
11583 /* convert implicit attachment to normal */
11584 pAttach->i_setImplicit(false);
11585
11586 if ( aOnline
11587 && pMedium
11588 && pAttach->i_getType() == DeviceType_HardDisk
11589 )
11590 {
11591 /* update the appropriate lock list */
11592 MediumLockList *pMediumLockList;
11593 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11594 AssertComRC(rc);
11595 if (pMediumLockList)
11596 {
11597 /* unlock if there's a need to change the locking */
11598 if (!fMediaNeedsLocking)
11599 {
11600 rc = mData->mSession.mLockedMedia.Unlock();
11601 AssertComRC(rc);
11602 fMediaNeedsLocking = true;
11603 }
11604 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11605 AssertComRC(rc);
11606 rc = pMediumLockList->Append(pMedium, true);
11607 AssertComRC(rc);
11608 }
11609 }
11610
11611 continue;
11612 }
11613
11614 if (pMedium)
11615 {
11616 /* was this medium attached before? */
11617 for (MediumAttachmentList::iterator
11618 oldIt = oldAtts.begin();
11619 oldIt != oldAtts.end();
11620 ++oldIt)
11621 {
11622 MediumAttachment *pOldAttach = *oldIt;
11623 if (pOldAttach->i_getMedium() == pMedium)
11624 {
11625 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11626
11627 /* yes: remove from old to avoid de-association */
11628 oldAtts.erase(oldIt);
11629 break;
11630 }
11631 }
11632 }
11633 }
11634
11635 /* enumerate remaining old attachments and de-associate from the
11636 * current machine state */
11637 for (MediumAttachmentList::const_iterator
11638 it = oldAtts.begin();
11639 it != oldAtts.end();
11640 ++it)
11641 {
11642 MediumAttachment *pAttach = *it;
11643 Medium *pMedium = pAttach->i_getMedium();
11644
11645 /* Detach only hard disks, since DVD/floppy media is detached
11646 * instantly in MountMedium. */
11647 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11648 {
11649 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11650
11651 /* now de-associate from the current machine state */
11652 rc = pMedium->i_removeBackReference(mData->mUuid);
11653 AssertComRC(rc);
11654
11655 if (aOnline)
11656 {
11657 /* unlock since medium is not used anymore */
11658 MediumLockList *pMediumLockList;
11659 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11660 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11661 {
11662 /* this happens for online snapshots, there the attachment
11663 * is changing, but only to a diff image created under
11664 * the old one, so there is no separate lock list */
11665 Assert(!pMediumLockList);
11666 }
11667 else
11668 {
11669 AssertComRC(rc);
11670 if (pMediumLockList)
11671 {
11672 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11673 AssertComRC(rc);
11674 }
11675 }
11676 }
11677 }
11678 }
11679
11680 /* take media locks again so that the locking state is consistent */
11681 if (fMediaNeedsLocking)
11682 {
11683 Assert(aOnline);
11684 rc = mData->mSession.mLockedMedia.Lock();
11685 AssertComRC(rc);
11686 }
11687
11688 /* commit the hard disk changes */
11689 mMediumAttachments.commit();
11690
11691 if (i_isSessionMachine())
11692 {
11693 /*
11694 * Update the parent machine to point to the new owner.
11695 * This is necessary because the stored parent will point to the
11696 * session machine otherwise and cause crashes or errors later
11697 * when the session machine gets invalid.
11698 */
11699 /** @todo Change the MediumAttachment class to behave like any other
11700 * class in this regard by creating peer MediumAttachment
11701 * objects for session machines and share the data with the peer
11702 * machine.
11703 */
11704 for (MediumAttachmentList::const_iterator
11705 it = mMediumAttachments->begin();
11706 it != mMediumAttachments->end();
11707 ++it)
11708 (*it)->i_updateParentMachine(mPeer);
11709
11710 /* attach new data to the primary machine and reshare it */
11711 mPeer->mMediumAttachments.attach(mMediumAttachments);
11712 }
11713
11714 return;
11715}
11716
11717/**
11718 * Perform deferred deletion of implicitly created diffs.
11719 *
11720 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11721 * changed (not backed up).
11722 *
11723 * @note Locks this object for writing!
11724 */
11725void Machine::i_rollbackMedia()
11726{
11727 AutoCaller autoCaller(this);
11728 AssertComRCReturnVoid(autoCaller.rc());
11729
11730 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11731 LogFlowThisFunc(("Entering rollbackMedia\n"));
11732
11733 HRESULT rc = S_OK;
11734
11735 /* no attach/detach operations -- nothing to do */
11736 if (!mMediumAttachments.isBackedUp())
11737 return;
11738
11739 /* enumerate new attachments */
11740 for (MediumAttachmentList::const_iterator
11741 it = mMediumAttachments->begin();
11742 it != mMediumAttachments->end();
11743 ++it)
11744 {
11745 MediumAttachment *pAttach = *it;
11746 /* Fix up the backrefs for DVD/floppy media. */
11747 if (pAttach->i_getType() != DeviceType_HardDisk)
11748 {
11749 Medium *pMedium = pAttach->i_getMedium();
11750 if (pMedium)
11751 {
11752 rc = pMedium->i_removeBackReference(mData->mUuid);
11753 AssertComRC(rc);
11754 }
11755 }
11756
11757 (*it)->i_rollback();
11758
11759 pAttach = *it;
11760 /* Fix up the backrefs for DVD/floppy media. */
11761 if (pAttach->i_getType() != DeviceType_HardDisk)
11762 {
11763 Medium *pMedium = pAttach->i_getMedium();
11764 if (pMedium)
11765 {
11766 rc = pMedium->i_addBackReference(mData->mUuid);
11767 AssertComRC(rc);
11768 }
11769 }
11770 }
11771
11772 /** @todo convert all this Machine-based voodoo to MediumAttachment
11773 * based rollback logic. */
11774 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11775
11776 return;
11777}
11778
11779/**
11780 * Returns true if the settings file is located in the directory named exactly
11781 * as the machine; this means, among other things, that the machine directory
11782 * should be auto-renamed.
11783 *
11784 * @param aSettingsDir if not NULL, the full machine settings file directory
11785 * name will be assigned there.
11786 *
11787 * @note Doesn't lock anything.
11788 * @note Not thread safe (must be called from this object's lock).
11789 */
11790bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11791{
11792 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11793 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11794 if (aSettingsDir)
11795 *aSettingsDir = strMachineDirName;
11796 strMachineDirName.stripPath(); // vmname
11797 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11798 strConfigFileOnly.stripPath() // vmname.vbox
11799 .stripSuffix(); // vmname
11800 /** @todo hack, make somehow use of ComposeMachineFilename */
11801 if (mUserData->s.fDirectoryIncludesUUID)
11802 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11803
11804 AssertReturn(!strMachineDirName.isEmpty(), false);
11805 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11806
11807 return strMachineDirName == strConfigFileOnly;
11808}
11809
11810/**
11811 * Discards all changes to machine settings.
11812 *
11813 * @param aNotify Whether to notify the direct session about changes or not.
11814 *
11815 * @note Locks objects for writing!
11816 */
11817void Machine::i_rollback(bool aNotify)
11818{
11819 AutoCaller autoCaller(this);
11820 AssertComRCReturn(autoCaller.rc(), (void)0);
11821
11822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11823
11824 if (!mStorageControllers.isNull())
11825 {
11826 if (mStorageControllers.isBackedUp())
11827 {
11828 /* unitialize all new devices (absent in the backed up list). */
11829 StorageControllerList *backedList = mStorageControllers.backedUpData();
11830 for (StorageControllerList::const_iterator
11831 it = mStorageControllers->begin();
11832 it != mStorageControllers->end();
11833 ++it)
11834 {
11835 if ( std::find(backedList->begin(), backedList->end(), *it)
11836 == backedList->end()
11837 )
11838 {
11839 (*it)->uninit();
11840 }
11841 }
11842
11843 /* restore the list */
11844 mStorageControllers.rollback();
11845 }
11846
11847 /* rollback any changes to devices after restoring the list */
11848 if (mData->flModifications & IsModified_Storage)
11849 {
11850 for (StorageControllerList::const_iterator
11851 it = mStorageControllers->begin();
11852 it != mStorageControllers->end();
11853 ++it)
11854 {
11855 (*it)->i_rollback();
11856 }
11857 }
11858 }
11859
11860 if (!mUSBControllers.isNull())
11861 {
11862 if (mUSBControllers.isBackedUp())
11863 {
11864 /* unitialize all new devices (absent in the backed up list). */
11865 USBControllerList *backedList = mUSBControllers.backedUpData();
11866 for (USBControllerList::const_iterator
11867 it = mUSBControllers->begin();
11868 it != mUSBControllers->end();
11869 ++it)
11870 {
11871 if ( std::find(backedList->begin(), backedList->end(), *it)
11872 == backedList->end()
11873 )
11874 {
11875 (*it)->uninit();
11876 }
11877 }
11878
11879 /* restore the list */
11880 mUSBControllers.rollback();
11881 }
11882
11883 /* rollback any changes to devices after restoring the list */
11884 if (mData->flModifications & IsModified_USB)
11885 {
11886 for (USBControllerList::const_iterator
11887 it = mUSBControllers->begin();
11888 it != mUSBControllers->end();
11889 ++it)
11890 {
11891 (*it)->i_rollback();
11892 }
11893 }
11894 }
11895
11896 mUserData.rollback();
11897
11898 mHWData.rollback();
11899
11900 if (mData->flModifications & IsModified_Storage)
11901 i_rollbackMedia();
11902
11903 if (mBIOSSettings)
11904 mBIOSSettings->i_rollback();
11905
11906 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11907 mVRDEServer->i_rollback();
11908
11909 if (mAudioAdapter)
11910 mAudioAdapter->i_rollback();
11911
11912 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11913 mUSBDeviceFilters->i_rollback();
11914
11915 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11916 mBandwidthControl->i_rollback();
11917
11918 if (!mHWData.isNull())
11919 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11920 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11921 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11922 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11923
11924 if (mData->flModifications & IsModified_NetworkAdapters)
11925 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11926 if ( mNetworkAdapters[slot]
11927 && mNetworkAdapters[slot]->i_isModified())
11928 {
11929 mNetworkAdapters[slot]->i_rollback();
11930 networkAdapters[slot] = mNetworkAdapters[slot];
11931 }
11932
11933 if (mData->flModifications & IsModified_SerialPorts)
11934 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11935 if ( mSerialPorts[slot]
11936 && mSerialPorts[slot]->i_isModified())
11937 {
11938 mSerialPorts[slot]->i_rollback();
11939 serialPorts[slot] = mSerialPorts[slot];
11940 }
11941
11942 if (mData->flModifications & IsModified_ParallelPorts)
11943 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11944 if ( mParallelPorts[slot]
11945 && mParallelPorts[slot]->i_isModified())
11946 {
11947 mParallelPorts[slot]->i_rollback();
11948 parallelPorts[slot] = mParallelPorts[slot];
11949 }
11950
11951 if (aNotify)
11952 {
11953 /* inform the direct session about changes */
11954
11955 ComObjPtr<Machine> that = this;
11956 uint32_t flModifications = mData->flModifications;
11957 alock.release();
11958
11959 if (flModifications & IsModified_SharedFolders)
11960 that->i_onSharedFolderChange();
11961
11962 if (flModifications & IsModified_VRDEServer)
11963 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11964 if (flModifications & IsModified_USB)
11965 that->i_onUSBControllerChange();
11966
11967 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11968 if (networkAdapters[slot])
11969 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11970 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11971 if (serialPorts[slot])
11972 that->i_onSerialPortChange(serialPorts[slot]);
11973 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11974 if (parallelPorts[slot])
11975 that->i_onParallelPortChange(parallelPorts[slot]);
11976
11977 if (flModifications & IsModified_Storage)
11978 that->i_onStorageControllerChange();
11979
11980#if 0
11981 if (flModifications & IsModified_BandwidthControl)
11982 that->onBandwidthControlChange();
11983#endif
11984 }
11985}
11986
11987/**
11988 * Commits all the changes to machine settings.
11989 *
11990 * Note that this operation is supposed to never fail.
11991 *
11992 * @note Locks this object and children for writing.
11993 */
11994void Machine::i_commit()
11995{
11996 AutoCaller autoCaller(this);
11997 AssertComRCReturnVoid(autoCaller.rc());
11998
11999 AutoCaller peerCaller(mPeer);
12000 AssertComRCReturnVoid(peerCaller.rc());
12001
12002 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12003
12004 /*
12005 * use safe commit to ensure Snapshot machines (that share mUserData)
12006 * will still refer to a valid memory location
12007 */
12008 mUserData.commitCopy();
12009
12010 mHWData.commit();
12011
12012 if (mMediumAttachments.isBackedUp())
12013 i_commitMedia(Global::IsOnline(mData->mMachineState));
12014
12015 mBIOSSettings->i_commit();
12016 mVRDEServer->i_commit();
12017 mAudioAdapter->i_commit();
12018 mUSBDeviceFilters->i_commit();
12019 mBandwidthControl->i_commit();
12020
12021 /* Since mNetworkAdapters is a list which might have been changed (resized)
12022 * without using the Backupable<> template we need to handle the copying
12023 * of the list entries manually, including the creation of peers for the
12024 * new objects. */
12025 bool commitNetworkAdapters = false;
12026 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12027 if (mPeer)
12028 {
12029 /* commit everything, even the ones which will go away */
12030 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12031 mNetworkAdapters[slot]->i_commit();
12032 /* copy over the new entries, creating a peer and uninit the original */
12033 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12034 for (size_t slot = 0; slot < newSize; slot++)
12035 {
12036 /* look if this adapter has a peer device */
12037 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12038 if (!peer)
12039 {
12040 /* no peer means the adapter is a newly created one;
12041 * create a peer owning data this data share it with */
12042 peer.createObject();
12043 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12044 }
12045 mPeer->mNetworkAdapters[slot] = peer;
12046 }
12047 /* uninit any no longer needed network adapters */
12048 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12049 mNetworkAdapters[slot]->uninit();
12050 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12051 {
12052 if (mPeer->mNetworkAdapters[slot])
12053 mPeer->mNetworkAdapters[slot]->uninit();
12054 }
12055 /* Keep the original network adapter count until this point, so that
12056 * discarding a chipset type change will not lose settings. */
12057 mNetworkAdapters.resize(newSize);
12058 mPeer->mNetworkAdapters.resize(newSize);
12059 }
12060 else
12061 {
12062 /* we have no peer (our parent is the newly created machine);
12063 * just commit changes to the network adapters */
12064 commitNetworkAdapters = true;
12065 }
12066 if (commitNetworkAdapters)
12067 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12068 mNetworkAdapters[slot]->i_commit();
12069
12070 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12071 mSerialPorts[slot]->i_commit();
12072 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12073 mParallelPorts[slot]->i_commit();
12074
12075 bool commitStorageControllers = false;
12076
12077 if (mStorageControllers.isBackedUp())
12078 {
12079 mStorageControllers.commit();
12080
12081 if (mPeer)
12082 {
12083 /* Commit all changes to new controllers (this will reshare data with
12084 * peers for those who have peers) */
12085 StorageControllerList *newList = new StorageControllerList();
12086 for (StorageControllerList::const_iterator
12087 it = mStorageControllers->begin();
12088 it != mStorageControllers->end();
12089 ++it)
12090 {
12091 (*it)->i_commit();
12092
12093 /* look if this controller has a peer device */
12094 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12095 if (!peer)
12096 {
12097 /* no peer means the device is a newly created one;
12098 * create a peer owning data this device share it with */
12099 peer.createObject();
12100 peer->init(mPeer, *it, true /* aReshare */);
12101 }
12102 else
12103 {
12104 /* remove peer from the old list */
12105 mPeer->mStorageControllers->remove(peer);
12106 }
12107 /* and add it to the new list */
12108 newList->push_back(peer);
12109 }
12110
12111 /* uninit old peer's controllers that are left */
12112 for (StorageControllerList::const_iterator
12113 it = mPeer->mStorageControllers->begin();
12114 it != mPeer->mStorageControllers->end();
12115 ++it)
12116 {
12117 (*it)->uninit();
12118 }
12119
12120 /* attach new list of controllers to our peer */
12121 mPeer->mStorageControllers.attach(newList);
12122 }
12123 else
12124 {
12125 /* we have no peer (our parent is the newly created machine);
12126 * just commit changes to devices */
12127 commitStorageControllers = true;
12128 }
12129 }
12130 else
12131 {
12132 /* the list of controllers itself is not changed,
12133 * just commit changes to controllers themselves */
12134 commitStorageControllers = true;
12135 }
12136
12137 if (commitStorageControllers)
12138 {
12139 for (StorageControllerList::const_iterator
12140 it = mStorageControllers->begin();
12141 it != mStorageControllers->end();
12142 ++it)
12143 {
12144 (*it)->i_commit();
12145 }
12146 }
12147
12148 bool commitUSBControllers = false;
12149
12150 if (mUSBControllers.isBackedUp())
12151 {
12152 mUSBControllers.commit();
12153
12154 if (mPeer)
12155 {
12156 /* Commit all changes to new controllers (this will reshare data with
12157 * peers for those who have peers) */
12158 USBControllerList *newList = new USBControllerList();
12159 for (USBControllerList::const_iterator
12160 it = mUSBControllers->begin();
12161 it != mUSBControllers->end();
12162 ++it)
12163 {
12164 (*it)->i_commit();
12165
12166 /* look if this controller has a peer device */
12167 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12168 if (!peer)
12169 {
12170 /* no peer means the device is a newly created one;
12171 * create a peer owning data this device share it with */
12172 peer.createObject();
12173 peer->init(mPeer, *it, true /* aReshare */);
12174 }
12175 else
12176 {
12177 /* remove peer from the old list */
12178 mPeer->mUSBControllers->remove(peer);
12179 }
12180 /* and add it to the new list */
12181 newList->push_back(peer);
12182 }
12183
12184 /* uninit old peer's controllers that are left */
12185 for (USBControllerList::const_iterator
12186 it = mPeer->mUSBControllers->begin();
12187 it != mPeer->mUSBControllers->end();
12188 ++it)
12189 {
12190 (*it)->uninit();
12191 }
12192
12193 /* attach new list of controllers to our peer */
12194 mPeer->mUSBControllers.attach(newList);
12195 }
12196 else
12197 {
12198 /* we have no peer (our parent is the newly created machine);
12199 * just commit changes to devices */
12200 commitUSBControllers = true;
12201 }
12202 }
12203 else
12204 {
12205 /* the list of controllers itself is not changed,
12206 * just commit changes to controllers themselves */
12207 commitUSBControllers = true;
12208 }
12209
12210 if (commitUSBControllers)
12211 {
12212 for (USBControllerList::const_iterator
12213 it = mUSBControllers->begin();
12214 it != mUSBControllers->end();
12215 ++it)
12216 {
12217 (*it)->i_commit();
12218 }
12219 }
12220
12221 if (i_isSessionMachine())
12222 {
12223 /* attach new data to the primary machine and reshare it */
12224 mPeer->mUserData.attach(mUserData);
12225 mPeer->mHWData.attach(mHWData);
12226 /* mmMediumAttachments is reshared by fixupMedia */
12227 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12228 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12229 }
12230}
12231
12232/**
12233 * Copies all the hardware data from the given machine.
12234 *
12235 * Currently, only called when the VM is being restored from a snapshot. In
12236 * particular, this implies that the VM is not running during this method's
12237 * call.
12238 *
12239 * @note This method must be called from under this object's lock.
12240 *
12241 * @note This method doesn't call #i_commit(), so all data remains backed up and
12242 * unsaved.
12243 */
12244void Machine::i_copyFrom(Machine *aThat)
12245{
12246 AssertReturnVoid(!i_isSnapshotMachine());
12247 AssertReturnVoid(aThat->i_isSnapshotMachine());
12248
12249 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12250
12251 mHWData.assignCopy(aThat->mHWData);
12252
12253 // create copies of all shared folders (mHWData after attaching a copy
12254 // contains just references to original objects)
12255 for (HWData::SharedFolderList::iterator
12256 it = mHWData->mSharedFolders.begin();
12257 it != mHWData->mSharedFolders.end();
12258 ++it)
12259 {
12260 ComObjPtr<SharedFolder> folder;
12261 folder.createObject();
12262 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12263 AssertComRC(rc);
12264 *it = folder;
12265 }
12266
12267 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12268 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12269 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12270 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12271 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12272
12273 /* create private copies of all controllers */
12274 mStorageControllers.backup();
12275 mStorageControllers->clear();
12276 for (StorageControllerList::const_iterator
12277 it = aThat->mStorageControllers->begin();
12278 it != aThat->mStorageControllers->end();
12279 ++it)
12280 {
12281 ComObjPtr<StorageController> ctrl;
12282 ctrl.createObject();
12283 ctrl->initCopy(this, *it);
12284 mStorageControllers->push_back(ctrl);
12285 }
12286
12287 /* create private copies of all USB controllers */
12288 mUSBControllers.backup();
12289 mUSBControllers->clear();
12290 for (USBControllerList::const_iterator
12291 it = aThat->mUSBControllers->begin();
12292 it != aThat->mUSBControllers->end();
12293 ++it)
12294 {
12295 ComObjPtr<USBController> ctrl;
12296 ctrl.createObject();
12297 ctrl->initCopy(this, *it);
12298 mUSBControllers->push_back(ctrl);
12299 }
12300
12301 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12302 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12303 {
12304 if (mNetworkAdapters[slot].isNotNull())
12305 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12306 else
12307 {
12308 unconst(mNetworkAdapters[slot]).createObject();
12309 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12310 }
12311 }
12312 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12313 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12314 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12315 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12316}
12317
12318/**
12319 * Returns whether the given storage controller is hotplug capable.
12320 *
12321 * @returns true if the controller supports hotplugging
12322 * false otherwise.
12323 * @param enmCtrlType The controller type to check for.
12324 */
12325bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12326{
12327 ComPtr<ISystemProperties> systemProperties;
12328 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12329 if (FAILED(rc))
12330 return false;
12331
12332 BOOL aHotplugCapable = FALSE;
12333 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12334
12335 return RT_BOOL(aHotplugCapable);
12336}
12337
12338#ifdef VBOX_WITH_RESOURCE_USAGE_API
12339
12340void Machine::i_getDiskList(MediaList &list)
12341{
12342 for (MediumAttachmentList::const_iterator
12343 it = mMediumAttachments->begin();
12344 it != mMediumAttachments->end();
12345 ++it)
12346 {
12347 MediumAttachment *pAttach = *it;
12348 /* just in case */
12349 AssertContinue(pAttach);
12350
12351 AutoCaller localAutoCallerA(pAttach);
12352 if (FAILED(localAutoCallerA.rc())) continue;
12353
12354 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12355
12356 if (pAttach->i_getType() == DeviceType_HardDisk)
12357 list.push_back(pAttach->i_getMedium());
12358 }
12359}
12360
12361void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12362{
12363 AssertReturnVoid(isWriteLockOnCurrentThread());
12364 AssertPtrReturnVoid(aCollector);
12365
12366 pm::CollectorHAL *hal = aCollector->getHAL();
12367 /* Create sub metrics */
12368 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12369 "Percentage of processor time spent in user mode by the VM process.");
12370 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12371 "Percentage of processor time spent in kernel mode by the VM process.");
12372 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12373 "Size of resident portion of VM process in memory.");
12374 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12375 "Actual size of all VM disks combined.");
12376 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12377 "Network receive rate.");
12378 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12379 "Network transmit rate.");
12380 /* Create and register base metrics */
12381 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12382 cpuLoadUser, cpuLoadKernel);
12383 aCollector->registerBaseMetric(cpuLoad);
12384 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12385 ramUsageUsed);
12386 aCollector->registerBaseMetric(ramUsage);
12387 MediaList disks;
12388 i_getDiskList(disks);
12389 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12390 diskUsageUsed);
12391 aCollector->registerBaseMetric(diskUsage);
12392
12393 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12394 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12395 new pm::AggregateAvg()));
12396 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12397 new pm::AggregateMin()));
12398 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12399 new pm::AggregateMax()));
12400 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12401 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12402 new pm::AggregateAvg()));
12403 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12404 new pm::AggregateMin()));
12405 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12406 new pm::AggregateMax()));
12407
12408 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12409 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12410 new pm::AggregateAvg()));
12411 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12412 new pm::AggregateMin()));
12413 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12414 new pm::AggregateMax()));
12415
12416 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12417 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12418 new pm::AggregateAvg()));
12419 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12420 new pm::AggregateMin()));
12421 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12422 new pm::AggregateMax()));
12423
12424
12425 /* Guest metrics collector */
12426 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12427 aCollector->registerGuest(mCollectorGuest);
12428 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12429
12430 /* Create sub metrics */
12431 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12432 "Percentage of processor time spent in user mode as seen by the guest.");
12433 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12434 "Percentage of processor time spent in kernel mode as seen by the guest.");
12435 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12436 "Percentage of processor time spent idling as seen by the guest.");
12437
12438 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12439 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12440 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12441 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12442 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12443 pm::SubMetric *guestMemCache = new pm::SubMetric(
12444 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12445
12446 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12447 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12448
12449 /* Create and register base metrics */
12450 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12451 machineNetRx, machineNetTx);
12452 aCollector->registerBaseMetric(machineNetRate);
12453
12454 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12455 guestLoadUser, guestLoadKernel, guestLoadIdle);
12456 aCollector->registerBaseMetric(guestCpuLoad);
12457
12458 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12459 guestMemTotal, guestMemFree,
12460 guestMemBalloon, guestMemShared,
12461 guestMemCache, guestPagedTotal);
12462 aCollector->registerBaseMetric(guestCpuMem);
12463
12464 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12465 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12466 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12467 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12468
12469 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12470 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12471 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12472 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12473
12474 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12475 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12476 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12477 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12478
12479 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12480 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12481 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12482 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12483
12484 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12485 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12486 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12487 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12488
12489 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12490 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12491 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12492 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12493
12494 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12495 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12496 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12497 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12498
12499 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12500 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12501 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12502 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12503
12504 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12505 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12506 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12507 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12508
12509 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12510 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12511 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12512 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12513
12514 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12515 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12516 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12517 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12518}
12519
12520void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12521{
12522 AssertReturnVoid(isWriteLockOnCurrentThread());
12523
12524 if (aCollector)
12525 {
12526 aCollector->unregisterMetricsFor(aMachine);
12527 aCollector->unregisterBaseMetricsFor(aMachine);
12528 }
12529}
12530
12531#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12532
12533
12534////////////////////////////////////////////////////////////////////////////////
12535
12536DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12537
12538HRESULT SessionMachine::FinalConstruct()
12539{
12540 LogFlowThisFunc(("\n"));
12541
12542 mClientToken = NULL;
12543
12544 return BaseFinalConstruct();
12545}
12546
12547void SessionMachine::FinalRelease()
12548{
12549 LogFlowThisFunc(("\n"));
12550
12551 Assert(!mClientToken);
12552 /* paranoia, should not hang around any more */
12553 if (mClientToken)
12554 {
12555 delete mClientToken;
12556 mClientToken = NULL;
12557 }
12558
12559 uninit(Uninit::Unexpected);
12560
12561 BaseFinalRelease();
12562}
12563
12564/**
12565 * @note Must be called only by Machine::LockMachine() from its own write lock.
12566 */
12567HRESULT SessionMachine::init(Machine *aMachine)
12568{
12569 LogFlowThisFuncEnter();
12570 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12571
12572 AssertReturn(aMachine, E_INVALIDARG);
12573
12574 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12575
12576 /* Enclose the state transition NotReady->InInit->Ready */
12577 AutoInitSpan autoInitSpan(this);
12578 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12579
12580 HRESULT rc = S_OK;
12581
12582 RT_ZERO(mAuthLibCtx);
12583
12584 /* create the machine client token */
12585 try
12586 {
12587 mClientToken = new ClientToken(aMachine, this);
12588 if (!mClientToken->isReady())
12589 {
12590 delete mClientToken;
12591 mClientToken = NULL;
12592 rc = E_FAIL;
12593 }
12594 }
12595 catch (std::bad_alloc &)
12596 {
12597 rc = E_OUTOFMEMORY;
12598 }
12599 if (FAILED(rc))
12600 return rc;
12601
12602 /* memorize the peer Machine */
12603 unconst(mPeer) = aMachine;
12604 /* share the parent pointer */
12605 unconst(mParent) = aMachine->mParent;
12606
12607 /* take the pointers to data to share */
12608 mData.share(aMachine->mData);
12609 mSSData.share(aMachine->mSSData);
12610
12611 mUserData.share(aMachine->mUserData);
12612 mHWData.share(aMachine->mHWData);
12613 mMediumAttachments.share(aMachine->mMediumAttachments);
12614
12615 mStorageControllers.allocate();
12616 for (StorageControllerList::const_iterator
12617 it = aMachine->mStorageControllers->begin();
12618 it != aMachine->mStorageControllers->end();
12619 ++it)
12620 {
12621 ComObjPtr<StorageController> ctl;
12622 ctl.createObject();
12623 ctl->init(this, *it);
12624 mStorageControllers->push_back(ctl);
12625 }
12626
12627 mUSBControllers.allocate();
12628 for (USBControllerList::const_iterator
12629 it = aMachine->mUSBControllers->begin();
12630 it != aMachine->mUSBControllers->end();
12631 ++it)
12632 {
12633 ComObjPtr<USBController> ctl;
12634 ctl.createObject();
12635 ctl->init(this, *it);
12636 mUSBControllers->push_back(ctl);
12637 }
12638
12639 unconst(mBIOSSettings).createObject();
12640 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12641 /* create another VRDEServer object that will be mutable */
12642 unconst(mVRDEServer).createObject();
12643 mVRDEServer->init(this, aMachine->mVRDEServer);
12644 /* create another audio adapter object that will be mutable */
12645 unconst(mAudioAdapter).createObject();
12646 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12647 /* create a list of serial ports that will be mutable */
12648 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12649 {
12650 unconst(mSerialPorts[slot]).createObject();
12651 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12652 }
12653 /* create a list of parallel ports that will be mutable */
12654 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12655 {
12656 unconst(mParallelPorts[slot]).createObject();
12657 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12658 }
12659
12660 /* create another USB device filters object that will be mutable */
12661 unconst(mUSBDeviceFilters).createObject();
12662 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12663
12664 /* create a list of network adapters that will be mutable */
12665 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12666 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12667 {
12668 unconst(mNetworkAdapters[slot]).createObject();
12669 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12670 }
12671
12672 /* create another bandwidth control object that will be mutable */
12673 unconst(mBandwidthControl).createObject();
12674 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12675
12676 /* default is to delete saved state on Saved -> PoweredOff transition */
12677 mRemoveSavedState = true;
12678
12679 /* Confirm a successful initialization when it's the case */
12680 autoInitSpan.setSucceeded();
12681
12682 miNATNetworksStarted = 0;
12683
12684 LogFlowThisFuncLeave();
12685 return rc;
12686}
12687
12688/**
12689 * Uninitializes this session object. If the reason is other than
12690 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12691 * or the client watcher code.
12692 *
12693 * @param aReason uninitialization reason
12694 *
12695 * @note Locks mParent + this object for writing.
12696 */
12697void SessionMachine::uninit(Uninit::Reason aReason)
12698{
12699 LogFlowThisFuncEnter();
12700 LogFlowThisFunc(("reason=%d\n", aReason));
12701
12702 /*
12703 * Strongly reference ourselves to prevent this object deletion after
12704 * mData->mSession.mMachine.setNull() below (which can release the last
12705 * reference and call the destructor). Important: this must be done before
12706 * accessing any members (and before AutoUninitSpan that does it as well).
12707 * This self reference will be released as the very last step on return.
12708 */
12709 ComObjPtr<SessionMachine> selfRef;
12710 if (aReason != Uninit::Unexpected)
12711 selfRef = this;
12712
12713 /* Enclose the state transition Ready->InUninit->NotReady */
12714 AutoUninitSpan autoUninitSpan(this);
12715 if (autoUninitSpan.uninitDone())
12716 {
12717 LogFlowThisFunc(("Already uninitialized\n"));
12718 LogFlowThisFuncLeave();
12719 return;
12720 }
12721
12722 if (autoUninitSpan.initFailed())
12723 {
12724 /* We've been called by init() because it's failed. It's not really
12725 * necessary (nor it's safe) to perform the regular uninit sequence
12726 * below, the following is enough.
12727 */
12728 LogFlowThisFunc(("Initialization failed.\n"));
12729 /* destroy the machine client token */
12730 if (mClientToken)
12731 {
12732 delete mClientToken;
12733 mClientToken = NULL;
12734 }
12735 uninitDataAndChildObjects();
12736 mData.free();
12737 unconst(mParent) = NULL;
12738 unconst(mPeer) = NULL;
12739 LogFlowThisFuncLeave();
12740 return;
12741 }
12742
12743 MachineState_T lastState;
12744 {
12745 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12746 lastState = mData->mMachineState;
12747 }
12748 NOREF(lastState);
12749
12750#ifdef VBOX_WITH_USB
12751 // release all captured USB devices, but do this before requesting the locks below
12752 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12753 {
12754 /* Console::captureUSBDevices() is called in the VM process only after
12755 * setting the machine state to Starting or Restoring.
12756 * Console::detachAllUSBDevices() will be called upon successful
12757 * termination. So, we need to release USB devices only if there was
12758 * an abnormal termination of a running VM.
12759 *
12760 * This is identical to SessionMachine::DetachAllUSBDevices except
12761 * for the aAbnormal argument. */
12762 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12763 AssertComRC(rc);
12764 NOREF(rc);
12765
12766 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12767 if (service)
12768 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12769 }
12770#endif /* VBOX_WITH_USB */
12771
12772 // we need to lock this object in uninit() because the lock is shared
12773 // with mPeer (as well as data we modify below). mParent lock is needed
12774 // by several calls to it.
12775 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12776
12777#ifdef VBOX_WITH_RESOURCE_USAGE_API
12778 /*
12779 * It is safe to call Machine::i_unregisterMetrics() here because
12780 * PerformanceCollector::samplerCallback no longer accesses guest methods
12781 * holding the lock.
12782 */
12783 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12784 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12785 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12786 if (mCollectorGuest)
12787 {
12788 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12789 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12790 mCollectorGuest = NULL;
12791 }
12792#endif
12793
12794 if (aReason == Uninit::Abnormal)
12795 {
12796 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12797
12798 /* reset the state to Aborted */
12799 if (mData->mMachineState != MachineState_Aborted)
12800 i_setMachineState(MachineState_Aborted);
12801 }
12802
12803 // any machine settings modified?
12804 if (mData->flModifications)
12805 {
12806 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12807 i_rollback(false /* aNotify */);
12808 }
12809
12810 mData->mSession.mPID = NIL_RTPROCESS;
12811
12812 if (aReason == Uninit::Unexpected)
12813 {
12814 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12815 * client watcher thread to update the set of machines that have open
12816 * sessions. */
12817 mParent->i_updateClientWatcher();
12818 }
12819
12820 /* uninitialize all remote controls */
12821 if (mData->mSession.mRemoteControls.size())
12822 {
12823 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12824 mData->mSession.mRemoteControls.size()));
12825
12826 /* Always restart a the beginning, since the iterator is invalidated
12827 * by using erase(). */
12828 for (Data::Session::RemoteControlList::iterator
12829 it = mData->mSession.mRemoteControls.begin();
12830 it != mData->mSession.mRemoteControls.end();
12831 it = mData->mSession.mRemoteControls.begin())
12832 {
12833 ComPtr<IInternalSessionControl> pControl = *it;
12834 mData->mSession.mRemoteControls.erase(it);
12835 multilock.release();
12836 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12837 HRESULT rc = pControl->Uninitialize();
12838 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12839 if (FAILED(rc))
12840 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12841 multilock.acquire();
12842 }
12843 mData->mSession.mRemoteControls.clear();
12844 }
12845
12846 /* Remove all references to the NAT network service. The service will stop
12847 * if all references (also from other VMs) are removed. */
12848 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12849 {
12850 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12851 {
12852 BOOL enabled;
12853 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12854 if ( FAILED(hrc)
12855 || !enabled)
12856 continue;
12857
12858 NetworkAttachmentType_T type;
12859 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12860 if ( SUCCEEDED(hrc)
12861 && type == NetworkAttachmentType_NATNetwork)
12862 {
12863 Bstr name;
12864 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12865 if (SUCCEEDED(hrc))
12866 {
12867 multilock.release();
12868 Utf8Str strName(name);
12869 LogRel(("VM '%s' stops using NAT network '%s'\n",
12870 mUserData->s.strName.c_str(), strName.c_str()));
12871 mParent->i_natNetworkRefDec(strName);
12872 multilock.acquire();
12873 }
12874 }
12875 }
12876 }
12877
12878 /*
12879 * An expected uninitialization can come only from #i_checkForDeath().
12880 * Otherwise it means that something's gone really wrong (for example,
12881 * the Session implementation has released the VirtualBox reference
12882 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12883 * etc). However, it's also possible, that the client releases the IPC
12884 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12885 * but the VirtualBox release event comes first to the server process.
12886 * This case is practically possible, so we should not assert on an
12887 * unexpected uninit, just log a warning.
12888 */
12889
12890 if (aReason == Uninit::Unexpected)
12891 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12892
12893 if (aReason != Uninit::Normal)
12894 {
12895 mData->mSession.mDirectControl.setNull();
12896 }
12897 else
12898 {
12899 /* this must be null here (see #OnSessionEnd()) */
12900 Assert(mData->mSession.mDirectControl.isNull());
12901 Assert(mData->mSession.mState == SessionState_Unlocking);
12902 Assert(!mData->mSession.mProgress.isNull());
12903 }
12904 if (mData->mSession.mProgress)
12905 {
12906 if (aReason == Uninit::Normal)
12907 mData->mSession.mProgress->i_notifyComplete(S_OK);
12908 else
12909 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12910 COM_IIDOF(ISession),
12911 getComponentName(),
12912 tr("The VM session was aborted"));
12913 mData->mSession.mProgress.setNull();
12914 }
12915
12916 if (mConsoleTaskData.mProgress)
12917 {
12918 Assert(aReason == Uninit::Abnormal);
12919 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12920 COM_IIDOF(ISession),
12921 getComponentName(),
12922 tr("The VM session was aborted"));
12923 mConsoleTaskData.mProgress.setNull();
12924 }
12925
12926 /* remove the association between the peer machine and this session machine */
12927 Assert( (SessionMachine*)mData->mSession.mMachine == this
12928 || aReason == Uninit::Unexpected);
12929
12930 /* reset the rest of session data */
12931 mData->mSession.mLockType = LockType_Null;
12932 mData->mSession.mMachine.setNull();
12933 mData->mSession.mState = SessionState_Unlocked;
12934 mData->mSession.mName.setNull();
12935
12936 /* destroy the machine client token before leaving the exclusive lock */
12937 if (mClientToken)
12938 {
12939 delete mClientToken;
12940 mClientToken = NULL;
12941 }
12942
12943 /* fire an event */
12944 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12945
12946 uninitDataAndChildObjects();
12947
12948 /* free the essential data structure last */
12949 mData.free();
12950
12951 /* release the exclusive lock before setting the below two to NULL */
12952 multilock.release();
12953
12954 unconst(mParent) = NULL;
12955 unconst(mPeer) = NULL;
12956
12957 AuthLibUnload(&mAuthLibCtx);
12958
12959 LogFlowThisFuncLeave();
12960}
12961
12962// util::Lockable interface
12963////////////////////////////////////////////////////////////////////////////////
12964
12965/**
12966 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12967 * with the primary Machine instance (mPeer).
12968 */
12969RWLockHandle *SessionMachine::lockHandle() const
12970{
12971 AssertReturn(mPeer != NULL, NULL);
12972 return mPeer->lockHandle();
12973}
12974
12975// IInternalMachineControl methods
12976////////////////////////////////////////////////////////////////////////////////
12977
12978/**
12979 * Passes collected guest statistics to performance collector object
12980 */
12981HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12982 ULONG aCpuKernel, ULONG aCpuIdle,
12983 ULONG aMemTotal, ULONG aMemFree,
12984 ULONG aMemBalloon, ULONG aMemShared,
12985 ULONG aMemCache, ULONG aPageTotal,
12986 ULONG aAllocVMM, ULONG aFreeVMM,
12987 ULONG aBalloonedVMM, ULONG aSharedVMM,
12988 ULONG aVmNetRx, ULONG aVmNetTx)
12989{
12990#ifdef VBOX_WITH_RESOURCE_USAGE_API
12991 if (mCollectorGuest)
12992 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12993 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12994 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12995 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12996
12997 return S_OK;
12998#else
12999 NOREF(aValidStats);
13000 NOREF(aCpuUser);
13001 NOREF(aCpuKernel);
13002 NOREF(aCpuIdle);
13003 NOREF(aMemTotal);
13004 NOREF(aMemFree);
13005 NOREF(aMemBalloon);
13006 NOREF(aMemShared);
13007 NOREF(aMemCache);
13008 NOREF(aPageTotal);
13009 NOREF(aAllocVMM);
13010 NOREF(aFreeVMM);
13011 NOREF(aBalloonedVMM);
13012 NOREF(aSharedVMM);
13013 NOREF(aVmNetRx);
13014 NOREF(aVmNetTx);
13015 return E_NOTIMPL;
13016#endif
13017}
13018
13019////////////////////////////////////////////////////////////////////////////////
13020//
13021// SessionMachine task records
13022//
13023////////////////////////////////////////////////////////////////////////////////
13024
13025/**
13026 * Task record for saving the machine state.
13027 */
13028class SessionMachine::SaveStateTask
13029 : public Machine::Task
13030{
13031public:
13032 SaveStateTask(SessionMachine *m,
13033 Progress *p,
13034 const Utf8Str &t,
13035 Reason_T enmReason,
13036 const Utf8Str &strStateFilePath)
13037 : Task(m, p, t),
13038 m_enmReason(enmReason),
13039 m_strStateFilePath(strStateFilePath)
13040 {}
13041
13042private:
13043 void handler()
13044 {
13045 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13046 }
13047
13048 Reason_T m_enmReason;
13049 Utf8Str m_strStateFilePath;
13050
13051 friend class SessionMachine;
13052};
13053
13054/**
13055 * Task thread implementation for SessionMachine::SaveState(), called from
13056 * SessionMachine::taskHandler().
13057 *
13058 * @note Locks this object for writing.
13059 *
13060 * @param task
13061 * @return
13062 */
13063void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13064{
13065 LogFlowThisFuncEnter();
13066
13067 AutoCaller autoCaller(this);
13068 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13069 if (FAILED(autoCaller.rc()))
13070 {
13071 /* we might have been uninitialized because the session was accidentally
13072 * closed by the client, so don't assert */
13073 HRESULT rc = setError(E_FAIL,
13074 tr("The session has been accidentally closed"));
13075 task.m_pProgress->i_notifyComplete(rc);
13076 LogFlowThisFuncLeave();
13077 return;
13078 }
13079
13080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13081
13082 HRESULT rc = S_OK;
13083
13084 try
13085 {
13086 ComPtr<IInternalSessionControl> directControl;
13087 if (mData->mSession.mLockType == LockType_VM)
13088 directControl = mData->mSession.mDirectControl;
13089 if (directControl.isNull())
13090 throw setError(VBOX_E_INVALID_VM_STATE,
13091 tr("Trying to save state without a running VM"));
13092 alock.release();
13093 BOOL fSuspendedBySave;
13094 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13095 Assert(!fSuspendedBySave);
13096 alock.acquire();
13097
13098 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13099 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13100 throw E_FAIL);
13101
13102 if (SUCCEEDED(rc))
13103 {
13104 mSSData->strStateFilePath = task.m_strStateFilePath;
13105
13106 /* save all VM settings */
13107 rc = i_saveSettings(NULL);
13108 // no need to check whether VirtualBox.xml needs saving also since
13109 // we can't have a name change pending at this point
13110 }
13111 else
13112 {
13113 // On failure, set the state to the state we had at the beginning.
13114 i_setMachineState(task.m_machineStateBackup);
13115 i_updateMachineStateOnClient();
13116
13117 // Delete the saved state file (might have been already created).
13118 // No need to check whether this is shared with a snapshot here
13119 // because we certainly created a fresh saved state file here.
13120 RTFileDelete(task.m_strStateFilePath.c_str());
13121 }
13122 }
13123 catch (HRESULT aRC) { rc = aRC; }
13124
13125 task.m_pProgress->i_notifyComplete(rc);
13126
13127 LogFlowThisFuncLeave();
13128}
13129
13130/**
13131 * @note Locks this object for writing.
13132 */
13133HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13134{
13135 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13136}
13137
13138HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13139{
13140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13141
13142 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13143 if (FAILED(rc)) return rc;
13144
13145 if ( mData->mMachineState != MachineState_Running
13146 && mData->mMachineState != MachineState_Paused
13147 )
13148 return setError(VBOX_E_INVALID_VM_STATE,
13149 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13150 Global::stringifyMachineState(mData->mMachineState));
13151
13152 ComObjPtr<Progress> pProgress;
13153 pProgress.createObject();
13154 rc = pProgress->init(i_getVirtualBox(),
13155 static_cast<IMachine *>(this) /* aInitiator */,
13156 tr("Saving the execution state of the virtual machine"),
13157 FALSE /* aCancelable */);
13158 if (FAILED(rc))
13159 return rc;
13160
13161 Utf8Str strStateFilePath;
13162 i_composeSavedStateFilename(strStateFilePath);
13163
13164 /* create and start the task on a separate thread (note that it will not
13165 * start working until we release alock) */
13166 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13167 rc = pTask->createThread();
13168 if (FAILED(rc))
13169 return rc;
13170
13171 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13172 i_setMachineState(MachineState_Saving);
13173 i_updateMachineStateOnClient();
13174
13175 pProgress.queryInterfaceTo(aProgress.asOutParam());
13176
13177 return S_OK;
13178}
13179
13180/**
13181 * @note Locks this object for writing.
13182 */
13183HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13184{
13185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13186
13187 HRESULT rc = i_checkStateDependency(MutableStateDep);
13188 if (FAILED(rc)) return rc;
13189
13190 if ( mData->mMachineState != MachineState_PoweredOff
13191 && mData->mMachineState != MachineState_Teleported
13192 && mData->mMachineState != MachineState_Aborted
13193 )
13194 return setError(VBOX_E_INVALID_VM_STATE,
13195 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13196 Global::stringifyMachineState(mData->mMachineState));
13197
13198 com::Utf8Str stateFilePathFull;
13199 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13200 if (RT_FAILURE(vrc))
13201 return setError(VBOX_E_FILE_ERROR,
13202 tr("Invalid saved state file path '%s' (%Rrc)"),
13203 aSavedStateFile.c_str(),
13204 vrc);
13205
13206 mSSData->strStateFilePath = stateFilePathFull;
13207
13208 /* The below i_setMachineState() will detect the state transition and will
13209 * update the settings file */
13210
13211 return i_setMachineState(MachineState_Saved);
13212}
13213
13214/**
13215 * @note Locks this object for writing.
13216 */
13217HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13218{
13219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13220
13221 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13222 if (FAILED(rc)) return rc;
13223
13224 if (mData->mMachineState != MachineState_Saved)
13225 return setError(VBOX_E_INVALID_VM_STATE,
13226 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13227 Global::stringifyMachineState(mData->mMachineState));
13228
13229 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13230
13231 /*
13232 * Saved -> PoweredOff transition will be detected in the SessionMachine
13233 * and properly handled.
13234 */
13235 rc = i_setMachineState(MachineState_PoweredOff);
13236 return rc;
13237}
13238
13239
13240/**
13241 * @note Locks the same as #i_setMachineState() does.
13242 */
13243HRESULT SessionMachine::updateState(MachineState_T aState)
13244{
13245 return i_setMachineState(aState);
13246}
13247
13248/**
13249 * @note Locks this object for writing.
13250 */
13251HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13252{
13253 IProgress *pProgress(aProgress);
13254
13255 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13256
13257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13258
13259 if (mData->mSession.mState != SessionState_Locked)
13260 return VBOX_E_INVALID_OBJECT_STATE;
13261
13262 if (!mData->mSession.mProgress.isNull())
13263 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13264
13265 /* If we didn't reference the NAT network service yet, add a reference to
13266 * force a start */
13267 if (miNATNetworksStarted < 1)
13268 {
13269 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13270 {
13271 BOOL enabled;
13272 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13273 if ( FAILED(hrc)
13274 || !enabled)
13275 continue;
13276
13277 NetworkAttachmentType_T type;
13278 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13279 if ( SUCCEEDED(hrc)
13280 && type == NetworkAttachmentType_NATNetwork)
13281 {
13282 Bstr name;
13283 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13284 if (SUCCEEDED(hrc))
13285 {
13286 Utf8Str strName(name);
13287 LogRel(("VM '%s' starts using NAT network '%s'\n",
13288 mUserData->s.strName.c_str(), strName.c_str()));
13289 mPeer->lockHandle()->unlockWrite();
13290 mParent->i_natNetworkRefInc(strName);
13291#ifdef RT_LOCK_STRICT
13292 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13293#else
13294 mPeer->lockHandle()->lockWrite();
13295#endif
13296 }
13297 }
13298 }
13299 miNATNetworksStarted++;
13300 }
13301
13302 LogFlowThisFunc(("returns S_OK.\n"));
13303 return S_OK;
13304}
13305
13306/**
13307 * @note Locks this object for writing.
13308 */
13309HRESULT SessionMachine::endPowerUp(LONG aResult)
13310{
13311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13312
13313 if (mData->mSession.mState != SessionState_Locked)
13314 return VBOX_E_INVALID_OBJECT_STATE;
13315
13316 /* Finalize the LaunchVMProcess progress object. */
13317 if (mData->mSession.mProgress)
13318 {
13319 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13320 mData->mSession.mProgress.setNull();
13321 }
13322
13323 if (SUCCEEDED((HRESULT)aResult))
13324 {
13325#ifdef VBOX_WITH_RESOURCE_USAGE_API
13326 /* The VM has been powered up successfully, so it makes sense
13327 * now to offer the performance metrics for a running machine
13328 * object. Doing it earlier wouldn't be safe. */
13329 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13330 mData->mSession.mPID);
13331#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13332 }
13333
13334 return S_OK;
13335}
13336
13337/**
13338 * @note Locks this object for writing.
13339 */
13340HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13341{
13342 LogFlowThisFuncEnter();
13343
13344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13345
13346 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13347 E_FAIL);
13348
13349 /* create a progress object to track operation completion */
13350 ComObjPtr<Progress> pProgress;
13351 pProgress.createObject();
13352 pProgress->init(i_getVirtualBox(),
13353 static_cast<IMachine *>(this) /* aInitiator */,
13354 tr("Stopping the virtual machine"),
13355 FALSE /* aCancelable */);
13356
13357 /* fill in the console task data */
13358 mConsoleTaskData.mLastState = mData->mMachineState;
13359 mConsoleTaskData.mProgress = pProgress;
13360
13361 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13362 i_setMachineState(MachineState_Stopping);
13363
13364 pProgress.queryInterfaceTo(aProgress.asOutParam());
13365
13366 return S_OK;
13367}
13368
13369/**
13370 * @note Locks this object for writing.
13371 */
13372HRESULT SessionMachine::endPoweringDown(LONG aResult,
13373 const com::Utf8Str &aErrMsg)
13374{
13375 LogFlowThisFuncEnter();
13376
13377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13378
13379 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13380 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13381 && mConsoleTaskData.mLastState != MachineState_Null,
13382 E_FAIL);
13383
13384 /*
13385 * On failure, set the state to the state we had when BeginPoweringDown()
13386 * was called (this is expected by Console::PowerDown() and the associated
13387 * task). On success the VM process already changed the state to
13388 * MachineState_PoweredOff, so no need to do anything.
13389 */
13390 if (FAILED(aResult))
13391 i_setMachineState(mConsoleTaskData.mLastState);
13392
13393 /* notify the progress object about operation completion */
13394 Assert(mConsoleTaskData.mProgress);
13395 if (SUCCEEDED(aResult))
13396 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13397 else
13398 {
13399 if (aErrMsg.length())
13400 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13401 COM_IIDOF(ISession),
13402 getComponentName(),
13403 aErrMsg.c_str());
13404 else
13405 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13406 }
13407
13408 /* clear out the temporary saved state data */
13409 mConsoleTaskData.mLastState = MachineState_Null;
13410 mConsoleTaskData.mProgress.setNull();
13411
13412 LogFlowThisFuncLeave();
13413 return S_OK;
13414}
13415
13416
13417/**
13418 * Goes through the USB filters of the given machine to see if the given
13419 * device matches any filter or not.
13420 *
13421 * @note Locks the same as USBController::hasMatchingFilter() does.
13422 */
13423HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13424 BOOL *aMatched,
13425 ULONG *aMaskedInterfaces)
13426{
13427 LogFlowThisFunc(("\n"));
13428
13429#ifdef VBOX_WITH_USB
13430 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13431#else
13432 NOREF(aDevice);
13433 NOREF(aMaskedInterfaces);
13434 *aMatched = FALSE;
13435#endif
13436
13437 return S_OK;
13438}
13439
13440/**
13441 * @note Locks the same as Host::captureUSBDevice() does.
13442 */
13443HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13444{
13445 LogFlowThisFunc(("\n"));
13446
13447#ifdef VBOX_WITH_USB
13448 /* if captureDeviceForVM() fails, it must have set extended error info */
13449 clearError();
13450 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13451 if (FAILED(rc)) return rc;
13452
13453 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13454 AssertReturn(service, E_FAIL);
13455 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13456#else
13457 NOREF(aId);
13458 return E_NOTIMPL;
13459#endif
13460}
13461
13462/**
13463 * @note Locks the same as Host::detachUSBDevice() does.
13464 */
13465HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13466 BOOL aDone)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470#ifdef VBOX_WITH_USB
13471 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13472 AssertReturn(service, E_FAIL);
13473 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13474#else
13475 NOREF(aId);
13476 NOREF(aDone);
13477 return E_NOTIMPL;
13478#endif
13479}
13480
13481/**
13482 * Inserts all machine filters to the USB proxy service and then calls
13483 * Host::autoCaptureUSBDevices().
13484 *
13485 * Called by Console from the VM process upon VM startup.
13486 *
13487 * @note Locks what called methods lock.
13488 */
13489HRESULT SessionMachine::autoCaptureUSBDevices()
13490{
13491 LogFlowThisFunc(("\n"));
13492
13493#ifdef VBOX_WITH_USB
13494 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13495 AssertComRC(rc);
13496 NOREF(rc);
13497
13498 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13499 AssertReturn(service, E_FAIL);
13500 return service->autoCaptureDevicesForVM(this);
13501#else
13502 return S_OK;
13503#endif
13504}
13505
13506/**
13507 * Removes all machine filters from the USB proxy service and then calls
13508 * Host::detachAllUSBDevices().
13509 *
13510 * Called by Console from the VM process upon normal VM termination or by
13511 * SessionMachine::uninit() upon abnormal VM termination (from under the
13512 * Machine/SessionMachine lock).
13513 *
13514 * @note Locks what called methods lock.
13515 */
13516HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13517{
13518 LogFlowThisFunc(("\n"));
13519
13520#ifdef VBOX_WITH_USB
13521 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13522 AssertComRC(rc);
13523 NOREF(rc);
13524
13525 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13526 AssertReturn(service, E_FAIL);
13527 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13528#else
13529 NOREF(aDone);
13530 return S_OK;
13531#endif
13532}
13533
13534/**
13535 * @note Locks this object for writing.
13536 */
13537HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13538 ComPtr<IProgress> &aProgress)
13539{
13540 LogFlowThisFuncEnter();
13541
13542 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13543 /*
13544 * We don't assert below because it might happen that a non-direct session
13545 * informs us it is closed right after we've been uninitialized -- it's ok.
13546 */
13547
13548 /* get IInternalSessionControl interface */
13549 ComPtr<IInternalSessionControl> control(aSession);
13550
13551 ComAssertRet(!control.isNull(), E_INVALIDARG);
13552
13553 /* Creating a Progress object requires the VirtualBox lock, and
13554 * thus locking it here is required by the lock order rules. */
13555 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13556
13557 if (control == mData->mSession.mDirectControl)
13558 {
13559 /* The direct session is being normally closed by the client process
13560 * ----------------------------------------------------------------- */
13561
13562 /* go to the closing state (essential for all open*Session() calls and
13563 * for #i_checkForDeath()) */
13564 Assert(mData->mSession.mState == SessionState_Locked);
13565 mData->mSession.mState = SessionState_Unlocking;
13566
13567 /* set direct control to NULL to release the remote instance */
13568 mData->mSession.mDirectControl.setNull();
13569 LogFlowThisFunc(("Direct control is set to NULL\n"));
13570
13571 if (mData->mSession.mProgress)
13572 {
13573 /* finalize the progress, someone might wait if a frontend
13574 * closes the session before powering on the VM. */
13575 mData->mSession.mProgress->notifyComplete(E_FAIL,
13576 COM_IIDOF(ISession),
13577 getComponentName(),
13578 tr("The VM session was closed before any attempt to power it on"));
13579 mData->mSession.mProgress.setNull();
13580 }
13581
13582 /* Create the progress object the client will use to wait until
13583 * #i_checkForDeath() is called to uninitialize this session object after
13584 * it releases the IPC semaphore.
13585 * Note! Because we're "reusing" mProgress here, this must be a proxy
13586 * object just like for LaunchVMProcess. */
13587 Assert(mData->mSession.mProgress.isNull());
13588 ComObjPtr<ProgressProxy> progress;
13589 progress.createObject();
13590 ComPtr<IUnknown> pPeer(mPeer);
13591 progress->init(mParent, pPeer,
13592 Bstr(tr("Closing session")).raw(),
13593 FALSE /* aCancelable */);
13594 progress.queryInterfaceTo(aProgress.asOutParam());
13595 mData->mSession.mProgress = progress;
13596 }
13597 else
13598 {
13599 /* the remote session is being normally closed */
13600 bool found = false;
13601 for (Data::Session::RemoteControlList::iterator
13602 it = mData->mSession.mRemoteControls.begin();
13603 it != mData->mSession.mRemoteControls.end();
13604 ++it)
13605 {
13606 if (control == *it)
13607 {
13608 found = true;
13609 // This MUST be erase(it), not remove(*it) as the latter
13610 // triggers a very nasty use after free due to the place where
13611 // the value "lives".
13612 mData->mSession.mRemoteControls.erase(it);
13613 break;
13614 }
13615 }
13616 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13617 E_INVALIDARG);
13618 }
13619
13620 /* signal the client watcher thread, because the client is going away */
13621 mParent->i_updateClientWatcher();
13622
13623 LogFlowThisFuncLeave();
13624 return S_OK;
13625}
13626
13627HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13628 std::vector<com::Utf8Str> &aValues,
13629 std::vector<LONG64> &aTimestamps,
13630 std::vector<com::Utf8Str> &aFlags)
13631{
13632 LogFlowThisFunc(("\n"));
13633
13634#ifdef VBOX_WITH_GUEST_PROPS
13635 using namespace guestProp;
13636
13637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13638
13639 size_t cEntries = mHWData->mGuestProperties.size();
13640 aNames.resize(cEntries);
13641 aValues.resize(cEntries);
13642 aTimestamps.resize(cEntries);
13643 aFlags.resize(cEntries);
13644
13645 size_t i = 0;
13646 for (HWData::GuestPropertyMap::const_iterator
13647 it = mHWData->mGuestProperties.begin();
13648 it != mHWData->mGuestProperties.end();
13649 ++it, ++i)
13650 {
13651 char szFlags[MAX_FLAGS_LEN + 1];
13652 aNames[i] = it->first;
13653 aValues[i] = it->second.strValue;
13654 aTimestamps[i] = it->second.mTimestamp;
13655
13656 /* If it is NULL, keep it NULL. */
13657 if (it->second.mFlags)
13658 {
13659 writeFlags(it->second.mFlags, szFlags);
13660 aFlags[i] = szFlags;
13661 }
13662 else
13663 aFlags[i] = "";
13664 }
13665 return S_OK;
13666#else
13667 ReturnComNotImplemented();
13668#endif
13669}
13670
13671HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13672 const com::Utf8Str &aValue,
13673 LONG64 aTimestamp,
13674 const com::Utf8Str &aFlags)
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678#ifdef VBOX_WITH_GUEST_PROPS
13679 using namespace guestProp;
13680
13681 try
13682 {
13683 /*
13684 * Convert input up front.
13685 */
13686 uint32_t fFlags = NILFLAG;
13687 if (aFlags.length())
13688 {
13689 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13690 AssertRCReturn(vrc, E_INVALIDARG);
13691 }
13692
13693 /*
13694 * Now grab the object lock, validate the state and do the update.
13695 */
13696
13697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13698
13699 if (!Global::IsOnline(mData->mMachineState))
13700 {
13701 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13702 VBOX_E_INVALID_VM_STATE);
13703 }
13704
13705 i_setModified(IsModified_MachineData);
13706 mHWData.backup();
13707
13708 bool fDelete = !aValue.length();
13709 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13710 if (it != mHWData->mGuestProperties.end())
13711 {
13712 if (!fDelete)
13713 {
13714 it->second.strValue = aValue;
13715 it->second.mTimestamp = aTimestamp;
13716 it->second.mFlags = fFlags;
13717 }
13718 else
13719 mHWData->mGuestProperties.erase(it);
13720
13721 mData->mGuestPropertiesModified = TRUE;
13722 }
13723 else if (!fDelete)
13724 {
13725 HWData::GuestProperty prop;
13726 prop.strValue = aValue;
13727 prop.mTimestamp = aTimestamp;
13728 prop.mFlags = fFlags;
13729
13730 mHWData->mGuestProperties[aName] = prop;
13731 mData->mGuestPropertiesModified = TRUE;
13732 }
13733
13734 alock.release();
13735
13736 mParent->i_onGuestPropertyChange(mData->mUuid,
13737 Bstr(aName).raw(),
13738 Bstr(aValue).raw(),
13739 Bstr(aFlags).raw());
13740 }
13741 catch (...)
13742 {
13743 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13744 }
13745 return S_OK;
13746#else
13747 ReturnComNotImplemented();
13748#endif
13749}
13750
13751
13752HRESULT SessionMachine::lockMedia()
13753{
13754 AutoMultiWriteLock2 alock(this->lockHandle(),
13755 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13756
13757 AssertReturn( mData->mMachineState == MachineState_Starting
13758 || mData->mMachineState == MachineState_Restoring
13759 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13760
13761 clearError();
13762 alock.release();
13763 return i_lockMedia();
13764}
13765
13766HRESULT SessionMachine::unlockMedia()
13767{
13768 HRESULT hrc = i_unlockMedia();
13769 return hrc;
13770}
13771
13772HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13773 ComPtr<IMediumAttachment> &aNewAttachment)
13774{
13775 // request the host lock first, since might be calling Host methods for getting host drives;
13776 // next, protect the media tree all the while we're in here, as well as our member variables
13777 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13778 this->lockHandle(),
13779 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13780
13781 IMediumAttachment *iAttach = aAttachment;
13782 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13783
13784 Utf8Str ctrlName;
13785 LONG lPort;
13786 LONG lDevice;
13787 bool fTempEject;
13788 {
13789 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13790
13791 /* Need to query the details first, as the IMediumAttachment reference
13792 * might be to the original settings, which we are going to change. */
13793 ctrlName = pAttach->i_getControllerName();
13794 lPort = pAttach->i_getPort();
13795 lDevice = pAttach->i_getDevice();
13796 fTempEject = pAttach->i_getTempEject();
13797 }
13798
13799 if (!fTempEject)
13800 {
13801 /* Remember previously mounted medium. The medium before taking the
13802 * backup is not necessarily the same thing. */
13803 ComObjPtr<Medium> oldmedium;
13804 oldmedium = pAttach->i_getMedium();
13805
13806 i_setModified(IsModified_Storage);
13807 mMediumAttachments.backup();
13808
13809 // The backup operation makes the pAttach reference point to the
13810 // old settings. Re-get the correct reference.
13811 pAttach = i_findAttachment(*mMediumAttachments.data(),
13812 ctrlName,
13813 lPort,
13814 lDevice);
13815
13816 {
13817 AutoCaller autoAttachCaller(this);
13818 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13819
13820 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13821 if (!oldmedium.isNull())
13822 oldmedium->i_removeBackReference(mData->mUuid);
13823
13824 pAttach->i_updateMedium(NULL);
13825 pAttach->i_updateEjected();
13826 }
13827
13828 i_setModified(IsModified_Storage);
13829 }
13830 else
13831 {
13832 {
13833 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13834 pAttach->i_updateEjected();
13835 }
13836 }
13837
13838 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13839
13840 return S_OK;
13841}
13842
13843HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13844 com::Utf8Str &aResult)
13845{
13846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13847
13848 HRESULT hr = S_OK;
13849
13850 if (!mAuthLibCtx.hAuthLibrary)
13851 {
13852 /* Load the external authentication library. */
13853 Bstr authLibrary;
13854 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13855
13856 Utf8Str filename = authLibrary;
13857
13858 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13859 if (RT_FAILURE(rc))
13860 {
13861 hr = setError(E_FAIL,
13862 tr("Could not load the external authentication library '%s' (%Rrc)"),
13863 filename.c_str(), rc);
13864 }
13865 }
13866
13867 /* The auth library might need the machine lock. */
13868 alock.release();
13869
13870 if (FAILED(hr))
13871 return hr;
13872
13873 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13874 {
13875 enum VRDEAuthParams
13876 {
13877 parmUuid = 1,
13878 parmGuestJudgement,
13879 parmUser,
13880 parmPassword,
13881 parmDomain,
13882 parmClientId
13883 };
13884
13885 AuthResult result = AuthResultAccessDenied;
13886
13887 Guid uuid(aAuthParams[parmUuid]);
13888 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13889 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13890
13891 result = AuthLibAuthenticate(&mAuthLibCtx,
13892 uuid.raw(), guestJudgement,
13893 aAuthParams[parmUser].c_str(),
13894 aAuthParams[parmPassword].c_str(),
13895 aAuthParams[parmDomain].c_str(),
13896 u32ClientId);
13897
13898 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13899 size_t cbPassword = aAuthParams[parmPassword].length();
13900 if (cbPassword)
13901 {
13902 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13903 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13904 }
13905
13906 if (result == AuthResultAccessGranted)
13907 aResult = "granted";
13908 else
13909 aResult = "denied";
13910
13911 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13912 aAuthParams[parmUser].c_str(), aResult.c_str()));
13913 }
13914 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13915 {
13916 enum VRDEAuthDisconnectParams
13917 {
13918 parmUuid = 1,
13919 parmClientId
13920 };
13921
13922 Guid uuid(aAuthParams[parmUuid]);
13923 uint32_t u32ClientId = 0;
13924 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13925 }
13926 else
13927 {
13928 hr = E_INVALIDARG;
13929 }
13930
13931 return hr;
13932}
13933
13934// public methods only for internal purposes
13935/////////////////////////////////////////////////////////////////////////////
13936
13937#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13938/**
13939 * Called from the client watcher thread to check for expected or unexpected
13940 * death of the client process that has a direct session to this machine.
13941 *
13942 * On Win32 and on OS/2, this method is called only when we've got the
13943 * mutex (i.e. the client has either died or terminated normally) so it always
13944 * returns @c true (the client is terminated, the session machine is
13945 * uninitialized).
13946 *
13947 * On other platforms, the method returns @c true if the client process has
13948 * terminated normally or abnormally and the session machine was uninitialized,
13949 * and @c false if the client process is still alive.
13950 *
13951 * @note Locks this object for writing.
13952 */
13953bool SessionMachine::i_checkForDeath()
13954{
13955 Uninit::Reason reason;
13956 bool terminated = false;
13957
13958 /* Enclose autoCaller with a block because calling uninit() from under it
13959 * will deadlock. */
13960 {
13961 AutoCaller autoCaller(this);
13962 if (!autoCaller.isOk())
13963 {
13964 /* return true if not ready, to cause the client watcher to exclude
13965 * the corresponding session from watching */
13966 LogFlowThisFunc(("Already uninitialized!\n"));
13967 return true;
13968 }
13969
13970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13971
13972 /* Determine the reason of death: if the session state is Closing here,
13973 * everything is fine. Otherwise it means that the client did not call
13974 * OnSessionEnd() before it released the IPC semaphore. This may happen
13975 * either because the client process has abnormally terminated, or
13976 * because it simply forgot to call ISession::Close() before exiting. We
13977 * threat the latter also as an abnormal termination (see
13978 * Session::uninit() for details). */
13979 reason = mData->mSession.mState == SessionState_Unlocking ?
13980 Uninit::Normal :
13981 Uninit::Abnormal;
13982
13983 if (mClientToken)
13984 terminated = mClientToken->release();
13985 } /* AutoCaller block */
13986
13987 if (terminated)
13988 uninit(reason);
13989
13990 return terminated;
13991}
13992
13993void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 strTokenId.setNull();
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturnVoid(autoCaller.rc());
14001
14002 Assert(mClientToken);
14003 if (mClientToken)
14004 mClientToken->getId(strTokenId);
14005}
14006#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14007IToken *SessionMachine::i_getToken()
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), NULL);
14013
14014 Assert(mClientToken);
14015 if (mClientToken)
14016 return mClientToken->getToken();
14017 else
14018 return NULL;
14019}
14020#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14021
14022Machine::ClientToken *SessionMachine::i_getClientToken()
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), NULL);
14028
14029 return mClientToken;
14030}
14031
14032
14033/**
14034 * @note Locks this object for reading.
14035 */
14036HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14037{
14038 LogFlowThisFunc(("\n"));
14039
14040 AutoCaller autoCaller(this);
14041 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14042
14043 ComPtr<IInternalSessionControl> directControl;
14044 {
14045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14046 if (mData->mSession.mLockType == LockType_VM)
14047 directControl = mData->mSession.mDirectControl;
14048 }
14049
14050 /* ignore notifications sent after #OnSessionEnd() is called */
14051 if (!directControl)
14052 return S_OK;
14053
14054 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14055}
14056
14057/**
14058 * @note Locks this object for reading.
14059 */
14060HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14061 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14062 IN_BSTR aGuestIp, LONG aGuestPort)
14063{
14064 LogFlowThisFunc(("\n"));
14065
14066 AutoCaller autoCaller(this);
14067 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14068
14069 ComPtr<IInternalSessionControl> directControl;
14070 {
14071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14072 if (mData->mSession.mLockType == LockType_VM)
14073 directControl = mData->mSession.mDirectControl;
14074 }
14075
14076 /* ignore notifications sent after #OnSessionEnd() is called */
14077 if (!directControl)
14078 return S_OK;
14079 /*
14080 * instead acting like callback we ask IVirtualBox deliver corresponding event
14081 */
14082
14083 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14084 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14085 return S_OK;
14086}
14087
14088/**
14089 * @note Locks this object for reading.
14090 */
14091HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14092{
14093 LogFlowThisFunc(("\n"));
14094
14095 AutoCaller autoCaller(this);
14096 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14097
14098 ComPtr<IInternalSessionControl> directControl;
14099 {
14100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14101 if (mData->mSession.mLockType == LockType_VM)
14102 directControl = mData->mSession.mDirectControl;
14103 }
14104
14105 /* ignore notifications sent after #OnSessionEnd() is called */
14106 if (!directControl)
14107 return S_OK;
14108
14109 return directControl->OnSerialPortChange(serialPort);
14110}
14111
14112/**
14113 * @note Locks this object for reading.
14114 */
14115HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14116{
14117 LogFlowThisFunc(("\n"));
14118
14119 AutoCaller autoCaller(this);
14120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14121
14122 ComPtr<IInternalSessionControl> directControl;
14123 {
14124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14125 if (mData->mSession.mLockType == LockType_VM)
14126 directControl = mData->mSession.mDirectControl;
14127 }
14128
14129 /* ignore notifications sent after #OnSessionEnd() is called */
14130 if (!directControl)
14131 return S_OK;
14132
14133 return directControl->OnParallelPortChange(parallelPort);
14134}
14135
14136/**
14137 * @note Locks this object for reading.
14138 */
14139HRESULT SessionMachine::i_onStorageControllerChange()
14140{
14141 LogFlowThisFunc(("\n"));
14142
14143 AutoCaller autoCaller(this);
14144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14145
14146 ComPtr<IInternalSessionControl> directControl;
14147 {
14148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14149 if (mData->mSession.mLockType == LockType_VM)
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnStorageControllerChange();
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 if (mData->mSession.mLockType == LockType_VM)
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnMediumChange(aAttachment, aForce);
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 if (mData->mSession.mLockType == LockType_VM)
14198 directControl = mData->mSession.mDirectControl;
14199 }
14200
14201 /* ignore notifications sent after #OnSessionEnd() is called */
14202 if (!directControl)
14203 return S_OK;
14204
14205 return directControl->OnCPUChange(aCPU, aRemove);
14206}
14207
14208HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 if (mData->mSession.mLockType == LockType_VM)
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 if (mData->mSession.mLockType == LockType_VM)
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnVRDEServerChange(aRestart);
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::i_onVideoCaptureChange()
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnVideoCaptureChange();
14275}
14276
14277/**
14278 * @note Locks this object for reading.
14279 */
14280HRESULT SessionMachine::i_onUSBControllerChange()
14281{
14282 LogFlowThisFunc(("\n"));
14283
14284 AutoCaller autoCaller(this);
14285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14286
14287 ComPtr<IInternalSessionControl> directControl;
14288 {
14289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14290 if (mData->mSession.mLockType == LockType_VM)
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 /* ignore notifications sent after #OnSessionEnd() is called */
14295 if (!directControl)
14296 return S_OK;
14297
14298 return directControl->OnUSBControllerChange();
14299}
14300
14301/**
14302 * @note Locks this object for reading.
14303 */
14304HRESULT SessionMachine::i_onSharedFolderChange()
14305{
14306 LogFlowThisFunc(("\n"));
14307
14308 AutoCaller autoCaller(this);
14309 AssertComRCReturnRC(autoCaller.rc());
14310
14311 ComPtr<IInternalSessionControl> directControl;
14312 {
14313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14314 if (mData->mSession.mLockType == LockType_VM)
14315 directControl = mData->mSession.mDirectControl;
14316 }
14317
14318 /* ignore notifications sent after #OnSessionEnd() is called */
14319 if (!directControl)
14320 return S_OK;
14321
14322 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14323}
14324
14325/**
14326 * @note Locks this object for reading.
14327 */
14328HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14329{
14330 LogFlowThisFunc(("\n"));
14331
14332 AutoCaller autoCaller(this);
14333 AssertComRCReturnRC(autoCaller.rc());
14334
14335 ComPtr<IInternalSessionControl> directControl;
14336 {
14337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14338 if (mData->mSession.mLockType == LockType_VM)
14339 directControl = mData->mSession.mDirectControl;
14340 }
14341
14342 /* ignore notifications sent after #OnSessionEnd() is called */
14343 if (!directControl)
14344 return S_OK;
14345
14346 return directControl->OnClipboardModeChange(aClipboardMode);
14347}
14348
14349/**
14350 * @note Locks this object for reading.
14351 */
14352HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14353{
14354 LogFlowThisFunc(("\n"));
14355
14356 AutoCaller autoCaller(this);
14357 AssertComRCReturnRC(autoCaller.rc());
14358
14359 ComPtr<IInternalSessionControl> directControl;
14360 {
14361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14362 if (mData->mSession.mLockType == LockType_VM)
14363 directControl = mData->mSession.mDirectControl;
14364 }
14365
14366 /* ignore notifications sent after #OnSessionEnd() is called */
14367 if (!directControl)
14368 return S_OK;
14369
14370 return directControl->OnDnDModeChange(aDnDMode);
14371}
14372
14373/**
14374 * @note Locks this object for reading.
14375 */
14376HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14377{
14378 LogFlowThisFunc(("\n"));
14379
14380 AutoCaller autoCaller(this);
14381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14382
14383 ComPtr<IInternalSessionControl> directControl;
14384 {
14385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14386 if (mData->mSession.mLockType == LockType_VM)
14387 directControl = mData->mSession.mDirectControl;
14388 }
14389
14390 /* ignore notifications sent after #OnSessionEnd() is called */
14391 if (!directControl)
14392 return S_OK;
14393
14394 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14395}
14396
14397/**
14398 * @note Locks this object for reading.
14399 */
14400HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14401{
14402 LogFlowThisFunc(("\n"));
14403
14404 AutoCaller autoCaller(this);
14405 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14406
14407 ComPtr<IInternalSessionControl> directControl;
14408 {
14409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14410 if (mData->mSession.mLockType == LockType_VM)
14411 directControl = mData->mSession.mDirectControl;
14412 }
14413
14414 /* ignore notifications sent after #OnSessionEnd() is called */
14415 if (!directControl)
14416 return S_OK;
14417
14418 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14419}
14420
14421/**
14422 * Returns @c true if this machine's USB controller reports it has a matching
14423 * filter for the given USB device and @c false otherwise.
14424 *
14425 * @note locks this object for reading.
14426 */
14427bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14428{
14429 AutoCaller autoCaller(this);
14430 /* silently return if not ready -- this method may be called after the
14431 * direct machine session has been called */
14432 if (!autoCaller.isOk())
14433 return false;
14434
14435#ifdef VBOX_WITH_USB
14436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14437
14438 switch (mData->mMachineState)
14439 {
14440 case MachineState_Starting:
14441 case MachineState_Restoring:
14442 case MachineState_TeleportingIn:
14443 case MachineState_Paused:
14444 case MachineState_Running:
14445 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14446 * elsewhere... */
14447 alock.release();
14448 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14449 default: break;
14450 }
14451#else
14452 NOREF(aDevice);
14453 NOREF(aMaskedIfs);
14454#endif
14455 return false;
14456}
14457
14458/**
14459 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14460 */
14461HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14462 IVirtualBoxErrorInfo *aError,
14463 ULONG aMaskedIfs,
14464 const com::Utf8Str &aCaptureFilename)
14465{
14466 LogFlowThisFunc(("\n"));
14467
14468 AutoCaller autoCaller(this);
14469
14470 /* This notification may happen after the machine object has been
14471 * uninitialized (the session was closed), so don't assert. */
14472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14473
14474 ComPtr<IInternalSessionControl> directControl;
14475 {
14476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14477 if (mData->mSession.mLockType == LockType_VM)
14478 directControl = mData->mSession.mDirectControl;
14479 }
14480
14481 /* fail on notifications sent after #OnSessionEnd() is called, it is
14482 * expected by the caller */
14483 if (!directControl)
14484 return E_FAIL;
14485
14486 /* No locks should be held at this point. */
14487 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14488 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14489
14490 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14491}
14492
14493/**
14494 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14495 */
14496HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14497 IVirtualBoxErrorInfo *aError)
14498{
14499 LogFlowThisFunc(("\n"));
14500
14501 AutoCaller autoCaller(this);
14502
14503 /* This notification may happen after the machine object has been
14504 * uninitialized (the session was closed), so don't assert. */
14505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14506
14507 ComPtr<IInternalSessionControl> directControl;
14508 {
14509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14510 if (mData->mSession.mLockType == LockType_VM)
14511 directControl = mData->mSession.mDirectControl;
14512 }
14513
14514 /* fail on notifications sent after #OnSessionEnd() is called, it is
14515 * expected by the caller */
14516 if (!directControl)
14517 return E_FAIL;
14518
14519 /* No locks should be held at this point. */
14520 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14521 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14522
14523 return directControl->OnUSBDeviceDetach(aId, aError);
14524}
14525
14526// protected methods
14527/////////////////////////////////////////////////////////////////////////////
14528
14529/**
14530 * Deletes the given file if it is no longer in use by either the current machine state
14531 * (if the machine is "saved") or any of the machine's snapshots.
14532 *
14533 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14534 * but is different for each SnapshotMachine. When calling this, the order of calling this
14535 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14536 * is therefore critical. I know, it's all rather messy.
14537 *
14538 * @param strStateFile
14539 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14540 * the test for whether the saved state file is in use.
14541 */
14542void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14543 Snapshot *pSnapshotToIgnore)
14544{
14545 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14546 if ( (strStateFile.isNotEmpty())
14547 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14548 )
14549 // ... and it must also not be shared with other snapshots
14550 if ( !mData->mFirstSnapshot
14551 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14552 // this checks the SnapshotMachine's state file paths
14553 )
14554 RTFileDelete(strStateFile.c_str());
14555}
14556
14557/**
14558 * Locks the attached media.
14559 *
14560 * All attached hard disks are locked for writing and DVD/floppy are locked for
14561 * reading. Parents of attached hard disks (if any) are locked for reading.
14562 *
14563 * This method also performs accessibility check of all media it locks: if some
14564 * media is inaccessible, the method will return a failure and a bunch of
14565 * extended error info objects per each inaccessible medium.
14566 *
14567 * Note that this method is atomic: if it returns a success, all media are
14568 * locked as described above; on failure no media is locked at all (all
14569 * succeeded individual locks will be undone).
14570 *
14571 * The caller is responsible for doing the necessary state sanity checks.
14572 *
14573 * The locks made by this method must be undone by calling #unlockMedia() when
14574 * no more needed.
14575 */
14576HRESULT SessionMachine::i_lockMedia()
14577{
14578 AutoCaller autoCaller(this);
14579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14580
14581 AutoMultiWriteLock2 alock(this->lockHandle(),
14582 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14583
14584 /* bail out if trying to lock things with already set up locking */
14585 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14586
14587 MultiResult mrc(S_OK);
14588
14589 /* Collect locking information for all medium objects attached to the VM. */
14590 for (MediumAttachmentList::const_iterator
14591 it = mMediumAttachments->begin();
14592 it != mMediumAttachments->end();
14593 ++it)
14594 {
14595 MediumAttachment *pAtt = *it;
14596 DeviceType_T devType = pAtt->i_getType();
14597 Medium *pMedium = pAtt->i_getMedium();
14598
14599 MediumLockList *pMediumLockList(new MediumLockList());
14600 // There can be attachments without a medium (floppy/dvd), and thus
14601 // it's impossible to create a medium lock list. It still makes sense
14602 // to have the empty medium lock list in the map in case a medium is
14603 // attached later.
14604 if (pMedium != NULL)
14605 {
14606 MediumType_T mediumType = pMedium->i_getType();
14607 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14608 || mediumType == MediumType_Shareable;
14609 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14610
14611 alock.release();
14612 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14613 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14614 false /* fMediumLockWriteAll */,
14615 NULL,
14616 *pMediumLockList);
14617 alock.acquire();
14618 if (FAILED(mrc))
14619 {
14620 delete pMediumLockList;
14621 mData->mSession.mLockedMedia.Clear();
14622 break;
14623 }
14624 }
14625
14626 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14627 if (FAILED(rc))
14628 {
14629 mData->mSession.mLockedMedia.Clear();
14630 mrc = setError(rc,
14631 tr("Collecting locking information for all attached media failed"));
14632 break;
14633 }
14634 }
14635
14636 if (SUCCEEDED(mrc))
14637 {
14638 /* Now lock all media. If this fails, nothing is locked. */
14639 alock.release();
14640 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14641 alock.acquire();
14642 if (FAILED(rc))
14643 {
14644 mrc = setError(rc,
14645 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14646 }
14647 }
14648
14649 return mrc;
14650}
14651
14652/**
14653 * Undoes the locks made by by #lockMedia().
14654 */
14655HRESULT SessionMachine::i_unlockMedia()
14656{
14657 AutoCaller autoCaller(this);
14658 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14659
14660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14661
14662 /* we may be holding important error info on the current thread;
14663 * preserve it */
14664 ErrorInfoKeeper eik;
14665
14666 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14667 AssertComRC(rc);
14668 return rc;
14669}
14670
14671/**
14672 * Helper to change the machine state (reimplementation).
14673 *
14674 * @note Locks this object for writing.
14675 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14676 * it can cause crashes in random places due to unexpectedly committing
14677 * the current settings. The caller is responsible for that. The call
14678 * to saveStateSettings is fine, because this method does not commit.
14679 */
14680HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14681{
14682 LogFlowThisFuncEnter();
14683 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14684
14685 AutoCaller autoCaller(this);
14686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14687
14688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14689
14690 MachineState_T oldMachineState = mData->mMachineState;
14691
14692 AssertMsgReturn(oldMachineState != aMachineState,
14693 ("oldMachineState=%s, aMachineState=%s\n",
14694 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14695 E_FAIL);
14696
14697 HRESULT rc = S_OK;
14698
14699 int stsFlags = 0;
14700 bool deleteSavedState = false;
14701
14702 /* detect some state transitions */
14703
14704 if ( ( oldMachineState == MachineState_Saved
14705 && aMachineState == MachineState_Restoring)
14706 || ( ( oldMachineState == MachineState_PoweredOff
14707 || oldMachineState == MachineState_Teleported
14708 || oldMachineState == MachineState_Aborted
14709 )
14710 && ( aMachineState == MachineState_TeleportingIn
14711 || aMachineState == MachineState_Starting
14712 )
14713 )
14714 )
14715 {
14716 /* The EMT thread is about to start */
14717
14718 /* Nothing to do here for now... */
14719
14720 /// @todo NEWMEDIA don't let mDVDDrive and other children
14721 /// change anything when in the Starting/Restoring state
14722 }
14723 else if ( ( oldMachineState == MachineState_Running
14724 || oldMachineState == MachineState_Paused
14725 || oldMachineState == MachineState_Teleporting
14726 || oldMachineState == MachineState_OnlineSnapshotting
14727 || oldMachineState == MachineState_LiveSnapshotting
14728 || oldMachineState == MachineState_Stuck
14729 || oldMachineState == MachineState_Starting
14730 || oldMachineState == MachineState_Stopping
14731 || oldMachineState == MachineState_Saving
14732 || oldMachineState == MachineState_Restoring
14733 || oldMachineState == MachineState_TeleportingPausedVM
14734 || oldMachineState == MachineState_TeleportingIn
14735 )
14736 && ( aMachineState == MachineState_PoweredOff
14737 || aMachineState == MachineState_Saved
14738 || aMachineState == MachineState_Teleported
14739 || aMachineState == MachineState_Aborted
14740 )
14741 )
14742 {
14743 /* The EMT thread has just stopped, unlock attached media. Note that as
14744 * opposed to locking that is done from Console, we do unlocking here
14745 * because the VM process may have aborted before having a chance to
14746 * properly unlock all media it locked. */
14747
14748 unlockMedia();
14749 }
14750
14751 if (oldMachineState == MachineState_Restoring)
14752 {
14753 if (aMachineState != MachineState_Saved)
14754 {
14755 /*
14756 * delete the saved state file once the machine has finished
14757 * restoring from it (note that Console sets the state from
14758 * Restoring to Saved if the VM couldn't restore successfully,
14759 * to give the user an ability to fix an error and retry --
14760 * we keep the saved state file in this case)
14761 */
14762 deleteSavedState = true;
14763 }
14764 }
14765 else if ( oldMachineState == MachineState_Saved
14766 && ( aMachineState == MachineState_PoweredOff
14767 || aMachineState == MachineState_Aborted
14768 || aMachineState == MachineState_Teleported
14769 )
14770 )
14771 {
14772 /*
14773 * delete the saved state after SessionMachine::ForgetSavedState() is called
14774 * or if the VM process (owning a direct VM session) crashed while the
14775 * VM was Saved
14776 */
14777
14778 /// @todo (dmik)
14779 // Not sure that deleting the saved state file just because of the
14780 // client death before it attempted to restore the VM is a good
14781 // thing. But when it crashes we need to go to the Aborted state
14782 // which cannot have the saved state file associated... The only
14783 // way to fix this is to make the Aborted condition not a VM state
14784 // but a bool flag: i.e., when a crash occurs, set it to true and
14785 // change the state to PoweredOff or Saved depending on the
14786 // saved state presence.
14787
14788 deleteSavedState = true;
14789 mData->mCurrentStateModified = TRUE;
14790 stsFlags |= SaveSTS_CurStateModified;
14791 }
14792
14793 if ( aMachineState == MachineState_Starting
14794 || aMachineState == MachineState_Restoring
14795 || aMachineState == MachineState_TeleportingIn
14796 )
14797 {
14798 /* set the current state modified flag to indicate that the current
14799 * state is no more identical to the state in the
14800 * current snapshot */
14801 if (!mData->mCurrentSnapshot.isNull())
14802 {
14803 mData->mCurrentStateModified = TRUE;
14804 stsFlags |= SaveSTS_CurStateModified;
14805 }
14806 }
14807
14808 if (deleteSavedState)
14809 {
14810 if (mRemoveSavedState)
14811 {
14812 Assert(!mSSData->strStateFilePath.isEmpty());
14813
14814 // it is safe to delete the saved state file if ...
14815 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14816 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14817 // ... none of the snapshots share the saved state file
14818 )
14819 RTFileDelete(mSSData->strStateFilePath.c_str());
14820 }
14821
14822 mSSData->strStateFilePath.setNull();
14823 stsFlags |= SaveSTS_StateFilePath;
14824 }
14825
14826 /* redirect to the underlying peer machine */
14827 mPeer->i_setMachineState(aMachineState);
14828
14829 if ( oldMachineState != MachineState_RestoringSnapshot
14830 && ( aMachineState == MachineState_PoweredOff
14831 || aMachineState == MachineState_Teleported
14832 || aMachineState == MachineState_Aborted
14833 || aMachineState == MachineState_Saved))
14834 {
14835 /* the machine has stopped execution
14836 * (or the saved state file was adopted) */
14837 stsFlags |= SaveSTS_StateTimeStamp;
14838 }
14839
14840 if ( ( oldMachineState == MachineState_PoweredOff
14841 || oldMachineState == MachineState_Aborted
14842 || oldMachineState == MachineState_Teleported
14843 )
14844 && aMachineState == MachineState_Saved)
14845 {
14846 /* the saved state file was adopted */
14847 Assert(!mSSData->strStateFilePath.isEmpty());
14848 stsFlags |= SaveSTS_StateFilePath;
14849 }
14850
14851#ifdef VBOX_WITH_GUEST_PROPS
14852 if ( aMachineState == MachineState_PoweredOff
14853 || aMachineState == MachineState_Aborted
14854 || aMachineState == MachineState_Teleported)
14855 {
14856 /* Make sure any transient guest properties get removed from the
14857 * property store on shutdown. */
14858 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14859
14860 /* remove it from the settings representation */
14861 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14862 for (settings::GuestPropertiesList::iterator
14863 it = llGuestProperties.begin();
14864 it != llGuestProperties.end();
14865 /*nothing*/)
14866 {
14867 const settings::GuestProperty &prop = *it;
14868 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14869 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14870 {
14871 it = llGuestProperties.erase(it);
14872 fNeedsSaving = true;
14873 }
14874 else
14875 {
14876 ++it;
14877 }
14878 }
14879
14880 /* Additionally remove it from the HWData representation. Required to
14881 * keep everything in sync, as this is what the API keeps using. */
14882 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14883 for (HWData::GuestPropertyMap::iterator
14884 it = llHWGuestProperties.begin();
14885 it != llHWGuestProperties.end();
14886 /*nothing*/)
14887 {
14888 uint32_t fFlags = it->second.mFlags;
14889 if ( fFlags & guestProp::TRANSIENT
14890 || fFlags & guestProp::TRANSRESET)
14891 {
14892 /* iterator where we need to continue after the erase call
14893 * (C++03 is a fact still, and it doesn't return the iterator
14894 * which would allow continuing) */
14895 HWData::GuestPropertyMap::iterator it2 = it;
14896 ++it2;
14897 llHWGuestProperties.erase(it);
14898 it = it2;
14899 fNeedsSaving = true;
14900 }
14901 else
14902 {
14903 ++it;
14904 }
14905 }
14906
14907 if (fNeedsSaving)
14908 {
14909 mData->mCurrentStateModified = TRUE;
14910 stsFlags |= SaveSTS_CurStateModified;
14911 }
14912 }
14913#endif /* VBOX_WITH_GUEST_PROPS */
14914
14915 rc = i_saveStateSettings(stsFlags);
14916
14917 if ( ( oldMachineState != MachineState_PoweredOff
14918 && oldMachineState != MachineState_Aborted
14919 && oldMachineState != MachineState_Teleported
14920 )
14921 && ( aMachineState == MachineState_PoweredOff
14922 || aMachineState == MachineState_Aborted
14923 || aMachineState == MachineState_Teleported
14924 )
14925 )
14926 {
14927 /* we've been shut down for any reason */
14928 /* no special action so far */
14929 }
14930
14931 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14932 LogFlowThisFuncLeave();
14933 return rc;
14934}
14935
14936/**
14937 * Sends the current machine state value to the VM process.
14938 *
14939 * @note Locks this object for reading, then calls a client process.
14940 */
14941HRESULT SessionMachine::i_updateMachineStateOnClient()
14942{
14943 AutoCaller autoCaller(this);
14944 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14945
14946 ComPtr<IInternalSessionControl> directControl;
14947 {
14948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14949 AssertReturn(!!mData, E_FAIL);
14950 if (mData->mSession.mLockType == LockType_VM)
14951 directControl = mData->mSession.mDirectControl;
14952
14953 /* directControl may be already set to NULL here in #OnSessionEnd()
14954 * called too early by the direct session process while there is still
14955 * some operation (like deleting the snapshot) in progress. The client
14956 * process in this case is waiting inside Session::close() for the
14957 * "end session" process object to complete, while #uninit() called by
14958 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14959 * operation to complete. For now, we accept this inconsistent behavior
14960 * and simply do nothing here. */
14961
14962 if (mData->mSession.mState == SessionState_Unlocking)
14963 return S_OK;
14964 }
14965
14966 /* ignore notifications sent after #OnSessionEnd() is called */
14967 if (!directControl)
14968 return S_OK;
14969
14970 return directControl->UpdateMachineState(mData->mMachineState);
14971}
14972
14973
14974/*static*/
14975HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14976{
14977 va_list args;
14978 va_start(args, pcszMsg);
14979 HRESULT rc = setErrorInternal(aResultCode,
14980 getStaticClassIID(),
14981 getStaticComponentName(),
14982 Utf8Str(pcszMsg, args),
14983 false /* aWarning */,
14984 true /* aLogIt */);
14985 va_end(args);
14986 return rc;
14987}
14988
14989
14990HRESULT Machine::updateState(MachineState_T aState)
14991{
14992 NOREF(aState);
14993 ReturnComNotImplemented();
14994}
14995
14996HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14997{
14998 NOREF(aProgress);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::endPowerUp(LONG aResult)
15003{
15004 NOREF(aResult);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15009{
15010 NOREF(aProgress);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::endPoweringDown(LONG aResult,
15015 const com::Utf8Str &aErrMsg)
15016{
15017 NOREF(aResult);
15018 NOREF(aErrMsg);
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15023 BOOL *aMatched,
15024 ULONG *aMaskedInterfaces)
15025{
15026 NOREF(aDevice);
15027 NOREF(aMatched);
15028 NOREF(aMaskedInterfaces);
15029 ReturnComNotImplemented();
15030
15031}
15032
15033HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15034{
15035 NOREF(aId); NOREF(aCaptureFilename);
15036 ReturnComNotImplemented();
15037}
15038
15039HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15040 BOOL aDone)
15041{
15042 NOREF(aId);
15043 NOREF(aDone);
15044 ReturnComNotImplemented();
15045}
15046
15047HRESULT Machine::autoCaptureUSBDevices()
15048{
15049 ReturnComNotImplemented();
15050}
15051
15052HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15053{
15054 NOREF(aDone);
15055 ReturnComNotImplemented();
15056}
15057
15058HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15059 ComPtr<IProgress> &aProgress)
15060{
15061 NOREF(aSession);
15062 NOREF(aProgress);
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::finishOnlineMergeMedium()
15067{
15068 ReturnComNotImplemented();
15069}
15070
15071HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15072 std::vector<com::Utf8Str> &aValues,
15073 std::vector<LONG64> &aTimestamps,
15074 std::vector<com::Utf8Str> &aFlags)
15075{
15076 NOREF(aNames);
15077 NOREF(aValues);
15078 NOREF(aTimestamps);
15079 NOREF(aFlags);
15080 ReturnComNotImplemented();
15081}
15082
15083HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15084 const com::Utf8Str &aValue,
15085 LONG64 aTimestamp,
15086 const com::Utf8Str &aFlags)
15087{
15088 NOREF(aName);
15089 NOREF(aValue);
15090 NOREF(aTimestamp);
15091 NOREF(aFlags);
15092 ReturnComNotImplemented();
15093}
15094
15095HRESULT Machine::lockMedia()
15096{
15097 ReturnComNotImplemented();
15098}
15099
15100HRESULT Machine::unlockMedia()
15101{
15102 ReturnComNotImplemented();
15103}
15104
15105HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15106 ComPtr<IMediumAttachment> &aNewAttachment)
15107{
15108 NOREF(aAttachment);
15109 NOREF(aNewAttachment);
15110 ReturnComNotImplemented();
15111}
15112
15113HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15114 ULONG aCpuUser,
15115 ULONG aCpuKernel,
15116 ULONG aCpuIdle,
15117 ULONG aMemTotal,
15118 ULONG aMemFree,
15119 ULONG aMemBalloon,
15120 ULONG aMemShared,
15121 ULONG aMemCache,
15122 ULONG aPagedTotal,
15123 ULONG aMemAllocTotal,
15124 ULONG aMemFreeTotal,
15125 ULONG aMemBalloonTotal,
15126 ULONG aMemSharedTotal,
15127 ULONG aVmNetRx,
15128 ULONG aVmNetTx)
15129{
15130 NOREF(aValidStats);
15131 NOREF(aCpuUser);
15132 NOREF(aCpuKernel);
15133 NOREF(aCpuIdle);
15134 NOREF(aMemTotal);
15135 NOREF(aMemFree);
15136 NOREF(aMemBalloon);
15137 NOREF(aMemShared);
15138 NOREF(aMemCache);
15139 NOREF(aPagedTotal);
15140 NOREF(aMemAllocTotal);
15141 NOREF(aMemFreeTotal);
15142 NOREF(aMemBalloonTotal);
15143 NOREF(aMemSharedTotal);
15144 NOREF(aVmNetRx);
15145 NOREF(aVmNetTx);
15146 ReturnComNotImplemented();
15147}
15148
15149HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15150 com::Utf8Str &aResult)
15151{
15152 NOREF(aAuthParams);
15153 NOREF(aResult);
15154 ReturnComNotImplemented();
15155}
15156
15157HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15158{
15159 NOREF(aFlags);
15160 ReturnComNotImplemented();
15161}
15162
15163/* This isn't handled entirely by the wrapper generator yet. */
15164#ifdef VBOX_WITH_XPCOM
15165NS_DECL_CLASSINFO(SessionMachine)
15166NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15167
15168NS_DECL_CLASSINFO(SnapshotMachine)
15169NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15170#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