VirtualBox

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

Last change on this file since 61644 was 61588, checked in by vboxsync, 9 years ago

Main/Machine: some comment fixing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 515.4 KB
Line 
1/* $Id: MachineImpl.cpp 61588 2016-06-08 15:39:18Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine() :
250#ifdef VBOX_WITH_RESOURCE_USAGE_API
251 mCollectorGuest(NULL),
252#endif
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Apply BIOS defaults */
348 mBIOSSettings->i_applyDefaults(aOsType);
349
350 /* Apply network adapters defaults */
351 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
352 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
353
354 /* Apply serial port defaults */
355 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
356 mSerialPorts[slot]->i_applyDefaults(aOsType);
357
358 /* Let the OS type select 64-bit ness. */
359 mHWData->mLongMode = aOsType->i_is64Bit()
360 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361 }
362
363 /* Apply parallel port defaults */
364 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
365 mParallelPorts[slot]->i_applyDefaults();
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 i_allowStateModification();
370
371 /* commit all changes made during the initialization */
372 i_commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = i_registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 i_allowStateModification();
472
473 i_commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->i_unregisterMachineMedia(i_getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->i_getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = i_loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 i_allowStateModification();
571
572 /* commit all changes made during the initialization */
573 i_commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 /* Ignore all errors from unregistering, they would destroy
585- * the more interesting error information we already have,
586- * pinpointing the issue with the VM config. */
587 ErrorInfoKeeper eik;
588
589 autoInitSpan.setLimited();
590
591 // uninit media from this machine's media registry, or else
592 // reloading the settings will fail
593 mParent->i_unregisterMachineMedia(i_getId());
594 }
595 }
596
597 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
598 "rc=%08X\n",
599 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
600 mData->mRegistered, mData->mAccessible, rc));
601
602 LogFlowThisFuncLeave();
603
604 return rc;
605}
606
607/**
608 * Shared code between the various init() implementations.
609 * @param aParent
610 * @return
611 */
612HRESULT Machine::initImpl(VirtualBox *aParent,
613 const Utf8Str &strConfigFile)
614{
615 LogFlowThisFuncEnter();
616
617 AssertReturn(aParent, E_INVALIDARG);
618 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
619
620 HRESULT rc = S_OK;
621
622 /* share the parent weakly */
623 unconst(mParent) = aParent;
624
625 /* allocate the essential machine data structure (the rest will be
626 * allocated later by initDataAndChildObjects() */
627 mData.allocate();
628
629 /* memorize the config file name (as provided) */
630 mData->m_strConfigFile = strConfigFile;
631
632 /* get the full file name */
633 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
634 if (RT_FAILURE(vrc1))
635 return setError(VBOX_E_FILE_ERROR,
636 tr("Invalid machine settings file name '%s' (%Rrc)"),
637 strConfigFile.c_str(),
638 vrc1);
639
640 LogFlowThisFuncLeave();
641
642 return rc;
643}
644
645/**
646 * Tries to create a machine settings file in the path stored in the machine
647 * instance data. Used when a new machine is created to fail gracefully if
648 * the settings file could not be written (e.g. because machine dir is read-only).
649 * @return
650 */
651HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
652{
653 HRESULT rc = S_OK;
654
655 // when we create a new machine, we must be able to create the settings file
656 RTFILE f = NIL_RTFILE;
657 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
658 if ( RT_SUCCESS(vrc)
659 || vrc == VERR_SHARING_VIOLATION
660 )
661 {
662 if (RT_SUCCESS(vrc))
663 RTFileClose(f);
664 if (!fForceOverwrite)
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Machine settings file '%s' already exists"),
667 mData->m_strConfigFileFull.c_str());
668 else
669 {
670 /* try to delete the config file, as otherwise the creation
671 * of a new settings file will fail. */
672 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
673 if (RT_FAILURE(vrc2))
674 rc = setError(VBOX_E_FILE_ERROR,
675 tr("Could not delete the existing settings file '%s' (%Rrc)"),
676 mData->m_strConfigFileFull.c_str(), vrc2);
677 }
678 }
679 else if ( vrc != VERR_FILE_NOT_FOUND
680 && vrc != VERR_PATH_NOT_FOUND
681 )
682 rc = setError(VBOX_E_FILE_ERROR,
683 tr("Invalid machine settings file name '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(),
685 vrc);
686 return rc;
687}
688
689/**
690 * Initializes the registered machine by loading the settings file.
691 * This method is separated from #init() in order to make it possible to
692 * retry the operation after VirtualBox startup instead of refusing to
693 * startup the whole VirtualBox server in case if the settings file of some
694 * registered VM is invalid or inaccessible.
695 *
696 * @note Must be always called from this object's write lock
697 * (unless called from #init() that doesn't need any locking).
698 * @note Locks the mUSBController method for writing.
699 * @note Subclasses must not call this method.
700 */
701HRESULT Machine::i_registeredInit()
702{
703 AssertReturn(!i_isSessionMachine(), E_FAIL);
704 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
705 AssertReturn(mData->mUuid.isValid(), E_FAIL);
706 AssertReturn(!mData->mAccessible, E_FAIL);
707
708 HRESULT rc = initDataAndChildObjects();
709
710 if (SUCCEEDED(rc))
711 {
712 /* Temporarily reset the registered flag in order to let setters
713 * potentially called from loadSettings() succeed (isMutable() used in
714 * all setters will return FALSE for a Machine instance if mRegistered
715 * is TRUE). */
716 mData->mRegistered = FALSE;
717
718 try
719 {
720 // load and parse machine XML; this will throw on XML or logic errors
721 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
722
723 if (mData->mUuid != mData->pMachineConfigFile->uuid)
724 throw setError(E_FAIL,
725 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
726 mData->pMachineConfigFile->uuid.raw(),
727 mData->m_strConfigFileFull.c_str(),
728 mData->mUuid.toString().c_str(),
729 mParent->i_settingsFilePath().c_str());
730
731 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
732 NULL /* const Guid *puuidRegistry */);
733 if (FAILED(rc)) throw rc;
734 }
735 catch (HRESULT err)
736 {
737 /* we assume that error info is set by the thrower */
738 rc = err;
739 }
740 catch (...)
741 {
742 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
743 }
744
745 /* Restore the registered flag (even on failure) */
746 mData->mRegistered = TRUE;
747 }
748
749 if (SUCCEEDED(rc))
750 {
751 /* Set mAccessible to TRUE only if we successfully locked and loaded
752 * the settings file */
753 mData->mAccessible = TRUE;
754
755 /* commit all changes made during loading the settings file */
756 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
757 /// @todo r=klaus for some reason the settings loading logic backs up
758 // the settings, and therefore a commit is needed. Should probably be changed.
759 }
760 else
761 {
762 /* If the machine is registered, then, instead of returning a
763 * failure, we mark it as inaccessible and set the result to
764 * success to give it a try later */
765
766 /* fetch the current error info */
767 mData->mAccessError = com::ErrorInfo();
768 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
769
770 /* rollback all changes */
771 i_rollback(false /* aNotify */);
772
773 // uninit media from this machine's media registry, or else
774 // reloading the settings will fail
775 mParent->i_unregisterMachineMedia(i_getId());
776
777 /* uninitialize the common part to make sure all data is reset to
778 * default (null) values */
779 uninitDataAndChildObjects();
780
781 rc = S_OK;
782 }
783
784 return rc;
785}
786
787/**
788 * Uninitializes the instance.
789 * Called either from FinalRelease() or by the parent when it gets destroyed.
790 *
791 * @note The caller of this method must make sure that this object
792 * a) doesn't have active callers on the current thread and b) is not locked
793 * by the current thread; otherwise uninit() will hang either a) due to
794 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
795 * a dead-lock caused by this thread waiting for all callers on the other
796 * threads are done but preventing them from doing so by holding a lock.
797 */
798void Machine::uninit()
799{
800 LogFlowThisFuncEnter();
801
802 Assert(!isWriteLockOnCurrentThread());
803
804 Assert(!uRegistryNeedsSaving);
805 if (uRegistryNeedsSaving)
806 {
807 AutoCaller autoCaller(this);
808 if (SUCCEEDED(autoCaller.rc()))
809 {
810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
811 i_saveSettings(NULL, Machine::SaveS_Force);
812 }
813 }
814
815 /* Enclose the state transition Ready->InUninit->NotReady */
816 AutoUninitSpan autoUninitSpan(this);
817 if (autoUninitSpan.uninitDone())
818 return;
819
820 Assert(!i_isSnapshotMachine());
821 Assert(!i_isSessionMachine());
822 Assert(!!mData);
823
824 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
825 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
826
827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
828
829 if (!mData->mSession.mMachine.isNull())
830 {
831 /* Theoretically, this can only happen if the VirtualBox server has been
832 * terminated while there were clients running that owned open direct
833 * sessions. Since in this case we are definitely called by
834 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
835 * won't happen on the client watcher thread (because it has a
836 * VirtualBox caller for the duration of the
837 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
838 * cannot happen until the VirtualBox caller is released). This is
839 * important, because SessionMachine::uninit() cannot correctly operate
840 * after we return from this method (it expects the Machine instance is
841 * still valid). We'll call it ourselves below.
842 */
843 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
844 (SessionMachine*)mData->mSession.mMachine));
845
846 if (Global::IsOnlineOrTransient(mData->mMachineState))
847 {
848 Log1WarningThisFunc(("Setting state to Aborted!\n"));
849 /* set machine state using SessionMachine reimplementation */
850 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
851 }
852
853 /*
854 * Uninitialize SessionMachine using public uninit() to indicate
855 * an unexpected uninitialization.
856 */
857 mData->mSession.mMachine->uninit();
858 /* SessionMachine::uninit() must set mSession.mMachine to null */
859 Assert(mData->mSession.mMachine.isNull());
860 }
861
862 // uninit media from this machine's media registry, if they're still there
863 Guid uuidMachine(i_getId());
864
865 /* the lock is no more necessary (SessionMachine is uninitialized) */
866 alock.release();
867
868 /* XXX This will fail with
869 * "cannot be closed because it is still attached to 1 virtual machines"
870 * because at this point we did not call uninitDataAndChildObjects() yet
871 * and therefore also removeBackReference() for all these mediums was not called! */
872
873 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
874 mParent->i_unregisterMachineMedia(uuidMachine);
875
876 // has machine been modified?
877 if (mData->flModifications)
878 {
879 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
880 i_rollback(false /* aNotify */);
881 }
882
883 if (mData->mAccessible)
884 uninitDataAndChildObjects();
885
886 /* free the essential data structure last */
887 mData.free();
888
889 LogFlowThisFuncLeave();
890}
891
892// Wrapped IMachine properties
893/////////////////////////////////////////////////////////////////////////////
894HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
895{
896 /* mParent is constant during life time, no need to lock */
897 ComObjPtr<VirtualBox> pVirtualBox(mParent);
898 aParent = pVirtualBox;
899
900 return S_OK;
901}
902
903
904HRESULT Machine::getAccessible(BOOL *aAccessible)
905{
906 /* In some cases (medium registry related), it is necessary to be able to
907 * go through the list of all machines. Happens when an inaccessible VM
908 * has a sensible medium registry. */
909 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->i_dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = i_registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->i_onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
956{
957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
958
959 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
960 {
961 /* return shortly */
962 aAccessError = NULL;
963 return S_OK;
964 }
965
966 HRESULT rc = S_OK;
967
968 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
969 rc = errorInfo.createObject();
970 if (SUCCEEDED(rc))
971 {
972 errorInfo->init(mData->mAccessError.getResultCode(),
973 mData->mAccessError.getInterfaceID().ref(),
974 Utf8Str(mData->mAccessError.getComponent()).c_str(),
975 Utf8Str(mData->mAccessError.getText()));
976 aAccessError = errorInfo;
977 }
978
979 return rc;
980}
981
982HRESULT Machine::getName(com::Utf8Str &aName)
983{
984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
985
986 aName = mUserData->s.strName;
987
988 return S_OK;
989}
990
991HRESULT Machine::setName(const com::Utf8Str &aName)
992{
993 // prohibit setting a UUID only as the machine name, or else it can
994 // never be found by findMachine()
995 Guid test(aName);
996
997 if (test.isValid())
998 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
999
1000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 HRESULT rc = i_checkStateDependency(MutableStateDep);
1003 if (FAILED(rc)) return rc;
1004
1005 i_setModified(IsModified_MachineData);
1006 mUserData.backup();
1007 mUserData->s.strName = aName;
1008
1009 return S_OK;
1010}
1011
1012HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1013{
1014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 aDescription = mUserData->s.strDescription;
1017
1018 return S_OK;
1019}
1020
1021HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1022{
1023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1024
1025 // this can be done in principle in any state as it doesn't affect the VM
1026 // significantly, but play safe by not messing around while complex
1027 // activities are going on
1028 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1029 if (FAILED(rc)) return rc;
1030
1031 i_setModified(IsModified_MachineData);
1032 mUserData.backup();
1033 mUserData->s.strDescription = aDescription;
1034
1035 return S_OK;
1036}
1037
1038HRESULT Machine::getId(com::Guid &aId)
1039{
1040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1041
1042 aId = mData->mUuid;
1043
1044 return S_OK;
1045}
1046
1047HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1048{
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050 aGroups.resize(mUserData->s.llGroups.size());
1051 size_t i = 0;
1052 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1053 it != mUserData->s.llGroups.end(); ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComPtr<IGuestOSType> guestOSType;
1091 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 /* when setting, always use the "etalon" value for consistency -- lookup
1095 * by ID is case-insensitive and the input value may have different case */
1096 Bstr osTypeId;
1097 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1098 if (FAILED(rc)) return rc;
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 aParavirtDebug = mHWData->mParavirtDebug;
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 /** @todo Parse/validate options? */
1238 if (aParavirtDebug != mHWData->mParavirtDebug)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtDebug = aParavirtDebug;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1258{
1259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 HRESULT rc = i_checkStateDependency(MutableStateDep);
1262 if (FAILED(rc)) return rc;
1263
1264 if (aParavirtProvider != mHWData->mParavirtProvider)
1265 {
1266 i_setModified(IsModified_MachineData);
1267 mHWData.backup();
1268 mHWData->mParavirtProvider = aParavirtProvider;
1269 }
1270
1271 return S_OK;
1272}
1273
1274HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1275{
1276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1277
1278 *aParavirtProvider = mHWData->mParavirtProvider;
1279 switch (mHWData->mParavirtProvider)
1280 {
1281 case ParavirtProvider_None:
1282 case ParavirtProvider_HyperV:
1283 case ParavirtProvider_KVM:
1284 case ParavirtProvider_Minimal:
1285 break;
1286
1287 /* Resolve dynamic provider types to the effective types. */
1288 default:
1289 {
1290 ComPtr<IGuestOSType> ptrGuestOSType;
1291 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1292 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1293
1294 Bstr guestTypeFamilyId;
1295 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1296 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1297 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1298
1299 switch (mHWData->mParavirtProvider)
1300 {
1301 case ParavirtProvider_Legacy:
1302 {
1303 if (fOsXGuest)
1304 *aParavirtProvider = ParavirtProvider_Minimal;
1305 else
1306 *aParavirtProvider = ParavirtProvider_None;
1307 break;
1308 }
1309
1310 case ParavirtProvider_Default:
1311 {
1312 if (fOsXGuest)
1313 *aParavirtProvider = ParavirtProvider_Minimal;
1314 else if ( mUserData->s.strOsType == "Windows10"
1315 || mUserData->s.strOsType == "Windows10_64"
1316 || mUserData->s.strOsType == "Windows81"
1317 || mUserData->s.strOsType == "Windows81_64"
1318 || mUserData->s.strOsType == "Windows8"
1319 || mUserData->s.strOsType == "Windows8_64"
1320 || mUserData->s.strOsType == "Windows7"
1321 || mUserData->s.strOsType == "Windows7_64"
1322 || mUserData->s.strOsType == "WindowsVista"
1323 || mUserData->s.strOsType == "WindowsVista_64"
1324 || mUserData->s.strOsType == "Windows2012"
1325 || mUserData->s.strOsType == "Windows2012_64"
1326 || mUserData->s.strOsType == "Windows2008"
1327 || mUserData->s.strOsType == "Windows2008_64")
1328 {
1329 *aParavirtProvider = ParavirtProvider_HyperV;
1330 }
1331 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1332 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1333 || mUserData->s.strOsType == "Linux"
1334 || mUserData->s.strOsType == "Linux_64"
1335 || mUserData->s.strOsType == "ArchLinux"
1336 || mUserData->s.strOsType == "ArchLinux_64"
1337 || mUserData->s.strOsType == "Debian"
1338 || mUserData->s.strOsType == "Debian_64"
1339 || mUserData->s.strOsType == "Fedora"
1340 || mUserData->s.strOsType == "Fedora_64"
1341 || mUserData->s.strOsType == "Gentoo"
1342 || mUserData->s.strOsType == "Gentoo_64"
1343 || mUserData->s.strOsType == "Mandriva"
1344 || mUserData->s.strOsType == "Mandriva_64"
1345 || mUserData->s.strOsType == "OpenSUSE"
1346 || mUserData->s.strOsType == "OpenSUSE_64"
1347 || mUserData->s.strOsType == "Oracle"
1348 || mUserData->s.strOsType == "Oracle_64"
1349 || mUserData->s.strOsType == "RedHat"
1350 || mUserData->s.strOsType == "RedHat_64"
1351 || mUserData->s.strOsType == "Turbolinux"
1352 || mUserData->s.strOsType == "Turbolinux_64"
1353 || mUserData->s.strOsType == "Ubuntu"
1354 || mUserData->s.strOsType == "Ubuntu_64"
1355 || mUserData->s.strOsType == "Xandros"
1356 || mUserData->s.strOsType == "Xandros_64")
1357 {
1358 *aParavirtProvider = ParavirtProvider_KVM;
1359 }
1360 else
1361 *aParavirtProvider = ParavirtProvider_None;
1362 break;
1363 }
1364 }
1365 break;
1366 }
1367 }
1368
1369 Assert( *aParavirtProvider == ParavirtProvider_None
1370 || *aParavirtProvider == ParavirtProvider_Minimal
1371 || *aParavirtProvider == ParavirtProvider_HyperV
1372 || *aParavirtProvider == ParavirtProvider_KVM);
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aHardwareVersion = mHWData->mHWVersion;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1386{
1387 /* check known version */
1388 Utf8Str hwVersion = aHardwareVersion;
1389 if ( hwVersion.compare("1") != 0
1390 && hwVersion.compare("2") != 0)
1391 return setError(E_INVALIDARG,
1392 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mHWVersion = aHardwareVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 if (!mHWData->mHardwareUUID.isZero())
1411 aHardwareUUID = mHWData->mHardwareUUID;
1412 else
1413 aHardwareUUID = mData->mUuid;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1419{
1420 if (!aHardwareUUID.isValid())
1421 return E_INVALIDARG;
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 if (aHardwareUUID == mData->mUuid)
1431 mHWData->mHardwareUUID.clear();
1432 else
1433 mHWData->mHardwareUUID = aHardwareUUID;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aMemorySize = mHWData->mMemorySize;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setMemorySize(ULONG aMemorySize)
1448{
1449 /* check RAM limits */
1450 if ( aMemorySize < MM_RAM_MIN_IN_MB
1451 || aMemorySize > MM_RAM_MAX_IN_MB
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1455 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 HRESULT rc = i_checkStateDependency(MutableStateDep);
1460 if (FAILED(rc)) return rc;
1461
1462 i_setModified(IsModified_MachineData);
1463 mHWData.backup();
1464 mHWData->mMemorySize = aMemorySize;
1465
1466 return S_OK;
1467}
1468
1469HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 *aCPUCount = mHWData->mCPUCount;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setCPUCount(ULONG aCPUCount)
1479{
1480 /* check CPU limits */
1481 if ( aCPUCount < SchemaDefs::MinCPUCount
1482 || aCPUCount > SchemaDefs::MaxCPUCount
1483 )
1484 return setError(E_INVALIDARG,
1485 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1486 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1491 if (mHWData->mCPUHotPlugEnabled)
1492 {
1493 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1494 {
1495 if (mHWData->mCPUAttached[idx])
1496 return setError(E_INVALIDARG,
1497 tr("There is still a CPU attached to socket %lu."
1498 "Detach the CPU before removing the socket"),
1499 aCPUCount, idx+1);
1500 }
1501 }
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mCPUCount = aCPUCount;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1523{
1524 HRESULT rc = S_OK;
1525
1526 /* check throttle limits */
1527 if ( aCPUExecutionCap < 1
1528 || aCPUExecutionCap > 100
1529 )
1530 return setError(E_INVALIDARG,
1531 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1532 aCPUExecutionCap, 1, 100);
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 alock.release();
1537 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1538 alock.acquire();
1539 if (FAILED(rc)) return rc;
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1544
1545 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1546 if (Global::IsOnline(mData->mMachineState))
1547 i_saveSettings(NULL);
1548
1549 return S_OK;
1550}
1551
1552HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1553{
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1562{
1563 HRESULT rc = S_OK;
1564
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1571 {
1572 if (aCPUHotPlugEnabled)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576
1577 /* Add the amount of CPUs currently attached */
1578 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1579 mHWData->mCPUAttached[i] = true;
1580 }
1581 else
1582 {
1583 /*
1584 * We can disable hotplug only if the amount of maximum CPUs is equal
1585 * to the amount of attached CPUs
1586 */
1587 unsigned cCpusAttached = 0;
1588 unsigned iHighestId = 0;
1589
1590 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1591 {
1592 if (mHWData->mCPUAttached[i])
1593 {
1594 cCpusAttached++;
1595 iHighestId = i;
1596 }
1597 }
1598
1599 if ( (cCpusAttached != mHWData->mCPUCount)
1600 || (iHighestId >= mHWData->mCPUCount))
1601 return setError(E_INVALIDARG,
1602 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606 }
1607 }
1608
1609 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1610
1611 return rc;
1612}
1613
1614HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1615{
1616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1619
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1624{
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1628 if (SUCCEEDED(hrc))
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1633 }
1634 return hrc;
1635}
1636
1637HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1638{
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640 aCPUProfile = mHWData->mCpuProfile;
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 /* Empty equals 'host'. */
1653 if (aCPUProfile.isNotEmpty())
1654 mHWData->mCpuProfile = aCPUProfile;
1655 else
1656 mHWData->mCpuProfile = "host";
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1662{
1663#ifdef VBOX_WITH_USB_CARDREADER
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEmulatedUSBCardReaderEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1681 if (FAILED(rc)) return rc;
1682
1683 i_setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1686
1687 return S_OK;
1688#else
1689 NOREF(aEmulatedUSBCardReaderEnabled);
1690 return E_NOTIMPL;
1691#endif
1692}
1693
1694HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aHPETEnabled = mHWData->mHPETEnabled;
1699
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1704{
1705 HRESULT rc = S_OK;
1706
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 rc = i_checkStateDependency(MutableStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714
1715 mHWData->mHPETEnabled = aHPETEnabled;
1716
1717 return rc;
1718}
1719
1720HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1729{
1730 HRESULT rc = S_OK;
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1737
1738 alock.release();
1739 rc = i_onVideoCaptureChange();
1740 alock.acquire();
1741 if (FAILED(rc))
1742 {
1743 /*
1744 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1745 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1746 * determine if it should start or stop capturing. Therefore we need to manually
1747 * undo change.
1748 */
1749 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1750 return rc;
1751 }
1752
1753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1754 if (Global::IsOnline(mData->mMachineState))
1755 i_saveSettings(NULL);
1756
1757 return rc;
1758}
1759
1760HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1764 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1765 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = i_onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 i_setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 i_saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1805 else
1806 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1811{
1812 Utf8Str strFile(aVideoCaptureFile);
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 i_getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1863 return S_OK;
1864}
1865
1866HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1867{
1868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 if ( Global::IsOnline(mData->mMachineState)
1871 && mHWData->mVideoCaptureEnabled)
1872 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1885 return S_OK;
1886}
1887
1888HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1889{
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 i_setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1907 return S_OK;
1908}
1909
1910HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1911{
1912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 if ( Global::IsOnline(mData->mMachineState)
1915 && mHWData->mVideoCaptureEnabled)
1916 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1917
1918 i_setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1921
1922 return S_OK;
1923}
1924
1925HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1933{
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 if ( Global::IsOnline(mData->mMachineState)
1937 && mHWData->mVideoCaptureEnabled)
1938 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 if ( Global::IsOnline(mData->mMachineState)
1959 && mHWData->mVideoCaptureEnabled)
1960 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1974 return S_OK;
1975}
1976
1977HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1978{
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 if ( Global::IsOnline(mData->mMachineState)
1982 && mHWData->mVideoCaptureEnabled)
1983 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1984
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2002{
2003 switch (aGraphicsControllerType)
2004 {
2005 case GraphicsControllerType_Null:
2006 case GraphicsControllerType_VBoxVGA:
2007#ifdef VBOX_WITH_VMSVGA
2008 case GraphicsControllerType_VMSVGA:
2009#endif
2010 break;
2011 default:
2012 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2013 }
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = i_checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aVRAMSize = mHWData->mVRAMSize;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2037{
2038 /* check VRAM limits */
2039 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2040 return setError(E_INVALIDARG,
2041 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = i_checkStateDependency(MutableStateDep);
2047 if (FAILED(rc)) return rc;
2048
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mVRAMSize = aVRAMSize;
2052
2053 return S_OK;
2054}
2055
2056/** @todo this method should not be public */
2057HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2062
2063 return S_OK;
2064}
2065
2066/**
2067 * Set the memory balloon size.
2068 *
2069 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2070 * we have to make sure that we never call IGuest from here.
2071 */
2072HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2073{
2074 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2075#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2076 /* check limits */
2077 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2078 return setError(E_INVALIDARG,
2079 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2080 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2087
2088 return S_OK;
2089#else
2090 NOREF(aMemoryBalloonSize);
2091 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2096{
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2104{
2105#ifdef VBOX_WITH_PAGE_SHARING
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 i_setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aPageFusionEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2129{
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = i_checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 /** @todo check validity! */
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2140
2141 return S_OK;
2142}
2143
2144
2145HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 /** @todo check validity! */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2170{
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *aMonitorCount = mHWData->mMonitorCount;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2179{
2180 /* make sure monitor count is a sensible number */
2181 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2182 return setError(E_INVALIDARG,
2183 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2184 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = i_checkStateDependency(MutableStateDep);
2189 if (FAILED(rc)) return rc;
2190
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 mHWData->mMonitorCount = aMonitorCount;
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2199{
2200 /* mBIOSSettings is constant during life time, no need to lock */
2201 aBIOSSettings = mBIOSSettings;
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2207{
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 switch (aProperty)
2211 {
2212 case CPUPropertyType_PAE:
2213 *aValue = mHWData->mPAEEnabled;
2214 break;
2215
2216 case CPUPropertyType_LongMode:
2217 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2218 *aValue = TRUE;
2219 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2220 *aValue = FALSE;
2221#if HC_ARCH_BITS == 64
2222 else
2223 *aValue = TRUE;
2224#else
2225 else
2226 {
2227 *aValue = FALSE;
2228
2229 ComPtr<IGuestOSType> ptrGuestOSType;
2230 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2231 if (SUCCEEDED(hrc2))
2232 {
2233 BOOL fIs64Bit = FALSE;
2234 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2235 if (SUCCEEDED(hrc2) && fIs64Bit)
2236 {
2237 ComObjPtr<Host> ptrHost = mParent->i_host();
2238 alock.release();
2239
2240 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2241 if (FAILED(hrc2))
2242 *aValue = FALSE;
2243 }
2244 }
2245 }
2246#endif
2247 break;
2248
2249 case CPUPropertyType_TripleFaultReset:
2250 *aValue = mHWData->mTripleFaultReset;
2251 break;
2252
2253 case CPUPropertyType_APIC:
2254 *aValue = mHWData->mAPIC;
2255 break;
2256
2257 case CPUPropertyType_X2APIC:
2258 *aValue = mHWData->mX2APIC;
2259 break;
2260
2261 default:
2262 return E_INVALIDARG;
2263 }
2264 return S_OK;
2265}
2266
2267HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2268{
2269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2270
2271 HRESULT rc = i_checkStateDependency(MutableStateDep);
2272 if (FAILED(rc)) return rc;
2273
2274 switch (aProperty)
2275 {
2276 case CPUPropertyType_PAE:
2277 i_setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mPAEEnabled = !!aValue;
2280 break;
2281
2282 case CPUPropertyType_LongMode:
2283 i_setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2286 break;
2287
2288 case CPUPropertyType_TripleFaultReset:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mTripleFaultReset = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_APIC:
2295 if (mHWData->mX2APIC)
2296 aValue = TRUE;
2297 i_setModified(IsModified_MachineData);
2298 mHWData.backup();
2299 mHWData->mAPIC = !!aValue;
2300 break;
2301
2302 case CPUPropertyType_X2APIC:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mX2APIC = !!aValue;
2306 if (aValue)
2307 mHWData->mAPIC = !!aValue;
2308 break;
2309
2310 default:
2311 return E_INVALIDARG;
2312 }
2313 return S_OK;
2314}
2315
2316HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2317{
2318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 switch(aId)
2321 {
2322 case 0x0:
2323 case 0x1:
2324 case 0x2:
2325 case 0x3:
2326 case 0x4:
2327 case 0x5:
2328 case 0x6:
2329 case 0x7:
2330 case 0x8:
2331 case 0x9:
2332 case 0xA:
2333 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2334 return E_INVALIDARG;
2335
2336 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2337 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2338 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2339 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2340 break;
2341
2342 case 0x80000000:
2343 case 0x80000001:
2344 case 0x80000002:
2345 case 0x80000003:
2346 case 0x80000004:
2347 case 0x80000005:
2348 case 0x80000006:
2349 case 0x80000007:
2350 case 0x80000008:
2351 case 0x80000009:
2352 case 0x8000000A:
2353 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2354 return E_INVALIDARG;
2355
2356 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2357 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2358 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2359 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2360 break;
2361
2362 default:
2363 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2364 }
2365 return S_OK;
2366}
2367
2368
2369HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2370{
2371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2372
2373 HRESULT rc = i_checkStateDependency(MutableStateDep);
2374 if (FAILED(rc)) return rc;
2375
2376 switch(aId)
2377 {
2378 case 0x0:
2379 case 0x1:
2380 case 0x2:
2381 case 0x3:
2382 case 0x4:
2383 case 0x5:
2384 case 0x6:
2385 case 0x7:
2386 case 0x8:
2387 case 0x9:
2388 case 0xA:
2389 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2390 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2391 i_setModified(IsModified_MachineData);
2392 mHWData.backup();
2393 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2394 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2395 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2396 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2397 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2398 break;
2399
2400 case 0x80000000:
2401 case 0x80000001:
2402 case 0x80000002:
2403 case 0x80000003:
2404 case 0x80000004:
2405 case 0x80000005:
2406 case 0x80000006:
2407 case 0x80000007:
2408 case 0x80000008:
2409 case 0x80000009:
2410 case 0x8000000A:
2411 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2412 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2413 i_setModified(IsModified_MachineData);
2414 mHWData.backup();
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2416 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2417 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2418 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2419 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2420 break;
2421
2422 default:
2423 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2424 }
2425 return S_OK;
2426}
2427
2428HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2429{
2430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 HRESULT rc = i_checkStateDependency(MutableStateDep);
2433 if (FAILED(rc)) return rc;
2434
2435 switch(aId)
2436 {
2437 case 0x0:
2438 case 0x1:
2439 case 0x2:
2440 case 0x3:
2441 case 0x4:
2442 case 0x5:
2443 case 0x6:
2444 case 0x7:
2445 case 0x8:
2446 case 0x9:
2447 case 0xA:
2448 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2449 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2450 i_setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 /* Invalidate leaf. */
2453 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2454 break;
2455
2456 case 0x80000000:
2457 case 0x80000001:
2458 case 0x80000002:
2459 case 0x80000003:
2460 case 0x80000004:
2461 case 0x80000005:
2462 case 0x80000006:
2463 case 0x80000007:
2464 case 0x80000008:
2465 case 0x80000009:
2466 case 0x8000000A:
2467 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2468 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 /* Invalidate leaf. */
2472 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2473 break;
2474
2475 default:
2476 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2477 }
2478 return S_OK;
2479}
2480
2481HRESULT Machine::removeAllCPUIDLeaves()
2482{
2483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 HRESULT rc = i_checkStateDependency(MutableStateDep);
2486 if (FAILED(rc)) return rc;
2487
2488 i_setModified(IsModified_MachineData);
2489 mHWData.backup();
2490
2491 /* Invalidate all standard leafs. */
2492 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2493 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2494
2495 /* Invalidate all extended leafs. */
2496 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2497 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2498
2499 return S_OK;
2500}
2501HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 switch(aProperty)
2506 {
2507 case HWVirtExPropertyType_Enabled:
2508 *aValue = mHWData->mHWVirtExEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_VPID:
2512 *aValue = mHWData->mHWVirtExVPIDEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_NestedPaging:
2516 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_UnrestrictedExecution:
2520 *aValue = mHWData->mHWVirtExUXEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_LargePages:
2524 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2525#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2526 *aValue = FALSE;
2527#endif
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 *aValue = mHWData->mHWVirtExForceEnabled;
2532 break;
2533
2534 default:
2535 return E_INVALIDARG;
2536 }
2537 return S_OK;
2538}
2539
2540HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2541{
2542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 HRESULT rc = i_checkStateDependency(MutableStateDep);
2545 if (FAILED(rc)) return rc;
2546
2547 switch(aProperty)
2548 {
2549 case HWVirtExPropertyType_Enabled:
2550 i_setModified(IsModified_MachineData);
2551 mHWData.backup();
2552 mHWData->mHWVirtExEnabled = !!aValue;
2553 break;
2554
2555 case HWVirtExPropertyType_VPID:
2556 i_setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2559 break;
2560
2561 case HWVirtExPropertyType_NestedPaging:
2562 i_setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2565 break;
2566
2567 case HWVirtExPropertyType_UnrestrictedExecution:
2568 i_setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExUXEnabled = !!aValue;
2571 break;
2572
2573 case HWVirtExPropertyType_LargePages:
2574 i_setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2577 break;
2578
2579 case HWVirtExPropertyType_Force:
2580 i_setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExForceEnabled = !!aValue;
2583 break;
2584
2585 default:
2586 return E_INVALIDARG;
2587 }
2588
2589 return S_OK;
2590}
2591
2592HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2593{
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2602{
2603 /* @todo (r=dmik):
2604 * 1. Allow to change the name of the snapshot folder containing snapshots
2605 * 2. Rename the folder on disk instead of just changing the property
2606 * value (to be smart and not to leave garbage). Note that it cannot be
2607 * done here because the change may be rolled back. Thus, the right
2608 * place is #saveSettings().
2609 */
2610
2611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = i_checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 if (!mData->mCurrentSnapshot.isNull())
2617 return setError(E_FAIL,
2618 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2619
2620 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2621
2622 if (strSnapshotFolder.isEmpty())
2623 strSnapshotFolder = "Snapshots";
2624 int vrc = i_calculateFullPath(strSnapshotFolder,
2625 strSnapshotFolder);
2626 if (RT_FAILURE(vrc))
2627 return setError(E_FAIL,
2628 tr("Invalid snapshot folder '%s' (%Rrc)"),
2629 strSnapshotFolder.c_str(), vrc);
2630
2631 i_setModified(IsModified_MachineData);
2632 mUserData.backup();
2633
2634 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2635
2636 return S_OK;
2637}
2638
2639HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2640{
2641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 aMediumAttachments.resize(mMediaData->mAttachments.size());
2644 size_t i = 0;
2645 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2646 it != mMediaData->mAttachments.end(); ++it, ++i)
2647 aMediumAttachments[i] = *it;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 Assert(!!mVRDEServer);
2657
2658 aVRDEServer = mVRDEServer;
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 aAudioAdapter = mAudioAdapter;
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2673{
2674#ifdef VBOX_WITH_VUSB
2675 clearError();
2676 MultiResult rc(S_OK);
2677
2678# ifdef VBOX_WITH_USB
2679 rc = mParent->i_host()->i_checkUSBProxyService();
2680 if (FAILED(rc)) return rc;
2681# endif
2682
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 USBControllerList data = *mUSBControllers.data();
2686 aUSBControllers.resize(data.size());
2687 size_t i = 0;
2688 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
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::getSettingsModified(BOOL *aSettingsModified)
2735{
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2739 if (FAILED(rc)) return rc;
2740
2741 if (!mData->pMachineConfigFile->fileExists())
2742 // this is a new machine, and no config file exists yet:
2743 *aSettingsModified = TRUE;
2744 else
2745 *aSettingsModified = (mData->flModifications != 0);
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 *aSessionState = mData->mSession.mState;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 aSessionName = mData->mSession.mName;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 *aSessionPID = mData->mSession.mPID;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getState(MachineState_T *aState)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aState = mData->mMachineState;
2782 Assert(mData->mMachineState != MachineState_Null);
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 aStateFilePath = mSSData->strStateFilePath;
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 i_getLogFolder(aLogFolder);
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 aCurrentSnapshot = mData->mCurrentSnapshot;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2828 ? 0
2829 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 /* Note: for machines with no snapshots, we always return FALSE
2839 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2840 * reasons :) */
2841
2842 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2843 ? FALSE
2844 : mData->mCurrentStateModified;
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 aSharedFolders.resize(mHWData->mSharedFolders.size());
2854 size_t i = 0;
2855 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2856 it != mHWData->mSharedFolders.end(); ++i, ++it)
2857 aSharedFolders[i] = *it;
2858
2859 return S_OK;
2860}
2861
2862HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aClipboardMode = mHWData->mClipboardMode;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2872{
2873 HRESULT rc = S_OK;
2874
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 alock.release();
2878 rc = i_onClipboardModeChange(aClipboardMode);
2879 alock.acquire();
2880 if (FAILED(rc)) return rc;
2881
2882 i_setModified(IsModified_MachineData);
2883 mHWData.backup();
2884 mHWData->mClipboardMode = aClipboardMode;
2885
2886 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2887 if (Global::IsOnline(mData->mMachineState))
2888 i_saveSettings(NULL);
2889
2890 return S_OK;
2891}
2892
2893HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2894{
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 *aDnDMode = mHWData->mDnDMode;
2898
2899 return S_OK;
2900}
2901
2902HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2903{
2904 HRESULT rc = S_OK;
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 alock.release();
2909 rc = i_onDnDModeChange(aDnDMode);
2910
2911 alock.acquire();
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mHWData.backup();
2916 mHWData->mDnDMode = aDnDMode;
2917
2918 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2919 if (Global::IsOnline(mData->mMachineState))
2920 i_saveSettings(NULL);
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928 StorageControllerList data = *mStorageControllers.data();
2929 size_t i = 0;
2930 aStorageControllers.resize(data.size());
2931 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2932 aStorageControllers[i] = *it;
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 *aEnabled = mUserData->s.fTeleporterEnabled;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2946{
2947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 /* Only allow it to be set to true when PoweredOff or Aborted.
2950 (Clearing it is always permitted.) */
2951 if ( aTeleporterEnabled
2952 && mData->mRegistered
2953 && ( !i_isSessionMachine()
2954 || ( mData->mMachineState != MachineState_PoweredOff
2955 && mData->mMachineState != MachineState_Teleported
2956 && mData->mMachineState != MachineState_Aborted
2957 )
2958 )
2959 )
2960 return setError(VBOX_E_INVALID_VM_STATE,
2961 tr("The machine is not powered off (state is %s)"),
2962 Global::stringifyMachineState(mData->mMachineState));
2963
2964 i_setModified(IsModified_MachineData);
2965 mUserData.backup();
2966 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2972{
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2976
2977 return S_OK;
2978}
2979
2980HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2981{
2982 if (aTeleporterPort >= _64K)
2983 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2984
2985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2988 if (FAILED(rc)) return rc;
2989
2990 i_setModified(IsModified_MachineData);
2991 mUserData.backup();
2992 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2993
2994 return S_OK;
2995}
2996
2997HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2998{
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3002
3003 return S_OK;
3004}
3005
3006HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3007{
3008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3009
3010 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3011 if (FAILED(rc)) return rc;
3012
3013 i_setModified(IsModified_MachineData);
3014 mUserData.backup();
3015 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3016
3017 return S_OK;
3018}
3019
3020HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3021{
3022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3023 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3024
3025 return S_OK;
3026}
3027
3028HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3029{
3030 /*
3031 * Hash the password first.
3032 */
3033 com::Utf8Str aT = aTeleporterPassword;
3034
3035 if (!aT.isEmpty())
3036 {
3037 if (VBoxIsPasswordHashed(&aT))
3038 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3039 VBoxHashPassword(&aT);
3040 }
3041
3042 /*
3043 * Do the update.
3044 */
3045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3046 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (SUCCEEDED(hrc))
3048 {
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strTeleporterPassword = aT;
3052 }
3053
3054 return hrc;
3055}
3056
3057HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3058{
3059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3066{
3067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 /* @todo deal with running state change. */
3070 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3071 if (FAILED(rc)) return rc;
3072
3073 i_setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3076 return S_OK;
3077}
3078
3079HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3080{
3081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3082
3083 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3084 return S_OK;
3085}
3086
3087HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3088{
3089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 /* @todo deal with running state change. */
3092 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3093 if (FAILED(rc)) return rc;
3094
3095 i_setModified(IsModified_MachineData);
3096 mUserData.backup();
3097 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3098 return S_OK;
3099}
3100
3101HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3102{
3103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3106 return S_OK;
3107}
3108
3109HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3110{
3111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3112
3113 /* @todo deal with running state change. */
3114 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3115 if (FAILED(rc)) return rc;
3116
3117 i_setModified(IsModified_MachineData);
3118 mUserData.backup();
3119 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3124{
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3128
3129 return S_OK;
3130}
3131
3132HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3133{
3134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3135
3136 /* @todo deal with running state change. */
3137 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3138 if (FAILED(rc)) return rc;
3139
3140 i_setModified(IsModified_MachineData);
3141 mUserData.backup();
3142 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3148{
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 /* @todo deal with running state change. */
3160 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3161 if (FAILED(rc)) return rc;
3162
3163 i_setModified(IsModified_MachineData);
3164 mUserData.backup();
3165 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 /* Only allow it to be set to true when PoweredOff or Aborted.
3183 (Clearing it is always permitted.) */
3184 if ( aRTCUseUTC
3185 && mData->mRegistered
3186 && ( !i_isSessionMachine()
3187 || ( mData->mMachineState != MachineState_PoweredOff
3188 && mData->mMachineState != MachineState_Teleported
3189 && mData->mMachineState != MachineState_Aborted
3190 )
3191 )
3192 )
3193 return setError(VBOX_E_INVALID_VM_STATE,
3194 tr("The machine is not powered off (state is %s)"),
3195 Global::stringifyMachineState(mData->mMachineState));
3196
3197 i_setModified(IsModified_MachineData);
3198 mUserData.backup();
3199 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3200
3201 return S_OK;
3202}
3203
3204HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3205{
3206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3207
3208 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3209
3210 return S_OK;
3211}
3212
3213HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3214{
3215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217 HRESULT rc = i_checkStateDependency(MutableStateDep);
3218 if (FAILED(rc)) return rc;
3219
3220 i_setModified(IsModified_MachineData);
3221 mHWData.backup();
3222 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3223
3224 return S_OK;
3225}
3226
3227HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3228{
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230
3231 *aIOCacheSize = mHWData->mIOCacheSize;
3232
3233 return S_OK;
3234}
3235
3236HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3237{
3238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3239
3240 HRESULT rc = i_checkStateDependency(MutableStateDep);
3241 if (FAILED(rc)) return rc;
3242
3243 i_setModified(IsModified_MachineData);
3244 mHWData.backup();
3245 mHWData->mIOCacheSize = aIOCacheSize;
3246
3247 return S_OK;
3248}
3249
3250
3251/**
3252 * @note Locks objects!
3253 */
3254HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3255 LockType_T aLockType)
3256{
3257 /* check the session state */
3258 SessionState_T state;
3259 HRESULT rc = aSession->COMGETTER(State)(&state);
3260 if (FAILED(rc)) return rc;
3261
3262 if (state != SessionState_Unlocked)
3263 return setError(VBOX_E_INVALID_OBJECT_STATE,
3264 tr("The given session is busy"));
3265
3266 // get the client's IInternalSessionControl interface
3267 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3268 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3269 E_INVALIDARG);
3270
3271 // session name (only used in some code paths)
3272 Utf8Str strSessionName;
3273
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 if (!mData->mRegistered)
3277 return setError(E_UNEXPECTED,
3278 tr("The machine '%s' is not registered"),
3279 mUserData->s.strName.c_str());
3280
3281 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3282
3283 SessionState_T oldState = mData->mSession.mState;
3284 /* Hack: in case the session is closing and there is a progress object
3285 * which allows waiting for the session to be closed, take the opportunity
3286 * and do a limited wait (max. 1 second). This helps a lot when the system
3287 * is busy and thus session closing can take a little while. */
3288 if ( mData->mSession.mState == SessionState_Unlocking
3289 && mData->mSession.mProgress)
3290 {
3291 alock.release();
3292 mData->mSession.mProgress->WaitForCompletion(1000);
3293 alock.acquire();
3294 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3295 }
3296
3297 // try again now
3298 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3299 // (i.e. session machine exists)
3300 && (aLockType == LockType_Shared) // caller wants a shared link to the
3301 // existing session that holds the write lock:
3302 )
3303 {
3304 // OK, share the session... we are now dealing with three processes:
3305 // 1) VBoxSVC (where this code runs);
3306 // 2) process C: the caller's client process (who wants a shared session);
3307 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3308
3309 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3310 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3311 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3312 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3313 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3314
3315 /*
3316 * Release the lock before calling the client process. It's safe here
3317 * since the only thing to do after we get the lock again is to add
3318 * the remote control to the list (which doesn't directly influence
3319 * anything).
3320 */
3321 alock.release();
3322
3323 // get the console of the session holding the write lock (this is a remote call)
3324 ComPtr<IConsole> pConsoleW;
3325 if (mData->mSession.mLockType == LockType_VM)
3326 {
3327 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3328 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3329 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3330 if (FAILED(rc))
3331 // the failure may occur w/o any error info (from RPC), so provide one
3332 return setError(VBOX_E_VM_ERROR,
3333 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3334 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3335 }
3336
3337 // share the session machine and W's console with the caller's session
3338 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3339 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3340 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3341
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 assign the machine to the session (%Rhrc)"), rc);
3346 alock.acquire();
3347
3348 // need to revalidate the state after acquiring the lock again
3349 if (mData->mSession.mState != SessionState_Locked)
3350 {
3351 pSessionControl->Uninitialize();
3352 return setError(VBOX_E_INVALID_SESSION_STATE,
3353 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3354 mUserData->s.strName.c_str());
3355 }
3356
3357 // add the caller's session to the list
3358 mData->mSession.mRemoteControls.push_back(pSessionControl);
3359 }
3360 else if ( mData->mSession.mState == SessionState_Locked
3361 || mData->mSession.mState == SessionState_Unlocking
3362 )
3363 {
3364 // sharing not permitted, or machine still unlocking:
3365 return setError(VBOX_E_INVALID_OBJECT_STATE,
3366 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3367 mUserData->s.strName.c_str());
3368 }
3369 else
3370 {
3371 // machine is not locked: then write-lock the machine (create the session machine)
3372
3373 // must not be busy
3374 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3375
3376 // get the caller's session PID
3377 RTPROCESS pid = NIL_RTPROCESS;
3378 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3379 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3380 Assert(pid != NIL_RTPROCESS);
3381
3382 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3383
3384 if (fLaunchingVMProcess)
3385 {
3386 if (mData->mSession.mPID == NIL_RTPROCESS)
3387 {
3388 // two or more clients racing for a lock, the one which set the
3389 // session state to Spawning will win, the others will get an
3390 // error as we can't decide here if waiting a little would help
3391 // (only for shared locks this would avoid an error)
3392 return setError(VBOX_E_INVALID_OBJECT_STATE,
3393 tr("The machine '%s' already has a lock request pending"),
3394 mUserData->s.strName.c_str());
3395 }
3396
3397 // this machine is awaiting for a spawning session to be opened:
3398 // then the calling process must be the one that got started by
3399 // LaunchVMProcess()
3400
3401 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3402 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3403
3404#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3405 /* Hardened windows builds spawns three processes when a VM is
3406 launched, the 3rd one is the one that will end up here. */
3407 RTPROCESS ppid;
3408 int rc = RTProcQueryParent(pid, &ppid);
3409 if (RT_SUCCESS(rc))
3410 rc = RTProcQueryParent(ppid, &ppid);
3411 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3412 || rc == VERR_ACCESS_DENIED)
3413 {
3414 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3415 mData->mSession.mPID = pid;
3416 }
3417#endif
3418
3419 if (mData->mSession.mPID != pid)
3420 return setError(E_ACCESSDENIED,
3421 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3422 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3423 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3424 }
3425
3426 // create the mutable SessionMachine from the current machine
3427 ComObjPtr<SessionMachine> sessionMachine;
3428 sessionMachine.createObject();
3429 rc = sessionMachine->init(this);
3430 AssertComRC(rc);
3431
3432 /* NOTE: doing return from this function after this point but
3433 * before the end is forbidden since it may call SessionMachine::uninit()
3434 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3435 * lock while still holding the Machine lock in alock so that a deadlock
3436 * is possible due to the wrong lock order. */
3437
3438 if (SUCCEEDED(rc))
3439 {
3440 /*
3441 * Set the session state to Spawning to protect against subsequent
3442 * attempts to open a session and to unregister the machine after
3443 * we release the lock.
3444 */
3445 SessionState_T origState = mData->mSession.mState;
3446 mData->mSession.mState = SessionState_Spawning;
3447
3448#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3449 /* Get the client token ID to be passed to the client process */
3450 Utf8Str strTokenId;
3451 sessionMachine->i_getTokenId(strTokenId);
3452 Assert(!strTokenId.isEmpty());
3453#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3454 /* Get the client token to be passed to the client process */
3455 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3456 /* The token is now "owned" by pToken, fix refcount */
3457 if (!pToken.isNull())
3458 pToken->Release();
3459#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3460
3461 /*
3462 * Release the lock before calling the client process -- it will call
3463 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3464 * because the state is Spawning, so that LaunchVMProcess() and
3465 * LockMachine() calls will fail. This method, called before we
3466 * acquire the lock again, will fail because of the wrong PID.
3467 *
3468 * Note that mData->mSession.mRemoteControls accessed outside
3469 * the lock may not be modified when state is Spawning, so it's safe.
3470 */
3471 alock.release();
3472
3473 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3474#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3475 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3476#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3477 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3478 /* Now the token is owned by the client process. */
3479 pToken.setNull();
3480#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3481 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3482
3483 /* The failure may occur w/o any error info (from RPC), so provide one */
3484 if (FAILED(rc))
3485 setError(VBOX_E_VM_ERROR,
3486 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3487
3488 // get session name, either to remember or to compare against
3489 // the already known session name.
3490 {
3491 Bstr bstrSessionName;
3492 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3493 if (SUCCEEDED(rc2))
3494 strSessionName = bstrSessionName;
3495 }
3496
3497 if ( SUCCEEDED(rc)
3498 && fLaunchingVMProcess
3499 )
3500 {
3501 /* complete the remote session initialization */
3502
3503 /* get the console from the direct session */
3504 ComPtr<IConsole> console;
3505 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3506 ComAssertComRC(rc);
3507
3508 if (SUCCEEDED(rc) && !console)
3509 {
3510 ComAssert(!!console);
3511 rc = E_FAIL;
3512 }
3513
3514 /* assign machine & console to the remote session */
3515 if (SUCCEEDED(rc))
3516 {
3517 /*
3518 * after LaunchVMProcess(), the first and the only
3519 * entry in remoteControls is that remote session
3520 */
3521 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3522 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3523 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3524
3525 /* The failure may occur w/o any error info (from RPC), so provide one */
3526 if (FAILED(rc))
3527 setError(VBOX_E_VM_ERROR,
3528 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3529 }
3530
3531 if (FAILED(rc))
3532 pSessionControl->Uninitialize();
3533 }
3534
3535 /* acquire the lock again */
3536 alock.acquire();
3537
3538 /* Restore the session state */
3539 mData->mSession.mState = origState;
3540 }
3541
3542 // finalize spawning anyway (this is why we don't return on errors above)
3543 if (fLaunchingVMProcess)
3544 {
3545 Assert(mData->mSession.mName == strSessionName);
3546 /* Note that the progress object is finalized later */
3547 /** @todo Consider checking mData->mSession.mProgress for cancellation
3548 * around here. */
3549
3550 /* We don't reset mSession.mPID here because it is necessary for
3551 * SessionMachine::uninit() to reap the child process later. */
3552
3553 if (FAILED(rc))
3554 {
3555 /* Close the remote session, remove the remote control from the list
3556 * and reset session state to Closed (@note keep the code in sync
3557 * with the relevant part in checkForSpawnFailure()). */
3558
3559 Assert(mData->mSession.mRemoteControls.size() == 1);
3560 if (mData->mSession.mRemoteControls.size() == 1)
3561 {
3562 ErrorInfoKeeper eik;
3563 mData->mSession.mRemoteControls.front()->Uninitialize();
3564 }
3565
3566 mData->mSession.mRemoteControls.clear();
3567 mData->mSession.mState = SessionState_Unlocked;
3568 }
3569 }
3570 else
3571 {
3572 /* memorize PID of the directly opened session */
3573 if (SUCCEEDED(rc))
3574 mData->mSession.mPID = pid;
3575 }
3576
3577 if (SUCCEEDED(rc))
3578 {
3579 mData->mSession.mLockType = aLockType;
3580 /* memorize the direct session control and cache IUnknown for it */
3581 mData->mSession.mDirectControl = pSessionControl;
3582 mData->mSession.mState = SessionState_Locked;
3583 if (!fLaunchingVMProcess)
3584 mData->mSession.mName = strSessionName;
3585 /* associate the SessionMachine with this Machine */
3586 mData->mSession.mMachine = sessionMachine;
3587
3588 /* request an IUnknown pointer early from the remote party for later
3589 * identity checks (it will be internally cached within mDirectControl
3590 * at least on XPCOM) */
3591 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3592 NOREF(unk);
3593 }
3594
3595 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3596 * would break the lock order */
3597 alock.release();
3598
3599 /* uninitialize the created session machine on failure */
3600 if (FAILED(rc))
3601 sessionMachine->uninit();
3602 }
3603
3604 if (SUCCEEDED(rc))
3605 {
3606 /*
3607 * tell the client watcher thread to update the set of
3608 * machines that have open sessions
3609 */
3610 mParent->i_updateClientWatcher();
3611
3612 if (oldState != SessionState_Locked)
3613 /* fire an event */
3614 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3615 }
3616
3617 return rc;
3618}
3619
3620/**
3621 * @note Locks objects!
3622 */
3623HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3624 const com::Utf8Str &aName,
3625 const com::Utf8Str &aEnvironment,
3626 ComPtr<IProgress> &aProgress)
3627{
3628 Utf8Str strFrontend(aName);
3629 /* "emergencystop" doesn't need the session, so skip the checks/interface
3630 * retrieval. This code doesn't quite fit in here, but introducing a
3631 * special API method would be even more effort, and would require explicit
3632 * support by every API client. It's better to hide the feature a bit. */
3633 if (strFrontend != "emergencystop")
3634 CheckComArgNotNull(aSession);
3635
3636 HRESULT rc = S_OK;
3637 if (strFrontend.isEmpty())
3638 {
3639 Bstr bstrFrontend;
3640 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3641 if (FAILED(rc))
3642 return rc;
3643 strFrontend = bstrFrontend;
3644 if (strFrontend.isEmpty())
3645 {
3646 ComPtr<ISystemProperties> systemProperties;
3647 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3648 if (FAILED(rc))
3649 return rc;
3650 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3651 if (FAILED(rc))
3652 return rc;
3653 strFrontend = bstrFrontend;
3654 }
3655 /* paranoia - emergencystop is not a valid default */
3656 if (strFrontend == "emergencystop")
3657 strFrontend = Utf8Str::Empty;
3658 }
3659 /* default frontend: Qt GUI */
3660 if (strFrontend.isEmpty())
3661 strFrontend = "GUI/Qt";
3662
3663 if (strFrontend != "emergencystop")
3664 {
3665 /* check the session state */
3666 SessionState_T state;
3667 rc = aSession->COMGETTER(State)(&state);
3668 if (FAILED(rc))
3669 return rc;
3670
3671 if (state != SessionState_Unlocked)
3672 return setError(VBOX_E_INVALID_OBJECT_STATE,
3673 tr("The given session is busy"));
3674
3675 /* get the IInternalSessionControl interface */
3676 ComPtr<IInternalSessionControl> control(aSession);
3677 ComAssertMsgRet(!control.isNull(),
3678 ("No IInternalSessionControl interface"),
3679 E_INVALIDARG);
3680
3681 /* get the teleporter enable state for the progress object init. */
3682 BOOL fTeleporterEnabled;
3683 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3684 if (FAILED(rc))
3685 return rc;
3686
3687 /* create a progress object */
3688 ComObjPtr<ProgressProxy> progress;
3689 progress.createObject();
3690 rc = progress->init(mParent,
3691 static_cast<IMachine*>(this),
3692 Bstr(tr("Starting VM")).raw(),
3693 TRUE /* aCancelable */,
3694 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3695 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3696 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3697 2 /* uFirstOperationWeight */,
3698 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3699
3700 if (SUCCEEDED(rc))
3701 {
3702 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3703 if (SUCCEEDED(rc))
3704 {
3705 aProgress = progress;
3706
3707 /* signal the client watcher thread */
3708 mParent->i_updateClientWatcher();
3709
3710 /* fire an event */
3711 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3712 }
3713 }
3714 }
3715 else
3716 {
3717 /* no progress object - either instant success or failure */
3718 aProgress = NULL;
3719
3720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3721
3722 if (mData->mSession.mState != SessionState_Locked)
3723 return setError(VBOX_E_INVALID_OBJECT_STATE,
3724 tr("The machine '%s' is not locked by a session"),
3725 mUserData->s.strName.c_str());
3726
3727 /* must have a VM process associated - do not kill normal API clients
3728 * with an open session */
3729 if (!Global::IsOnline(mData->mMachineState))
3730 return setError(VBOX_E_INVALID_OBJECT_STATE,
3731 tr("The machine '%s' does not have a VM process"),
3732 mUserData->s.strName.c_str());
3733
3734 /* forcibly terminate the VM process */
3735 if (mData->mSession.mPID != NIL_RTPROCESS)
3736 RTProcTerminate(mData->mSession.mPID);
3737
3738 /* signal the client watcher thread, as most likely the client has
3739 * been terminated */
3740 mParent->i_updateClientWatcher();
3741 }
3742
3743 return rc;
3744}
3745
3746HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3747{
3748 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3749 return setError(E_INVALIDARG,
3750 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3751 aPosition, SchemaDefs::MaxBootPosition);
3752
3753 if (aDevice == DeviceType_USB)
3754 return setError(E_NOTIMPL,
3755 tr("Booting from USB device is currently not supported"));
3756
3757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3758
3759 HRESULT rc = i_checkStateDependency(MutableStateDep);
3760 if (FAILED(rc)) return rc;
3761
3762 i_setModified(IsModified_MachineData);
3763 mHWData.backup();
3764 mHWData->mBootOrder[aPosition - 1] = aDevice;
3765
3766 return S_OK;
3767}
3768
3769HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3770{
3771 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3772 return setError(E_INVALIDARG,
3773 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3774 aPosition, SchemaDefs::MaxBootPosition);
3775
3776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3777
3778 *aDevice = mHWData->mBootOrder[aPosition - 1];
3779
3780 return S_OK;
3781}
3782
3783HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3784 LONG aControllerPort,
3785 LONG aDevice,
3786 DeviceType_T aType,
3787 const ComPtr<IMedium> &aMedium)
3788{
3789 IMedium *aM = aMedium;
3790 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3791 aName.c_str(), aControllerPort, aDevice, aType, aM));
3792
3793 // request the host lock first, since might be calling Host methods for getting host drives;
3794 // next, protect the media tree all the while we're in here, as well as our member variables
3795 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3796 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3797
3798 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3799 if (FAILED(rc)) return rc;
3800
3801 /// @todo NEWMEDIA implicit machine registration
3802 if (!mData->mRegistered)
3803 return setError(VBOX_E_INVALID_OBJECT_STATE,
3804 tr("Cannot attach storage devices to an unregistered machine"));
3805
3806 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3807
3808 /* Check for an existing controller. */
3809 ComObjPtr<StorageController> ctl;
3810 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3811 if (FAILED(rc)) return rc;
3812
3813 StorageControllerType_T ctrlType;
3814 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3815 if (FAILED(rc))
3816 return setError(E_FAIL,
3817 tr("Could not get type of controller '%s'"),
3818 aName.c_str());
3819
3820 bool fSilent = false;
3821 Utf8Str strReconfig;
3822
3823 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3824 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3825 if ( mData->mMachineState == MachineState_Paused
3826 && strReconfig == "1")
3827 fSilent = true;
3828
3829 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3830 bool fHotplug = false;
3831 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3832 fHotplug = true;
3833
3834 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3835 return setError(VBOX_E_INVALID_VM_STATE,
3836 tr("Controller '%s' does not support hotplugging"),
3837 aName.c_str());
3838
3839 // check that the port and device are not out of range
3840 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3841 if (FAILED(rc)) return rc;
3842
3843 /* check if the device slot is already busy */
3844 MediumAttachment *pAttachTemp;
3845 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3846 Bstr(aName).raw(),
3847 aControllerPort,
3848 aDevice)))
3849 {
3850 Medium *pMedium = pAttachTemp->i_getMedium();
3851 if (pMedium)
3852 {
3853 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3854 return setError(VBOX_E_OBJECT_IN_USE,
3855 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3856 pMedium->i_getLocationFull().c_str(),
3857 aControllerPort,
3858 aDevice,
3859 aName.c_str());
3860 }
3861 else
3862 return setError(VBOX_E_OBJECT_IN_USE,
3863 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3864 aControllerPort, aDevice, aName.c_str());
3865 }
3866
3867 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3868 if (aMedium && medium.isNull())
3869 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3870
3871 AutoCaller mediumCaller(medium);
3872 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3873
3874 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3875
3876 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3877 && !medium.isNull()
3878 )
3879 return setError(VBOX_E_OBJECT_IN_USE,
3880 tr("Medium '%s' is already attached to this virtual machine"),
3881 medium->i_getLocationFull().c_str());
3882
3883 if (!medium.isNull())
3884 {
3885 MediumType_T mtype = medium->i_getType();
3886 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3887 // For DVDs it's not written to the config file, so needs no global config
3888 // version bump. For floppies it's a new attribute "type", which is ignored
3889 // by older VirtualBox version, so needs no global config version bump either.
3890 // For hard disks this type is not accepted.
3891 if (mtype == MediumType_MultiAttach)
3892 {
3893 // This type is new with VirtualBox 4.0 and therefore requires settings
3894 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3895 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3896 // two reasons: The medium type is a property of the media registry tree, which
3897 // can reside in the global config file (for pre-4.0 media); we would therefore
3898 // possibly need to bump the global config version. We don't want to do that though
3899 // because that might make downgrading to pre-4.0 impossible.
3900 // As a result, we can only use these two new types if the medium is NOT in the
3901 // global registry:
3902 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3903 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3904 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3905 )
3906 return setError(VBOX_E_INVALID_OBJECT_STATE,
3907 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3908 "to machines that were created with VirtualBox 4.0 or later"),
3909 medium->i_getLocationFull().c_str());
3910 }
3911 }
3912
3913 bool fIndirect = false;
3914 if (!medium.isNull())
3915 fIndirect = medium->i_isReadOnly();
3916 bool associate = true;
3917
3918 do
3919 {
3920 if ( aType == DeviceType_HardDisk
3921 && mMediaData.isBackedUp())
3922 {
3923 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3924
3925 /* check if the medium was attached to the VM before we started
3926 * changing attachments in which case the attachment just needs to
3927 * be restored */
3928 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3929 {
3930 AssertReturn(!fIndirect, E_FAIL);
3931
3932 /* see if it's the same bus/channel/device */
3933 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3934 {
3935 /* the simplest case: restore the whole attachment
3936 * and return, nothing else to do */
3937 mMediaData->mAttachments.push_back(pAttachTemp);
3938
3939 /* Reattach the medium to the VM. */
3940 if (fHotplug || fSilent)
3941 {
3942 mediumLock.release();
3943 treeLock.release();
3944 alock.release();
3945
3946 MediumLockList *pMediumLockList(new MediumLockList());
3947
3948 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3949 medium /* pToLockWrite */,
3950 false /* fMediumLockWriteAll */,
3951 NULL,
3952 *pMediumLockList);
3953 alock.acquire();
3954 if (FAILED(rc))
3955 delete pMediumLockList;
3956 else
3957 {
3958 mData->mSession.mLockedMedia.Unlock();
3959 alock.release();
3960 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3961 mData->mSession.mLockedMedia.Lock();
3962 alock.acquire();
3963 }
3964 alock.release();
3965
3966 if (SUCCEEDED(rc))
3967 {
3968 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3969 /* Remove lock list in case of error. */
3970 if (FAILED(rc))
3971 {
3972 mData->mSession.mLockedMedia.Unlock();
3973 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3974 mData->mSession.mLockedMedia.Lock();
3975 }
3976 }
3977 }
3978
3979 return S_OK;
3980 }
3981
3982 /* bus/channel/device differ; we need a new attachment object,
3983 * but don't try to associate it again */
3984 associate = false;
3985 break;
3986 }
3987 }
3988
3989 /* go further only if the attachment is to be indirect */
3990 if (!fIndirect)
3991 break;
3992
3993 /* perform the so called smart attachment logic for indirect
3994 * attachments. Note that smart attachment is only applicable to base
3995 * hard disks. */
3996
3997 if (medium->i_getParent().isNull())
3998 {
3999 /* first, investigate the backup copy of the current hard disk
4000 * attachments to make it possible to re-attach existing diffs to
4001 * another device slot w/o losing their contents */
4002 if (mMediaData.isBackedUp())
4003 {
4004 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4005
4006 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4007 uint32_t foundLevel = 0;
4008
4009 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
4010 {
4011 uint32_t level = 0;
4012 MediumAttachment *pAttach = *it;
4013 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4014 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4015 if (pMedium.isNull())
4016 continue;
4017
4018 if (pMedium->i_getBase(&level) == medium)
4019 {
4020 /* skip the hard disk if its currently attached (we
4021 * cannot attach the same hard disk twice) */
4022 if (i_findAttachment(mMediaData->mAttachments,
4023 pMedium))
4024 continue;
4025
4026 /* matched device, channel and bus (i.e. attached to the
4027 * same place) will win and immediately stop the search;
4028 * otherwise the attachment that has the youngest
4029 * descendant of medium will be used
4030 */
4031 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
4032 {
4033 /* the simplest case: restore the whole attachment
4034 * and return, nothing else to do */
4035 mMediaData->mAttachments.push_back(*it);
4036
4037 /* Reattach the medium to the VM. */
4038 if (fHotplug || fSilent)
4039 {
4040 mediumLock.release();
4041 treeLock.release();
4042 alock.release();
4043
4044 MediumLockList *pMediumLockList(new MediumLockList());
4045
4046 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4047 medium /* pToLockWrite */,
4048 false /* fMediumLockWriteAll */,
4049 NULL,
4050 *pMediumLockList);
4051 alock.acquire();
4052 if (FAILED(rc))
4053 delete pMediumLockList;
4054 else
4055 {
4056 mData->mSession.mLockedMedia.Unlock();
4057 alock.release();
4058 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4059 mData->mSession.mLockedMedia.Lock();
4060 alock.acquire();
4061 }
4062 alock.release();
4063
4064 if (SUCCEEDED(rc))
4065 {
4066 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4067 /* Remove lock list in case of error. */
4068 if (FAILED(rc))
4069 {
4070 mData->mSession.mLockedMedia.Unlock();
4071 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4072 mData->mSession.mLockedMedia.Lock();
4073 }
4074 }
4075 }
4076
4077 return S_OK;
4078 }
4079 else if ( foundIt == oldAtts.end()
4080 || level > foundLevel /* prefer younger */
4081 )
4082 {
4083 foundIt = it;
4084 foundLevel = level;
4085 }
4086 }
4087 }
4088
4089 if (foundIt != oldAtts.end())
4090 {
4091 /* use the previously attached hard disk */
4092 medium = (*foundIt)->i_getMedium();
4093 mediumCaller.attach(medium);
4094 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4095 mediumLock.attach(medium);
4096 /* not implicit, doesn't require association with this VM */
4097 fIndirect = false;
4098 associate = false;
4099 /* go right to the MediumAttachment creation */
4100 break;
4101 }
4102 }
4103
4104 /* must give up the medium lock and medium tree lock as below we
4105 * go over snapshots, which needs a lock with higher lock order. */
4106 mediumLock.release();
4107 treeLock.release();
4108
4109 /* then, search through snapshots for the best diff in the given
4110 * hard disk's chain to base the new diff on */
4111
4112 ComObjPtr<Medium> base;
4113 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4114 while (snap)
4115 {
4116 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4117
4118 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4119
4120 MediumAttachment *pAttachFound = NULL;
4121 uint32_t foundLevel = 0;
4122
4123 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4124 {
4125 MediumAttachment *pAttach = *it;
4126 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4127 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4128 if (pMedium.isNull())
4129 continue;
4130
4131 uint32_t level = 0;
4132 if (pMedium->i_getBase(&level) == medium)
4133 {
4134 /* matched device, channel and bus (i.e. attached to the
4135 * same place) will win and immediately stop the search;
4136 * otherwise the attachment that has the youngest
4137 * descendant of medium will be used
4138 */
4139 if ( pAttach->i_getDevice() == aDevice
4140 && pAttach->i_getPort() == aControllerPort
4141 && pAttach->i_getControllerName() == aName
4142 )
4143 {
4144 pAttachFound = pAttach;
4145 break;
4146 }
4147 else if ( !pAttachFound
4148 || level > foundLevel /* prefer younger */
4149 )
4150 {
4151 pAttachFound = pAttach;
4152 foundLevel = level;
4153 }
4154 }
4155 }
4156
4157 if (pAttachFound)
4158 {
4159 base = pAttachFound->i_getMedium();
4160 break;
4161 }
4162
4163 snap = snap->i_getParent();
4164 }
4165
4166 /* re-lock medium tree and the medium, as we need it below */
4167 treeLock.acquire();
4168 mediumLock.acquire();
4169
4170 /* found a suitable diff, use it as a base */
4171 if (!base.isNull())
4172 {
4173 medium = base;
4174 mediumCaller.attach(medium);
4175 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4176 mediumLock.attach(medium);
4177 }
4178 }
4179
4180 Utf8Str strFullSnapshotFolder;
4181 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4182
4183 ComObjPtr<Medium> diff;
4184 diff.createObject();
4185 // store this diff in the same registry as the parent
4186 Guid uuidRegistryParent;
4187 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4188 {
4189 // parent image has no registry: this can happen if we're attaching a new immutable
4190 // image that has not yet been attached (medium then points to the base and we're
4191 // creating the diff image for the immutable, and the parent is not yet registered);
4192 // put the parent in the machine registry then
4193 mediumLock.release();
4194 treeLock.release();
4195 alock.release();
4196 i_addMediumToRegistry(medium);
4197 alock.acquire();
4198 treeLock.acquire();
4199 mediumLock.acquire();
4200 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4201 }
4202 rc = diff->init(mParent,
4203 medium->i_getPreferredDiffFormat(),
4204 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4205 uuidRegistryParent,
4206 DeviceType_HardDisk);
4207 if (FAILED(rc)) return rc;
4208
4209 /* Apply the normal locking logic to the entire chain. */
4210 MediumLockList *pMediumLockList(new MediumLockList());
4211 mediumLock.release();
4212 treeLock.release();
4213 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4214 diff /* pToLockWrite */,
4215 false /* fMediumLockWriteAll */,
4216 medium,
4217 *pMediumLockList);
4218 treeLock.acquire();
4219 mediumLock.acquire();
4220 if (SUCCEEDED(rc))
4221 {
4222 mediumLock.release();
4223 treeLock.release();
4224 rc = pMediumLockList->Lock();
4225 treeLock.acquire();
4226 mediumLock.acquire();
4227 if (FAILED(rc))
4228 setError(rc,
4229 tr("Could not lock medium when creating diff '%s'"),
4230 diff->i_getLocationFull().c_str());
4231 else
4232 {
4233 /* will release the lock before the potentially lengthy
4234 * operation, so protect with the special state */
4235 MachineState_T oldState = mData->mMachineState;
4236 i_setMachineState(MachineState_SettingUp);
4237
4238 mediumLock.release();
4239 treeLock.release();
4240 alock.release();
4241
4242 rc = medium->i_createDiffStorage(diff,
4243 medium->i_getPreferredDiffVariant(),
4244 pMediumLockList,
4245 NULL /* aProgress */,
4246 true /* aWait */);
4247
4248 alock.acquire();
4249 treeLock.acquire();
4250 mediumLock.acquire();
4251
4252 i_setMachineState(oldState);
4253 }
4254 }
4255
4256 /* Unlock the media and free the associated memory. */
4257 delete pMediumLockList;
4258
4259 if (FAILED(rc)) return rc;
4260
4261 /* use the created diff for the actual attachment */
4262 medium = diff;
4263 mediumCaller.attach(medium);
4264 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4265 mediumLock.attach(medium);
4266 }
4267 while (0);
4268
4269 ComObjPtr<MediumAttachment> attachment;
4270 attachment.createObject();
4271 rc = attachment->init(this,
4272 medium,
4273 aName,
4274 aControllerPort,
4275 aDevice,
4276 aType,
4277 fIndirect,
4278 false /* fPassthrough */,
4279 false /* fTempEject */,
4280 false /* fNonRotational */,
4281 false /* fDiscard */,
4282 fHotplug /* fHotPluggable */,
4283 Utf8Str::Empty);
4284 if (FAILED(rc)) return rc;
4285
4286 if (associate && !medium.isNull())
4287 {
4288 // as the last step, associate the medium to the VM
4289 rc = medium->i_addBackReference(mData->mUuid);
4290 // here we can fail because of Deleting, or being in process of creating a Diff
4291 if (FAILED(rc)) return rc;
4292
4293 mediumLock.release();
4294 treeLock.release();
4295 alock.release();
4296 i_addMediumToRegistry(medium);
4297 alock.acquire();
4298 treeLock.acquire();
4299 mediumLock.acquire();
4300 }
4301
4302 /* success: finally remember the attachment */
4303 i_setModified(IsModified_Storage);
4304 mMediaData.backup();
4305 mMediaData->mAttachments.push_back(attachment);
4306
4307 mediumLock.release();
4308 treeLock.release();
4309 alock.release();
4310
4311 if (fHotplug || fSilent)
4312 {
4313 if (!medium.isNull())
4314 {
4315 MediumLockList *pMediumLockList(new MediumLockList());
4316
4317 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4318 medium /* pToLockWrite */,
4319 false /* fMediumLockWriteAll */,
4320 NULL,
4321 *pMediumLockList);
4322 alock.acquire();
4323 if (FAILED(rc))
4324 delete pMediumLockList;
4325 else
4326 {
4327 mData->mSession.mLockedMedia.Unlock();
4328 alock.release();
4329 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4330 mData->mSession.mLockedMedia.Lock();
4331 alock.acquire();
4332 }
4333 alock.release();
4334 }
4335
4336 if (SUCCEEDED(rc))
4337 {
4338 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4339 /* Remove lock list in case of error. */
4340 if (FAILED(rc))
4341 {
4342 mData->mSession.mLockedMedia.Unlock();
4343 mData->mSession.mLockedMedia.Remove(attachment);
4344 mData->mSession.mLockedMedia.Lock();
4345 }
4346 }
4347 }
4348
4349 /* Save modified registries, but skip this machine as it's the caller's
4350 * job to save its settings like all other settings changes. */
4351 mParent->i_unmarkRegistryModified(i_getId());
4352 mParent->i_saveModifiedRegistries();
4353
4354 return rc;
4355}
4356
4357HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice)
4359{
4360 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4361 aName.c_str(), aControllerPort, aDevice));
4362
4363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4364
4365 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4366 if (FAILED(rc)) return rc;
4367
4368 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4369
4370 /* Check for an existing controller. */
4371 ComObjPtr<StorageController> ctl;
4372 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4373 if (FAILED(rc)) return rc;
4374
4375 StorageControllerType_T ctrlType;
4376 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4377 if (FAILED(rc))
4378 return setError(E_FAIL,
4379 tr("Could not get type of controller '%s'"),
4380 aName.c_str());
4381
4382 bool fSilent = false;
4383 Utf8Str strReconfig;
4384
4385 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4386 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4387 if ( mData->mMachineState == MachineState_Paused
4388 && strReconfig == "1")
4389 fSilent = true;
4390
4391 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4392 bool fHotplug = false;
4393 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4394 fHotplug = true;
4395
4396 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4397 return setError(VBOX_E_INVALID_VM_STATE,
4398 tr("Controller '%s' does not support hotplugging"),
4399 aName.c_str());
4400
4401 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4402 Bstr(aName).raw(),
4403 aControllerPort,
4404 aDevice);
4405 if (!pAttach)
4406 return setError(VBOX_E_OBJECT_NOT_FOUND,
4407 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4408 aDevice, aControllerPort, aName.c_str());
4409
4410 if (fHotplug && !pAttach->i_getHotPluggable())
4411 return setError(VBOX_E_NOT_SUPPORTED,
4412 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4413 aDevice, aControllerPort, aName.c_str());
4414
4415 /*
4416 * The VM has to detach the device before we delete any implicit diffs.
4417 * If this fails we can roll back without loosing data.
4418 */
4419 if (fHotplug || fSilent)
4420 {
4421 alock.release();
4422 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4423 alock.acquire();
4424 }
4425 if (FAILED(rc)) return rc;
4426
4427 /* If we are here everything went well and we can delete the implicit now. */
4428 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4429
4430 alock.release();
4431
4432 /* Save modified registries, but skip this machine as it's the caller's
4433 * job to save its settings like all other settings changes. */
4434 mParent->i_unmarkRegistryModified(i_getId());
4435 mParent->i_saveModifiedRegistries();
4436
4437 return rc;
4438}
4439
4440HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4441 LONG aDevice, BOOL aPassthrough)
4442{
4443 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4444 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4445
4446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4447
4448 HRESULT rc = i_checkStateDependency(MutableStateDep);
4449 if (FAILED(rc)) return rc;
4450
4451 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4452
4453 if (Global::IsOnlineOrTransient(mData->mMachineState))
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Invalid machine state: %s"),
4456 Global::stringifyMachineState(mData->mMachineState));
4457
4458 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4459 Bstr(aName).raw(),
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4465 aDevice, aControllerPort, aName.c_str());
4466
4467
4468 i_setModified(IsModified_Storage);
4469 mMediaData.backup();
4470
4471 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4472
4473 if (pAttach->i_getType() != DeviceType_DVD)
4474 return setError(E_INVALIDARG,
4475 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4476 aDevice, aControllerPort, aName.c_str());
4477 pAttach->i_updatePassthrough(!!aPassthrough);
4478
4479 return S_OK;
4480}
4481
4482HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4483 LONG aDevice, BOOL aTemporaryEject)
4484{
4485
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4488
4489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4492 if (FAILED(rc)) return rc;
4493
4494 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4495 Bstr(aName).raw(),
4496 aControllerPort,
4497 aDevice);
4498 if (!pAttach)
4499 return setError(VBOX_E_OBJECT_NOT_FOUND,
4500 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4501 aDevice, aControllerPort, aName.c_str());
4502
4503
4504 i_setModified(IsModified_Storage);
4505 mMediaData.backup();
4506
4507 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4508
4509 if (pAttach->i_getType() != DeviceType_DVD)
4510 return setError(E_INVALIDARG,
4511 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4512 aDevice, aControllerPort, aName.c_str());
4513 pAttach->i_updateTempEject(!!aTemporaryEject);
4514
4515 return S_OK;
4516}
4517
4518HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4519 LONG aDevice, BOOL aNonRotational)
4520{
4521
4522 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4523 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4524
4525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4526
4527 HRESULT rc = i_checkStateDependency(MutableStateDep);
4528 if (FAILED(rc)) return rc;
4529
4530 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4531
4532 if (Global::IsOnlineOrTransient(mData->mMachineState))
4533 return setError(VBOX_E_INVALID_VM_STATE,
4534 tr("Invalid machine state: %s"),
4535 Global::stringifyMachineState(mData->mMachineState));
4536
4537 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4538 Bstr(aName).raw(),
4539 aControllerPort,
4540 aDevice);
4541 if (!pAttach)
4542 return setError(VBOX_E_OBJECT_NOT_FOUND,
4543 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4544 aDevice, aControllerPort, aName.c_str());
4545
4546
4547 i_setModified(IsModified_Storage);
4548 mMediaData.backup();
4549
4550 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4551
4552 if (pAttach->i_getType() != DeviceType_HardDisk)
4553 return setError(E_INVALIDARG,
4554 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"),
4555 aDevice, aControllerPort, aName.c_str());
4556 pAttach->i_updateNonRotational(!!aNonRotational);
4557
4558 return S_OK;
4559}
4560
4561HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4562 LONG aDevice, BOOL aDiscard)
4563{
4564
4565 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4566 aName.c_str(), aControllerPort, aDevice, aDiscard));
4567
4568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4569
4570 HRESULT rc = i_checkStateDependency(MutableStateDep);
4571 if (FAILED(rc)) return rc;
4572
4573 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4574
4575 if (Global::IsOnlineOrTransient(mData->mMachineState))
4576 return setError(VBOX_E_INVALID_VM_STATE,
4577 tr("Invalid machine state: %s"),
4578 Global::stringifyMachineState(mData->mMachineState));
4579
4580 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4581 Bstr(aName).raw(),
4582 aControllerPort,
4583 aDevice);
4584 if (!pAttach)
4585 return setError(VBOX_E_OBJECT_NOT_FOUND,
4586 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4587 aDevice, aControllerPort, aName.c_str());
4588
4589
4590 i_setModified(IsModified_Storage);
4591 mMediaData.backup();
4592
4593 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4594
4595 if (pAttach->i_getType() != DeviceType_HardDisk)
4596 return setError(E_INVALIDARG,
4597 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"),
4598 aDevice, aControllerPort, aName.c_str());
4599 pAttach->i_updateDiscard(!!aDiscard);
4600
4601 return S_OK;
4602}
4603
4604HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4605 LONG aDevice, BOOL aHotPluggable)
4606{
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4608 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 HRESULT rc = i_checkStateDependency(MutableStateDep);
4613 if (FAILED(rc)) return rc;
4614
4615 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4616
4617 if (Global::IsOnlineOrTransient(mData->mMachineState))
4618 return setError(VBOX_E_INVALID_VM_STATE,
4619 tr("Invalid machine state: %s"),
4620 Global::stringifyMachineState(mData->mMachineState));
4621
4622 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4623 Bstr(aName).raw(),
4624 aControllerPort,
4625 aDevice);
4626 if (!pAttach)
4627 return setError(VBOX_E_OBJECT_NOT_FOUND,
4628 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4629 aDevice, aControllerPort, aName.c_str());
4630
4631 /* Check for an existing controller. */
4632 ComObjPtr<StorageController> ctl;
4633 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4634 if (FAILED(rc)) return rc;
4635
4636 StorageControllerType_T ctrlType;
4637 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4638 if (FAILED(rc))
4639 return setError(E_FAIL,
4640 tr("Could not get type of controller '%s'"),
4641 aName.c_str());
4642
4643 if (!i_isControllerHotplugCapable(ctrlType))
4644 return setError(VBOX_E_NOT_SUPPORTED,
4645 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4646 aName.c_str());
4647
4648 i_setModified(IsModified_Storage);
4649 mMediaData.backup();
4650
4651 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4652
4653 if (pAttach->i_getType() == DeviceType_Floppy)
4654 return setError(E_INVALIDARG,
4655 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"),
4656 aDevice, aControllerPort, aName.c_str());
4657 pAttach->i_updateHotPluggable(!!aHotPluggable);
4658
4659 return S_OK;
4660}
4661
4662HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4663 LONG aDevice)
4664{
4665 int rc = S_OK;
4666 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4667 aName.c_str(), aControllerPort, aDevice));
4668
4669 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4670
4671 return rc;
4672}
4673
4674HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4675 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4676{
4677 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4678 aName.c_str(), aControllerPort, aDevice));
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 if (Global::IsOnlineOrTransient(mData->mMachineState))
4686 return setError(VBOX_E_INVALID_VM_STATE,
4687 tr("Invalid machine state: %s"),
4688 Global::stringifyMachineState(mData->mMachineState));
4689
4690 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4691 Bstr(aName).raw(),
4692 aControllerPort,
4693 aDevice);
4694 if (!pAttach)
4695 return setError(VBOX_E_OBJECT_NOT_FOUND,
4696 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4697 aDevice, aControllerPort, aName.c_str());
4698
4699
4700 i_setModified(IsModified_Storage);
4701 mMediaData.backup();
4702
4703 IBandwidthGroup *iB = aBandwidthGroup;
4704 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4705 if (aBandwidthGroup && group.isNull())
4706 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4707
4708 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4709
4710 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4711 if (strBandwidthGroupOld.isNotEmpty())
4712 {
4713 /* Get the bandwidth group object and release it - this must not fail. */
4714 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4715 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4716 Assert(SUCCEEDED(rc));
4717
4718 pBandwidthGroupOld->i_release();
4719 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4720 }
4721
4722 if (!group.isNull())
4723 {
4724 group->i_reference();
4725 pAttach->i_updateBandwidthGroup(group->i_getName());
4726 }
4727
4728 return S_OK;
4729}
4730
4731HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4732 LONG aControllerPort,
4733 LONG aDevice,
4734 DeviceType_T aType)
4735{
4736 HRESULT rc = S_OK;
4737
4738 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4739 aName.c_str(), aControllerPort, aDevice, aType));
4740
4741 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4742
4743 return rc;
4744}
4745
4746
4747HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4748 LONG aControllerPort,
4749 LONG aDevice,
4750 BOOL aForce)
4751{
4752 int rc = S_OK;
4753 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4754 aName.c_str(), aControllerPort, aForce));
4755
4756 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4757
4758 return rc;
4759}
4760
4761HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4762 LONG aControllerPort,
4763 LONG aDevice,
4764 const ComPtr<IMedium> &aMedium,
4765 BOOL aForce)
4766{
4767 int rc = S_OK;
4768 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4769 aName.c_str(), aControllerPort, aDevice, aForce));
4770
4771 // request the host lock first, since might be calling Host methods for getting host drives;
4772 // next, protect the media tree all the while we're in here, as well as our member variables
4773 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4774 this->lockHandle(),
4775 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4776
4777 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4778 Bstr(aName).raw(),
4779 aControllerPort,
4780 aDevice);
4781 if (pAttach.isNull())
4782 return setError(VBOX_E_OBJECT_NOT_FOUND,
4783 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4784 aDevice, aControllerPort, aName.c_str());
4785
4786 /* Remember previously mounted medium. The medium before taking the
4787 * backup is not necessarily the same thing. */
4788 ComObjPtr<Medium> oldmedium;
4789 oldmedium = pAttach->i_getMedium();
4790
4791 IMedium *iM = aMedium;
4792 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4793 if (aMedium && pMedium.isNull())
4794 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4795
4796 AutoCaller mediumCaller(pMedium);
4797 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4798
4799 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4800 if (pMedium)
4801 {
4802 DeviceType_T mediumType = pAttach->i_getType();
4803 switch (mediumType)
4804 {
4805 case DeviceType_DVD:
4806 case DeviceType_Floppy:
4807 break;
4808
4809 default:
4810 return setError(VBOX_E_INVALID_OBJECT_STATE,
4811 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4812 aControllerPort,
4813 aDevice,
4814 aName.c_str());
4815 }
4816 }
4817
4818 i_setModified(IsModified_Storage);
4819 mMediaData.backup();
4820
4821 {
4822 // The backup operation makes the pAttach reference point to the
4823 // old settings. Re-get the correct reference.
4824 pAttach = i_findAttachment(mMediaData->mAttachments,
4825 Bstr(aName).raw(),
4826 aControllerPort,
4827 aDevice);
4828 if (!oldmedium.isNull())
4829 oldmedium->i_removeBackReference(mData->mUuid);
4830 if (!pMedium.isNull())
4831 {
4832 pMedium->i_addBackReference(mData->mUuid);
4833
4834 mediumLock.release();
4835 multiLock.release();
4836 i_addMediumToRegistry(pMedium);
4837 multiLock.acquire();
4838 mediumLock.acquire();
4839 }
4840
4841 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4842 pAttach->i_updateMedium(pMedium);
4843 }
4844
4845 i_setModified(IsModified_Storage);
4846
4847 mediumLock.release();
4848 multiLock.release();
4849 rc = i_onMediumChange(pAttach, aForce);
4850 multiLock.acquire();
4851 mediumLock.acquire();
4852
4853 /* On error roll back this change only. */
4854 if (FAILED(rc))
4855 {
4856 if (!pMedium.isNull())
4857 pMedium->i_removeBackReference(mData->mUuid);
4858 pAttach = i_findAttachment(mMediaData->mAttachments,
4859 Bstr(aName).raw(),
4860 aControllerPort,
4861 aDevice);
4862 /* If the attachment is gone in the meantime, bail out. */
4863 if (pAttach.isNull())
4864 return rc;
4865 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4866 if (!oldmedium.isNull())
4867 oldmedium->i_addBackReference(mData->mUuid);
4868 pAttach->i_updateMedium(oldmedium);
4869 }
4870
4871 mediumLock.release();
4872 multiLock.release();
4873
4874 /* Save modified registries, but skip this machine as it's the caller's
4875 * job to save its settings like all other settings changes. */
4876 mParent->i_unmarkRegistryModified(i_getId());
4877 mParent->i_saveModifiedRegistries();
4878
4879 return rc;
4880}
4881HRESULT Machine::getMedium(const com::Utf8Str &aName,
4882 LONG aControllerPort,
4883 LONG aDevice,
4884 ComPtr<IMedium> &aMedium)
4885{
4886 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4887 aName.c_str(), aControllerPort, aDevice));
4888
4889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4890
4891 aMedium = NULL;
4892
4893 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4894 Bstr(aName).raw(),
4895 aControllerPort,
4896 aDevice);
4897 if (pAttach.isNull())
4898 return setError(VBOX_E_OBJECT_NOT_FOUND,
4899 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4900 aDevice, aControllerPort, aName.c_str());
4901
4902 aMedium = pAttach->i_getMedium();
4903
4904 return S_OK;
4905}
4906
4907HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4908{
4909
4910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4911
4912 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4913
4914 return S_OK;
4915}
4916
4917HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4918{
4919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4922
4923 return S_OK;
4924}
4925
4926HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4927{
4928 /* Do not assert if slot is out of range, just return the advertised
4929 status. testdriver/vbox.py triggers this in logVmInfo. */
4930 if (aSlot >= mNetworkAdapters.size())
4931 return setError(E_INVALIDARG,
4932 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4933 aSlot, mNetworkAdapters.size());
4934
4935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4936
4937 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4938
4939 return S_OK;
4940}
4941
4942HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4943{
4944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4945
4946 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4947 size_t i = 0;
4948 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4949 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4950 ++it, ++i)
4951 aKeys[i] = it->first;
4952
4953 return S_OK;
4954}
4955
4956 /**
4957 * @note Locks this object for reading.
4958 */
4959HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4960 com::Utf8Str &aValue)
4961{
4962 /* start with nothing found */
4963 aValue = "";
4964
4965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4966
4967 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4968 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4969 // found:
4970 aValue = it->second; // source is a Utf8Str
4971
4972 /* return the result to caller (may be empty) */
4973 return S_OK;
4974}
4975
4976 /**
4977 * @note Locks mParent for writing + this object for writing.
4978 */
4979HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4980{
4981 Utf8Str strOldValue; // empty
4982
4983 // locking note: we only hold the read lock briefly to look up the old value,
4984 // then release it and call the onExtraCanChange callbacks. There is a small
4985 // chance of a race insofar as the callback might be called twice if two callers
4986 // change the same key at the same time, but that's a much better solution
4987 // than the deadlock we had here before. The actual changing of the extradata
4988 // is then performed under the write lock and race-free.
4989
4990 // look up the old value first; if nothing has changed then we need not do anything
4991 {
4992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4993
4994 // For snapshots don't even think about allowing changes, extradata
4995 // is global for a machine, so there is nothing snapshot specific.
4996 if (i_isSnapshotMachine())
4997 return setError(VBOX_E_INVALID_VM_STATE,
4998 tr("Cannot set extradata for a snapshot"));
4999
5000 // check if the right IMachine instance is used
5001 if (mData->mRegistered && !i_isSessionMachine())
5002 return setError(VBOX_E_INVALID_VM_STATE,
5003 tr("Cannot set extradata for an immutable machine"));
5004
5005 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5006 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5007 strOldValue = it->second;
5008 }
5009
5010 bool fChanged;
5011 if ((fChanged = (strOldValue != aValue)))
5012 {
5013 // ask for permission from all listeners outside the locks;
5014 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5015 // lock to copy the list of callbacks to invoke
5016 Bstr error;
5017 Bstr bstrValue(aValue);
5018
5019 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5020 {
5021 const char *sep = error.isEmpty() ? "" : ": ";
5022 CBSTR err = error.raw();
5023 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5024 return setError(E_ACCESSDENIED,
5025 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5026 aKey.c_str(),
5027 aValue.c_str(),
5028 sep,
5029 err);
5030 }
5031
5032 // data is changing and change not vetoed: then write it out under the lock
5033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5034
5035 if (aValue.isEmpty())
5036 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5037 else
5038 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5039 // creates a new key if needed
5040
5041 bool fNeedsGlobalSaveSettings = false;
5042 // This saving of settings is tricky: there is no "old state" for the
5043 // extradata items at all (unlike all other settings), so the old/new
5044 // settings comparison would give a wrong result!
5045 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5046
5047 if (fNeedsGlobalSaveSettings)
5048 {
5049 // save the global settings; for that we should hold only the VirtualBox lock
5050 alock.release();
5051 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5052 mParent->i_saveSettings();
5053 }
5054 }
5055
5056 // fire notification outside the lock
5057 if (fChanged)
5058 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5059
5060 return S_OK;
5061}
5062
5063HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5064{
5065 aProgress = NULL;
5066 NOREF(aSettingsFilePath);
5067 ReturnComNotImplemented();
5068}
5069
5070HRESULT Machine::saveSettings()
5071{
5072 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5073
5074 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5075 if (FAILED(rc)) return rc;
5076
5077 /* the settings file path may never be null */
5078 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5079
5080 /* save all VM data excluding snapshots */
5081 bool fNeedsGlobalSaveSettings = false;
5082 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5083 mlock.release();
5084
5085 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5086 {
5087 // save the global settings; for that we should hold only the VirtualBox lock
5088 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5089 rc = mParent->i_saveSettings();
5090 }
5091
5092 return rc;
5093}
5094
5095
5096HRESULT Machine::discardSettings()
5097{
5098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5099
5100 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5101 if (FAILED(rc)) return rc;
5102
5103 /*
5104 * during this rollback, the session will be notified if data has
5105 * been actually changed
5106 */
5107 i_rollback(true /* aNotify */);
5108
5109 return S_OK;
5110}
5111
5112/** @note Locks objects! */
5113HRESULT Machine::unregister(AutoCaller &autoCaller,
5114 CleanupMode_T aCleanupMode,
5115 std::vector<ComPtr<IMedium> > &aMedia)
5116{
5117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5118
5119 Guid id(i_getId());
5120
5121 if (mData->mSession.mState != SessionState_Unlocked)
5122 return setError(VBOX_E_INVALID_OBJECT_STATE,
5123 tr("Cannot unregister the machine '%s' while it is locked"),
5124 mUserData->s.strName.c_str());
5125
5126 // wait for state dependents to drop to zero
5127 i_ensureNoStateDependencies();
5128
5129 if (!mData->mAccessible)
5130 {
5131 // inaccessible maschines can only be unregistered; uninitialize ourselves
5132 // here because currently there may be no unregistered that are inaccessible
5133 // (this state combination is not supported). Note releasing the caller and
5134 // leaving the lock before calling uninit()
5135 alock.release();
5136 autoCaller.release();
5137
5138 uninit();
5139
5140 mParent->i_unregisterMachine(this, id);
5141 // calls VirtualBox::i_saveSettings()
5142
5143 return S_OK;
5144 }
5145
5146 HRESULT rc = S_OK;
5147
5148 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5149 // discard saved state
5150 if (mData->mMachineState == MachineState_Saved)
5151 {
5152 // add the saved state file to the list of files the caller should delete
5153 Assert(!mSSData->strStateFilePath.isEmpty());
5154 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5155
5156 mSSData->strStateFilePath.setNull();
5157
5158 // unconditionally set the machine state to powered off, we now
5159 // know no session has locked the machine
5160 mData->mMachineState = MachineState_PoweredOff;
5161 }
5162
5163 size_t cSnapshots = 0;
5164 if (mData->mFirstSnapshot)
5165 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5166 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5167 // fail now before we start detaching media
5168 return setError(VBOX_E_INVALID_OBJECT_STATE,
5169 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5170 mUserData->s.strName.c_str(), cSnapshots);
5171
5172 // This list collects the medium objects from all medium attachments
5173 // which we will detach from the machine and its snapshots, in a specific
5174 // order which allows for closing all media without getting "media in use"
5175 // errors, simply by going through the list from the front to the back:
5176 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5177 // and must be closed before the parent media from the snapshots, or closing the parents
5178 // will fail because they still have children);
5179 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5180 // the root ("first") snapshot of the machine.
5181 MediaList llMedia;
5182
5183 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5184 && mMediaData->mAttachments.size()
5185 )
5186 {
5187 // we have media attachments: detach them all and add the Medium objects to our list
5188 if (aCleanupMode != CleanupMode_UnregisterOnly)
5189 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5190 else
5191 return setError(VBOX_E_INVALID_OBJECT_STATE,
5192 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5193 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5194 }
5195
5196 if (cSnapshots)
5197 {
5198 // add the media from the medium attachments of the snapshots to llMedia
5199 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5200 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5201 // into the children first
5202
5203 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5204 MachineState_T oldState = mData->mMachineState;
5205 mData->mMachineState = MachineState_DeletingSnapshot;
5206
5207 // make a copy of the first snapshot so the refcount does not drop to 0
5208 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5209 // because of the AutoCaller voodoo)
5210 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5211
5212 // GO!
5213 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5214
5215 mData->mMachineState = oldState;
5216 }
5217
5218 if (FAILED(rc))
5219 {
5220 i_rollbackMedia();
5221 return rc;
5222 }
5223
5224 // commit all the media changes made above
5225 i_commitMedia();
5226
5227 mData->mRegistered = false;
5228
5229 // machine lock no longer needed
5230 alock.release();
5231
5232 // return media to caller
5233 size_t i = 0;
5234 aMedia.resize(llMedia.size());
5235 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5236 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5237
5238 mParent->i_unregisterMachine(this, id);
5239 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5240
5241 return S_OK;
5242}
5243
5244/**
5245 * Task record for deleting a machine config.
5246 */
5247struct Machine::DeleteConfigTask
5248 : public Machine::Task
5249{
5250 DeleteConfigTask(Machine *m,
5251 Progress *p,
5252 const Utf8Str &t,
5253 const RTCList<ComPtr<IMedium> > &llMediums,
5254 const StringsList &llFilesToDelete)
5255 : Task(m, p, t),
5256 m_llMediums(llMediums),
5257 m_llFilesToDelete(llFilesToDelete)
5258 {}
5259
5260 void handler()
5261 {
5262 m_pMachine->i_deleteConfigHandler(*this);
5263 }
5264
5265 RTCList<ComPtr<IMedium> > m_llMediums;
5266 StringsList m_llFilesToDelete;
5267};
5268
5269/**
5270 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5271 * SessionMachine::taskHandler().
5272 *
5273 * @note Locks this object for writing.
5274 *
5275 * @param task
5276 * @return
5277 */
5278void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5279{
5280 LogFlowThisFuncEnter();
5281
5282 AutoCaller autoCaller(this);
5283 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5284 if (FAILED(autoCaller.rc()))
5285 {
5286 /* we might have been uninitialized because the session was accidentally
5287 * closed by the client, so don't assert */
5288 HRESULT rc = setError(E_FAIL,
5289 tr("The session has been accidentally closed"));
5290 task.m_pProgress->i_notifyComplete(rc);
5291 LogFlowThisFuncLeave();
5292 return;
5293 }
5294
5295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5296
5297 HRESULT rc = S_OK;
5298
5299 try
5300 {
5301 ULONG uLogHistoryCount = 3;
5302 ComPtr<ISystemProperties> systemProperties;
5303 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5304 if (FAILED(rc)) throw rc;
5305
5306 if (!systemProperties.isNull())
5307 {
5308 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5309 if (FAILED(rc)) throw rc;
5310 }
5311
5312 MachineState_T oldState = mData->mMachineState;
5313 i_setMachineState(MachineState_SettingUp);
5314 alock.release();
5315 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5316 {
5317 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5318 {
5319 AutoCaller mac(pMedium);
5320 if (FAILED(mac.rc())) throw mac.rc();
5321 Utf8Str strLocation = pMedium->i_getLocationFull();
5322 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5323 if (FAILED(rc)) throw rc;
5324 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5325 }
5326 if (pMedium->i_isMediumFormatFile())
5327 {
5328 ComPtr<IProgress> pProgress2;
5329 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5330 if (FAILED(rc)) throw rc;
5331 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5332 if (FAILED(rc)) throw rc;
5333 /* Check the result of the asynchronous process. */
5334 LONG iRc;
5335 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5336 if (FAILED(rc)) throw rc;
5337 /* If the thread of the progress object has an error, then
5338 * retrieve the error info from there, or it'll be lost. */
5339 if (FAILED(iRc))
5340 throw setError(ProgressErrorInfo(pProgress2));
5341 }
5342
5343 /* Close the medium, deliberately without checking the return
5344 * code, and without leaving any trace in the error info, as
5345 * a failure here is a very minor issue, which shouldn't happen
5346 * as above we even managed to delete the medium. */
5347 {
5348 ErrorInfoKeeper eik;
5349 pMedium->Close();
5350 }
5351 }
5352 i_setMachineState(oldState);
5353 alock.acquire();
5354
5355 // delete the files pushed on the task list by Machine::Delete()
5356 // (this includes saved states of the machine and snapshots and
5357 // medium storage files from the IMedium list passed in, and the
5358 // machine XML file)
5359 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5360 while (it != task.m_llFilesToDelete.end())
5361 {
5362 const Utf8Str &strFile = *it;
5363 LogFunc(("Deleting file %s\n", strFile.c_str()));
5364 int vrc = RTFileDelete(strFile.c_str());
5365 if (RT_FAILURE(vrc))
5366 throw setError(VBOX_E_IPRT_ERROR,
5367 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5368
5369 ++it;
5370 if (it == task.m_llFilesToDelete.end())
5371 {
5372 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5373 if (FAILED(rc)) throw rc;
5374 break;
5375 }
5376
5377 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5378 if (FAILED(rc)) throw rc;
5379 }
5380
5381 /* delete the settings only when the file actually exists */
5382 if (mData->pMachineConfigFile->fileExists())
5383 {
5384 /* Delete any backup or uncommitted XML files. Ignore failures.
5385 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5386 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5387 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5388 RTFileDelete(otherXml.c_str());
5389 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5390 RTFileDelete(otherXml.c_str());
5391
5392 /* delete the Logs folder, nothing important should be left
5393 * there (we don't check for errors because the user might have
5394 * some private files there that we don't want to delete) */
5395 Utf8Str logFolder;
5396 getLogFolder(logFolder);
5397 Assert(logFolder.length());
5398 if (RTDirExists(logFolder.c_str()))
5399 {
5400 /* Delete all VBox.log[.N] files from the Logs folder
5401 * (this must be in sync with the rotation logic in
5402 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5403 * files that may have been created by the GUI. */
5404 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5405 logFolder.c_str(), RTPATH_DELIMITER);
5406 RTFileDelete(log.c_str());
5407 log = Utf8StrFmt("%s%cVBox.png",
5408 logFolder.c_str(), RTPATH_DELIMITER);
5409 RTFileDelete(log.c_str());
5410 for (int i = uLogHistoryCount; i > 0; i--)
5411 {
5412 log = Utf8StrFmt("%s%cVBox.log.%d",
5413 logFolder.c_str(), RTPATH_DELIMITER, i);
5414 RTFileDelete(log.c_str());
5415 log = Utf8StrFmt("%s%cVBox.png.%d",
5416 logFolder.c_str(), RTPATH_DELIMITER, i);
5417 RTFileDelete(log.c_str());
5418 }
5419#if defined(RT_OS_WINDOWS)
5420 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5421 RTFileDelete(log.c_str());
5422 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5423 RTFileDelete(log.c_str());
5424#endif
5425
5426 RTDirRemove(logFolder.c_str());
5427 }
5428
5429 /* delete the Snapshots folder, nothing important should be left
5430 * there (we don't check for errors because the user might have
5431 * some private files there that we don't want to delete) */
5432 Utf8Str strFullSnapshotFolder;
5433 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5434 Assert(!strFullSnapshotFolder.isEmpty());
5435 if (RTDirExists(strFullSnapshotFolder.c_str()))
5436 RTDirRemove(strFullSnapshotFolder.c_str());
5437
5438 // delete the directory that contains the settings file, but only
5439 // if it matches the VM name
5440 Utf8Str settingsDir;
5441 if (i_isInOwnDir(&settingsDir))
5442 RTDirRemove(settingsDir.c_str());
5443 }
5444
5445 alock.release();
5446
5447 mParent->i_saveModifiedRegistries();
5448 }
5449 catch (HRESULT aRC) { rc = aRC; }
5450
5451 task.m_pProgress->i_notifyComplete(rc);
5452
5453 LogFlowThisFuncLeave();
5454}
5455
5456HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5457{
5458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5459
5460 HRESULT rc = i_checkStateDependency(MutableStateDep);
5461 if (FAILED(rc)) return rc;
5462
5463 if (mData->mRegistered)
5464 return setError(VBOX_E_INVALID_VM_STATE,
5465 tr("Cannot delete settings of a registered machine"));
5466
5467 // collect files to delete
5468 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5469 if (mData->pMachineConfigFile->fileExists())
5470 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5471
5472 RTCList<ComPtr<IMedium> > llMediums;
5473 for (size_t i = 0; i < aMedia.size(); ++i)
5474 {
5475 IMedium *pIMedium(aMedia[i]);
5476 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5477 if (pMedium.isNull())
5478 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5479 SafeArray<BSTR> ids;
5480 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5481 if (FAILED(rc)) return rc;
5482 /* At this point the medium should not have any back references
5483 * anymore. If it has it is attached to another VM and *must* not
5484 * deleted. */
5485 if (ids.size() < 1)
5486 llMediums.append(pMedium);
5487 }
5488
5489 ComObjPtr<Progress> pProgress;
5490 pProgress.createObject();
5491 rc = pProgress->init(i_getVirtualBox(),
5492 static_cast<IMachine*>(this) /* aInitiator */,
5493 Bstr(tr("Deleting files")).raw(),
5494 true /* fCancellable */,
5495 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5496 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5497 if (FAILED(rc))
5498 return rc;
5499
5500 /* create and start the task on a separate thread (note that it will not
5501 * start working until we release alock) */
5502 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5503 rc = pTask->createThread();
5504 if (FAILED(rc))
5505 return rc;
5506
5507 pProgress.queryInterfaceTo(aProgress.asOutParam());
5508
5509 LogFlowFuncLeave();
5510
5511 return S_OK;
5512}
5513
5514HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5515{
5516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5517
5518 ComObjPtr<Snapshot> pSnapshot;
5519 HRESULT rc;
5520
5521 if (aNameOrId.isEmpty())
5522 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5523 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5524 else
5525 {
5526 Guid uuid(aNameOrId);
5527 if (uuid.isValid())
5528 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5529 else
5530 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5531 }
5532 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5533
5534 return rc;
5535}
5536
5537HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5538{
5539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5540
5541 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5542 if (FAILED(rc)) return rc;
5543
5544 ComObjPtr<SharedFolder> sharedFolder;
5545 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5546 if (SUCCEEDED(rc))
5547 return setError(VBOX_E_OBJECT_IN_USE,
5548 tr("Shared folder named '%s' already exists"),
5549 aName.c_str());
5550
5551 sharedFolder.createObject();
5552 rc = sharedFolder->init(i_getMachine(),
5553 aName,
5554 aHostPath,
5555 !!aWritable,
5556 !!aAutomount,
5557 true /* fFailOnError */);
5558 if (FAILED(rc)) return rc;
5559
5560 i_setModified(IsModified_SharedFolders);
5561 mHWData.backup();
5562 mHWData->mSharedFolders.push_back(sharedFolder);
5563
5564 /* inform the direct session if any */
5565 alock.release();
5566 i_onSharedFolderChange();
5567
5568 return S_OK;
5569}
5570
5571HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5572{
5573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5576 if (FAILED(rc)) return rc;
5577
5578 ComObjPtr<SharedFolder> sharedFolder;
5579 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5580 if (FAILED(rc)) return rc;
5581
5582 i_setModified(IsModified_SharedFolders);
5583 mHWData.backup();
5584 mHWData->mSharedFolders.remove(sharedFolder);
5585
5586 /* inform the direct session if any */
5587 alock.release();
5588 i_onSharedFolderChange();
5589
5590 return S_OK;
5591}
5592
5593HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5594{
5595 /* start with No */
5596 *aCanShow = FALSE;
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601
5602 if (mData->mSession.mState != SessionState_Locked)
5603 return setError(VBOX_E_INVALID_VM_STATE,
5604 tr("Machine is not locked for session (session state: %s)"),
5605 Global::stringifySessionState(mData->mSession.mState));
5606
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 /* ignore calls made after #OnSessionEnd() is called */
5612 if (!directControl)
5613 return S_OK;
5614
5615 LONG64 dummy;
5616 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5617}
5618
5619HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5620{
5621 ComPtr<IInternalSessionControl> directControl;
5622 {
5623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5624
5625 if (mData->mSession.mState != SessionState_Locked)
5626 return setError(E_FAIL,
5627 tr("Machine is not locked for session (session state: %s)"),
5628 Global::stringifySessionState(mData->mSession.mState));
5629
5630 if (mData->mSession.mLockType == LockType_VM)
5631 directControl = mData->mSession.mDirectControl;
5632 }
5633
5634 /* ignore calls made after #OnSessionEnd() is called */
5635 if (!directControl)
5636 return S_OK;
5637
5638 BOOL dummy;
5639 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5640}
5641
5642#ifdef VBOX_WITH_GUEST_PROPS
5643/**
5644 * Look up a guest property in VBoxSVC's internal structures.
5645 */
5646HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5647 com::Utf8Str &aValue,
5648 LONG64 *aTimestamp,
5649 com::Utf8Str &aFlags) const
5650{
5651 using namespace guestProp;
5652
5653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5654 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5655
5656 if (it != mHWData->mGuestProperties.end())
5657 {
5658 char szFlags[MAX_FLAGS_LEN + 1];
5659 aValue = it->second.strValue;
5660 *aTimestamp = it->second.mTimestamp;
5661 writeFlags(it->second.mFlags, szFlags);
5662 aFlags = Utf8Str(szFlags);
5663 }
5664
5665 return S_OK;
5666}
5667
5668/**
5669 * Query the VM that a guest property belongs to for the property.
5670 * @returns E_ACCESSDENIED if the VM process is not available or not
5671 * currently handling queries and the lookup should then be done in
5672 * VBoxSVC.
5673 */
5674HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5675 com::Utf8Str &aValue,
5676 LONG64 *aTimestamp,
5677 com::Utf8Str &aFlags) const
5678{
5679 HRESULT rc = S_OK;
5680 BSTR bValue = NULL;
5681 BSTR bFlags = NULL;
5682
5683 ComPtr<IInternalSessionControl> directControl;
5684 {
5685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5686 if (mData->mSession.mLockType == LockType_VM)
5687 directControl = mData->mSession.mDirectControl;
5688 }
5689
5690 /* ignore calls made after #OnSessionEnd() is called */
5691 if (!directControl)
5692 rc = E_ACCESSDENIED;
5693 else
5694 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5695 0 /* accessMode */,
5696 &bValue, aTimestamp, &bFlags);
5697
5698 aValue = bValue;
5699 aFlags = bFlags;
5700
5701 return rc;
5702}
5703#endif // VBOX_WITH_GUEST_PROPS
5704
5705HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5706 com::Utf8Str &aValue,
5707 LONG64 *aTimestamp,
5708 com::Utf8Str &aFlags)
5709{
5710#ifndef VBOX_WITH_GUEST_PROPS
5711 ReturnComNotImplemented();
5712#else // VBOX_WITH_GUEST_PROPS
5713
5714 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5715
5716 if (rc == E_ACCESSDENIED)
5717 /* The VM is not running or the service is not (yet) accessible */
5718 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5719 return rc;
5720#endif // VBOX_WITH_GUEST_PROPS
5721}
5722
5723HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5724{
5725 LONG64 dummyTimestamp;
5726 com::Utf8Str dummyFlags;
5727 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5728 return rc;
5729
5730}
5731HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5732{
5733 com::Utf8Str dummyFlags;
5734 com::Utf8Str dummyValue;
5735 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5736 return rc;
5737}
5738
5739#ifdef VBOX_WITH_GUEST_PROPS
5740/**
5741 * Set a guest property in VBoxSVC's internal structures.
5742 */
5743HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5744 const com::Utf8Str &aFlags, bool fDelete)
5745{
5746 using namespace guestProp;
5747
5748 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5749 HRESULT rc = S_OK;
5750
5751 rc = i_checkStateDependency(MutableOrSavedStateDep);
5752 if (FAILED(rc)) return rc;
5753
5754 try
5755 {
5756 uint32_t fFlags = NILFLAG;
5757 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5758 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5759
5760 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5761 if (it == mHWData->mGuestProperties.end())
5762 {
5763 if (!fDelete)
5764 {
5765 i_setModified(IsModified_MachineData);
5766 mHWData.backupEx();
5767
5768 RTTIMESPEC time;
5769 HWData::GuestProperty prop;
5770 prop.strValue = Bstr(aValue).raw();
5771 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5772 prop.mFlags = fFlags;
5773 mHWData->mGuestProperties[aName] = prop;
5774 }
5775 }
5776 else
5777 {
5778 if (it->second.mFlags & (RDONLYHOST))
5779 {
5780 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5781 }
5782 else
5783 {
5784 i_setModified(IsModified_MachineData);
5785 mHWData.backupEx();
5786
5787 /* The backupEx() operation invalidates our iterator,
5788 * so get a new one. */
5789 it = mHWData->mGuestProperties.find(aName);
5790 Assert(it != mHWData->mGuestProperties.end());
5791
5792 if (!fDelete)
5793 {
5794 RTTIMESPEC time;
5795 it->second.strValue = aValue;
5796 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5797 it->second.mFlags = fFlags;
5798 }
5799 else
5800 mHWData->mGuestProperties.erase(it);
5801 }
5802 }
5803
5804 if (SUCCEEDED(rc))
5805 {
5806 alock.release();
5807
5808 mParent->i_onGuestPropertyChange(mData->mUuid,
5809 Bstr(aName).raw(),
5810 Bstr(aValue).raw(),
5811 Bstr(aFlags).raw());
5812 }
5813 }
5814 catch (std::bad_alloc &)
5815 {
5816 rc = E_OUTOFMEMORY;
5817 }
5818
5819 return rc;
5820}
5821
5822/**
5823 * Set a property on the VM that that property belongs to.
5824 * @returns E_ACCESSDENIED if the VM process is not available or not
5825 * currently handling queries and the setting should then be done in
5826 * VBoxSVC.
5827 */
5828HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5829 const com::Utf8Str &aFlags, bool fDelete)
5830{
5831 HRESULT rc;
5832
5833 try
5834 {
5835 ComPtr<IInternalSessionControl> directControl;
5836 {
5837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5838 if (mData->mSession.mLockType == LockType_VM)
5839 directControl = mData->mSession.mDirectControl;
5840 }
5841
5842 BSTR dummy = NULL; /* will not be changed (setter) */
5843 LONG64 dummy64;
5844 if (!directControl)
5845 rc = E_ACCESSDENIED;
5846 else
5847 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5848 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5849 fDelete? 2: 1 /* accessMode */,
5850 &dummy, &dummy64, &dummy);
5851 }
5852 catch (std::bad_alloc &)
5853 {
5854 rc = E_OUTOFMEMORY;
5855 }
5856
5857 return rc;
5858}
5859#endif // VBOX_WITH_GUEST_PROPS
5860
5861HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5862 const com::Utf8Str &aFlags)
5863{
5864#ifndef VBOX_WITH_GUEST_PROPS
5865 ReturnComNotImplemented();
5866#else // VBOX_WITH_GUEST_PROPS
5867 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5868 if (rc == E_ACCESSDENIED)
5869 /* The VM is not running or the service is not (yet) accessible */
5870 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5871 return rc;
5872#endif // VBOX_WITH_GUEST_PROPS
5873}
5874
5875HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5876{
5877 return setGuestProperty(aProperty, aValue, "");
5878}
5879
5880HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5881{
5882#ifndef VBOX_WITH_GUEST_PROPS
5883 ReturnComNotImplemented();
5884#else // VBOX_WITH_GUEST_PROPS
5885 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5886 if (rc == E_ACCESSDENIED)
5887 /* The VM is not running or the service is not (yet) accessible */
5888 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5889 return rc;
5890#endif // VBOX_WITH_GUEST_PROPS
5891}
5892
5893#ifdef VBOX_WITH_GUEST_PROPS
5894/**
5895 * Enumerate the guest properties in VBoxSVC's internal structures.
5896 */
5897HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5898 std::vector<com::Utf8Str> &aNames,
5899 std::vector<com::Utf8Str> &aValues,
5900 std::vector<LONG64> &aTimestamps,
5901 std::vector<com::Utf8Str> &aFlags)
5902{
5903 using namespace guestProp;
5904
5905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5906 Utf8Str strPatterns(aPatterns);
5907
5908 HWData::GuestPropertyMap propMap;
5909
5910 /*
5911 * Look for matching patterns and build up a list.
5912 */
5913 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5914 while (it != mHWData->mGuestProperties.end())
5915 {
5916 if ( strPatterns.isEmpty()
5917 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5918 RTSTR_MAX,
5919 it->first.c_str(),
5920 RTSTR_MAX,
5921 NULL)
5922 )
5923 propMap.insert(*it);
5924 ++it;
5925 }
5926
5927 alock.release();
5928
5929 /*
5930 * And build up the arrays for returning the property information.
5931 */
5932 size_t cEntries = propMap.size();
5933
5934 aNames.resize(cEntries);
5935 aValues.resize(cEntries);
5936 aTimestamps.resize(cEntries);
5937 aFlags.resize(cEntries);
5938
5939 char szFlags[MAX_FLAGS_LEN + 1];
5940 size_t i= 0;
5941 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5942 {
5943 aNames[i] = it->first;
5944 aValues[i] = it->second.strValue;
5945 aTimestamps[i] = it->second.mTimestamp;
5946 writeFlags(it->second.mFlags, szFlags);
5947 aFlags[i] = Utf8Str(szFlags);
5948 }
5949
5950 return S_OK;
5951}
5952
5953/**
5954 * Enumerate the properties managed by a VM.
5955 * @returns E_ACCESSDENIED if the VM process is not available or not
5956 * currently handling queries and the setting should then be done in
5957 * VBoxSVC.
5958 */
5959HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5960 std::vector<com::Utf8Str> &aNames,
5961 std::vector<com::Utf8Str> &aValues,
5962 std::vector<LONG64> &aTimestamps,
5963 std::vector<com::Utf8Str> &aFlags)
5964{
5965 HRESULT rc;
5966 ComPtr<IInternalSessionControl> directControl;
5967 {
5968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5969 if (mData->mSession.mLockType == LockType_VM)
5970 directControl = mData->mSession.mDirectControl;
5971 }
5972
5973 com::SafeArray<BSTR> bNames;
5974 com::SafeArray<BSTR> bValues;
5975 com::SafeArray<LONG64> bTimestamps;
5976 com::SafeArray<BSTR> bFlags;
5977
5978 if (!directControl)
5979 rc = E_ACCESSDENIED;
5980 else
5981 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5982 ComSafeArrayAsOutParam(bNames),
5983 ComSafeArrayAsOutParam(bValues),
5984 ComSafeArrayAsOutParam(bTimestamps),
5985 ComSafeArrayAsOutParam(bFlags));
5986 size_t i;
5987 aNames.resize(bNames.size());
5988 for (i = 0; i < bNames.size(); ++i)
5989 aNames[i] = Utf8Str(bNames[i]);
5990 aValues.resize(bValues.size());
5991 for (i = 0; i < bValues.size(); ++i)
5992 aValues[i] = Utf8Str(bValues[i]);
5993 aTimestamps.resize(bTimestamps.size());
5994 for (i = 0; i < bTimestamps.size(); ++i)
5995 aTimestamps[i] = bTimestamps[i];
5996 aFlags.resize(bFlags.size());
5997 for (i = 0; i < bFlags.size(); ++i)
5998 aFlags[i] = Utf8Str(bFlags[i]);
5999
6000 return rc;
6001}
6002#endif // VBOX_WITH_GUEST_PROPS
6003HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6004 std::vector<com::Utf8Str> &aNames,
6005 std::vector<com::Utf8Str> &aValues,
6006 std::vector<LONG64> &aTimestamps,
6007 std::vector<com::Utf8Str> &aFlags)
6008{
6009#ifndef VBOX_WITH_GUEST_PROPS
6010 ReturnComNotImplemented();
6011#else // VBOX_WITH_GUEST_PROPS
6012
6013 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6014
6015 if (rc == E_ACCESSDENIED)
6016 /* The VM is not running or the service is not (yet) accessible */
6017 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6018 return rc;
6019#endif // VBOX_WITH_GUEST_PROPS
6020}
6021
6022HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6023 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6024{
6025 MediaData::AttachmentList atts;
6026
6027 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6028 if (FAILED(rc)) return rc;
6029
6030 size_t i = 0;
6031 aMediumAttachments.resize(atts.size());
6032 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
6033 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6034
6035 return S_OK;
6036}
6037
6038HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6039 LONG aControllerPort,
6040 LONG aDevice,
6041 ComPtr<IMediumAttachment> &aAttachment)
6042{
6043 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6044 aName.c_str(), aControllerPort, aDevice));
6045
6046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6047
6048 aAttachment = NULL;
6049
6050 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
6051 Bstr(aName).raw(),
6052 aControllerPort,
6053 aDevice);
6054 if (pAttach.isNull())
6055 return setError(VBOX_E_OBJECT_NOT_FOUND,
6056 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6057 aDevice, aControllerPort, aName.c_str());
6058
6059 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6060
6061 return S_OK;
6062}
6063
6064
6065HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6066 StorageBus_T aConnectionType,
6067 ComPtr<IStorageController> &aController)
6068{
6069 if ( (aConnectionType <= StorageBus_Null)
6070 || (aConnectionType > StorageBus_USB))
6071 return setError(E_INVALIDARG,
6072 tr("Invalid connection type: %d"),
6073 aConnectionType);
6074
6075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6076
6077 HRESULT rc = i_checkStateDependency(MutableStateDep);
6078 if (FAILED(rc)) return rc;
6079
6080 /* try to find one with the name first. */
6081 ComObjPtr<StorageController> ctrl;
6082
6083 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6084 if (SUCCEEDED(rc))
6085 return setError(VBOX_E_OBJECT_IN_USE,
6086 tr("Storage controller named '%s' already exists"),
6087 aName.c_str());
6088
6089 ctrl.createObject();
6090
6091 /* get a new instance number for the storage controller */
6092 ULONG ulInstance = 0;
6093 bool fBootable = true;
6094 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6095 it != mStorageControllers->end();
6096 ++it)
6097 {
6098 if ((*it)->i_getStorageBus() == aConnectionType)
6099 {
6100 ULONG ulCurInst = (*it)->i_getInstance();
6101
6102 if (ulCurInst >= ulInstance)
6103 ulInstance = ulCurInst + 1;
6104
6105 /* Only one controller of each type can be marked as bootable. */
6106 if ((*it)->i_getBootable())
6107 fBootable = false;
6108 }
6109 }
6110
6111 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6112 if (FAILED(rc)) return rc;
6113
6114 i_setModified(IsModified_Storage);
6115 mStorageControllers.backup();
6116 mStorageControllers->push_back(ctrl);
6117
6118 ctrl.queryInterfaceTo(aController.asOutParam());
6119
6120 /* inform the direct session if any */
6121 alock.release();
6122 i_onStorageControllerChange();
6123
6124 return S_OK;
6125}
6126
6127HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6128 ComPtr<IStorageController> &aStorageController)
6129{
6130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6131
6132 ComObjPtr<StorageController> ctrl;
6133
6134 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6135 if (SUCCEEDED(rc))
6136 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6137
6138 return rc;
6139}
6140
6141HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6142 ULONG aInstance,
6143 ComPtr<IStorageController> &aStorageController)
6144{
6145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6146
6147 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6148 it != mStorageControllers->end();
6149 ++it)
6150 {
6151 if ( (*it)->i_getStorageBus() == aConnectionType
6152 && (*it)->i_getInstance() == aInstance)
6153 {
6154 (*it).queryInterfaceTo(aStorageController.asOutParam());
6155 return S_OK;
6156 }
6157 }
6158
6159 return setError(VBOX_E_OBJECT_NOT_FOUND,
6160 tr("Could not find a storage controller with instance number '%lu'"),
6161 aInstance);
6162}
6163
6164HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6165{
6166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6167
6168 HRESULT rc = i_checkStateDependency(MutableStateDep);
6169 if (FAILED(rc)) return rc;
6170
6171 ComObjPtr<StorageController> ctrl;
6172
6173 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6174 if (SUCCEEDED(rc))
6175 {
6176 /* Ensure that only one controller of each type is marked as bootable. */
6177 if (aBootable == TRUE)
6178 {
6179 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6180 it != mStorageControllers->end();
6181 ++it)
6182 {
6183 ComObjPtr<StorageController> aCtrl = (*it);
6184
6185 if ( (aCtrl->i_getName() != aName)
6186 && aCtrl->i_getBootable() == TRUE
6187 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6188 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6189 {
6190 aCtrl->i_setBootable(FALSE);
6191 break;
6192 }
6193 }
6194 }
6195
6196 if (SUCCEEDED(rc))
6197 {
6198 ctrl->i_setBootable(aBootable);
6199 i_setModified(IsModified_Storage);
6200 }
6201 }
6202
6203 if (SUCCEEDED(rc))
6204 {
6205 /* inform the direct session if any */
6206 alock.release();
6207 i_onStorageControllerChange();
6208 }
6209
6210 return rc;
6211}
6212
6213HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6214{
6215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6216
6217 HRESULT rc = i_checkStateDependency(MutableStateDep);
6218 if (FAILED(rc)) return rc;
6219
6220 ComObjPtr<StorageController> ctrl;
6221 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6222 if (FAILED(rc)) return rc;
6223
6224 {
6225 /* find all attached devices to the appropriate storage controller and detach them all */
6226 // make a temporary list because detachDevice invalidates iterators into
6227 // mMediaData->mAttachments
6228 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6229
6230 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6231 it != llAttachments2.end();
6232 ++it)
6233 {
6234 MediumAttachment *pAttachTemp = *it;
6235
6236 AutoCaller localAutoCaller(pAttachTemp);
6237 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6238
6239 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6240
6241 if (pAttachTemp->i_getControllerName() == aName)
6242 {
6243 rc = i_detachDevice(pAttachTemp, alock, NULL);
6244 if (FAILED(rc)) return rc;
6245 }
6246 }
6247 }
6248
6249 /* We can remove it now. */
6250 i_setModified(IsModified_Storage);
6251 mStorageControllers.backup();
6252
6253 ctrl->i_unshare();
6254
6255 mStorageControllers->remove(ctrl);
6256
6257 /* inform the direct session if any */
6258 alock.release();
6259 i_onStorageControllerChange();
6260
6261 return S_OK;
6262}
6263
6264HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6265 ComPtr<IUSBController> &aController)
6266{
6267 if ( (aType <= USBControllerType_Null)
6268 || (aType >= USBControllerType_Last))
6269 return setError(E_INVALIDARG,
6270 tr("Invalid USB controller type: %d"),
6271 aType);
6272
6273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 HRESULT rc = i_checkStateDependency(MutableStateDep);
6276 if (FAILED(rc)) return rc;
6277
6278 /* try to find one with the same type first. */
6279 ComObjPtr<USBController> ctrl;
6280
6281 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 return setError(VBOX_E_OBJECT_IN_USE,
6284 tr("USB controller named '%s' already exists"),
6285 aName.c_str());
6286
6287 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6288 ULONG maxInstances;
6289 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6290 if (FAILED(rc))
6291 return rc;
6292
6293 ULONG cInstances = i_getUSBControllerCountByType(aType);
6294 if (cInstances >= maxInstances)
6295 return setError(E_INVALIDARG,
6296 tr("Too many USB controllers of this type"));
6297
6298 ctrl.createObject();
6299
6300 rc = ctrl->init(this, aName, aType);
6301 if (FAILED(rc)) return rc;
6302
6303 i_setModified(IsModified_USB);
6304 mUSBControllers.backup();
6305 mUSBControllers->push_back(ctrl);
6306
6307 ctrl.queryInterfaceTo(aController.asOutParam());
6308
6309 /* inform the direct session if any */
6310 alock.release();
6311 i_onUSBControllerChange();
6312
6313 return S_OK;
6314}
6315
6316HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6317{
6318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 ComObjPtr<USBController> ctrl;
6321
6322 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6323 if (SUCCEEDED(rc))
6324 ctrl.queryInterfaceTo(aController.asOutParam());
6325
6326 return rc;
6327}
6328
6329HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6330 ULONG *aControllers)
6331{
6332 if ( (aType <= USBControllerType_Null)
6333 || (aType >= USBControllerType_Last))
6334 return setError(E_INVALIDARG,
6335 tr("Invalid USB controller type: %d"),
6336 aType);
6337
6338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6339
6340 ComObjPtr<USBController> ctrl;
6341
6342 *aControllers = i_getUSBControllerCountByType(aType);
6343
6344 return S_OK;
6345}
6346
6347HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6348{
6349
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT rc = i_checkStateDependency(MutableStateDep);
6353 if (FAILED(rc)) return rc;
6354
6355 ComObjPtr<USBController> ctrl;
6356 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6357 if (FAILED(rc)) return rc;
6358
6359 i_setModified(IsModified_USB);
6360 mUSBControllers.backup();
6361
6362 ctrl->i_unshare();
6363
6364 mUSBControllers->remove(ctrl);
6365
6366 /* inform the direct session if any */
6367 alock.release();
6368 i_onUSBControllerChange();
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6374 ULONG *aOriginX,
6375 ULONG *aOriginY,
6376 ULONG *aWidth,
6377 ULONG *aHeight,
6378 BOOL *aEnabled)
6379{
6380 uint32_t u32OriginX= 0;
6381 uint32_t u32OriginY= 0;
6382 uint32_t u32Width = 0;
6383 uint32_t u32Height = 0;
6384 uint16_t u16Flags = 0;
6385
6386 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6387 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6388 if (RT_FAILURE(vrc))
6389 {
6390#ifdef RT_OS_WINDOWS
6391 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6392 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6393 * So just assign fEnable to TRUE again.
6394 * The right fix would be to change GUI API wrappers to make sure that parameters
6395 * are changed only if API succeeds.
6396 */
6397 *aEnabled = TRUE;
6398#endif
6399 return setError(VBOX_E_IPRT_ERROR,
6400 tr("Saved guest size is not available (%Rrc)"),
6401 vrc);
6402 }
6403
6404 *aOriginX = u32OriginX;
6405 *aOriginY = u32OriginY;
6406 *aWidth = u32Width;
6407 *aHeight = u32Height;
6408 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6409
6410 return S_OK;
6411}
6412
6413HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6414 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6415{
6416 if (aScreenId != 0)
6417 return E_NOTIMPL;
6418
6419 if ( aBitmapFormat != BitmapFormat_BGR0
6420 && aBitmapFormat != BitmapFormat_BGRA
6421 && aBitmapFormat != BitmapFormat_RGBA
6422 && aBitmapFormat != BitmapFormat_PNG)
6423 return setError(E_NOTIMPL,
6424 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 uint8_t *pu8Data = NULL;
6429 uint32_t cbData = 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432
6433 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6434
6435 if (RT_FAILURE(vrc))
6436 return setError(VBOX_E_IPRT_ERROR,
6437 tr("Saved thumbnail data is not available (%Rrc)"),
6438 vrc);
6439
6440 HRESULT hr = S_OK;
6441
6442 *aWidth = u32Width;
6443 *aHeight = u32Height;
6444
6445 if (cbData > 0)
6446 {
6447 /* Convert pixels to the format expected by the API caller. */
6448 if (aBitmapFormat == BitmapFormat_BGR0)
6449 {
6450 /* [0] B, [1] G, [2] R, [3] 0. */
6451 aData.resize(cbData);
6452 memcpy(&aData.front(), pu8Data, cbData);
6453 }
6454 else if (aBitmapFormat == BitmapFormat_BGRA)
6455 {
6456 /* [0] B, [1] G, [2] R, [3] A. */
6457 aData.resize(cbData);
6458 for (uint32_t i = 0; i < cbData; i += 4)
6459 {
6460 aData[i] = pu8Data[i];
6461 aData[i + 1] = pu8Data[i + 1];
6462 aData[i + 2] = pu8Data[i + 2];
6463 aData[i + 3] = 0xff;
6464 }
6465 }
6466 else if (aBitmapFormat == BitmapFormat_RGBA)
6467 {
6468 /* [0] R, [1] G, [2] B, [3] A. */
6469 aData.resize(cbData);
6470 for (uint32_t i = 0; i < cbData; i += 4)
6471 {
6472 aData[i] = pu8Data[i + 2];
6473 aData[i + 1] = pu8Data[i + 1];
6474 aData[i + 2] = pu8Data[i];
6475 aData[i + 3] = 0xff;
6476 }
6477 }
6478 else if (aBitmapFormat == BitmapFormat_PNG)
6479 {
6480 uint8_t *pu8PNG = NULL;
6481 uint32_t cbPNG = 0;
6482 uint32_t cxPNG = 0;
6483 uint32_t cyPNG = 0;
6484
6485 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6486
6487 if (RT_SUCCESS(vrc))
6488 {
6489 aData.resize(cbPNG);
6490 if (cbPNG)
6491 memcpy(&aData.front(), pu8PNG, cbPNG);
6492 }
6493 else
6494 hr = setError(VBOX_E_IPRT_ERROR,
6495 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6496 vrc);
6497
6498 RTMemFree(pu8PNG);
6499 }
6500 }
6501
6502 freeSavedDisplayScreenshot(pu8Data);
6503
6504 return hr;
6505}
6506
6507HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6508 ULONG *aWidth,
6509 ULONG *aHeight,
6510 std::vector<BitmapFormat_T> &aBitmapFormats)
6511{
6512 if (aScreenId != 0)
6513 return E_NOTIMPL;
6514
6515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 uint8_t *pu8Data = NULL;
6518 uint32_t cbData = 0;
6519 uint32_t u32Width = 0;
6520 uint32_t u32Height = 0;
6521
6522 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6523
6524 if (RT_FAILURE(vrc))
6525 return setError(VBOX_E_IPRT_ERROR,
6526 tr("Saved screenshot data is not available (%Rrc)"),
6527 vrc);
6528
6529 *aWidth = u32Width;
6530 *aHeight = u32Height;
6531 aBitmapFormats.resize(1);
6532 aBitmapFormats[0] = BitmapFormat_PNG;
6533
6534 freeSavedDisplayScreenshot(pu8Data);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6540 BitmapFormat_T aBitmapFormat,
6541 ULONG *aWidth,
6542 ULONG *aHeight,
6543 std::vector<BYTE> &aData)
6544{
6545 if (aScreenId != 0)
6546 return E_NOTIMPL;
6547
6548 if (aBitmapFormat != BitmapFormat_PNG)
6549 return E_NOTIMPL;
6550
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 uint8_t *pu8Data = NULL;
6554 uint32_t cbData = 0;
6555 uint32_t u32Width = 0;
6556 uint32_t u32Height = 0;
6557
6558 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6559
6560 if (RT_FAILURE(vrc))
6561 return setError(VBOX_E_IPRT_ERROR,
6562 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6563 vrc);
6564
6565 *aWidth = u32Width;
6566 *aHeight = u32Height;
6567
6568 aData.resize(cbData);
6569 if (cbData)
6570 memcpy(&aData.front(), pu8Data, cbData);
6571
6572 freeSavedDisplayScreenshot(pu8Data);
6573
6574 return S_OK;
6575}
6576
6577HRESULT Machine::hotPlugCPU(ULONG aCpu)
6578{
6579 HRESULT rc = S_OK;
6580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6581
6582 if (!mHWData->mCPUHotPlugEnabled)
6583 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6584
6585 if (aCpu >= mHWData->mCPUCount)
6586 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6587
6588 if (mHWData->mCPUAttached[aCpu])
6589 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6590
6591 alock.release();
6592 rc = i_onCPUChange(aCpu, false);
6593 alock.acquire();
6594 if (FAILED(rc)) return rc;
6595
6596 i_setModified(IsModified_MachineData);
6597 mHWData.backup();
6598 mHWData->mCPUAttached[aCpu] = true;
6599
6600 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6601 if (Global::IsOnline(mData->mMachineState))
6602 i_saveSettings(NULL);
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6608{
6609 HRESULT rc = S_OK;
6610
6611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6612
6613 if (!mHWData->mCPUHotPlugEnabled)
6614 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6615
6616 if (aCpu >= SchemaDefs::MaxCPUCount)
6617 return setError(E_INVALIDARG,
6618 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6619 SchemaDefs::MaxCPUCount);
6620
6621 if (!mHWData->mCPUAttached[aCpu])
6622 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6623
6624 /* CPU 0 can't be detached */
6625 if (aCpu == 0)
6626 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6627
6628 alock.release();
6629 rc = i_onCPUChange(aCpu, true);
6630 alock.acquire();
6631 if (FAILED(rc)) return rc;
6632
6633 i_setModified(IsModified_MachineData);
6634 mHWData.backup();
6635 mHWData->mCPUAttached[aCpu] = false;
6636
6637 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6638 if (Global::IsOnline(mData->mMachineState))
6639 i_saveSettings(NULL);
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6645{
6646 *aAttached = false;
6647
6648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 /* If hotplug is enabled the CPU is always enabled. */
6651 if (!mHWData->mCPUHotPlugEnabled)
6652 {
6653 if (aCpu < mHWData->mCPUCount)
6654 *aAttached = true;
6655 }
6656 else
6657 {
6658 if (aCpu < SchemaDefs::MaxCPUCount)
6659 *aAttached = mHWData->mCPUAttached[aCpu];
6660 }
6661
6662 return S_OK;
6663}
6664
6665HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 Utf8Str log = i_getLogFilename(aIdx);
6670 if (!RTFileExists(log.c_str()))
6671 log.setNull();
6672 aFilename = log;
6673
6674 return S_OK;
6675}
6676
6677HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6678{
6679 if (aSize < 0)
6680 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT rc = S_OK;
6685 Utf8Str log = i_getLogFilename(aIdx);
6686
6687 /* do not unnecessarily hold the lock while doing something which does
6688 * not need the lock and potentially takes a long time. */
6689 alock.release();
6690
6691 /* Limit the chunk size to 32K for now, as that gives better performance
6692 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6693 * One byte expands to approx. 25 bytes of breathtaking XML. */
6694 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6695 aData.resize(cbData);
6696
6697 RTFILE LogFile;
6698 int vrc = RTFileOpen(&LogFile, log.c_str(),
6699 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6700 if (RT_SUCCESS(vrc))
6701 {
6702 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6703 if (RT_SUCCESS(vrc))
6704 aData.resize(cbData);
6705 else
6706 rc = setError(VBOX_E_IPRT_ERROR,
6707 tr("Could not read log file '%s' (%Rrc)"),
6708 log.c_str(), vrc);
6709 RTFileClose(LogFile);
6710 }
6711 else
6712 rc = setError(VBOX_E_IPRT_ERROR,
6713 tr("Could not open log file '%s' (%Rrc)"),
6714 log.c_str(), vrc);
6715
6716 if (FAILED(rc))
6717 aData.resize(0);
6718
6719 return rc;
6720}
6721
6722
6723/**
6724 * Currently this method doesn't attach device to the running VM,
6725 * just makes sure it's plugged on next VM start.
6726 */
6727HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6728{
6729 // lock scope
6730 {
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 HRESULT rc = i_checkStateDependency(MutableStateDep);
6734 if (FAILED(rc)) return rc;
6735
6736 ChipsetType_T aChipset = ChipsetType_PIIX3;
6737 COMGETTER(ChipsetType)(&aChipset);
6738
6739 if (aChipset != ChipsetType_ICH9)
6740 {
6741 return setError(E_INVALIDARG,
6742 tr("Host PCI attachment only supported with ICH9 chipset"));
6743 }
6744
6745 // check if device with this host PCI address already attached
6746 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6747 it != mHWData->mPCIDeviceAssignments.end();
6748 ++it)
6749 {
6750 LONG iHostAddress = -1;
6751 ComPtr<PCIDeviceAttachment> pAttach;
6752 pAttach = *it;
6753 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6754 if (iHostAddress == aHostAddress)
6755 return setError(E_INVALIDARG,
6756 tr("Device with host PCI address already attached to this VM"));
6757 }
6758
6759 ComObjPtr<PCIDeviceAttachment> pda;
6760 char name[32];
6761
6762 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6763 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6764 Bstr bname(name);
6765 pda.createObject();
6766 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6767 i_setModified(IsModified_MachineData);
6768 mHWData.backup();
6769 mHWData->mPCIDeviceAssignments.push_back(pda);
6770 }
6771
6772 return S_OK;
6773}
6774
6775/**
6776 * Currently this method doesn't detach device from the running VM,
6777 * just makes sure it's not plugged on next VM start.
6778 */
6779HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6780{
6781 ComObjPtr<PCIDeviceAttachment> pAttach;
6782 bool fRemoved = false;
6783 HRESULT rc;
6784
6785 // lock scope
6786 {
6787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 rc = i_checkStateDependency(MutableStateDep);
6790 if (FAILED(rc)) return rc;
6791
6792 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6793 it != mHWData->mPCIDeviceAssignments.end();
6794 ++it)
6795 {
6796 LONG iHostAddress = -1;
6797 pAttach = *it;
6798 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6799 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6800 {
6801 i_setModified(IsModified_MachineData);
6802 mHWData.backup();
6803 mHWData->mPCIDeviceAssignments.remove(pAttach);
6804 fRemoved = true;
6805 break;
6806 }
6807 }
6808 }
6809
6810
6811 /* Fire event outside of the lock */
6812 if (fRemoved)
6813 {
6814 Assert(!pAttach.isNull());
6815 ComPtr<IEventSource> es;
6816 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6817 Assert(SUCCEEDED(rc));
6818 Bstr mid;
6819 rc = this->COMGETTER(Id)(mid.asOutParam());
6820 Assert(SUCCEEDED(rc));
6821 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6822 }
6823
6824 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6825 tr("No host PCI device %08x attached"),
6826 aHostAddress
6827 );
6828}
6829
6830HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6831{
6832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6833
6834 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6835
6836 size_t i = 0;
6837 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6838 it != mHWData->mPCIDeviceAssignments.end();
6839 ++i, ++it)
6840 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6841
6842 return S_OK;
6843}
6844
6845HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6846{
6847 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6848
6849 return S_OK;
6850}
6851
6852HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6853{
6854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6855
6856 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6857
6858 return S_OK;
6859}
6860
6861HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6862{
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6865 if (SUCCEEDED(hrc))
6866 {
6867 hrc = mHWData.backupEx();
6868 if (SUCCEEDED(hrc))
6869 {
6870 i_setModified(IsModified_MachineData);
6871 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6872 }
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6878{
6879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6880 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6881 return S_OK;
6882}
6883
6884HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6885{
6886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6887 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6888 if (SUCCEEDED(hrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6894 if (SUCCEEDED(hrc))
6895 i_setModified(IsModified_MachineData);
6896 }
6897 }
6898 return hrc;
6899}
6900
6901HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6902{
6903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6904
6905 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6906
6907 return S_OK;
6908}
6909
6910HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6911{
6912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6913 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6914 if (SUCCEEDED(hrc))
6915 {
6916 hrc = mHWData.backupEx();
6917 if (SUCCEEDED(hrc))
6918 {
6919 i_setModified(IsModified_MachineData);
6920 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6921 }
6922 }
6923 return hrc;
6924}
6925
6926HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6927{
6928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6929
6930 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6931
6932 return S_OK;
6933}
6934
6935HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6936{
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938
6939 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6940 if ( SUCCEEDED(hrc)
6941 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6942 {
6943 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6944 int vrc;
6945
6946 if (aAutostartEnabled)
6947 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6948 else
6949 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6950
6951 if (RT_SUCCESS(vrc))
6952 {
6953 hrc = mHWData.backupEx();
6954 if (SUCCEEDED(hrc))
6955 {
6956 i_setModified(IsModified_MachineData);
6957 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6958 }
6959 }
6960 else if (vrc == VERR_NOT_SUPPORTED)
6961 hrc = setError(VBOX_E_NOT_SUPPORTED,
6962 tr("The VM autostart feature is not supported on this platform"));
6963 else if (vrc == VERR_PATH_NOT_FOUND)
6964 hrc = setError(E_FAIL,
6965 tr("The path to the autostart database is not set"));
6966 else
6967 hrc = setError(E_UNEXPECTED,
6968 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6969 aAutostartEnabled ? "Adding" : "Removing",
6970 mUserData->s.strName.c_str(), vrc);
6971 }
6972 return hrc;
6973}
6974
6975HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6976{
6977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6980
6981 return S_OK;
6982}
6983
6984HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6985{
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6988 if (SUCCEEDED(hrc))
6989 {
6990 hrc = mHWData.backupEx();
6991 if (SUCCEEDED(hrc))
6992 {
6993 i_setModified(IsModified_MachineData);
6994 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6995 }
6996 }
6997 return hrc;
6998}
6999
7000HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7001{
7002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7005
7006 return S_OK;
7007}
7008
7009HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7010{
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7013 if ( SUCCEEDED(hrc)
7014 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7015 {
7016 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7017 int vrc;
7018
7019 if (aAutostopType != AutostopType_Disabled)
7020 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7021 else
7022 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7023
7024 if (RT_SUCCESS(vrc))
7025 {
7026 hrc = mHWData.backupEx();
7027 if (SUCCEEDED(hrc))
7028 {
7029 i_setModified(IsModified_MachineData);
7030 mHWData->mAutostart.enmAutostopType = aAutostopType;
7031 }
7032 }
7033 else if (vrc == VERR_NOT_SUPPORTED)
7034 hrc = setError(VBOX_E_NOT_SUPPORTED,
7035 tr("The VM autostop feature is not supported on this platform"));
7036 else if (vrc == VERR_PATH_NOT_FOUND)
7037 hrc = setError(E_FAIL,
7038 tr("The path to the autostart database is not set"));
7039 else
7040 hrc = setError(E_UNEXPECTED,
7041 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7042 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7043 mUserData->s.strName.c_str(), vrc);
7044 }
7045 return hrc;
7046}
7047
7048HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7049{
7050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 aDefaultFrontend = mHWData->mDefaultFrontend;
7053
7054 return S_OK;
7055}
7056
7057HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 hrc = mHWData.backupEx();
7064 if (SUCCEEDED(hrc))
7065 {
7066 i_setModified(IsModified_MachineData);
7067 mHWData->mDefaultFrontend = aDefaultFrontend;
7068 }
7069 }
7070 return hrc;
7071}
7072
7073HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7074{
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076 size_t cbIcon = mUserData->s.ovIcon.size();
7077 aIcon.resize(cbIcon);
7078 if (cbIcon)
7079 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7080 return S_OK;
7081}
7082
7083HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7087 if (SUCCEEDED(hrc))
7088 {
7089 i_setModified(IsModified_MachineData);
7090 mUserData.backup();
7091 size_t cbIcon = aIcon.size();
7092 mUserData->s.ovIcon.resize(cbIcon);
7093 if (cbIcon)
7094 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7095 }
7096 return hrc;
7097}
7098
7099HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7100{
7101#ifdef VBOX_WITH_USB
7102 *aUSBProxyAvailable = true;
7103#else
7104 *aUSBProxyAvailable = false;
7105#endif
7106 return S_OK;
7107}
7108
7109HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7110{
7111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7112
7113 aVMProcessPriority = mUserData->s.strVMPriority;
7114
7115 return S_OK;
7116}
7117
7118HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7119{
7120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7121 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7122 if (SUCCEEDED(hrc))
7123 {
7124 /** @todo r=klaus: currently this is marked as not implemented, as
7125 * the code for setting the priority of the process is not there
7126 * (neither when starting the VM nor at runtime). */
7127 ReturnComNotImplemented();
7128 hrc = mUserData.backupEx();
7129 if (SUCCEEDED(hrc))
7130 {
7131 i_setModified(IsModified_MachineData);
7132 mUserData->s.strVMPriority = aVMProcessPriority;
7133 }
7134 }
7135 return hrc;
7136}
7137
7138HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7139 ComPtr<IProgress> &aProgress)
7140{
7141 ComObjPtr<Progress> pP;
7142 Progress *ppP = pP;
7143 IProgress *iP = static_cast<IProgress *>(ppP);
7144 IProgress **pProgress = &iP;
7145
7146 IMachine *pTarget = aTarget;
7147
7148 /* Convert the options. */
7149 RTCList<CloneOptions_T> optList;
7150 if (aOptions.size())
7151 for (size_t i = 0; i < aOptions.size(); ++i)
7152 optList.append(aOptions[i]);
7153
7154 if (optList.contains(CloneOptions_Link))
7155 {
7156 if (!i_isSnapshotMachine())
7157 return setError(E_INVALIDARG,
7158 tr("Linked clone can only be created from a snapshot"));
7159 if (aMode != CloneMode_MachineState)
7160 return setError(E_INVALIDARG,
7161 tr("Linked clone can only be created for a single machine state"));
7162 }
7163 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7164
7165 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7166
7167 HRESULT rc = pWorker->start(pProgress);
7168
7169 pP = static_cast<Progress *>(*pProgress);
7170 pP.queryInterfaceTo(aProgress.asOutParam());
7171
7172 return rc;
7173
7174}
7175
7176HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7177{
7178 NOREF(aProgress);
7179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7180
7181 // This check should always fail.
7182 HRESULT rc = i_checkStateDependency(MutableStateDep);
7183 if (FAILED(rc)) return rc;
7184
7185 AssertFailedReturn(E_NOTIMPL);
7186}
7187
7188HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7189{
7190 NOREF(aSavedStateFile);
7191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7192
7193 // This check should always fail.
7194 HRESULT rc = i_checkStateDependency(MutableStateDep);
7195 if (FAILED(rc)) return rc;
7196
7197 AssertFailedReturn(E_NOTIMPL);
7198}
7199
7200HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7201{
7202 NOREF(aFRemoveFile);
7203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 // This check should always fail.
7206 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7207 if (FAILED(rc)) return rc;
7208
7209 AssertFailedReturn(E_NOTIMPL);
7210}
7211
7212// public methods for internal purposes
7213/////////////////////////////////////////////////////////////////////////////
7214
7215/**
7216 * Adds the given IsModified_* flag to the dirty flags of the machine.
7217 * This must be called either during i_loadSettings or under the machine write lock.
7218 * @param fl
7219 */
7220void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7221{
7222 mData->flModifications |= fl;
7223 if (fAllowStateModification && i_isStateModificationAllowed())
7224 mData->mCurrentStateModified = true;
7225}
7226
7227/**
7228 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7229 * care of the write locking.
7230 *
7231 * @param fModifications The flag to add.
7232 */
7233void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7234{
7235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7236 i_setModified(fModification, fAllowStateModification);
7237}
7238
7239/**
7240 * Saves the registry entry of this machine to the given configuration node.
7241 *
7242 * @param aEntryNode Node to save the registry entry to.
7243 *
7244 * @note locks this object for reading.
7245 */
7246HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7247{
7248 AutoLimitedCaller autoCaller(this);
7249 AssertComRCReturnRC(autoCaller.rc());
7250
7251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7252
7253 data.uuid = mData->mUuid;
7254 data.strSettingsFile = mData->m_strConfigFile;
7255
7256 return S_OK;
7257}
7258
7259/**
7260 * Calculates the absolute path of the given path taking the directory of the
7261 * machine settings file as the current directory.
7262 *
7263 * @param aPath Path to calculate the absolute path for.
7264 * @param aResult Where to put the result (used only on success, can be the
7265 * same Utf8Str instance as passed in @a aPath).
7266 * @return IPRT result.
7267 *
7268 * @note Locks this object for reading.
7269 */
7270int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7271{
7272 AutoCaller autoCaller(this);
7273 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7274
7275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7276
7277 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7278
7279 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7280
7281 strSettingsDir.stripFilename();
7282 char folder[RTPATH_MAX];
7283 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7284 if (RT_SUCCESS(vrc))
7285 aResult = folder;
7286
7287 return vrc;
7288}
7289
7290/**
7291 * Copies strSource to strTarget, making it relative to the machine folder
7292 * if it is a subdirectory thereof, or simply copying it otherwise.
7293 *
7294 * @param strSource Path to evaluate and copy.
7295 * @param strTarget Buffer to receive target path.
7296 *
7297 * @note Locks this object for reading.
7298 */
7299void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7300 Utf8Str &strTarget)
7301{
7302 AutoCaller autoCaller(this);
7303 AssertComRCReturn(autoCaller.rc(), (void)0);
7304
7305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7306
7307 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7308 // use strTarget as a temporary buffer to hold the machine settings dir
7309 strTarget = mData->m_strConfigFileFull;
7310 strTarget.stripFilename();
7311 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7312 {
7313 // is relative: then append what's left
7314 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7315 // for empty paths (only possible for subdirs) use "." to avoid
7316 // triggering default settings for not present config attributes.
7317 if (strTarget.isEmpty())
7318 strTarget = ".";
7319 }
7320 else
7321 // is not relative: then overwrite
7322 strTarget = strSource;
7323}
7324
7325/**
7326 * Returns the full path to the machine's log folder in the
7327 * \a aLogFolder argument.
7328 */
7329void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7330{
7331 AutoCaller autoCaller(this);
7332 AssertComRCReturnVoid(autoCaller.rc());
7333
7334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7335
7336 char szTmp[RTPATH_MAX];
7337 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7338 if (RT_SUCCESS(vrc))
7339 {
7340 if (szTmp[0] && !mUserData.isNull())
7341 {
7342 char szTmp2[RTPATH_MAX];
7343 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7344 if (RT_SUCCESS(vrc))
7345 aLogFolder = BstrFmt("%s%c%s",
7346 szTmp2,
7347 RTPATH_DELIMITER,
7348 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7349 }
7350 else
7351 vrc = VERR_PATH_IS_RELATIVE;
7352 }
7353
7354 if (RT_FAILURE(vrc))
7355 {
7356 // fallback if VBOX_USER_LOGHOME is not set or invalid
7357 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7358 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7359 aLogFolder.append(RTPATH_DELIMITER);
7360 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7361 }
7362}
7363
7364/**
7365 * Returns the full path to the machine's log file for an given index.
7366 */
7367Utf8Str Machine::i_getLogFilename(ULONG idx)
7368{
7369 Utf8Str logFolder;
7370 getLogFolder(logFolder);
7371 Assert(logFolder.length());
7372
7373 Utf8Str log;
7374 if (idx == 0)
7375 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7376#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7377 else if (idx == 1)
7378 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7379 else
7380 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7381#else
7382 else
7383 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7384#endif
7385 return log;
7386}
7387
7388/**
7389 * Returns the full path to the machine's hardened log file.
7390 */
7391Utf8Str Machine::i_getHardeningLogFilename(void)
7392{
7393 Utf8Str strFilename;
7394 getLogFolder(strFilename);
7395 Assert(strFilename.length());
7396 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7397 return strFilename;
7398}
7399
7400
7401/**
7402 * Composes a unique saved state filename based on the current system time. The filename is
7403 * granular to the second so this will work so long as no more than one snapshot is taken on
7404 * a machine per second.
7405 *
7406 * Before version 4.1, we used this formula for saved state files:
7407 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7408 * which no longer works because saved state files can now be shared between the saved state of the
7409 * "saved" machine and an online snapshot, and the following would cause problems:
7410 * 1) save machine
7411 * 2) create online snapshot from that machine state --> reusing saved state file
7412 * 3) save machine again --> filename would be reused, breaking the online snapshot
7413 *
7414 * So instead we now use a timestamp.
7415 *
7416 * @param str
7417 */
7418
7419void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7420{
7421 AutoCaller autoCaller(this);
7422 AssertComRCReturnVoid(autoCaller.rc());
7423
7424 {
7425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7426 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7427 }
7428
7429 RTTIMESPEC ts;
7430 RTTimeNow(&ts);
7431 RTTIME time;
7432 RTTimeExplode(&time, &ts);
7433
7434 strStateFilePath += RTPATH_DELIMITER;
7435 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7436 time.i32Year, time.u8Month, time.u8MonthDay,
7437 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7438}
7439
7440/**
7441 * Returns the full path to the default video capture file.
7442 */
7443void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7444{
7445 AutoCaller autoCaller(this);
7446 AssertComRCReturnVoid(autoCaller.rc());
7447
7448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7449
7450 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7451 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7452 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7453}
7454
7455/**
7456 * Returns whether at least one USB controller is present for the VM.
7457 */
7458bool Machine::i_isUSBControllerPresent()
7459{
7460 AutoCaller autoCaller(this);
7461 AssertComRCReturn(autoCaller.rc(), false);
7462
7463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7464
7465 return (mUSBControllers->size() > 0);
7466}
7467
7468/**
7469 * @note Locks this object for writing, calls the client process
7470 * (inside the lock).
7471 */
7472HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7473 const Utf8Str &strFrontend,
7474 const Utf8Str &strEnvironment,
7475 ProgressProxy *aProgress)
7476{
7477 LogFlowThisFuncEnter();
7478
7479 AssertReturn(aControl, E_FAIL);
7480 AssertReturn(aProgress, E_FAIL);
7481 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7482
7483 AutoCaller autoCaller(this);
7484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7485
7486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7487
7488 if (!mData->mRegistered)
7489 return setError(E_UNEXPECTED,
7490 tr("The machine '%s' is not registered"),
7491 mUserData->s.strName.c_str());
7492
7493 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7494
7495 /* The process started when launching a VM with separate UI/VM processes is always
7496 * the UI process, i.e. needs special handling as it won't claim the session. */
7497 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7498
7499 if (fSeparate)
7500 {
7501 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7502 return setError(VBOX_E_INVALID_OBJECT_STATE,
7503 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7504 mUserData->s.strName.c_str());
7505 }
7506 else
7507 {
7508 if ( mData->mSession.mState == SessionState_Locked
7509 || mData->mSession.mState == SessionState_Spawning
7510 || mData->mSession.mState == SessionState_Unlocking)
7511 return setError(VBOX_E_INVALID_OBJECT_STATE,
7512 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7513 mUserData->s.strName.c_str());
7514
7515 /* may not be busy */
7516 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7517 }
7518
7519 /* get the path to the executable */
7520 char szPath[RTPATH_MAX];
7521 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7522 size_t cchBufLeft = strlen(szPath);
7523 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7524 szPath[cchBufLeft] = 0;
7525 char *pszNamePart = szPath + cchBufLeft;
7526 cchBufLeft = sizeof(szPath) - cchBufLeft;
7527
7528 int vrc = VINF_SUCCESS;
7529 RTPROCESS pid = NIL_RTPROCESS;
7530
7531 RTENV env = RTENV_DEFAULT;
7532
7533 if (!strEnvironment.isEmpty())
7534 {
7535 char *newEnvStr = NULL;
7536
7537 do
7538 {
7539 /* clone the current environment */
7540 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7541 AssertRCBreakStmt(vrc2, vrc = vrc2);
7542
7543 newEnvStr = RTStrDup(strEnvironment.c_str());
7544 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7545
7546 /* put new variables to the environment
7547 * (ignore empty variable names here since RTEnv API
7548 * intentionally doesn't do that) */
7549 char *var = newEnvStr;
7550 for (char *p = newEnvStr; *p; ++p)
7551 {
7552 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7553 {
7554 *p = '\0';
7555 if (*var)
7556 {
7557 char *val = strchr(var, '=');
7558 if (val)
7559 {
7560 *val++ = '\0';
7561 vrc2 = RTEnvSetEx(env, var, val);
7562 }
7563 else
7564 vrc2 = RTEnvUnsetEx(env, var);
7565 if (RT_FAILURE(vrc2))
7566 break;
7567 }
7568 var = p + 1;
7569 }
7570 }
7571 if (RT_SUCCESS(vrc2) && *var)
7572 vrc2 = RTEnvPutEx(env, var);
7573
7574 AssertRCBreakStmt(vrc2, vrc = vrc2);
7575 }
7576 while (0);
7577
7578 if (newEnvStr != NULL)
7579 RTStrFree(newEnvStr);
7580 }
7581
7582 /* Hardening logging */
7583#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7584 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7585 {
7586 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7587 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7588 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7589 {
7590 Utf8Str strStartupLogDir = strHardeningLogFile;
7591 strStartupLogDir.stripFilename();
7592 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7593 file without stripping the file. */
7594 }
7595 strSupHardeningLogArg.append(strHardeningLogFile);
7596
7597 /* Remove legacy log filename to avoid confusion. */
7598 Utf8Str strOldStartupLogFile;
7599 getLogFolder(strOldStartupLogFile);
7600 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7601 RTFileDelete(strOldStartupLogFile.c_str());
7602 }
7603 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7604#else
7605 const char *pszSupHardeningLogArg = NULL;
7606#endif
7607
7608 Utf8Str strCanonicalName;
7609
7610#ifdef VBOX_WITH_QTGUI
7611 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7612 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7613 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7614 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7615 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7616 {
7617 strCanonicalName = "GUI/Qt";
7618# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7619 /* Modify the base path so that we don't need to use ".." below. */
7620 RTPathStripTrailingSlash(szPath);
7621 RTPathStripFilename(szPath);
7622 cchBufLeft = strlen(szPath);
7623 pszNamePart = szPath + cchBufLeft;
7624 cchBufLeft = sizeof(szPath) - cchBufLeft;
7625
7626# define OSX_APP_NAME "VirtualBoxVM"
7627# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7628
7629 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7630 if ( strAppOverride.contains(".")
7631 || strAppOverride.contains("/")
7632 || strAppOverride.contains("\\")
7633 || strAppOverride.contains(":"))
7634 strAppOverride.setNull();
7635 Utf8Str strAppPath;
7636 if (!strAppOverride.isEmpty())
7637 {
7638 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7639 Utf8Str strFullPath(szPath);
7640 strFullPath.append(strAppPath);
7641 /* there is a race, but people using this deserve the failure */
7642 if (!RTFileExists(strFullPath.c_str()))
7643 strAppOverride.setNull();
7644 }
7645 if (strAppOverride.isEmpty())
7646 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7647 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7648 strcpy(pszNamePart, strAppPath.c_str());
7649# else
7650 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7651 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7652 strcpy(pszNamePart, s_szVirtualBox_exe);
7653# endif
7654
7655 Utf8Str idStr = mData->mUuid.toString();
7656 const char *apszArgs[] =
7657 {
7658 szPath,
7659 "--comment", mUserData->s.strName.c_str(),
7660 "--startvm", idStr.c_str(),
7661 "--no-startvm-errormsgbox",
7662 NULL, /* For "--separate". */
7663 NULL, /* For "--sup-startup-log". */
7664 NULL
7665 };
7666 unsigned iArg = 6;
7667 if (fSeparate)
7668 apszArgs[iArg++] = "--separate";
7669 apszArgs[iArg++] = pszSupHardeningLogArg;
7670
7671 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7672 }
7673#else /* !VBOX_WITH_QTGUI */
7674 if (0)
7675 ;
7676#endif /* VBOX_WITH_QTGUI */
7677
7678 else
7679
7680#ifdef VBOX_WITH_VBOXSDL
7681 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7682 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7683 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7684 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7685 {
7686 strCanonicalName = "GUI/SDL";
7687 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7688 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7689 strcpy(pszNamePart, s_szVBoxSDL_exe);
7690
7691 Utf8Str idStr = mData->mUuid.toString();
7692 const char *apszArgs[] =
7693 {
7694 szPath,
7695 "--comment", mUserData->s.strName.c_str(),
7696 "--startvm", idStr.c_str(),
7697 NULL, /* For "--separate". */
7698 NULL, /* For "--sup-startup-log". */
7699 NULL
7700 };
7701 unsigned iArg = 5;
7702 if (fSeparate)
7703 apszArgs[iArg++] = "--separate";
7704 apszArgs[iArg++] = pszSupHardeningLogArg;
7705
7706 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7707 }
7708#else /* !VBOX_WITH_VBOXSDL */
7709 if (0)
7710 ;
7711#endif /* !VBOX_WITH_VBOXSDL */
7712
7713 else
7714
7715#ifdef VBOX_WITH_HEADLESS
7716 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7717 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7718 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7719 )
7720 {
7721 strCanonicalName = "headless";
7722 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7723 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7724 * and a VM works even if the server has not been installed.
7725 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7726 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7727 * differently in 4.0 and 3.x.
7728 */
7729 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7730 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7731 strcpy(pszNamePart, s_szVBoxHeadless_exe);
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 "--vrde", "config",
7740 NULL, /* For "--capture". */
7741 NULL, /* For "--sup-startup-log". */
7742 NULL
7743 };
7744 unsigned iArg = 7;
7745 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7746 apszArgs[iArg++] = "--capture";
7747 apszArgs[iArg++] = pszSupHardeningLogArg;
7748
7749# ifdef RT_OS_WINDOWS
7750 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7751# else
7752 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7753# endif
7754 }
7755#else /* !VBOX_WITH_HEADLESS */
7756 if (0)
7757 ;
7758#endif /* !VBOX_WITH_HEADLESS */
7759 else
7760 {
7761 RTEnvDestroy(env);
7762 return setError(E_INVALIDARG,
7763 tr("Invalid frontend name: '%s'"),
7764 strFrontend.c_str());
7765 }
7766
7767 RTEnvDestroy(env);
7768
7769 if (RT_FAILURE(vrc))
7770 return setError(VBOX_E_IPRT_ERROR,
7771 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7772 mUserData->s.strName.c_str(), vrc);
7773
7774 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7775
7776 if (!fSeparate)
7777 {
7778 /*
7779 * Note that we don't release the lock here before calling the client,
7780 * because it doesn't need to call us back if called with a NULL argument.
7781 * Releasing the lock here is dangerous because we didn't prepare the
7782 * launch data yet, but the client we've just started may happen to be
7783 * too fast and call LockMachine() that will fail (because of PID, etc.),
7784 * so that the Machine will never get out of the Spawning session state.
7785 */
7786
7787 /* inform the session that it will be a remote one */
7788 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7789#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7790 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7791#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7792 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7793#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7794 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7795
7796 if (FAILED(rc))
7797 {
7798 /* restore the session state */
7799 mData->mSession.mState = SessionState_Unlocked;
7800 alock.release();
7801 mParent->i_addProcessToReap(pid);
7802 /* The failure may occur w/o any error info (from RPC), so provide one */
7803 return setError(VBOX_E_VM_ERROR,
7804 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7805 }
7806
7807 /* attach launch data to the machine */
7808 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7809 mData->mSession.mRemoteControls.push_back(aControl);
7810 mData->mSession.mProgress = aProgress;
7811 mData->mSession.mPID = pid;
7812 mData->mSession.mState = SessionState_Spawning;
7813 Assert(strCanonicalName.isNotEmpty());
7814 mData->mSession.mName = strCanonicalName;
7815 }
7816 else
7817 {
7818 /* For separate UI process we declare the launch as completed instantly, as the
7819 * actual headless VM start may or may not come. No point in remembering anything
7820 * yet, as what matters for us is when the headless VM gets started. */
7821 aProgress->i_notifyComplete(S_OK);
7822 }
7823
7824 alock.release();
7825 mParent->i_addProcessToReap(pid);
7826
7827 LogFlowThisFuncLeave();
7828 return S_OK;
7829}
7830
7831/**
7832 * Returns @c true if the given session machine instance has an open direct
7833 * session (and optionally also for direct sessions which are closing) and
7834 * returns the session control machine instance if so.
7835 *
7836 * Note that when the method returns @c false, the arguments remain unchanged.
7837 *
7838 * @param aMachine Session machine object.
7839 * @param aControl Direct session control object (optional).
7840 * @param aRequireVM If true then only allow VM sessions.
7841 * @param aAllowClosing If true then additionally a session which is currently
7842 * being closed will also be allowed.
7843 *
7844 * @note locks this object for reading.
7845 */
7846bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7847 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7848 bool aRequireVM /*= false*/,
7849 bool aAllowClosing /*= false*/)
7850{
7851 AutoLimitedCaller autoCaller(this);
7852 AssertComRCReturn(autoCaller.rc(), false);
7853
7854 /* just return false for inaccessible machines */
7855 if (getObjectState().getState() != ObjectState::Ready)
7856 return false;
7857
7858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7859
7860 if ( ( mData->mSession.mState == SessionState_Locked
7861 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7862 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7863 )
7864 {
7865 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7866
7867 aMachine = mData->mSession.mMachine;
7868
7869 if (aControl != NULL)
7870 *aControl = mData->mSession.mDirectControl;
7871
7872 return true;
7873 }
7874
7875 return false;
7876}
7877
7878/**
7879 * Returns @c true if the given machine has an spawning direct session.
7880 *
7881 * @note locks this object for reading.
7882 */
7883bool Machine::i_isSessionSpawning()
7884{
7885 AutoLimitedCaller autoCaller(this);
7886 AssertComRCReturn(autoCaller.rc(), false);
7887
7888 /* just return false for inaccessible machines */
7889 if (getObjectState().getState() != ObjectState::Ready)
7890 return false;
7891
7892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7893
7894 if (mData->mSession.mState == SessionState_Spawning)
7895 return true;
7896
7897 return false;
7898}
7899
7900/**
7901 * Called from the client watcher thread to check for unexpected client process
7902 * death during Session_Spawning state (e.g. before it successfully opened a
7903 * direct session).
7904 *
7905 * On Win32 and on OS/2, this method is called only when we've got the
7906 * direct client's process termination notification, so it always returns @c
7907 * true.
7908 *
7909 * On other platforms, this method returns @c true if the client process is
7910 * terminated and @c false if it's still alive.
7911 *
7912 * @note Locks this object for writing.
7913 */
7914bool Machine::i_checkForSpawnFailure()
7915{
7916 AutoCaller autoCaller(this);
7917 if (!autoCaller.isOk())
7918 {
7919 /* nothing to do */
7920 LogFlowThisFunc(("Already uninitialized!\n"));
7921 return true;
7922 }
7923
7924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7925
7926 if (mData->mSession.mState != SessionState_Spawning)
7927 {
7928 /* nothing to do */
7929 LogFlowThisFunc(("Not spawning any more!\n"));
7930 return true;
7931 }
7932
7933 HRESULT rc = S_OK;
7934
7935 /* PID not yet initialized, skip check. */
7936 if (mData->mSession.mPID == NIL_RTPROCESS)
7937 return false;
7938
7939 RTPROCSTATUS status;
7940 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7941
7942 if (vrc != VERR_PROCESS_RUNNING)
7943 {
7944 Utf8Str strExtraInfo;
7945
7946#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7947 /* If the startup logfile exists and is of non-zero length, tell the
7948 user to look there for more details to encourage them to attach it
7949 when reporting startup issues. */
7950 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7951 uint64_t cbStartupLogFile = 0;
7952 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7953 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7954 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7955#endif
7956
7957 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7958 rc = setError(E_FAIL,
7959 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7960 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7961 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7962 rc = setError(E_FAIL,
7963 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7964 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7965 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7966 rc = setError(E_FAIL,
7967 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7968 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7969 else
7970 rc = setError(E_FAIL,
7971 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7972 i_getName().c_str(), vrc, strExtraInfo.c_str());
7973 }
7974
7975 if (FAILED(rc))
7976 {
7977 /* Close the remote session, remove the remote control from the list
7978 * and reset session state to Closed (@note keep the code in sync with
7979 * the relevant part in LockMachine()). */
7980
7981 Assert(mData->mSession.mRemoteControls.size() == 1);
7982 if (mData->mSession.mRemoteControls.size() == 1)
7983 {
7984 ErrorInfoKeeper eik;
7985 mData->mSession.mRemoteControls.front()->Uninitialize();
7986 }
7987
7988 mData->mSession.mRemoteControls.clear();
7989 mData->mSession.mState = SessionState_Unlocked;
7990
7991 /* finalize the progress after setting the state */
7992 if (!mData->mSession.mProgress.isNull())
7993 {
7994 mData->mSession.mProgress->notifyComplete(rc);
7995 mData->mSession.mProgress.setNull();
7996 }
7997
7998 mData->mSession.mPID = NIL_RTPROCESS;
7999
8000 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8001 return true;
8002 }
8003
8004 return false;
8005}
8006
8007/**
8008 * Checks whether the machine can be registered. If so, commits and saves
8009 * all settings.
8010 *
8011 * @note Must be called from mParent's write lock. Locks this object and
8012 * children for writing.
8013 */
8014HRESULT Machine::i_prepareRegister()
8015{
8016 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8017
8018 AutoLimitedCaller autoCaller(this);
8019 AssertComRCReturnRC(autoCaller.rc());
8020
8021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 /* wait for state dependents to drop to zero */
8024 i_ensureNoStateDependencies();
8025
8026 if (!mData->mAccessible)
8027 return setError(VBOX_E_INVALID_OBJECT_STATE,
8028 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8029 mUserData->s.strName.c_str(),
8030 mData->mUuid.toString().c_str());
8031
8032 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8033
8034 if (mData->mRegistered)
8035 return setError(VBOX_E_INVALID_OBJECT_STATE,
8036 tr("The machine '%s' with UUID {%s} is already registered"),
8037 mUserData->s.strName.c_str(),
8038 mData->mUuid.toString().c_str());
8039
8040 HRESULT rc = S_OK;
8041
8042 // Ensure the settings are saved. If we are going to be registered and
8043 // no config file exists yet, create it by calling i_saveSettings() too.
8044 if ( (mData->flModifications)
8045 || (!mData->pMachineConfigFile->fileExists())
8046 )
8047 {
8048 rc = i_saveSettings(NULL);
8049 // no need to check whether VirtualBox.xml needs saving too since
8050 // we can't have a machine XML file rename pending
8051 if (FAILED(rc)) return rc;
8052 }
8053
8054 /* more config checking goes here */
8055
8056 if (SUCCEEDED(rc))
8057 {
8058 /* we may have had implicit modifications we want to fix on success */
8059 i_commit();
8060
8061 mData->mRegistered = true;
8062 }
8063 else
8064 {
8065 /* we may have had implicit modifications we want to cancel on failure*/
8066 i_rollback(false /* aNotify */);
8067 }
8068
8069 return rc;
8070}
8071
8072/**
8073 * Increases the number of objects dependent on the machine state or on the
8074 * registered state. Guarantees that these two states will not change at least
8075 * until #releaseStateDependency() is called.
8076 *
8077 * Depending on the @a aDepType value, additional state checks may be made.
8078 * These checks will set extended error info on failure. See
8079 * #checkStateDependency() for more info.
8080 *
8081 * If this method returns a failure, the dependency is not added and the caller
8082 * is not allowed to rely on any particular machine state or registration state
8083 * value and may return the failed result code to the upper level.
8084 *
8085 * @param aDepType Dependency type to add.
8086 * @param aState Current machine state (NULL if not interested).
8087 * @param aRegistered Current registered state (NULL if not interested).
8088 *
8089 * @note Locks this object for writing.
8090 */
8091HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8092 MachineState_T *aState /* = NULL */,
8093 BOOL *aRegistered /* = NULL */)
8094{
8095 AutoCaller autoCaller(this);
8096 AssertComRCReturnRC(autoCaller.rc());
8097
8098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8099
8100 HRESULT rc = i_checkStateDependency(aDepType);
8101 if (FAILED(rc)) return rc;
8102
8103 {
8104 if (mData->mMachineStateChangePending != 0)
8105 {
8106 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8107 * drop to zero so don't add more. It may make sense to wait a bit
8108 * and retry before reporting an error (since the pending state
8109 * transition should be really quick) but let's just assert for
8110 * now to see if it ever happens on practice. */
8111
8112 AssertFailed();
8113
8114 return setError(E_ACCESSDENIED,
8115 tr("Machine state change is in progress. Please retry the operation later."));
8116 }
8117
8118 ++mData->mMachineStateDeps;
8119 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8120 }
8121
8122 if (aState)
8123 *aState = mData->mMachineState;
8124 if (aRegistered)
8125 *aRegistered = mData->mRegistered;
8126
8127 return S_OK;
8128}
8129
8130/**
8131 * Decreases the number of objects dependent on the machine state.
8132 * Must always complete the #addStateDependency() call after the state
8133 * dependency is no more necessary.
8134 */
8135void Machine::i_releaseStateDependency()
8136{
8137 AutoCaller autoCaller(this);
8138 AssertComRCReturnVoid(autoCaller.rc());
8139
8140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8141
8142 /* releaseStateDependency() w/o addStateDependency()? */
8143 AssertReturnVoid(mData->mMachineStateDeps != 0);
8144 -- mData->mMachineStateDeps;
8145
8146 if (mData->mMachineStateDeps == 0)
8147 {
8148 /* inform i_ensureNoStateDependencies() that there are no more deps */
8149 if (mData->mMachineStateChangePending != 0)
8150 {
8151 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8152 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8153 }
8154 }
8155}
8156
8157Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8158{
8159 /* start with nothing found */
8160 Utf8Str strResult("");
8161
8162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8163
8164 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8165 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8166 // found:
8167 strResult = it->second; // source is a Utf8Str
8168
8169 return strResult;
8170}
8171
8172// protected methods
8173/////////////////////////////////////////////////////////////////////////////
8174
8175/**
8176 * Performs machine state checks based on the @a aDepType value. If a check
8177 * fails, this method will set extended error info, otherwise it will return
8178 * S_OK. It is supposed, that on failure, the caller will immediately return
8179 * the return value of this method to the upper level.
8180 *
8181 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8182 *
8183 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8184 * current state of this machine object allows to change settings of the
8185 * machine (i.e. the machine is not registered, or registered but not running
8186 * and not saved). It is useful to call this method from Machine setters
8187 * before performing any change.
8188 *
8189 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8190 * as for MutableStateDep except that if the machine is saved, S_OK is also
8191 * returned. This is useful in setters which allow changing machine
8192 * properties when it is in the saved state.
8193 *
8194 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8195 * if the current state of this machine object allows to change runtime
8196 * changeable settings of the machine (i.e. the machine is not registered, or
8197 * registered but either running or not running and not saved). It is useful
8198 * to call this method from Machine setters before performing any changes to
8199 * runtime changeable settings.
8200 *
8201 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8202 * the same as for MutableOrRunningStateDep except that if the machine is
8203 * saved, S_OK is also returned. This is useful in setters which allow
8204 * changing runtime and saved state changeable machine properties.
8205 *
8206 * @param aDepType Dependency type to check.
8207 *
8208 * @note Non Machine based classes should use #addStateDependency() and
8209 * #releaseStateDependency() methods or the smart AutoStateDependency
8210 * template.
8211 *
8212 * @note This method must be called from under this object's read or write
8213 * lock.
8214 */
8215HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8216{
8217 switch (aDepType)
8218 {
8219 case AnyStateDep:
8220 {
8221 break;
8222 }
8223 case MutableStateDep:
8224 {
8225 if ( mData->mRegistered
8226 && ( !i_isSessionMachine()
8227 || ( mData->mMachineState != MachineState_Aborted
8228 && mData->mMachineState != MachineState_Teleported
8229 && mData->mMachineState != MachineState_PoweredOff
8230 )
8231 )
8232 )
8233 return setError(VBOX_E_INVALID_VM_STATE,
8234 tr("The machine is not mutable (state is %s)"),
8235 Global::stringifyMachineState(mData->mMachineState));
8236 break;
8237 }
8238 case MutableOrSavedStateDep:
8239 {
8240 if ( mData->mRegistered
8241 && ( !i_isSessionMachine()
8242 || ( mData->mMachineState != MachineState_Aborted
8243 && mData->mMachineState != MachineState_Teleported
8244 && mData->mMachineState != MachineState_Saved
8245 && mData->mMachineState != MachineState_PoweredOff
8246 )
8247 )
8248 )
8249 return setError(VBOX_E_INVALID_VM_STATE,
8250 tr("The machine is not mutable or saved (state is %s)"),
8251 Global::stringifyMachineState(mData->mMachineState));
8252 break;
8253 }
8254 case MutableOrRunningStateDep:
8255 {
8256 if ( mData->mRegistered
8257 && ( !i_isSessionMachine()
8258 || ( mData->mMachineState != MachineState_Aborted
8259 && mData->mMachineState != MachineState_Teleported
8260 && mData->mMachineState != MachineState_PoweredOff
8261 && !Global::IsOnline(mData->mMachineState)
8262 )
8263 )
8264 )
8265 return setError(VBOX_E_INVALID_VM_STATE,
8266 tr("The machine is not mutable or running (state is %s)"),
8267 Global::stringifyMachineState(mData->mMachineState));
8268 break;
8269 }
8270 case MutableOrSavedOrRunningStateDep:
8271 {
8272 if ( mData->mRegistered
8273 && ( !i_isSessionMachine()
8274 || ( mData->mMachineState != MachineState_Aborted
8275 && mData->mMachineState != MachineState_Teleported
8276 && mData->mMachineState != MachineState_Saved
8277 && mData->mMachineState != MachineState_PoweredOff
8278 && !Global::IsOnline(mData->mMachineState)
8279 )
8280 )
8281 )
8282 return setError(VBOX_E_INVALID_VM_STATE,
8283 tr("The machine is not mutable, saved or running (state is %s)"),
8284 Global::stringifyMachineState(mData->mMachineState));
8285 break;
8286 }
8287 }
8288
8289 return S_OK;
8290}
8291
8292/**
8293 * Helper to initialize all associated child objects and allocate data
8294 * structures.
8295 *
8296 * This method must be called as a part of the object's initialization procedure
8297 * (usually done in the #init() method).
8298 *
8299 * @note Must be called only from #init() or from #registeredInit().
8300 */
8301HRESULT Machine::initDataAndChildObjects()
8302{
8303 AutoCaller autoCaller(this);
8304 AssertComRCReturnRC(autoCaller.rc());
8305 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8306 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8307
8308 AssertReturn(!mData->mAccessible, E_FAIL);
8309
8310 /* allocate data structures */
8311 mSSData.allocate();
8312 mUserData.allocate();
8313 mHWData.allocate();
8314 mMediaData.allocate();
8315 mStorageControllers.allocate();
8316 mUSBControllers.allocate();
8317
8318 /* initialize mOSTypeId */
8319 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8320
8321 /* create associated BIOS settings object */
8322 unconst(mBIOSSettings).createObject();
8323 mBIOSSettings->init(this);
8324
8325 /* create an associated VRDE object (default is disabled) */
8326 unconst(mVRDEServer).createObject();
8327 mVRDEServer->init(this);
8328
8329 /* create associated serial port objects */
8330 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8331 {
8332 unconst(mSerialPorts[slot]).createObject();
8333 mSerialPorts[slot]->init(this, slot);
8334 }
8335
8336 /* create associated parallel port objects */
8337 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8338 {
8339 unconst(mParallelPorts[slot]).createObject();
8340 mParallelPorts[slot]->init(this, slot);
8341 }
8342
8343 /* create the audio adapter object (always present, default is disabled) */
8344 unconst(mAudioAdapter).createObject();
8345 mAudioAdapter->init(this);
8346
8347 /* create the USB device filters object (always present) */
8348 unconst(mUSBDeviceFilters).createObject();
8349 mUSBDeviceFilters->init(this);
8350
8351 /* create associated network adapter objects */
8352 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8353 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8354 {
8355 unconst(mNetworkAdapters[slot]).createObject();
8356 mNetworkAdapters[slot]->init(this, slot);
8357 }
8358
8359 /* create the bandwidth control */
8360 unconst(mBandwidthControl).createObject();
8361 mBandwidthControl->init(this);
8362
8363 return S_OK;
8364}
8365
8366/**
8367 * Helper to uninitialize all associated child objects and to free all data
8368 * structures.
8369 *
8370 * This method must be called as a part of the object's uninitialization
8371 * procedure (usually done in the #uninit() method).
8372 *
8373 * @note Must be called only from #uninit() or from #registeredInit().
8374 */
8375void Machine::uninitDataAndChildObjects()
8376{
8377 AutoCaller autoCaller(this);
8378 AssertComRCReturnVoid(autoCaller.rc());
8379 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8380 || getObjectState().getState() == ObjectState::Limited);
8381
8382 /* tell all our other child objects we've been uninitialized */
8383 if (mBandwidthControl)
8384 {
8385 mBandwidthControl->uninit();
8386 unconst(mBandwidthControl).setNull();
8387 }
8388
8389 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8390 {
8391 if (mNetworkAdapters[slot])
8392 {
8393 mNetworkAdapters[slot]->uninit();
8394 unconst(mNetworkAdapters[slot]).setNull();
8395 }
8396 }
8397
8398 if (mUSBDeviceFilters)
8399 {
8400 mUSBDeviceFilters->uninit();
8401 unconst(mUSBDeviceFilters).setNull();
8402 }
8403
8404 if (mAudioAdapter)
8405 {
8406 mAudioAdapter->uninit();
8407 unconst(mAudioAdapter).setNull();
8408 }
8409
8410 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8411 {
8412 if (mParallelPorts[slot])
8413 {
8414 mParallelPorts[slot]->uninit();
8415 unconst(mParallelPorts[slot]).setNull();
8416 }
8417 }
8418
8419 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8420 {
8421 if (mSerialPorts[slot])
8422 {
8423 mSerialPorts[slot]->uninit();
8424 unconst(mSerialPorts[slot]).setNull();
8425 }
8426 }
8427
8428 if (mVRDEServer)
8429 {
8430 mVRDEServer->uninit();
8431 unconst(mVRDEServer).setNull();
8432 }
8433
8434 if (mBIOSSettings)
8435 {
8436 mBIOSSettings->uninit();
8437 unconst(mBIOSSettings).setNull();
8438 }
8439
8440 /* Deassociate media (only when a real Machine or a SnapshotMachine
8441 * instance is uninitialized; SessionMachine instances refer to real
8442 * Machine media). This is necessary for a clean re-initialization of
8443 * the VM after successfully re-checking the accessibility state. Note
8444 * that in case of normal Machine or SnapshotMachine uninitialization (as
8445 * a result of unregistering or deleting the snapshot), outdated media
8446 * attachments will already be uninitialized and deleted, so this
8447 * code will not affect them. */
8448 if ( !!mMediaData
8449 && (!i_isSessionMachine())
8450 )
8451 {
8452 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8453 it != mMediaData->mAttachments.end();
8454 ++it)
8455 {
8456 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8457 if (pMedium.isNull())
8458 continue;
8459 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8460 AssertComRC(rc);
8461 }
8462 }
8463
8464 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8465 {
8466 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8467 if (mData->mFirstSnapshot)
8468 {
8469 // snapshots tree is protected by machine write lock; strictly
8470 // this isn't necessary here since we're deleting the entire
8471 // machine, but otherwise we assert in Snapshot::uninit()
8472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8473 mData->mFirstSnapshot->uninit();
8474 mData->mFirstSnapshot.setNull();
8475 }
8476
8477 mData->mCurrentSnapshot.setNull();
8478 }
8479
8480 /* free data structures (the essential mData structure is not freed here
8481 * since it may be still in use) */
8482 mMediaData.free();
8483 mStorageControllers.free();
8484 mUSBControllers.free();
8485 mHWData.free();
8486 mUserData.free();
8487 mSSData.free();
8488}
8489
8490/**
8491 * Returns a pointer to the Machine object for this machine that acts like a
8492 * parent for complex machine data objects such as shared folders, etc.
8493 *
8494 * For primary Machine objects and for SnapshotMachine objects, returns this
8495 * object's pointer itself. For SessionMachine objects, returns the peer
8496 * (primary) machine pointer.
8497 */
8498Machine* Machine::i_getMachine()
8499{
8500 if (i_isSessionMachine())
8501 return (Machine*)mPeer;
8502 return this;
8503}
8504
8505/**
8506 * Makes sure that there are no machine state dependents. If necessary, waits
8507 * for the number of dependents to drop to zero.
8508 *
8509 * Make sure this method is called from under this object's write lock to
8510 * guarantee that no new dependents may be added when this method returns
8511 * control to the caller.
8512 *
8513 * @note Locks this object for writing. The lock will be released while waiting
8514 * (if necessary).
8515 *
8516 * @warning To be used only in methods that change the machine state!
8517 */
8518void Machine::i_ensureNoStateDependencies()
8519{
8520 AssertReturnVoid(isWriteLockOnCurrentThread());
8521
8522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8523
8524 /* Wait for all state dependents if necessary */
8525 if (mData->mMachineStateDeps != 0)
8526 {
8527 /* lazy semaphore creation */
8528 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8529 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8530
8531 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8532 mData->mMachineStateDeps));
8533
8534 ++mData->mMachineStateChangePending;
8535
8536 /* reset the semaphore before waiting, the last dependent will signal
8537 * it */
8538 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8539
8540 alock.release();
8541
8542 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8543
8544 alock.acquire();
8545
8546 -- mData->mMachineStateChangePending;
8547 }
8548}
8549
8550/**
8551 * Changes the machine state and informs callbacks.
8552 *
8553 * This method is not intended to fail so it either returns S_OK or asserts (and
8554 * returns a failure).
8555 *
8556 * @note Locks this object for writing.
8557 */
8558HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8559{
8560 LogFlowThisFuncEnter();
8561 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8562 Assert(aMachineState != MachineState_Null);
8563
8564 AutoCaller autoCaller(this);
8565 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8566
8567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8568
8569 /* wait for state dependents to drop to zero */
8570 i_ensureNoStateDependencies();
8571
8572 MachineState_T const enmOldState = mData->mMachineState;
8573 if (enmOldState != aMachineState)
8574 {
8575 mData->mMachineState = aMachineState;
8576 RTTimeNow(&mData->mLastStateChange);
8577
8578#ifdef VBOX_WITH_DTRACE_R3_MAIN
8579 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8580#endif
8581 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8582 }
8583
8584 LogFlowThisFuncLeave();
8585 return S_OK;
8586}
8587
8588/**
8589 * Searches for a shared folder with the given logical name
8590 * in the collection of shared folders.
8591 *
8592 * @param aName logical name of the shared folder
8593 * @param aSharedFolder where to return the found object
8594 * @param aSetError whether to set the error info if the folder is
8595 * not found
8596 * @return
8597 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8598 *
8599 * @note
8600 * must be called from under the object's lock!
8601 */
8602HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8603 ComObjPtr<SharedFolder> &aSharedFolder,
8604 bool aSetError /* = false */)
8605{
8606 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8607 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8608 it != mHWData->mSharedFolders.end();
8609 ++it)
8610 {
8611 SharedFolder *pSF = *it;
8612 AutoCaller autoCaller(pSF);
8613 if (pSF->i_getName() == aName)
8614 {
8615 aSharedFolder = pSF;
8616 rc = S_OK;
8617 break;
8618 }
8619 }
8620
8621 if (aSetError && FAILED(rc))
8622 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8623
8624 return rc;
8625}
8626
8627/**
8628 * Initializes all machine instance data from the given settings structures
8629 * from XML. The exception is the machine UUID which needs special handling
8630 * depending on the caller's use case, so the caller needs to set that herself.
8631 *
8632 * This gets called in several contexts during machine initialization:
8633 *
8634 * -- When machine XML exists on disk already and needs to be loaded into memory,
8635 * for example, from registeredInit() to load all registered machines on
8636 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8637 * attached to the machine should be part of some media registry already.
8638 *
8639 * -- During OVF import, when a machine config has been constructed from an
8640 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8641 * ensure that the media listed as attachments in the config (which have
8642 * been imported from the OVF) receive the correct registry ID.
8643 *
8644 * -- During VM cloning.
8645 *
8646 * @param config Machine settings from XML.
8647 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8648 * for each attached medium in the config.
8649 * @return
8650 */
8651HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8652 const Guid *puuidRegistry)
8653{
8654 // copy name, description, OS type, teleporter, UTC etc.
8655 mUserData->s = config.machineUserData;
8656
8657 // look up the object by Id to check it is valid
8658 ComPtr<IGuestOSType> guestOSType;
8659 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8660 guestOSType.asOutParam());
8661 if (FAILED(rc)) return rc;
8662
8663 // stateFile (optional)
8664 if (config.strStateFile.isEmpty())
8665 mSSData->strStateFilePath.setNull();
8666 else
8667 {
8668 Utf8Str stateFilePathFull(config.strStateFile);
8669 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8670 if (RT_FAILURE(vrc))
8671 return setError(E_FAIL,
8672 tr("Invalid saved state file path '%s' (%Rrc)"),
8673 config.strStateFile.c_str(),
8674 vrc);
8675 mSSData->strStateFilePath = stateFilePathFull;
8676 }
8677
8678 // snapshot folder needs special processing so set it again
8679 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8680 if (FAILED(rc)) return rc;
8681
8682 /* Copy the extra data items (Not in any case config is already the same as
8683 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8684 * make sure the extra data map is copied). */
8685 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8686
8687 /* currentStateModified (optional, default is true) */
8688 mData->mCurrentStateModified = config.fCurrentStateModified;
8689
8690 mData->mLastStateChange = config.timeLastStateChange;
8691
8692 /*
8693 * note: all mUserData members must be assigned prior this point because
8694 * we need to commit changes in order to let mUserData be shared by all
8695 * snapshot machine instances.
8696 */
8697 mUserData.commitCopy();
8698
8699 // machine registry, if present (must be loaded before snapshots)
8700 if (config.canHaveOwnMediaRegistry())
8701 {
8702 // determine machine folder
8703 Utf8Str strMachineFolder = i_getSettingsFileFull();
8704 strMachineFolder.stripFilename();
8705 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8706 config.mediaRegistry,
8707 strMachineFolder);
8708 if (FAILED(rc)) return rc;
8709 }
8710
8711 /* Snapshot node (optional) */
8712 size_t cRootSnapshots;
8713 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8714 {
8715 // there must be only one root snapshot
8716 Assert(cRootSnapshots == 1);
8717
8718 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8719
8720 rc = i_loadSnapshot(snap,
8721 config.uuidCurrentSnapshot,
8722 NULL); // no parent == first snapshot
8723 if (FAILED(rc)) return rc;
8724 }
8725
8726 // hardware data
8727 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8728 if (FAILED(rc)) return rc;
8729
8730 /*
8731 * NOTE: the assignment below must be the last thing to do,
8732 * otherwise it will be not possible to change the settings
8733 * somewhere in the code above because all setters will be
8734 * blocked by i_checkStateDependency(MutableStateDep).
8735 */
8736
8737 /* set the machine state to Aborted or Saved when appropriate */
8738 if (config.fAborted)
8739 {
8740 mSSData->strStateFilePath.setNull();
8741
8742 /* no need to use i_setMachineState() during init() */
8743 mData->mMachineState = MachineState_Aborted;
8744 }
8745 else if (!mSSData->strStateFilePath.isEmpty())
8746 {
8747 /* no need to use i_setMachineState() during init() */
8748 mData->mMachineState = MachineState_Saved;
8749 }
8750
8751 // after loading settings, we are no longer different from the XML on disk
8752 mData->flModifications = 0;
8753
8754 return S_OK;
8755}
8756
8757/**
8758 * Recursively loads all snapshots starting from the given.
8759 *
8760 * @param aNode <Snapshot> node.
8761 * @param aCurSnapshotId Current snapshot ID from the settings file.
8762 * @param aParentSnapshot Parent snapshot.
8763 */
8764HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8765 const Guid &aCurSnapshotId,
8766 Snapshot *aParentSnapshot)
8767{
8768 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8769 AssertReturn(!i_isSessionMachine(), E_FAIL);
8770
8771 HRESULT rc = S_OK;
8772
8773 Utf8Str strStateFile;
8774 if (!data.strStateFile.isEmpty())
8775 {
8776 /* optional */
8777 strStateFile = data.strStateFile;
8778 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8779 if (RT_FAILURE(vrc))
8780 return setError(E_FAIL,
8781 tr("Invalid saved state file path '%s' (%Rrc)"),
8782 strStateFile.c_str(),
8783 vrc);
8784 }
8785
8786 /* create a snapshot machine object */
8787 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8788 pSnapshotMachine.createObject();
8789 rc = pSnapshotMachine->initFromSettings(this,
8790 data.hardware,
8791 &data.debugging,
8792 &data.autostart,
8793 data.uuid.ref(),
8794 strStateFile);
8795 if (FAILED(rc)) return rc;
8796
8797 /* create a snapshot object */
8798 ComObjPtr<Snapshot> pSnapshot;
8799 pSnapshot.createObject();
8800 /* initialize the snapshot */
8801 rc = pSnapshot->init(mParent, // VirtualBox object
8802 data.uuid,
8803 data.strName,
8804 data.strDescription,
8805 data.timestamp,
8806 pSnapshotMachine,
8807 aParentSnapshot);
8808 if (FAILED(rc)) return rc;
8809
8810 /* memorize the first snapshot if necessary */
8811 if (!mData->mFirstSnapshot)
8812 mData->mFirstSnapshot = pSnapshot;
8813
8814 /* memorize the current snapshot when appropriate */
8815 if ( !mData->mCurrentSnapshot
8816 && pSnapshot->i_getId() == aCurSnapshotId
8817 )
8818 mData->mCurrentSnapshot = pSnapshot;
8819
8820 // now create the children
8821 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8822 it != data.llChildSnapshots.end();
8823 ++it)
8824 {
8825 const settings::Snapshot &childData = *it;
8826 // recurse
8827 rc = i_loadSnapshot(childData,
8828 aCurSnapshotId,
8829 pSnapshot); // parent = the one we created above
8830 if (FAILED(rc)) return rc;
8831 }
8832
8833 return rc;
8834}
8835
8836/**
8837 * Loads settings into mHWData.
8838 *
8839 * @param data Reference to the hardware settings.
8840 * @param pDbg Pointer to the debugging settings.
8841 * @param pAutostart Pointer to the autostart settings.
8842 */
8843HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8844 const Guid *puuidSnapshot,
8845 const settings::Hardware &data,
8846 const settings::Debugging *pDbg,
8847 const settings::Autostart *pAutostart)
8848{
8849 AssertReturn(!i_isSessionMachine(), E_FAIL);
8850
8851 HRESULT rc = S_OK;
8852
8853 try
8854 {
8855 /* The hardware version attribute (optional). */
8856 mHWData->mHWVersion = data.strVersion;
8857 mHWData->mHardwareUUID = data.uuid;
8858
8859 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8860 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8861 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8862 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8863 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8864 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8865 mHWData->mPAEEnabled = data.fPAE;
8866 mHWData->mLongMode = data.enmLongMode;
8867 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8868 mHWData->mAPIC = data.fAPIC;
8869 mHWData->mX2APIC = data.fX2APIC;
8870 mHWData->mCPUCount = data.cCPUs;
8871 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8872 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8873 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8874 mHWData->mCpuProfile = data.strCpuProfile;
8875
8876 // cpu
8877 if (mHWData->mCPUHotPlugEnabled)
8878 {
8879 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8880 it != data.llCpus.end();
8881 ++it)
8882 {
8883 const settings::Cpu &cpu = *it;
8884
8885 mHWData->mCPUAttached[cpu.ulId] = true;
8886 }
8887 }
8888
8889 // cpuid leafs
8890 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8891 it != data.llCpuIdLeafs.end();
8892 ++it)
8893 {
8894 const settings::CpuIdLeaf &leaf = *it;
8895
8896 switch (leaf.ulId)
8897 {
8898 case 0x0:
8899 case 0x1:
8900 case 0x2:
8901 case 0x3:
8902 case 0x4:
8903 case 0x5:
8904 case 0x6:
8905 case 0x7:
8906 case 0x8:
8907 case 0x9:
8908 case 0xA:
8909 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8910 break;
8911
8912 case 0x80000000:
8913 case 0x80000001:
8914 case 0x80000002:
8915 case 0x80000003:
8916 case 0x80000004:
8917 case 0x80000005:
8918 case 0x80000006:
8919 case 0x80000007:
8920 case 0x80000008:
8921 case 0x80000009:
8922 case 0x8000000A:
8923 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8924 break;
8925
8926 default:
8927 /* just ignore */
8928 break;
8929 }
8930 }
8931
8932 mHWData->mMemorySize = data.ulMemorySizeMB;
8933 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8934
8935 // boot order
8936 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8937 {
8938 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8939 if (it == data.mapBootOrder.end())
8940 mHWData->mBootOrder[i] = DeviceType_Null;
8941 else
8942 mHWData->mBootOrder[i] = it->second;
8943 }
8944
8945 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8946 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8947 mHWData->mMonitorCount = data.cMonitors;
8948 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8949 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8950 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8951 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8952 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8953 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8954 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8955 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8956 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8957 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8958 if (!data.strVideoCaptureFile.isEmpty())
8959 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8960 else
8961 mHWData->mVideoCaptureFile.setNull();
8962 mHWData->mFirmwareType = data.firmwareType;
8963 mHWData->mPointingHIDType = data.pointingHIDType;
8964 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8965 mHWData->mChipsetType = data.chipsetType;
8966 mHWData->mParavirtProvider = data.paravirtProvider;
8967 mHWData->mParavirtDebug = data.strParavirtDebug;
8968 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8969 mHWData->mHPETEnabled = data.fHPETEnabled;
8970
8971 /* VRDEServer */
8972 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8973 if (FAILED(rc)) return rc;
8974
8975 /* BIOS */
8976 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8977 if (FAILED(rc)) return rc;
8978
8979 // Bandwidth control (must come before network adapters)
8980 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8981 if (FAILED(rc)) return rc;
8982
8983 /* Shared folders */
8984 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8985 it != data.usbSettings.llUSBControllers.end();
8986 ++it)
8987 {
8988 const settings::USBController &settingsCtrl = *it;
8989 ComObjPtr<USBController> newCtrl;
8990
8991 newCtrl.createObject();
8992 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8993 mUSBControllers->push_back(newCtrl);
8994 }
8995
8996 /* USB device filters */
8997 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8998 if (FAILED(rc)) return rc;
8999
9000 // network adapters
9001 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9002 size_t oldCount = mNetworkAdapters.size();
9003 if (newCount > oldCount)
9004 {
9005 mNetworkAdapters.resize(newCount);
9006 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9007 {
9008 unconst(mNetworkAdapters[slot]).createObject();
9009 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9010 }
9011 }
9012 else if (newCount < oldCount)
9013 mNetworkAdapters.resize(newCount);
9014 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9015 it != data.llNetworkAdapters.end();
9016 ++it)
9017 {
9018 const settings::NetworkAdapter &nic = *it;
9019
9020 /* slot unicity is guaranteed by XML Schema */
9021 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9022 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9023 if (FAILED(rc)) return rc;
9024 }
9025
9026 // serial ports
9027 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9028 it != data.llSerialPorts.end();
9029 ++it)
9030 {
9031 const settings::SerialPort &s = *it;
9032
9033 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9034 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9035 if (FAILED(rc)) return rc;
9036 }
9037
9038 // parallel ports (optional)
9039 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9040 it != data.llParallelPorts.end();
9041 ++it)
9042 {
9043 const settings::ParallelPort &p = *it;
9044
9045 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9046 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9047 if (FAILED(rc)) return rc;
9048 }
9049
9050 /* AudioAdapter */
9051 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9052 if (FAILED(rc)) return rc;
9053
9054 /* storage controllers */
9055 rc = i_loadStorageControllers(data.storage,
9056 puuidRegistry,
9057 puuidSnapshot);
9058 if (FAILED(rc)) return rc;
9059
9060 /* Shared folders */
9061 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9062 it != data.llSharedFolders.end();
9063 ++it)
9064 {
9065 const settings::SharedFolder &sf = *it;
9066
9067 ComObjPtr<SharedFolder> sharedFolder;
9068 /* Check for double entries. Not allowed! */
9069 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9070 if (SUCCEEDED(rc))
9071 return setError(VBOX_E_OBJECT_IN_USE,
9072 tr("Shared folder named '%s' already exists"),
9073 sf.strName.c_str());
9074
9075 /* Create the new shared folder. Don't break on error. This will be
9076 * reported when the machine starts. */
9077 sharedFolder.createObject();
9078 rc = sharedFolder->init(i_getMachine(),
9079 sf.strName,
9080 sf.strHostPath,
9081 RT_BOOL(sf.fWritable),
9082 RT_BOOL(sf.fAutoMount),
9083 false /* fFailOnError */);
9084 if (FAILED(rc)) return rc;
9085 mHWData->mSharedFolders.push_back(sharedFolder);
9086 }
9087
9088 // Clipboard
9089 mHWData->mClipboardMode = data.clipboardMode;
9090
9091 // drag'n'drop
9092 mHWData->mDnDMode = data.dndMode;
9093
9094 // guest settings
9095 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9096
9097 // IO settings
9098 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9099 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9100
9101 // Host PCI devices
9102 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9103 it != data.pciAttachments.end();
9104 ++it)
9105 {
9106 const settings::HostPCIDeviceAttachment &hpda = *it;
9107 ComObjPtr<PCIDeviceAttachment> pda;
9108
9109 pda.createObject();
9110 pda->i_loadSettings(this, hpda);
9111 mHWData->mPCIDeviceAssignments.push_back(pda);
9112 }
9113
9114 /*
9115 * (The following isn't really real hardware, but it lives in HWData
9116 * for reasons of convenience.)
9117 */
9118
9119#ifdef VBOX_WITH_GUEST_PROPS
9120 /* Guest properties (optional) */
9121
9122 /* Only load transient guest properties for configs which have saved
9123 * state, because there shouldn't be any for powered off VMs. The same
9124 * logic applies for snapshots, as offline snapshots shouldn't have
9125 * any such properties. They confuse the code in various places.
9126 * Note: can't rely on the machine state, as it isn't set yet. */
9127 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9128 /* apologies for the hacky unconst() usage, but this needs hacking
9129 * actually inconsistent settings into consistency, otherwise there
9130 * will be some corner cases where the inconsistency survives
9131 * surprisingly long without getting fixed, especially for snapshots
9132 * as there are no config changes. */
9133 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9134 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9135 it != llGuestProperties.end();
9136 /*nothing*/)
9137 {
9138 const settings::GuestProperty &prop = *it;
9139 uint32_t fFlags = guestProp::NILFLAG;
9140 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9141 if ( fSkipTransientGuestProperties
9142 && ( fFlags & guestProp::TRANSIENT
9143 || fFlags & guestProp::TRANSRESET))
9144 {
9145 it = llGuestProperties.erase(it);
9146 continue;
9147 }
9148 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9149 mHWData->mGuestProperties[prop.strName] = property;
9150 ++it;
9151 }
9152#endif /* VBOX_WITH_GUEST_PROPS defined */
9153
9154 rc = i_loadDebugging(pDbg);
9155 if (FAILED(rc))
9156 return rc;
9157
9158 mHWData->mAutostart = *pAutostart;
9159
9160 /* default frontend */
9161 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9162 }
9163 catch(std::bad_alloc &)
9164 {
9165 return E_OUTOFMEMORY;
9166 }
9167
9168 AssertComRC(rc);
9169 return rc;
9170}
9171
9172/**
9173 * Called from Machine::loadHardware() to load the debugging settings of the
9174 * machine.
9175 *
9176 * @param pDbg Pointer to the settings.
9177 */
9178HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9179{
9180 mHWData->mDebugging = *pDbg;
9181 /* no more processing currently required, this will probably change. */
9182 return S_OK;
9183}
9184
9185/**
9186 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9187 *
9188 * @param data
9189 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9190 * @param puuidSnapshot
9191 * @return
9192 */
9193HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9194 const Guid *puuidRegistry,
9195 const Guid *puuidSnapshot)
9196{
9197 AssertReturn(!i_isSessionMachine(), E_FAIL);
9198
9199 HRESULT rc = S_OK;
9200
9201 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9202 it != data.llStorageControllers.end();
9203 ++it)
9204 {
9205 const settings::StorageController &ctlData = *it;
9206
9207 ComObjPtr<StorageController> pCtl;
9208 /* Try to find one with the name first. */
9209 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9210 if (SUCCEEDED(rc))
9211 return setError(VBOX_E_OBJECT_IN_USE,
9212 tr("Storage controller named '%s' already exists"),
9213 ctlData.strName.c_str());
9214
9215 pCtl.createObject();
9216 rc = pCtl->init(this,
9217 ctlData.strName,
9218 ctlData.storageBus,
9219 ctlData.ulInstance,
9220 ctlData.fBootable);
9221 if (FAILED(rc)) return rc;
9222
9223 mStorageControllers->push_back(pCtl);
9224
9225 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9226 if (FAILED(rc)) return rc;
9227
9228 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9229 if (FAILED(rc)) return rc;
9230
9231 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9232 if (FAILED(rc)) return rc;
9233
9234 /* Load the attached devices now. */
9235 rc = i_loadStorageDevices(pCtl,
9236 ctlData,
9237 puuidRegistry,
9238 puuidSnapshot);
9239 if (FAILED(rc)) return rc;
9240 }
9241
9242 return S_OK;
9243}
9244
9245/**
9246 * Called from i_loadStorageControllers for a controller's devices.
9247 *
9248 * @param aStorageController
9249 * @param data
9250 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9251 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9252 * @return
9253 */
9254HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9255 const settings::StorageController &data,
9256 const Guid *puuidRegistry,
9257 const Guid *puuidSnapshot)
9258{
9259 HRESULT rc = S_OK;
9260
9261 /* paranoia: detect duplicate attachments */
9262 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9263 it != data.llAttachedDevices.end();
9264 ++it)
9265 {
9266 const settings::AttachedDevice &ad = *it;
9267
9268 for (settings::AttachedDevicesList::const_iterator it2 = it;
9269 it2 != data.llAttachedDevices.end();
9270 ++it2)
9271 {
9272 if (it == it2)
9273 continue;
9274
9275 const settings::AttachedDevice &ad2 = *it2;
9276
9277 if ( ad.lPort == ad2.lPort
9278 && ad.lDevice == ad2.lDevice)
9279 {
9280 return setError(E_FAIL,
9281 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9282 aStorageController->i_getName().c_str(),
9283 ad.lPort,
9284 ad.lDevice,
9285 mUserData->s.strName.c_str());
9286 }
9287 }
9288 }
9289
9290 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9291 it != data.llAttachedDevices.end();
9292 ++it)
9293 {
9294 const settings::AttachedDevice &dev = *it;
9295 ComObjPtr<Medium> medium;
9296
9297 switch (dev.deviceType)
9298 {
9299 case DeviceType_Floppy:
9300 case DeviceType_DVD:
9301 if (dev.strHostDriveSrc.isNotEmpty())
9302 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9303 false /* fRefresh */, medium);
9304 else
9305 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9306 dev.uuid,
9307 false /* fRefresh */,
9308 false /* aSetError */,
9309 medium);
9310 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9311 // This is not an error. The host drive or UUID might have vanished, so just go
9312 // ahead without this removeable medium attachment
9313 rc = S_OK;
9314 break;
9315
9316 case DeviceType_HardDisk:
9317 {
9318 /* find a hard disk by UUID */
9319 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9320 if (FAILED(rc))
9321 {
9322 if (i_isSnapshotMachine())
9323 {
9324 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9325 // so the user knows that the bad disk is in a snapshot somewhere
9326 com::ErrorInfo info;
9327 return setError(E_FAIL,
9328 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9329 puuidSnapshot->raw(),
9330 info.getText().raw());
9331 }
9332 else
9333 return rc;
9334 }
9335
9336 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9337
9338 if (medium->i_getType() == MediumType_Immutable)
9339 {
9340 if (i_isSnapshotMachine())
9341 return setError(E_FAIL,
9342 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9343 "of the virtual machine '%s' ('%s')"),
9344 medium->i_getLocationFull().c_str(),
9345 dev.uuid.raw(),
9346 puuidSnapshot->raw(),
9347 mUserData->s.strName.c_str(),
9348 mData->m_strConfigFileFull.c_str());
9349
9350 return setError(E_FAIL,
9351 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9352 medium->i_getLocationFull().c_str(),
9353 dev.uuid.raw(),
9354 mUserData->s.strName.c_str(),
9355 mData->m_strConfigFileFull.c_str());
9356 }
9357
9358 if (medium->i_getType() == MediumType_MultiAttach)
9359 {
9360 if (i_isSnapshotMachine())
9361 return setError(E_FAIL,
9362 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9363 "of the virtual machine '%s' ('%s')"),
9364 medium->i_getLocationFull().c_str(),
9365 dev.uuid.raw(),
9366 puuidSnapshot->raw(),
9367 mUserData->s.strName.c_str(),
9368 mData->m_strConfigFileFull.c_str());
9369
9370 return setError(E_FAIL,
9371 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9372 medium->i_getLocationFull().c_str(),
9373 dev.uuid.raw(),
9374 mUserData->s.strName.c_str(),
9375 mData->m_strConfigFileFull.c_str());
9376 }
9377
9378 if ( !i_isSnapshotMachine()
9379 && medium->i_getChildren().size() != 0
9380 )
9381 return setError(E_FAIL,
9382 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9383 "because it has %d differencing child hard disks"),
9384 medium->i_getLocationFull().c_str(),
9385 dev.uuid.raw(),
9386 mUserData->s.strName.c_str(),
9387 mData->m_strConfigFileFull.c_str(),
9388 medium->i_getChildren().size());
9389
9390 if (i_findAttachment(mMediaData->mAttachments,
9391 medium))
9392 return setError(E_FAIL,
9393 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9394 medium->i_getLocationFull().c_str(),
9395 dev.uuid.raw(),
9396 mUserData->s.strName.c_str(),
9397 mData->m_strConfigFileFull.c_str());
9398
9399 break;
9400 }
9401
9402 default:
9403 return setError(E_FAIL,
9404 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9405 medium->i_getLocationFull().c_str(),
9406 mUserData->s.strName.c_str(),
9407 mData->m_strConfigFileFull.c_str());
9408 }
9409
9410 if (FAILED(rc))
9411 break;
9412
9413 /* Bandwidth groups are loaded at this point. */
9414 ComObjPtr<BandwidthGroup> pBwGroup;
9415
9416 if (!dev.strBwGroup.isEmpty())
9417 {
9418 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9419 if (FAILED(rc))
9420 return setError(E_FAIL,
9421 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9422 medium->i_getLocationFull().c_str(),
9423 dev.strBwGroup.c_str(),
9424 mUserData->s.strName.c_str(),
9425 mData->m_strConfigFileFull.c_str());
9426 pBwGroup->i_reference();
9427 }
9428
9429 const Bstr controllerName = aStorageController->i_getName();
9430 ComObjPtr<MediumAttachment> pAttachment;
9431 pAttachment.createObject();
9432 rc = pAttachment->init(this,
9433 medium,
9434 controllerName,
9435 dev.lPort,
9436 dev.lDevice,
9437 dev.deviceType,
9438 false,
9439 dev.fPassThrough,
9440 dev.fTempEject,
9441 dev.fNonRotational,
9442 dev.fDiscard,
9443 dev.fHotPluggable,
9444 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9445 if (FAILED(rc)) break;
9446
9447 /* associate the medium with this machine and snapshot */
9448 if (!medium.isNull())
9449 {
9450 AutoCaller medCaller(medium);
9451 if (FAILED(medCaller.rc())) return medCaller.rc();
9452 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9453
9454 if (i_isSnapshotMachine())
9455 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9456 else
9457 rc = medium->i_addBackReference(mData->mUuid);
9458 /* If the medium->addBackReference fails it sets an appropriate
9459 * error message, so no need to do any guesswork here. */
9460
9461 if (puuidRegistry)
9462 // caller wants registry ID to be set on all attached media (OVF import case)
9463 medium->i_addRegistry(*puuidRegistry);
9464 }
9465
9466 if (FAILED(rc))
9467 break;
9468
9469 /* back up mMediaData to let registeredInit() properly rollback on failure
9470 * (= limited accessibility) */
9471 i_setModified(IsModified_Storage);
9472 mMediaData.backup();
9473 mMediaData->mAttachments.push_back(pAttachment);
9474 }
9475
9476 return rc;
9477}
9478
9479/**
9480 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9481 *
9482 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9483 * @param aSnapshot where to return the found snapshot
9484 * @param aSetError true to set extended error info on failure
9485 */
9486HRESULT Machine::i_findSnapshotById(const Guid &aId,
9487 ComObjPtr<Snapshot> &aSnapshot,
9488 bool aSetError /* = false */)
9489{
9490 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9491
9492 if (!mData->mFirstSnapshot)
9493 {
9494 if (aSetError)
9495 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9496 return E_FAIL;
9497 }
9498
9499 if (aId.isZero())
9500 aSnapshot = mData->mFirstSnapshot;
9501 else
9502 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9503
9504 if (!aSnapshot)
9505 {
9506 if (aSetError)
9507 return setError(E_FAIL,
9508 tr("Could not find a snapshot with UUID {%s}"),
9509 aId.toString().c_str());
9510 return E_FAIL;
9511 }
9512
9513 return S_OK;
9514}
9515
9516/**
9517 * Returns the snapshot with the given name or fails of no such snapshot.
9518 *
9519 * @param aName snapshot name to find
9520 * @param aSnapshot where to return the found snapshot
9521 * @param aSetError true to set extended error info on failure
9522 */
9523HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9524 ComObjPtr<Snapshot> &aSnapshot,
9525 bool aSetError /* = false */)
9526{
9527 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9528
9529 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9530
9531 if (!mData->mFirstSnapshot)
9532 {
9533 if (aSetError)
9534 return setError(VBOX_E_OBJECT_NOT_FOUND,
9535 tr("This machine does not have any snapshots"));
9536 return VBOX_E_OBJECT_NOT_FOUND;
9537 }
9538
9539 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9540
9541 if (!aSnapshot)
9542 {
9543 if (aSetError)
9544 return setError(VBOX_E_OBJECT_NOT_FOUND,
9545 tr("Could not find a snapshot named '%s'"), strName.c_str());
9546 return VBOX_E_OBJECT_NOT_FOUND;
9547 }
9548
9549 return S_OK;
9550}
9551
9552/**
9553 * Returns a storage controller object with the given name.
9554 *
9555 * @param aName storage controller name to find
9556 * @param aStorageController where to return the found storage controller
9557 * @param aSetError true to set extended error info on failure
9558 */
9559HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9560 ComObjPtr<StorageController> &aStorageController,
9561 bool aSetError /* = false */)
9562{
9563 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9564
9565 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9566 it != mStorageControllers->end();
9567 ++it)
9568 {
9569 if ((*it)->i_getName() == aName)
9570 {
9571 aStorageController = (*it);
9572 return S_OK;
9573 }
9574 }
9575
9576 if (aSetError)
9577 return setError(VBOX_E_OBJECT_NOT_FOUND,
9578 tr("Could not find a storage controller named '%s'"),
9579 aName.c_str());
9580 return VBOX_E_OBJECT_NOT_FOUND;
9581}
9582
9583/**
9584 * Returns a USB controller object with the given name.
9585 *
9586 * @param aName USB controller name to find
9587 * @param aUSBController where to return the found USB controller
9588 * @param aSetError true to set extended error info on failure
9589 */
9590HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9591 ComObjPtr<USBController> &aUSBController,
9592 bool aSetError /* = false */)
9593{
9594 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9595
9596 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9597 it != mUSBControllers->end();
9598 ++it)
9599 {
9600 if ((*it)->i_getName() == aName)
9601 {
9602 aUSBController = (*it);
9603 return S_OK;
9604 }
9605 }
9606
9607 if (aSetError)
9608 return setError(VBOX_E_OBJECT_NOT_FOUND,
9609 tr("Could not find a storage controller named '%s'"),
9610 aName.c_str());
9611 return VBOX_E_OBJECT_NOT_FOUND;
9612}
9613
9614/**
9615 * Returns the number of USB controller instance of the given type.
9616 *
9617 * @param enmType USB controller type.
9618 */
9619ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9620{
9621 ULONG cCtrls = 0;
9622
9623 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9624 it != mUSBControllers->end();
9625 ++it)
9626 {
9627 if ((*it)->i_getControllerType() == enmType)
9628 cCtrls++;
9629 }
9630
9631 return cCtrls;
9632}
9633
9634HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9635 MediaData::AttachmentList &atts)
9636{
9637 AutoCaller autoCaller(this);
9638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9639
9640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9641
9642 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9643 it != mMediaData->mAttachments.end();
9644 ++it)
9645 {
9646 const ComObjPtr<MediumAttachment> &pAtt = *it;
9647 // should never happen, but deal with NULL pointers in the list.
9648 AssertContinue(!pAtt.isNull());
9649
9650 // getControllerName() needs caller+read lock
9651 AutoCaller autoAttCaller(pAtt);
9652 if (FAILED(autoAttCaller.rc()))
9653 {
9654 atts.clear();
9655 return autoAttCaller.rc();
9656 }
9657 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9658
9659 if (pAtt->i_getControllerName() == aName)
9660 atts.push_back(pAtt);
9661 }
9662
9663 return S_OK;
9664}
9665
9666
9667/**
9668 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9669 * file if the machine name was changed and about creating a new settings file
9670 * if this is a new machine.
9671 *
9672 * @note Must be never called directly but only from #saveSettings().
9673 */
9674HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9675{
9676 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9677
9678 HRESULT rc = S_OK;
9679
9680 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9681
9682 /// @todo need to handle primary group change, too
9683
9684 /* attempt to rename the settings file if machine name is changed */
9685 if ( mUserData->s.fNameSync
9686 && mUserData.isBackedUp()
9687 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9688 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9689 )
9690 {
9691 bool dirRenamed = false;
9692 bool fileRenamed = false;
9693
9694 Utf8Str configFile, newConfigFile;
9695 Utf8Str configFilePrev, newConfigFilePrev;
9696 Utf8Str configDir, newConfigDir;
9697
9698 do
9699 {
9700 int vrc = VINF_SUCCESS;
9701
9702 Utf8Str name = mUserData.backedUpData()->s.strName;
9703 Utf8Str newName = mUserData->s.strName;
9704 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9705 if (group == "/")
9706 group.setNull();
9707 Utf8Str newGroup = mUserData->s.llGroups.front();
9708 if (newGroup == "/")
9709 newGroup.setNull();
9710
9711 configFile = mData->m_strConfigFileFull;
9712
9713 /* first, rename the directory if it matches the group and machine name */
9714 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9715 group.c_str(), RTPATH_DELIMITER, name.c_str());
9716 /** @todo hack, make somehow use of ComposeMachineFilename */
9717 if (mUserData->s.fDirectoryIncludesUUID)
9718 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9719 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9720 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9721 /** @todo hack, make somehow use of ComposeMachineFilename */
9722 if (mUserData->s.fDirectoryIncludesUUID)
9723 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9724 configDir = configFile;
9725 configDir.stripFilename();
9726 newConfigDir = configDir;
9727 if ( configDir.length() >= groupPlusName.length()
9728 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9729 groupPlusName.c_str()))
9730 {
9731 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9732 Utf8Str newConfigBaseDir(newConfigDir);
9733 newConfigDir.append(newGroupPlusName);
9734 /* consistency: use \ if appropriate on the platform */
9735 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9736 /* new dir and old dir cannot be equal here because of 'if'
9737 * above and because name != newName */
9738 Assert(configDir != newConfigDir);
9739 if (!fSettingsFileIsNew)
9740 {
9741 /* perform real rename only if the machine is not new */
9742 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9743 if ( vrc == VERR_FILE_NOT_FOUND
9744 || vrc == VERR_PATH_NOT_FOUND)
9745 {
9746 /* create the parent directory, then retry renaming */
9747 Utf8Str parent(newConfigDir);
9748 parent.stripFilename();
9749 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9750 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9751 }
9752 if (RT_FAILURE(vrc))
9753 {
9754 rc = setError(E_FAIL,
9755 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9756 configDir.c_str(),
9757 newConfigDir.c_str(),
9758 vrc);
9759 break;
9760 }
9761 /* delete subdirectories which are no longer needed */
9762 Utf8Str dir(configDir);
9763 dir.stripFilename();
9764 while (dir != newConfigBaseDir && dir != ".")
9765 {
9766 vrc = RTDirRemove(dir.c_str());
9767 if (RT_FAILURE(vrc))
9768 break;
9769 dir.stripFilename();
9770 }
9771 dirRenamed = true;
9772 }
9773 }
9774
9775 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9776 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9777
9778 /* then try to rename the settings file itself */
9779 if (newConfigFile != configFile)
9780 {
9781 /* get the path to old settings file in renamed directory */
9782 configFile = Utf8StrFmt("%s%c%s",
9783 newConfigDir.c_str(),
9784 RTPATH_DELIMITER,
9785 RTPathFilename(configFile.c_str()));
9786 if (!fSettingsFileIsNew)
9787 {
9788 /* perform real rename only if the machine is not new */
9789 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9790 if (RT_FAILURE(vrc))
9791 {
9792 rc = setError(E_FAIL,
9793 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9794 configFile.c_str(),
9795 newConfigFile.c_str(),
9796 vrc);
9797 break;
9798 }
9799 fileRenamed = true;
9800 configFilePrev = configFile;
9801 configFilePrev += "-prev";
9802 newConfigFilePrev = newConfigFile;
9803 newConfigFilePrev += "-prev";
9804 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9805 }
9806 }
9807
9808 // update m_strConfigFileFull amd mConfigFile
9809 mData->m_strConfigFileFull = newConfigFile;
9810 // compute the relative path too
9811 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9812
9813 // store the old and new so that VirtualBox::i_saveSettings() can update
9814 // the media registry
9815 if ( mData->mRegistered
9816 && (configDir != newConfigDir || configFile != newConfigFile))
9817 {
9818 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9819
9820 if (pfNeedsGlobalSaveSettings)
9821 *pfNeedsGlobalSaveSettings = true;
9822 }
9823
9824 // in the saved state file path, replace the old directory with the new directory
9825 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9826 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9827
9828 // and do the same thing for the saved state file paths of all the online snapshots
9829 if (mData->mFirstSnapshot)
9830 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9831 newConfigDir.c_str());
9832 }
9833 while (0);
9834
9835 if (FAILED(rc))
9836 {
9837 /* silently try to rename everything back */
9838 if (fileRenamed)
9839 {
9840 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9841 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9842 }
9843 if (dirRenamed)
9844 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9845 }
9846
9847 if (FAILED(rc)) return rc;
9848 }
9849
9850 if (fSettingsFileIsNew)
9851 {
9852 /* create a virgin config file */
9853 int vrc = VINF_SUCCESS;
9854
9855 /* ensure the settings directory exists */
9856 Utf8Str path(mData->m_strConfigFileFull);
9857 path.stripFilename();
9858 if (!RTDirExists(path.c_str()))
9859 {
9860 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9861 if (RT_FAILURE(vrc))
9862 {
9863 return setError(E_FAIL,
9864 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9865 path.c_str(),
9866 vrc);
9867 }
9868 }
9869
9870 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9871 path = Utf8Str(mData->m_strConfigFileFull);
9872 RTFILE f = NIL_RTFILE;
9873 vrc = RTFileOpen(&f, path.c_str(),
9874 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9875 if (RT_FAILURE(vrc))
9876 return setError(E_FAIL,
9877 tr("Could not create the settings file '%s' (%Rrc)"),
9878 path.c_str(),
9879 vrc);
9880 RTFileClose(f);
9881 }
9882
9883 return rc;
9884}
9885
9886/**
9887 * Saves and commits machine data, user data and hardware data.
9888 *
9889 * Note that on failure, the data remains uncommitted.
9890 *
9891 * @a aFlags may combine the following flags:
9892 *
9893 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9894 * Used when saving settings after an operation that makes them 100%
9895 * correspond to the settings from the current snapshot.
9896 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9897 * #isReallyModified() returns false. This is necessary for cases when we
9898 * change machine data directly, not through the backup()/commit() mechanism.
9899 * - SaveS_Force: settings will be saved without doing a deep compare of the
9900 * settings structures. This is used when this is called because snapshots
9901 * have changed to avoid the overhead of the deep compare.
9902 *
9903 * @note Must be called from under this object's write lock. Locks children for
9904 * writing.
9905 *
9906 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9907 * initialized to false and that will be set to true by this function if
9908 * the caller must invoke VirtualBox::i_saveSettings() because the global
9909 * settings have changed. This will happen if a machine rename has been
9910 * saved and the global machine and media registries will therefore need
9911 * updating.
9912 */
9913HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9914 int aFlags /*= 0*/)
9915{
9916 LogFlowThisFuncEnter();
9917
9918 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9919
9920 /* make sure child objects are unable to modify the settings while we are
9921 * saving them */
9922 i_ensureNoStateDependencies();
9923
9924 AssertReturn(!i_isSnapshotMachine(),
9925 E_FAIL);
9926
9927 HRESULT rc = S_OK;
9928 bool fNeedsWrite = false;
9929
9930 /* First, prepare to save settings. It will care about renaming the
9931 * settings directory and file if the machine name was changed and about
9932 * creating a new settings file if this is a new machine. */
9933 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9934 if (FAILED(rc)) return rc;
9935
9936 // keep a pointer to the current settings structures
9937 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9938 settings::MachineConfigFile *pNewConfig = NULL;
9939
9940 try
9941 {
9942 // make a fresh one to have everyone write stuff into
9943 pNewConfig = new settings::MachineConfigFile(NULL);
9944 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9945
9946 // now go and copy all the settings data from COM to the settings structures
9947 // (this calles i_saveSettings() on all the COM objects in the machine)
9948 i_copyMachineDataToSettings(*pNewConfig);
9949
9950 if (aFlags & SaveS_ResetCurStateModified)
9951 {
9952 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9953 mData->mCurrentStateModified = FALSE;
9954 fNeedsWrite = true; // always, no need to compare
9955 }
9956 else if (aFlags & SaveS_Force)
9957 {
9958 fNeedsWrite = true; // always, no need to compare
9959 }
9960 else
9961 {
9962 if (!mData->mCurrentStateModified)
9963 {
9964 // do a deep compare of the settings that we just saved with the settings
9965 // previously stored in the config file; this invokes MachineConfigFile::operator==
9966 // which does a deep compare of all the settings, which is expensive but less expensive
9967 // than writing out XML in vain
9968 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9969
9970 // could still be modified if any settings changed
9971 mData->mCurrentStateModified = fAnySettingsChanged;
9972
9973 fNeedsWrite = fAnySettingsChanged;
9974 }
9975 else
9976 fNeedsWrite = true;
9977 }
9978
9979 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9980
9981 if (fNeedsWrite)
9982 // now spit it all out!
9983 pNewConfig->write(mData->m_strConfigFileFull);
9984
9985 mData->pMachineConfigFile = pNewConfig;
9986 delete pOldConfig;
9987 i_commit();
9988
9989 // after saving settings, we are no longer different from the XML on disk
9990 mData->flModifications = 0;
9991 }
9992 catch (HRESULT err)
9993 {
9994 // we assume that error info is set by the thrower
9995 rc = err;
9996
9997 // restore old config
9998 delete pNewConfig;
9999 mData->pMachineConfigFile = pOldConfig;
10000 }
10001 catch (...)
10002 {
10003 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10004 }
10005
10006 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10007 {
10008 /* Fire the data change event, even on failure (since we've already
10009 * committed all data). This is done only for SessionMachines because
10010 * mutable Machine instances are always not registered (i.e. private
10011 * to the client process that creates them) and thus don't need to
10012 * inform callbacks. */
10013 if (i_isSessionMachine())
10014 mParent->i_onMachineDataChange(mData->mUuid);
10015 }
10016
10017 LogFlowThisFunc(("rc=%08X\n", rc));
10018 LogFlowThisFuncLeave();
10019 return rc;
10020}
10021
10022/**
10023 * Implementation for saving the machine settings into the given
10024 * settings::MachineConfigFile instance. This copies machine extradata
10025 * from the previous machine config file in the instance data, if any.
10026 *
10027 * This gets called from two locations:
10028 *
10029 * -- Machine::i_saveSettings(), during the regular XML writing;
10030 *
10031 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10032 * exported to OVF and we write the VirtualBox proprietary XML
10033 * into a <vbox:Machine> tag.
10034 *
10035 * This routine fills all the fields in there, including snapshots, *except*
10036 * for the following:
10037 *
10038 * -- fCurrentStateModified. There is some special logic associated with that.
10039 *
10040 * The caller can then call MachineConfigFile::write() or do something else
10041 * with it.
10042 *
10043 * Caller must hold the machine lock!
10044 *
10045 * This throws XML errors and HRESULT, so the caller must have a catch block!
10046 */
10047void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10048{
10049 // deep copy extradata
10050 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10051
10052 config.uuid = mData->mUuid;
10053
10054 // copy name, description, OS type, teleport, UTC etc.
10055 config.machineUserData = mUserData->s;
10056
10057 if ( mData->mMachineState == MachineState_Saved
10058 || mData->mMachineState == MachineState_Restoring
10059 // when doing certain snapshot operations we may or may not have
10060 // a saved state in the current state, so keep everything as is
10061 || ( ( mData->mMachineState == MachineState_Snapshotting
10062 || mData->mMachineState == MachineState_DeletingSnapshot
10063 || mData->mMachineState == MachineState_RestoringSnapshot)
10064 && (!mSSData->strStateFilePath.isEmpty())
10065 )
10066 )
10067 {
10068 Assert(!mSSData->strStateFilePath.isEmpty());
10069 /* try to make the file name relative to the settings file dir */
10070 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10071 }
10072 else
10073 {
10074 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10075 config.strStateFile.setNull();
10076 }
10077
10078 if (mData->mCurrentSnapshot)
10079 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10080 else
10081 config.uuidCurrentSnapshot.clear();
10082
10083 config.timeLastStateChange = mData->mLastStateChange;
10084 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10085 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10086
10087 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10088 if (FAILED(rc)) throw rc;
10089
10090 // save machine's media registry if this is VirtualBox 4.0 or later
10091 if (config.canHaveOwnMediaRegistry())
10092 {
10093 // determine machine folder
10094 Utf8Str strMachineFolder = i_getSettingsFileFull();
10095 strMachineFolder.stripFilename();
10096 mParent->i_saveMediaRegistry(config.mediaRegistry,
10097 i_getId(), // only media with registry ID == machine UUID
10098 strMachineFolder);
10099 // this throws HRESULT
10100 }
10101
10102 // save snapshots
10103 rc = i_saveAllSnapshots(config);
10104 if (FAILED(rc)) throw rc;
10105}
10106
10107/**
10108 * Saves all snapshots of the machine into the given machine config file. Called
10109 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10110 * @param config
10111 * @return
10112 */
10113HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10114{
10115 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10116
10117 HRESULT rc = S_OK;
10118
10119 try
10120 {
10121 config.llFirstSnapshot.clear();
10122
10123 if (mData->mFirstSnapshot)
10124 {
10125 // the settings use a list for "the first snapshot"
10126 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10127
10128 // get reference to the snapshot on the list and work on that
10129 // element straight in the list to avoid excessive copying later
10130 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10131 if (FAILED(rc)) throw rc;
10132 }
10133
10134// if (mType == IsSessionMachine)
10135// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10136
10137 }
10138 catch (HRESULT err)
10139 {
10140 /* we assume that error info is set by the thrower */
10141 rc = err;
10142 }
10143 catch (...)
10144 {
10145 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10146 }
10147
10148 return rc;
10149}
10150
10151/**
10152 * Saves the VM hardware configuration. It is assumed that the
10153 * given node is empty.
10154 *
10155 * @param data Reference to the settings object for the hardware config.
10156 * @param pDbg Pointer to the settings object for the debugging config
10157 * which happens to live in mHWData.
10158 * @param pAutostart Pointer to the settings object for the autostart config
10159 * which happens to live in mHWData.
10160 */
10161HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10162 settings::Autostart *pAutostart)
10163{
10164 HRESULT rc = S_OK;
10165
10166 try
10167 {
10168 /* The hardware version attribute (optional).
10169 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10170 if ( mHWData->mHWVersion == "1"
10171 && mSSData->strStateFilePath.isEmpty()
10172 )
10173 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10174 other point needs to be found where this can be done. */
10175
10176 data.strVersion = mHWData->mHWVersion;
10177 data.uuid = mHWData->mHardwareUUID;
10178
10179 // CPU
10180 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10181 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10182 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10183 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10184 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10185 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10186 data.fPAE = !!mHWData->mPAEEnabled;
10187 data.enmLongMode = mHWData->mLongMode;
10188 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10189 data.fAPIC = !!mHWData->mAPIC;
10190 data.fX2APIC = !!mHWData->mX2APIC;
10191 data.cCPUs = mHWData->mCPUCount;
10192 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10193 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10194 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10195 data.strCpuProfile = mHWData->mCpuProfile;
10196
10197 data.llCpus.clear();
10198 if (data.fCpuHotPlug)
10199 {
10200 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10201 {
10202 if (mHWData->mCPUAttached[idx])
10203 {
10204 settings::Cpu cpu;
10205 cpu.ulId = idx;
10206 data.llCpus.push_back(cpu);
10207 }
10208 }
10209 }
10210
10211 /* Standard and Extended CPUID leafs. */
10212 data.llCpuIdLeafs.clear();
10213 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10214 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10215 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10216 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10217 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10218 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10219
10220 // memory
10221 data.ulMemorySizeMB = mHWData->mMemorySize;
10222 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10223
10224 // firmware
10225 data.firmwareType = mHWData->mFirmwareType;
10226
10227 // HID
10228 data.pointingHIDType = mHWData->mPointingHIDType;
10229 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10230
10231 // chipset
10232 data.chipsetType = mHWData->mChipsetType;
10233
10234 // paravirt
10235 data.paravirtProvider = mHWData->mParavirtProvider;
10236 data.strParavirtDebug = mHWData->mParavirtDebug;
10237
10238 // emulated USB card reader
10239 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10240
10241 // HPET
10242 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10243
10244 // boot order
10245 data.mapBootOrder.clear();
10246 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10247 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10248
10249 // display
10250 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10251 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10252 data.cMonitors = mHWData->mMonitorCount;
10253 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10254 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10255 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10256 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10257 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10258 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10259 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10260 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10261 {
10262 if (mHWData->maVideoCaptureScreens[i])
10263 ASMBitSet(&data.u64VideoCaptureScreens, i);
10264 else
10265 ASMBitClear(&data.u64VideoCaptureScreens, i);
10266 }
10267 /* store relative video capture file if possible */
10268 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10269
10270 /* VRDEServer settings (optional) */
10271 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10272 if (FAILED(rc)) throw rc;
10273
10274 /* BIOS (required) */
10275 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10276 if (FAILED(rc)) throw rc;
10277
10278 /* USB Controller (required) */
10279 data.usbSettings.llUSBControllers.clear();
10280 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10281 {
10282 ComObjPtr<USBController> ctrl = *it;
10283 settings::USBController settingsCtrl;
10284
10285 settingsCtrl.strName = ctrl->i_getName();
10286 settingsCtrl.enmType = ctrl->i_getControllerType();
10287
10288 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10289 }
10290
10291 /* USB device filters (required) */
10292 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10293 if (FAILED(rc)) throw rc;
10294
10295 /* Network adapters (required) */
10296 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10297 data.llNetworkAdapters.clear();
10298 /* Write out only the nominal number of network adapters for this
10299 * chipset type. Since Machine::commit() hasn't been called there
10300 * may be extra NIC settings in the vector. */
10301 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10302 {
10303 settings::NetworkAdapter nic;
10304 nic.ulSlot = (uint32_t)slot;
10305 /* paranoia check... must not be NULL, but must not crash either. */
10306 if (mNetworkAdapters[slot])
10307 {
10308 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10309 if (FAILED(rc)) throw rc;
10310
10311 data.llNetworkAdapters.push_back(nic);
10312 }
10313 }
10314
10315 /* Serial ports */
10316 data.llSerialPorts.clear();
10317 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10318 {
10319 if (mSerialPorts[slot]->i_hasDefaults())
10320 continue;
10321
10322 settings::SerialPort s;
10323 s.ulSlot = slot;
10324 rc = mSerialPorts[slot]->i_saveSettings(s);
10325 if (FAILED(rc)) return rc;
10326
10327 data.llSerialPorts.push_back(s);
10328 }
10329
10330 /* Parallel ports */
10331 data.llParallelPorts.clear();
10332 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10333 {
10334 if (mParallelPorts[slot]->i_hasDefaults())
10335 continue;
10336
10337 settings::ParallelPort p;
10338 p.ulSlot = slot;
10339 rc = mParallelPorts[slot]->i_saveSettings(p);
10340 if (FAILED(rc)) return rc;
10341
10342 data.llParallelPorts.push_back(p);
10343 }
10344
10345 /* Audio adapter */
10346 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10347 if (FAILED(rc)) return rc;
10348
10349 rc = i_saveStorageControllers(data.storage);
10350 if (FAILED(rc)) return rc;
10351
10352 /* Shared folders */
10353 data.llSharedFolders.clear();
10354 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10355 it != mHWData->mSharedFolders.end();
10356 ++it)
10357 {
10358 SharedFolder *pSF = *it;
10359 AutoCaller sfCaller(pSF);
10360 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10361 settings::SharedFolder sf;
10362 sf.strName = pSF->i_getName();
10363 sf.strHostPath = pSF->i_getHostPath();
10364 sf.fWritable = !!pSF->i_isWritable();
10365 sf.fAutoMount = !!pSF->i_isAutoMounted();
10366
10367 data.llSharedFolders.push_back(sf);
10368 }
10369
10370 // clipboard
10371 data.clipboardMode = mHWData->mClipboardMode;
10372
10373 // drag'n'drop
10374 data.dndMode = mHWData->mDnDMode;
10375
10376 /* Guest */
10377 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10378
10379 // IO settings
10380 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10381 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10382
10383 /* BandwidthControl (required) */
10384 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10385 if (FAILED(rc)) throw rc;
10386
10387 /* Host PCI devices */
10388 data.pciAttachments.clear();
10389 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10390 it != mHWData->mPCIDeviceAssignments.end();
10391 ++it)
10392 {
10393 ComObjPtr<PCIDeviceAttachment> pda = *it;
10394 settings::HostPCIDeviceAttachment hpda;
10395
10396 rc = pda->i_saveSettings(hpda);
10397 if (FAILED(rc)) throw rc;
10398
10399 data.pciAttachments.push_back(hpda);
10400 }
10401
10402 // guest properties
10403 data.llGuestProperties.clear();
10404#ifdef VBOX_WITH_GUEST_PROPS
10405 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10406 it != mHWData->mGuestProperties.end();
10407 ++it)
10408 {
10409 HWData::GuestProperty property = it->second;
10410
10411 /* Remove transient guest properties at shutdown unless we
10412 * are saving state. Note that restoring snapshot intentionally
10413 * keeps them, they will be removed if appropriate once the final
10414 * machine state is set (as crashes etc. need to work). */
10415 if ( ( mData->mMachineState == MachineState_PoweredOff
10416 || mData->mMachineState == MachineState_Aborted
10417 || mData->mMachineState == MachineState_Teleported)
10418 && ( property.mFlags & guestProp::TRANSIENT
10419 || property.mFlags & guestProp::TRANSRESET))
10420 continue;
10421 settings::GuestProperty prop;
10422 prop.strName = it->first;
10423 prop.strValue = property.strValue;
10424 prop.timestamp = property.mTimestamp;
10425 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10426 guestProp::writeFlags(property.mFlags, szFlags);
10427 prop.strFlags = szFlags;
10428
10429 data.llGuestProperties.push_back(prop);
10430 }
10431
10432 /* I presume this doesn't require a backup(). */
10433 mData->mGuestPropertiesModified = FALSE;
10434#endif /* VBOX_WITH_GUEST_PROPS defined */
10435
10436 *pDbg = mHWData->mDebugging;
10437 *pAutostart = mHWData->mAutostart;
10438
10439 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10440 }
10441 catch(std::bad_alloc &)
10442 {
10443 return E_OUTOFMEMORY;
10444 }
10445
10446 AssertComRC(rc);
10447 return rc;
10448}
10449
10450/**
10451 * Saves the storage controller configuration.
10452 *
10453 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10454 */
10455HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10456{
10457 data.llStorageControllers.clear();
10458
10459 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10460 it != mStorageControllers->end();
10461 ++it)
10462 {
10463 HRESULT rc;
10464 ComObjPtr<StorageController> pCtl = *it;
10465
10466 settings::StorageController ctl;
10467 ctl.strName = pCtl->i_getName();
10468 ctl.controllerType = pCtl->i_getControllerType();
10469 ctl.storageBus = pCtl->i_getStorageBus();
10470 ctl.ulInstance = pCtl->i_getInstance();
10471 ctl.fBootable = pCtl->i_getBootable();
10472
10473 /* Save the port count. */
10474 ULONG portCount;
10475 rc = pCtl->COMGETTER(PortCount)(&portCount);
10476 ComAssertComRCRet(rc, rc);
10477 ctl.ulPortCount = portCount;
10478
10479 /* Save fUseHostIOCache */
10480 BOOL fUseHostIOCache;
10481 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10482 ComAssertComRCRet(rc, rc);
10483 ctl.fUseHostIOCache = !!fUseHostIOCache;
10484
10485 /* save the devices now. */
10486 rc = i_saveStorageDevices(pCtl, ctl);
10487 ComAssertComRCRet(rc, rc);
10488
10489 data.llStorageControllers.push_back(ctl);
10490 }
10491
10492 return S_OK;
10493}
10494
10495/**
10496 * Saves the hard disk configuration.
10497 */
10498HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10499 settings::StorageController &data)
10500{
10501 MediaData::AttachmentList atts;
10502
10503 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10504 if (FAILED(rc)) return rc;
10505
10506 data.llAttachedDevices.clear();
10507 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10508 it != atts.end();
10509 ++it)
10510 {
10511 settings::AttachedDevice dev;
10512 IMediumAttachment *iA = *it;
10513 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10514 Medium *pMedium = pAttach->i_getMedium();
10515
10516 dev.deviceType = pAttach->i_getType();
10517 dev.lPort = pAttach->i_getPort();
10518 dev.lDevice = pAttach->i_getDevice();
10519 dev.fPassThrough = pAttach->i_getPassthrough();
10520 dev.fHotPluggable = pAttach->i_getHotPluggable();
10521 if (pMedium)
10522 {
10523 if (pMedium->i_isHostDrive())
10524 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10525 else
10526 dev.uuid = pMedium->i_getId();
10527 dev.fTempEject = pAttach->i_getTempEject();
10528 dev.fNonRotational = pAttach->i_getNonRotational();
10529 dev.fDiscard = pAttach->i_getDiscard();
10530 }
10531
10532 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10533
10534 data.llAttachedDevices.push_back(dev);
10535 }
10536
10537 return S_OK;
10538}
10539
10540/**
10541 * Saves machine state settings as defined by aFlags
10542 * (SaveSTS_* values).
10543 *
10544 * @param aFlags Combination of SaveSTS_* flags.
10545 *
10546 * @note Locks objects for writing.
10547 */
10548HRESULT Machine::i_saveStateSettings(int aFlags)
10549{
10550 if (aFlags == 0)
10551 return S_OK;
10552
10553 AutoCaller autoCaller(this);
10554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10555
10556 /* This object's write lock is also necessary to serialize file access
10557 * (prevent concurrent reads and writes) */
10558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10559
10560 HRESULT rc = S_OK;
10561
10562 Assert(mData->pMachineConfigFile);
10563
10564 try
10565 {
10566 if (aFlags & SaveSTS_CurStateModified)
10567 mData->pMachineConfigFile->fCurrentStateModified = true;
10568
10569 if (aFlags & SaveSTS_StateFilePath)
10570 {
10571 if (!mSSData->strStateFilePath.isEmpty())
10572 /* try to make the file name relative to the settings file dir */
10573 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10574 else
10575 mData->pMachineConfigFile->strStateFile.setNull();
10576 }
10577
10578 if (aFlags & SaveSTS_StateTimeStamp)
10579 {
10580 Assert( mData->mMachineState != MachineState_Aborted
10581 || mSSData->strStateFilePath.isEmpty());
10582
10583 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10584
10585 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10586//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10587 }
10588
10589 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10590 }
10591 catch (...)
10592 {
10593 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10594 }
10595
10596 return rc;
10597}
10598
10599/**
10600 * Ensures that the given medium is added to a media registry. If this machine
10601 * was created with 4.0 or later, then the machine registry is used. Otherwise
10602 * the global VirtualBox media registry is used.
10603 *
10604 * Caller must NOT hold machine lock, media tree or any medium locks!
10605 *
10606 * @param pMedium
10607 */
10608void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10609{
10610 /* Paranoia checks: do not hold machine or media tree locks. */
10611 AssertReturnVoid(!isWriteLockOnCurrentThread());
10612 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10613
10614 ComObjPtr<Medium> pBase;
10615 {
10616 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10617 pBase = pMedium->i_getBase();
10618 }
10619
10620 /* Paranoia checks: do not hold medium locks. */
10621 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10622 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10623
10624 // decide which medium registry to use now that the medium is attached:
10625 Guid uuid;
10626 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10627 // machine XML is VirtualBox 4.0 or higher:
10628 uuid = i_getId(); // machine UUID
10629 else
10630 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10631
10632 if (pMedium->i_addRegistry(uuid))
10633 mParent->i_markRegistryModified(uuid);
10634
10635 /* For more complex hard disk structures it can happen that the base
10636 * medium isn't yet associated with any medium registry. Do that now. */
10637 if (pMedium != pBase)
10638 {
10639 /* Tree lock needed by Medium::addRegistry when recursing. */
10640 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10641 if (pBase->i_addRegistryRecursive(uuid))
10642 {
10643 treeLock.release();
10644 mParent->i_markRegistryModified(uuid);
10645 }
10646 }
10647}
10648
10649/**
10650 * Creates differencing hard disks for all normal hard disks attached to this
10651 * machine and a new set of attachments to refer to created disks.
10652 *
10653 * Used when taking a snapshot or when deleting the current state. Gets called
10654 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10655 *
10656 * This method assumes that mMediaData contains the original hard disk attachments
10657 * it needs to create diffs for. On success, these attachments will be replaced
10658 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10659 * called to delete created diffs which will also rollback mMediaData and restore
10660 * whatever was backed up before calling this method.
10661 *
10662 * Attachments with non-normal hard disks are left as is.
10663 *
10664 * If @a aOnline is @c false then the original hard disks that require implicit
10665 * diffs will be locked for reading. Otherwise it is assumed that they are
10666 * already locked for writing (when the VM was started). Note that in the latter
10667 * case it is responsibility of the caller to lock the newly created diffs for
10668 * writing if this method succeeds.
10669 *
10670 * @param aProgress Progress object to run (must contain at least as
10671 * many operations left as the number of hard disks
10672 * attached).
10673 * @param aOnline Whether the VM was online prior to this operation.
10674 *
10675 * @note The progress object is not marked as completed, neither on success nor
10676 * on failure. This is a responsibility of the caller.
10677 *
10678 * @note Locks this object and the media tree for writing.
10679 */
10680HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10681 ULONG aWeight,
10682 bool aOnline)
10683{
10684 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10685
10686 AutoCaller autoCaller(this);
10687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10688
10689 AutoMultiWriteLock2 alock(this->lockHandle(),
10690 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10691
10692 /* must be in a protective state because we release the lock below */
10693 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10694 || mData->mMachineState == MachineState_OnlineSnapshotting
10695 || mData->mMachineState == MachineState_LiveSnapshotting
10696 || mData->mMachineState == MachineState_RestoringSnapshot
10697 || mData->mMachineState == MachineState_DeletingSnapshot
10698 , E_FAIL);
10699
10700 HRESULT rc = S_OK;
10701
10702 // use appropriate locked media map (online or offline)
10703 MediumLockListMap lockedMediaOffline;
10704 MediumLockListMap *lockedMediaMap;
10705 if (aOnline)
10706 lockedMediaMap = &mData->mSession.mLockedMedia;
10707 else
10708 lockedMediaMap = &lockedMediaOffline;
10709
10710 try
10711 {
10712 if (!aOnline)
10713 {
10714 /* lock all attached hard disks early to detect "in use"
10715 * situations before creating actual diffs */
10716 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10717 it != mMediaData->mAttachments.end();
10718 ++it)
10719 {
10720 MediumAttachment* pAtt = *it;
10721 if (pAtt->i_getType() == DeviceType_HardDisk)
10722 {
10723 Medium* pMedium = pAtt->i_getMedium();
10724 Assert(pMedium);
10725
10726 MediumLockList *pMediumLockList(new MediumLockList());
10727 alock.release();
10728 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10729 NULL /* pToLockWrite */,
10730 false /* fMediumLockWriteAll */,
10731 NULL,
10732 *pMediumLockList);
10733 alock.acquire();
10734 if (FAILED(rc))
10735 {
10736 delete pMediumLockList;
10737 throw rc;
10738 }
10739 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10740 if (FAILED(rc))
10741 {
10742 throw setError(rc,
10743 tr("Collecting locking information for all attached media failed"));
10744 }
10745 }
10746 }
10747
10748 /* Now lock all media. If this fails, nothing is locked. */
10749 alock.release();
10750 rc = lockedMediaMap->Lock();
10751 alock.acquire();
10752 if (FAILED(rc))
10753 {
10754 throw setError(rc,
10755 tr("Locking of attached media failed"));
10756 }
10757 }
10758
10759 /* remember the current list (note that we don't use backup() since
10760 * mMediaData may be already backed up) */
10761 MediaData::AttachmentList atts = mMediaData->mAttachments;
10762
10763 /* start from scratch */
10764 mMediaData->mAttachments.clear();
10765
10766 /* go through remembered attachments and create diffs for normal hard
10767 * disks and attach them */
10768 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10769 it != atts.end();
10770 ++it)
10771 {
10772 MediumAttachment* pAtt = *it;
10773
10774 DeviceType_T devType = pAtt->i_getType();
10775 Medium* pMedium = pAtt->i_getMedium();
10776
10777 if ( devType != DeviceType_HardDisk
10778 || pMedium == NULL
10779 || pMedium->i_getType() != MediumType_Normal)
10780 {
10781 /* copy the attachment as is */
10782
10783 /** @todo the progress object created in SessionMachine::TakeSnaphot
10784 * only expects operations for hard disks. Later other
10785 * device types need to show up in the progress as well. */
10786 if (devType == DeviceType_HardDisk)
10787 {
10788 if (pMedium == NULL)
10789 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10790 aWeight); // weight
10791 else
10792 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10793 pMedium->i_getBase()->i_getName().c_str()).raw(),
10794 aWeight); // weight
10795 }
10796
10797 mMediaData->mAttachments.push_back(pAtt);
10798 continue;
10799 }
10800
10801 /* need a diff */
10802 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10803 pMedium->i_getBase()->i_getName().c_str()).raw(),
10804 aWeight); // weight
10805
10806 Utf8Str strFullSnapshotFolder;
10807 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10808
10809 ComObjPtr<Medium> diff;
10810 diff.createObject();
10811 // store the diff in the same registry as the parent
10812 // (this cannot fail here because we can't create implicit diffs for
10813 // unregistered images)
10814 Guid uuidRegistryParent;
10815 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10816 Assert(fInRegistry); NOREF(fInRegistry);
10817 rc = diff->init(mParent,
10818 pMedium->i_getPreferredDiffFormat(),
10819 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10820 uuidRegistryParent,
10821 DeviceType_HardDisk);
10822 if (FAILED(rc)) throw rc;
10823
10824 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10825 * the push_back? Looks like we're going to release medium with the
10826 * wrong kind of lock (general issue with if we fail anywhere at all)
10827 * and an orphaned VDI in the snapshots folder. */
10828
10829 /* update the appropriate lock list */
10830 MediumLockList *pMediumLockList;
10831 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10832 AssertComRCThrowRC(rc);
10833 if (aOnline)
10834 {
10835 alock.release();
10836 /* The currently attached medium will be read-only, change
10837 * the lock type to read. */
10838 rc = pMediumLockList->Update(pMedium, false);
10839 alock.acquire();
10840 AssertComRCThrowRC(rc);
10841 }
10842
10843 /* release the locks before the potentially lengthy operation */
10844 alock.release();
10845 rc = pMedium->i_createDiffStorage(diff,
10846 pMedium->i_getPreferredDiffVariant(),
10847 pMediumLockList,
10848 NULL /* aProgress */,
10849 true /* aWait */);
10850 alock.acquire();
10851 if (FAILED(rc)) throw rc;
10852
10853 /* actual lock list update is done in Machine::i_commitMedia */
10854
10855 rc = diff->i_addBackReference(mData->mUuid);
10856 AssertComRCThrowRC(rc);
10857
10858 /* add a new attachment */
10859 ComObjPtr<MediumAttachment> attachment;
10860 attachment.createObject();
10861 rc = attachment->init(this,
10862 diff,
10863 pAtt->i_getControllerName(),
10864 pAtt->i_getPort(),
10865 pAtt->i_getDevice(),
10866 DeviceType_HardDisk,
10867 true /* aImplicit */,
10868 false /* aPassthrough */,
10869 false /* aTempEject */,
10870 pAtt->i_getNonRotational(),
10871 pAtt->i_getDiscard(),
10872 pAtt->i_getHotPluggable(),
10873 pAtt->i_getBandwidthGroup());
10874 if (FAILED(rc)) throw rc;
10875
10876 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10877 AssertComRCThrowRC(rc);
10878 mMediaData->mAttachments.push_back(attachment);
10879 }
10880 }
10881 catch (HRESULT aRC) { rc = aRC; }
10882
10883 /* unlock all hard disks we locked when there is no VM */
10884 if (!aOnline)
10885 {
10886 ErrorInfoKeeper eik;
10887
10888 HRESULT rc1 = lockedMediaMap->Clear();
10889 AssertComRC(rc1);
10890 }
10891
10892 return rc;
10893}
10894
10895/**
10896 * Deletes implicit differencing hard disks created either by
10897 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10898 *
10899 * Note that to delete hard disks created by #AttachDevice() this method is
10900 * called from #fixupMedia() when the changes are rolled back.
10901 *
10902 * @note Locks this object and the media tree for writing.
10903 */
10904HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10905{
10906 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10907
10908 AutoCaller autoCaller(this);
10909 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10910
10911 AutoMultiWriteLock2 alock(this->lockHandle(),
10912 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10913
10914 /* We absolutely must have backed up state. */
10915 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10916
10917 /* Check if there are any implicitly created diff images. */
10918 bool fImplicitDiffs = false;
10919 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10920 it != mMediaData->mAttachments.end();
10921 ++it)
10922 {
10923 const ComObjPtr<MediumAttachment> &pAtt = *it;
10924 if (pAtt->i_isImplicit())
10925 {
10926 fImplicitDiffs = true;
10927 break;
10928 }
10929 }
10930 /* If there is nothing to do, leave early. This saves lots of image locking
10931 * effort. It also avoids a MachineStateChanged event without real reason.
10932 * This is important e.g. when loading a VM config, because there should be
10933 * no events. Otherwise API clients can become thoroughly confused for
10934 * inaccessible VMs (the code for loading VM configs uses this method for
10935 * cleanup if the config makes no sense), as they take such events as an
10936 * indication that the VM is alive, and they would force the VM config to
10937 * be reread, leading to an endless loop. */
10938 if (!fImplicitDiffs)
10939 return S_OK;
10940
10941 HRESULT rc = S_OK;
10942 MachineState_T oldState = mData->mMachineState;
10943
10944 /* will release the lock before the potentially lengthy operation,
10945 * so protect with the special state (unless already protected) */
10946 if ( oldState != MachineState_Snapshotting
10947 && oldState != MachineState_OnlineSnapshotting
10948 && oldState != MachineState_LiveSnapshotting
10949 && oldState != MachineState_RestoringSnapshot
10950 && oldState != MachineState_DeletingSnapshot
10951 && oldState != MachineState_DeletingSnapshotOnline
10952 && oldState != MachineState_DeletingSnapshotPaused
10953 )
10954 i_setMachineState(MachineState_SettingUp);
10955
10956 // use appropriate locked media map (online or offline)
10957 MediumLockListMap lockedMediaOffline;
10958 MediumLockListMap *lockedMediaMap;
10959 if (aOnline)
10960 lockedMediaMap = &mData->mSession.mLockedMedia;
10961 else
10962 lockedMediaMap = &lockedMediaOffline;
10963
10964 try
10965 {
10966 if (!aOnline)
10967 {
10968 /* lock all attached hard disks early to detect "in use"
10969 * situations before deleting actual diffs */
10970 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10971 it != mMediaData->mAttachments.end();
10972 ++it)
10973 {
10974 MediumAttachment* pAtt = *it;
10975 if (pAtt->i_getType() == DeviceType_HardDisk)
10976 {
10977 Medium* pMedium = pAtt->i_getMedium();
10978 Assert(pMedium);
10979
10980 MediumLockList *pMediumLockList(new MediumLockList());
10981 alock.release();
10982 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10983 NULL /* pToLockWrite */,
10984 false /* fMediumLockWriteAll */,
10985 NULL,
10986 *pMediumLockList);
10987 alock.acquire();
10988
10989 if (FAILED(rc))
10990 {
10991 delete pMediumLockList;
10992 throw rc;
10993 }
10994
10995 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10996 if (FAILED(rc))
10997 throw rc;
10998 }
10999 }
11000
11001 if (FAILED(rc))
11002 throw rc;
11003 } // end of offline
11004
11005 /* Lock lists are now up to date and include implicitly created media */
11006
11007 /* Go through remembered attachments and delete all implicitly created
11008 * diffs and fix up the attachment information */
11009 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11010 MediaData::AttachmentList implicitAtts;
11011 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11012 it != mMediaData->mAttachments.end();
11013 ++it)
11014 {
11015 ComObjPtr<MediumAttachment> pAtt = *it;
11016 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11017 if (pMedium.isNull())
11018 continue;
11019
11020 // Implicit attachments go on the list for deletion and back references are removed.
11021 if (pAtt->i_isImplicit())
11022 {
11023 /* Deassociate and mark for deletion */
11024 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11025 rc = pMedium->i_removeBackReference(mData->mUuid);
11026 if (FAILED(rc))
11027 throw rc;
11028 implicitAtts.push_back(pAtt);
11029 continue;
11030 }
11031
11032 /* Was this medium attached before? */
11033 if (!i_findAttachment(oldAtts, pMedium))
11034 {
11035 /* no: de-associate */
11036 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11037 rc = pMedium->i_removeBackReference(mData->mUuid);
11038 if (FAILED(rc))
11039 throw rc;
11040 continue;
11041 }
11042 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11043 }
11044
11045 /* If there are implicit attachments to delete, throw away the lock
11046 * map contents (which will unlock all media) since the medium
11047 * attachments will be rolled back. Below we need to completely
11048 * recreate the lock map anyway since it is infinitely complex to
11049 * do this incrementally (would need reconstructing each attachment
11050 * change, which would be extremely hairy). */
11051 if (implicitAtts.size() != 0)
11052 {
11053 ErrorInfoKeeper eik;
11054
11055 HRESULT rc1 = lockedMediaMap->Clear();
11056 AssertComRC(rc1);
11057 }
11058
11059 /* rollback hard disk changes */
11060 mMediaData.rollback();
11061
11062 MultiResult mrc(S_OK);
11063
11064 // Delete unused implicit diffs.
11065 if (implicitAtts.size() != 0)
11066 {
11067 alock.release();
11068
11069 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11070 {
11071 // Remove medium associated with this attachment.
11072 ComObjPtr<MediumAttachment> pAtt = *it;
11073 Assert(pAtt);
11074 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11075 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11076 Assert(pMedium);
11077
11078 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11079 // continue on delete failure, just collect error messages
11080 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11081 pMedium->i_getLocationFull().c_str() ));
11082 mrc = rc;
11083 }
11084 // Clear the list of deleted implicit attachments now, while not
11085 // holding the lock, as it will ultimately trigger Medium::uninit()
11086 // calls which assume that the media tree lock isn't held.
11087 implicitAtts.clear();
11088
11089 alock.acquire();
11090
11091 /* if there is a VM recreate media lock map as mentioned above,
11092 * otherwise it is a waste of time and we leave things unlocked */
11093 if (aOnline)
11094 {
11095 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11096 /* must never be NULL, but better safe than sorry */
11097 if (!pMachine.isNull())
11098 {
11099 alock.release();
11100 rc = mData->mSession.mMachine->i_lockMedia();
11101 alock.acquire();
11102 if (FAILED(rc))
11103 throw rc;
11104 }
11105 }
11106 }
11107 }
11108 catch (HRESULT aRC) {rc = aRC;}
11109
11110 if (mData->mMachineState == MachineState_SettingUp)
11111 i_setMachineState(oldState);
11112
11113 /* unlock all hard disks we locked when there is no VM */
11114 if (!aOnline)
11115 {
11116 ErrorInfoKeeper eik;
11117
11118 HRESULT rc1 = lockedMediaMap->Clear();
11119 AssertComRC(rc1);
11120 }
11121
11122 return rc;
11123}
11124
11125
11126/**
11127 * Looks through the given list of media attachments for one with the given parameters
11128 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11129 * can be searched as well if needed.
11130 *
11131 * @param list
11132 * @param aControllerName
11133 * @param aControllerPort
11134 * @param aDevice
11135 * @return
11136 */
11137MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11138 const Utf8Str &aControllerName,
11139 LONG aControllerPort,
11140 LONG aDevice)
11141{
11142 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11143 {
11144 MediumAttachment *pAttach = *it;
11145 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11146 return pAttach;
11147 }
11148
11149 return NULL;
11150}
11151
11152/**
11153 * Looks through the given list of media attachments for one with the given parameters
11154 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11155 * can be searched as well if needed.
11156 *
11157 * @param list
11158 * @param aControllerName
11159 * @param aControllerPort
11160 * @param aDevice
11161 * @return
11162 */
11163MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11164 ComObjPtr<Medium> pMedium)
11165{
11166 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11167 {
11168 MediumAttachment *pAttach = *it;
11169 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11170 if (pMediumThis == pMedium)
11171 return pAttach;
11172 }
11173
11174 return NULL;
11175}
11176
11177/**
11178 * Looks through the given list of media attachments for one with the given parameters
11179 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11180 * can be searched as well if needed.
11181 *
11182 * @param list
11183 * @param aControllerName
11184 * @param aControllerPort
11185 * @param aDevice
11186 * @return
11187 */
11188MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11189 Guid &id)
11190{
11191 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11192 {
11193 MediumAttachment *pAttach = *it;
11194 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11195 if (pMediumThis->i_getId() == id)
11196 return pAttach;
11197 }
11198
11199 return NULL;
11200}
11201
11202/**
11203 * Main implementation for Machine::DetachDevice. This also gets called
11204 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11205 *
11206 * @param pAttach Medium attachment to detach.
11207 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11208 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11209 * SnapshotMachine, and this must be its snapshot.
11210 * @return
11211 */
11212HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11213 AutoWriteLock &writeLock,
11214 Snapshot *pSnapshot)
11215{
11216 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11217 DeviceType_T mediumType = pAttach->i_getType();
11218
11219 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11220
11221 if (pAttach->i_isImplicit())
11222 {
11223 /* attempt to implicitly delete the implicitly created diff */
11224
11225 /// @todo move the implicit flag from MediumAttachment to Medium
11226 /// and forbid any hard disk operation when it is implicit. Or maybe
11227 /// a special media state for it to make it even more simple.
11228
11229 Assert(mMediaData.isBackedUp());
11230
11231 /* will release the lock before the potentially lengthy operation, so
11232 * protect with the special state */
11233 MachineState_T oldState = mData->mMachineState;
11234 i_setMachineState(MachineState_SettingUp);
11235
11236 writeLock.release();
11237
11238 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11239 true /*aWait*/);
11240
11241 writeLock.acquire();
11242
11243 i_setMachineState(oldState);
11244
11245 if (FAILED(rc)) return rc;
11246 }
11247
11248 i_setModified(IsModified_Storage);
11249 mMediaData.backup();
11250 mMediaData->mAttachments.remove(pAttach);
11251
11252 if (!oldmedium.isNull())
11253 {
11254 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11255 if (pSnapshot)
11256 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11257 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11258 else if (mediumType != DeviceType_HardDisk)
11259 oldmedium->i_removeBackReference(mData->mUuid);
11260 }
11261
11262 return S_OK;
11263}
11264
11265/**
11266 * Goes thru all media of the given list and
11267 *
11268 * 1) calls i_detachDevice() on each of them for this machine and
11269 * 2) adds all Medium objects found in the process to the given list,
11270 * depending on cleanupMode.
11271 *
11272 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11273 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11274 * media to the list.
11275 *
11276 * This gets called from Machine::Unregister, both for the actual Machine and
11277 * the SnapshotMachine objects that might be found in the snapshots.
11278 *
11279 * Requires caller and locking. The machine lock must be passed in because it
11280 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11281 *
11282 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11283 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11284 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11285 * Full, then all media get added;
11286 * otherwise no media get added.
11287 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11288 * @return
11289 */
11290HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11291 Snapshot *pSnapshot,
11292 CleanupMode_T cleanupMode,
11293 MediaList &llMedia)
11294{
11295 Assert(isWriteLockOnCurrentThread());
11296
11297 HRESULT rc;
11298
11299 // make a temporary list because i_detachDevice invalidates iterators into
11300 // mMediaData->mAttachments
11301 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11302
11303 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11304 {
11305 ComObjPtr<MediumAttachment> &pAttach = *it;
11306 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11307
11308 if (!pMedium.isNull())
11309 {
11310 AutoCaller mac(pMedium);
11311 if (FAILED(mac.rc())) return mac.rc();
11312 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11313 DeviceType_T devType = pMedium->i_getDeviceType();
11314 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11315 && devType == DeviceType_HardDisk)
11316 || (cleanupMode == CleanupMode_Full)
11317 )
11318 {
11319 llMedia.push_back(pMedium);
11320 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11321 /* Not allowed to keep this lock as below we need the parent
11322 * medium lock, and the lock order is parent to child. */
11323 lock.release();
11324 /*
11325 * Search for medias which are not attached to any machine, but
11326 * in the chain to an attached disk. Mediums are only consided
11327 * if they are:
11328 * - have only one child
11329 * - no references to any machines
11330 * - are of normal medium type
11331 */
11332 while (!pParent.isNull())
11333 {
11334 AutoCaller mac1(pParent);
11335 if (FAILED(mac1.rc())) return mac1.rc();
11336 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11337 if (pParent->i_getChildren().size() == 1)
11338 {
11339 if ( pParent->i_getMachineBackRefCount() == 0
11340 && pParent->i_getType() == MediumType_Normal
11341 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11342 llMedia.push_back(pParent);
11343 }
11344 else
11345 break;
11346 pParent = pParent->i_getParent();
11347 }
11348 }
11349 }
11350
11351 // real machine: then we need to use the proper method
11352 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11353
11354 if (FAILED(rc))
11355 return rc;
11356 }
11357
11358 return S_OK;
11359}
11360
11361/**
11362 * Perform deferred hard disk detachments.
11363 *
11364 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11365 * backed up).
11366 *
11367 * If @a aOnline is @c true then this method will also unlock the old hard disks
11368 * for which the new implicit diffs were created and will lock these new diffs for
11369 * writing.
11370 *
11371 * @param aOnline Whether the VM was online prior to this operation.
11372 *
11373 * @note Locks this object for writing!
11374 */
11375void Machine::i_commitMedia(bool aOnline /*= false*/)
11376{
11377 AutoCaller autoCaller(this);
11378 AssertComRCReturnVoid(autoCaller.rc());
11379
11380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11381
11382 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11383
11384 HRESULT rc = S_OK;
11385
11386 /* no attach/detach operations -- nothing to do */
11387 if (!mMediaData.isBackedUp())
11388 return;
11389
11390 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11391 bool fMediaNeedsLocking = false;
11392
11393 /* enumerate new attachments */
11394 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11395 it != mMediaData->mAttachments.end();
11396 ++it)
11397 {
11398 MediumAttachment *pAttach = *it;
11399
11400 pAttach->i_commit();
11401
11402 Medium* pMedium = pAttach->i_getMedium();
11403 bool fImplicit = pAttach->i_isImplicit();
11404
11405 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11406 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11407 fImplicit));
11408
11409 /** @todo convert all this Machine-based voodoo to MediumAttachment
11410 * based commit logic. */
11411 if (fImplicit)
11412 {
11413 /* convert implicit attachment to normal */
11414 pAttach->i_setImplicit(false);
11415
11416 if ( aOnline
11417 && pMedium
11418 && pAttach->i_getType() == DeviceType_HardDisk
11419 )
11420 {
11421 /* update the appropriate lock list */
11422 MediumLockList *pMediumLockList;
11423 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11424 AssertComRC(rc);
11425 if (pMediumLockList)
11426 {
11427 /* unlock if there's a need to change the locking */
11428 if (!fMediaNeedsLocking)
11429 {
11430 rc = mData->mSession.mLockedMedia.Unlock();
11431 AssertComRC(rc);
11432 fMediaNeedsLocking = true;
11433 }
11434 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11435 AssertComRC(rc);
11436 rc = pMediumLockList->Append(pMedium, true);
11437 AssertComRC(rc);
11438 }
11439 }
11440
11441 continue;
11442 }
11443
11444 if (pMedium)
11445 {
11446 /* was this medium attached before? */
11447 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11448 {
11449 MediumAttachment *pOldAttach = *oldIt;
11450 if (pOldAttach->i_getMedium() == pMedium)
11451 {
11452 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11453
11454 /* yes: remove from old to avoid de-association */
11455 oldAtts.erase(oldIt);
11456 break;
11457 }
11458 }
11459 }
11460 }
11461
11462 /* enumerate remaining old attachments and de-associate from the
11463 * current machine state */
11464 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11465 {
11466 MediumAttachment *pAttach = *it;
11467 Medium* pMedium = pAttach->i_getMedium();
11468
11469 /* Detach only hard disks, since DVD/floppy media is detached
11470 * instantly in MountMedium. */
11471 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11472 {
11473 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11474
11475 /* now de-associate from the current machine state */
11476 rc = pMedium->i_removeBackReference(mData->mUuid);
11477 AssertComRC(rc);
11478
11479 if (aOnline)
11480 {
11481 /* unlock since medium is not used anymore */
11482 MediumLockList *pMediumLockList;
11483 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11484 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11485 {
11486 /* this happens for online snapshots, there the attachment
11487 * is changing, but only to a diff image created under
11488 * the old one, so there is no separate lock list */
11489 Assert(!pMediumLockList);
11490 }
11491 else
11492 {
11493 AssertComRC(rc);
11494 if (pMediumLockList)
11495 {
11496 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11497 AssertComRC(rc);
11498 }
11499 }
11500 }
11501 }
11502 }
11503
11504 /* take media locks again so that the locking state is consistent */
11505 if (fMediaNeedsLocking)
11506 {
11507 Assert(aOnline);
11508 rc = mData->mSession.mLockedMedia.Lock();
11509 AssertComRC(rc);
11510 }
11511
11512 /* commit the hard disk changes */
11513 mMediaData.commit();
11514
11515 if (i_isSessionMachine())
11516 {
11517 /*
11518 * Update the parent machine to point to the new owner.
11519 * This is necessary because the stored parent will point to the
11520 * session machine otherwise and cause crashes or errors later
11521 * when the session machine gets invalid.
11522 */
11523 /** @todo Change the MediumAttachment class to behave like any other
11524 * class in this regard by creating peer MediumAttachment
11525 * objects for session machines and share the data with the peer
11526 * machine.
11527 */
11528 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11529 it != mMediaData->mAttachments.end();
11530 ++it)
11531 (*it)->i_updateParentMachine(mPeer);
11532
11533 /* attach new data to the primary machine and reshare it */
11534 mPeer->mMediaData.attach(mMediaData);
11535 }
11536
11537 return;
11538}
11539
11540/**
11541 * Perform deferred deletion of implicitly created diffs.
11542 *
11543 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11544 * backed up).
11545 *
11546 * @note Locks this object for writing!
11547 */
11548void Machine::i_rollbackMedia()
11549{
11550 AutoCaller autoCaller(this);
11551 AssertComRCReturnVoid(autoCaller.rc());
11552
11553 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11554 LogFlowThisFunc(("Entering rollbackMedia\n"));
11555
11556 HRESULT rc = S_OK;
11557
11558 /* no attach/detach operations -- nothing to do */
11559 if (!mMediaData.isBackedUp())
11560 return;
11561
11562 /* enumerate new attachments */
11563 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11564 it != mMediaData->mAttachments.end();
11565 ++it)
11566 {
11567 MediumAttachment *pAttach = *it;
11568 /* Fix up the backrefs for DVD/floppy media. */
11569 if (pAttach->i_getType() != DeviceType_HardDisk)
11570 {
11571 Medium* pMedium = pAttach->i_getMedium();
11572 if (pMedium)
11573 {
11574 rc = pMedium->i_removeBackReference(mData->mUuid);
11575 AssertComRC(rc);
11576 }
11577 }
11578
11579 (*it)->i_rollback();
11580
11581 pAttach = *it;
11582 /* Fix up the backrefs for DVD/floppy media. */
11583 if (pAttach->i_getType() != DeviceType_HardDisk)
11584 {
11585 Medium* pMedium = pAttach->i_getMedium();
11586 if (pMedium)
11587 {
11588 rc = pMedium->i_addBackReference(mData->mUuid);
11589 AssertComRC(rc);
11590 }
11591 }
11592 }
11593
11594 /** @todo convert all this Machine-based voodoo to MediumAttachment
11595 * based rollback logic. */
11596 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11597
11598 return;
11599}
11600
11601/**
11602 * Returns true if the settings file is located in the directory named exactly
11603 * as the machine; this means, among other things, that the machine directory
11604 * should be auto-renamed.
11605 *
11606 * @param aSettingsDir if not NULL, the full machine settings file directory
11607 * name will be assigned there.
11608 *
11609 * @note Doesn't lock anything.
11610 * @note Not thread safe (must be called from this object's lock).
11611 */
11612bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11613{
11614 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11615 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11616 if (aSettingsDir)
11617 *aSettingsDir = strMachineDirName;
11618 strMachineDirName.stripPath(); // vmname
11619 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11620 strConfigFileOnly.stripPath() // vmname.vbox
11621 .stripSuffix(); // vmname
11622 /** @todo hack, make somehow use of ComposeMachineFilename */
11623 if (mUserData->s.fDirectoryIncludesUUID)
11624 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11625
11626 AssertReturn(!strMachineDirName.isEmpty(), false);
11627 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11628
11629 return strMachineDirName == strConfigFileOnly;
11630}
11631
11632/**
11633 * Discards all changes to machine settings.
11634 *
11635 * @param aNotify Whether to notify the direct session about changes or not.
11636 *
11637 * @note Locks objects for writing!
11638 */
11639void Machine::i_rollback(bool aNotify)
11640{
11641 AutoCaller autoCaller(this);
11642 AssertComRCReturn(autoCaller.rc(), (void)0);
11643
11644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11645
11646 if (!mStorageControllers.isNull())
11647 {
11648 if (mStorageControllers.isBackedUp())
11649 {
11650 /* unitialize all new devices (absent in the backed up list). */
11651 StorageControllerList::const_iterator it = mStorageControllers->begin();
11652 StorageControllerList *backedList = mStorageControllers.backedUpData();
11653 while (it != mStorageControllers->end())
11654 {
11655 if ( std::find(backedList->begin(), backedList->end(), *it)
11656 == backedList->end()
11657 )
11658 {
11659 (*it)->uninit();
11660 }
11661 ++it;
11662 }
11663
11664 /* restore the list */
11665 mStorageControllers.rollback();
11666 }
11667
11668 /* rollback any changes to devices after restoring the list */
11669 if (mData->flModifications & IsModified_Storage)
11670 {
11671 StorageControllerList::const_iterator it = mStorageControllers->begin();
11672 while (it != mStorageControllers->end())
11673 {
11674 (*it)->i_rollback();
11675 ++it;
11676 }
11677 }
11678 }
11679
11680 if (!mUSBControllers.isNull())
11681 {
11682 if (mUSBControllers.isBackedUp())
11683 {
11684 /* unitialize all new devices (absent in the backed up list). */
11685 USBControllerList::const_iterator it = mUSBControllers->begin();
11686 USBControllerList *backedList = mUSBControllers.backedUpData();
11687 while (it != mUSBControllers->end())
11688 {
11689 if ( std::find(backedList->begin(), backedList->end(), *it)
11690 == backedList->end()
11691 )
11692 {
11693 (*it)->uninit();
11694 }
11695 ++it;
11696 }
11697
11698 /* restore the list */
11699 mUSBControllers.rollback();
11700 }
11701
11702 /* rollback any changes to devices after restoring the list */
11703 if (mData->flModifications & IsModified_USB)
11704 {
11705 USBControllerList::const_iterator it = mUSBControllers->begin();
11706 while (it != mUSBControllers->end())
11707 {
11708 (*it)->i_rollback();
11709 ++it;
11710 }
11711 }
11712 }
11713
11714 mUserData.rollback();
11715
11716 mHWData.rollback();
11717
11718 if (mData->flModifications & IsModified_Storage)
11719 i_rollbackMedia();
11720
11721 if (mBIOSSettings)
11722 mBIOSSettings->i_rollback();
11723
11724 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11725 mVRDEServer->i_rollback();
11726
11727 if (mAudioAdapter)
11728 mAudioAdapter->i_rollback();
11729
11730 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11731 mUSBDeviceFilters->i_rollback();
11732
11733 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11734 mBandwidthControl->i_rollback();
11735
11736 if (!mHWData.isNull())
11737 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11738 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11739 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11740 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11741
11742 if (mData->flModifications & IsModified_NetworkAdapters)
11743 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11744 if ( mNetworkAdapters[slot]
11745 && mNetworkAdapters[slot]->i_isModified())
11746 {
11747 mNetworkAdapters[slot]->i_rollback();
11748 networkAdapters[slot] = mNetworkAdapters[slot];
11749 }
11750
11751 if (mData->flModifications & IsModified_SerialPorts)
11752 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11753 if ( mSerialPorts[slot]
11754 && mSerialPorts[slot]->i_isModified())
11755 {
11756 mSerialPorts[slot]->i_rollback();
11757 serialPorts[slot] = mSerialPorts[slot];
11758 }
11759
11760 if (mData->flModifications & IsModified_ParallelPorts)
11761 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11762 if ( mParallelPorts[slot]
11763 && mParallelPorts[slot]->i_isModified())
11764 {
11765 mParallelPorts[slot]->i_rollback();
11766 parallelPorts[slot] = mParallelPorts[slot];
11767 }
11768
11769 if (aNotify)
11770 {
11771 /* inform the direct session about changes */
11772
11773 ComObjPtr<Machine> that = this;
11774 uint32_t flModifications = mData->flModifications;
11775 alock.release();
11776
11777 if (flModifications & IsModified_SharedFolders)
11778 that->i_onSharedFolderChange();
11779
11780 if (flModifications & IsModified_VRDEServer)
11781 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11782 if (flModifications & IsModified_USB)
11783 that->i_onUSBControllerChange();
11784
11785 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11786 if (networkAdapters[slot])
11787 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11788 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11789 if (serialPorts[slot])
11790 that->i_onSerialPortChange(serialPorts[slot]);
11791 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11792 if (parallelPorts[slot])
11793 that->i_onParallelPortChange(parallelPorts[slot]);
11794
11795 if (flModifications & IsModified_Storage)
11796 that->i_onStorageControllerChange();
11797
11798#if 0
11799 if (flModifications & IsModified_BandwidthControl)
11800 that->onBandwidthControlChange();
11801#endif
11802 }
11803}
11804
11805/**
11806 * Commits all the changes to machine settings.
11807 *
11808 * Note that this operation is supposed to never fail.
11809 *
11810 * @note Locks this object and children for writing.
11811 */
11812void Machine::i_commit()
11813{
11814 AutoCaller autoCaller(this);
11815 AssertComRCReturnVoid(autoCaller.rc());
11816
11817 AutoCaller peerCaller(mPeer);
11818 AssertComRCReturnVoid(peerCaller.rc());
11819
11820 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11821
11822 /*
11823 * use safe commit to ensure Snapshot machines (that share mUserData)
11824 * will still refer to a valid memory location
11825 */
11826 mUserData.commitCopy();
11827
11828 mHWData.commit();
11829
11830 if (mMediaData.isBackedUp())
11831 i_commitMedia(Global::IsOnline(mData->mMachineState));
11832
11833 mBIOSSettings->i_commit();
11834 mVRDEServer->i_commit();
11835 mAudioAdapter->i_commit();
11836 mUSBDeviceFilters->i_commit();
11837 mBandwidthControl->i_commit();
11838
11839 /* Since mNetworkAdapters is a list which might have been changed (resized)
11840 * without using the Backupable<> template we need to handle the copying
11841 * of the list entries manually, including the creation of peers for the
11842 * new objects. */
11843 bool commitNetworkAdapters = false;
11844 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11845 if (mPeer)
11846 {
11847 /* commit everything, even the ones which will go away */
11848 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11849 mNetworkAdapters[slot]->i_commit();
11850 /* copy over the new entries, creating a peer and uninit the original */
11851 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11852 for (size_t slot = 0; slot < newSize; slot++)
11853 {
11854 /* look if this adapter has a peer device */
11855 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11856 if (!peer)
11857 {
11858 /* no peer means the adapter is a newly created one;
11859 * create a peer owning data this data share it with */
11860 peer.createObject();
11861 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11862 }
11863 mPeer->mNetworkAdapters[slot] = peer;
11864 }
11865 /* uninit any no longer needed network adapters */
11866 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11867 mNetworkAdapters[slot]->uninit();
11868 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11869 {
11870 if (mPeer->mNetworkAdapters[slot])
11871 mPeer->mNetworkAdapters[slot]->uninit();
11872 }
11873 /* Keep the original network adapter count until this point, so that
11874 * discarding a chipset type change will not lose settings. */
11875 mNetworkAdapters.resize(newSize);
11876 mPeer->mNetworkAdapters.resize(newSize);
11877 }
11878 else
11879 {
11880 /* we have no peer (our parent is the newly created machine);
11881 * just commit changes to the network adapters */
11882 commitNetworkAdapters = true;
11883 }
11884 if (commitNetworkAdapters)
11885 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11886 mNetworkAdapters[slot]->i_commit();
11887
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11889 mSerialPorts[slot]->i_commit();
11890 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11891 mParallelPorts[slot]->i_commit();
11892
11893 bool commitStorageControllers = false;
11894
11895 if (mStorageControllers.isBackedUp())
11896 {
11897 mStorageControllers.commit();
11898
11899 if (mPeer)
11900 {
11901 /* Commit all changes to new controllers (this will reshare data with
11902 * peers for those who have peers) */
11903 StorageControllerList *newList = new StorageControllerList();
11904 StorageControllerList::const_iterator it = mStorageControllers->begin();
11905 while (it != mStorageControllers->end())
11906 {
11907 (*it)->i_commit();
11908
11909 /* look if this controller has a peer device */
11910 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11911 if (!peer)
11912 {
11913 /* no peer means the device is a newly created one;
11914 * create a peer owning data this device share it with */
11915 peer.createObject();
11916 peer->init(mPeer, *it, true /* aReshare */);
11917 }
11918 else
11919 {
11920 /* remove peer from the old list */
11921 mPeer->mStorageControllers->remove(peer);
11922 }
11923 /* and add it to the new list */
11924 newList->push_back(peer);
11925
11926 ++it;
11927 }
11928
11929 /* uninit old peer's controllers that are left */
11930 it = mPeer->mStorageControllers->begin();
11931 while (it != mPeer->mStorageControllers->end())
11932 {
11933 (*it)->uninit();
11934 ++it;
11935 }
11936
11937 /* attach new list of controllers to our peer */
11938 mPeer->mStorageControllers.attach(newList);
11939 }
11940 else
11941 {
11942 /* we have no peer (our parent is the newly created machine);
11943 * just commit changes to devices */
11944 commitStorageControllers = true;
11945 }
11946 }
11947 else
11948 {
11949 /* the list of controllers itself is not changed,
11950 * just commit changes to controllers themselves */
11951 commitStorageControllers = true;
11952 }
11953
11954 if (commitStorageControllers)
11955 {
11956 StorageControllerList::const_iterator it = mStorageControllers->begin();
11957 while (it != mStorageControllers->end())
11958 {
11959 (*it)->i_commit();
11960 ++it;
11961 }
11962 }
11963
11964 bool commitUSBControllers = false;
11965
11966 if (mUSBControllers.isBackedUp())
11967 {
11968 mUSBControllers.commit();
11969
11970 if (mPeer)
11971 {
11972 /* Commit all changes to new controllers (this will reshare data with
11973 * peers for those who have peers) */
11974 USBControllerList *newList = new USBControllerList();
11975 USBControllerList::const_iterator it = mUSBControllers->begin();
11976 while (it != mUSBControllers->end())
11977 {
11978 (*it)->i_commit();
11979
11980 /* look if this controller has a peer device */
11981 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11982 if (!peer)
11983 {
11984 /* no peer means the device is a newly created one;
11985 * create a peer owning data this device share it with */
11986 peer.createObject();
11987 peer->init(mPeer, *it, true /* aReshare */);
11988 }
11989 else
11990 {
11991 /* remove peer from the old list */
11992 mPeer->mUSBControllers->remove(peer);
11993 }
11994 /* and add it to the new list */
11995 newList->push_back(peer);
11996
11997 ++it;
11998 }
11999
12000 /* uninit old peer's controllers that are left */
12001 it = mPeer->mUSBControllers->begin();
12002 while (it != mPeer->mUSBControllers->end())
12003 {
12004 (*it)->uninit();
12005 ++it;
12006 }
12007
12008 /* attach new list of controllers to our peer */
12009 mPeer->mUSBControllers.attach(newList);
12010 }
12011 else
12012 {
12013 /* we have no peer (our parent is the newly created machine);
12014 * just commit changes to devices */
12015 commitUSBControllers = true;
12016 }
12017 }
12018 else
12019 {
12020 /* the list of controllers itself is not changed,
12021 * just commit changes to controllers themselves */
12022 commitUSBControllers = true;
12023 }
12024
12025 if (commitUSBControllers)
12026 {
12027 USBControllerList::const_iterator it = mUSBControllers->begin();
12028 while (it != mUSBControllers->end())
12029 {
12030 (*it)->i_commit();
12031 ++it;
12032 }
12033 }
12034
12035 if (i_isSessionMachine())
12036 {
12037 /* attach new data to the primary machine and reshare it */
12038 mPeer->mUserData.attach(mUserData);
12039 mPeer->mHWData.attach(mHWData);
12040 /* mMediaData is reshared by fixupMedia */
12041 // mPeer->mMediaData.attach(mMediaData);
12042 Assert(mPeer->mMediaData.data() == mMediaData.data());
12043 }
12044}
12045
12046/**
12047 * Copies all the hardware data from the given machine.
12048 *
12049 * Currently, only called when the VM is being restored from a snapshot. In
12050 * particular, this implies that the VM is not running during this method's
12051 * call.
12052 *
12053 * @note This method must be called from under this object's lock.
12054 *
12055 * @note This method doesn't call #commit(), so all data remains backed up and
12056 * unsaved.
12057 */
12058void Machine::i_copyFrom(Machine *aThat)
12059{
12060 AssertReturnVoid(!i_isSnapshotMachine());
12061 AssertReturnVoid(aThat->i_isSnapshotMachine());
12062
12063 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12064
12065 mHWData.assignCopy(aThat->mHWData);
12066
12067 // create copies of all shared folders (mHWData after attaching a copy
12068 // contains just references to original objects)
12069 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12070 it != mHWData->mSharedFolders.end();
12071 ++it)
12072 {
12073 ComObjPtr<SharedFolder> folder;
12074 folder.createObject();
12075 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12076 AssertComRC(rc);
12077 *it = folder;
12078 }
12079
12080 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12081 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12082 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12083 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12084 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12085
12086 /* create private copies of all controllers */
12087 mStorageControllers.backup();
12088 mStorageControllers->clear();
12089 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12090 it != aThat->mStorageControllers->end();
12091 ++it)
12092 {
12093 ComObjPtr<StorageController> ctrl;
12094 ctrl.createObject();
12095 ctrl->initCopy(this, *it);
12096 mStorageControllers->push_back(ctrl);
12097 }
12098
12099 /* create private copies of all USB controllers */
12100 mUSBControllers.backup();
12101 mUSBControllers->clear();
12102 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12103 it != aThat->mUSBControllers->end();
12104 ++it)
12105 {
12106 ComObjPtr<USBController> ctrl;
12107 ctrl.createObject();
12108 ctrl->initCopy(this, *it);
12109 mUSBControllers->push_back(ctrl);
12110 }
12111
12112 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12113 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12114 {
12115 if (mNetworkAdapters[slot].isNotNull())
12116 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12117 else
12118 {
12119 unconst(mNetworkAdapters[slot]).createObject();
12120 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12121 }
12122 }
12123 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12124 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12125 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12126 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12127}
12128
12129/**
12130 * Returns whether the given storage controller is hotplug capable.
12131 *
12132 * @returns true if the controller supports hotplugging
12133 * false otherwise.
12134 * @param enmCtrlType The controller type to check for.
12135 */
12136bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12137{
12138 ComPtr<ISystemProperties> systemProperties;
12139 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12140 if (FAILED(rc))
12141 return false;
12142
12143 BOOL aHotplugCapable = FALSE;
12144 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12145
12146 return RT_BOOL(aHotplugCapable);
12147}
12148
12149#ifdef VBOX_WITH_RESOURCE_USAGE_API
12150
12151void Machine::i_getDiskList(MediaList &list)
12152{
12153 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12154 it != mMediaData->mAttachments.end();
12155 ++it)
12156 {
12157 MediumAttachment* pAttach = *it;
12158 /* just in case */
12159 AssertContinue(pAttach);
12160
12161 AutoCaller localAutoCallerA(pAttach);
12162 if (FAILED(localAutoCallerA.rc())) continue;
12163
12164 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12165
12166 if (pAttach->i_getType() == DeviceType_HardDisk)
12167 list.push_back(pAttach->i_getMedium());
12168 }
12169}
12170
12171void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12172{
12173 AssertReturnVoid(isWriteLockOnCurrentThread());
12174 AssertPtrReturnVoid(aCollector);
12175
12176 pm::CollectorHAL *hal = aCollector->getHAL();
12177 /* Create sub metrics */
12178 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12179 "Percentage of processor time spent in user mode by the VM process.");
12180 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12181 "Percentage of processor time spent in kernel mode by the VM process.");
12182 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12183 "Size of resident portion of VM process in memory.");
12184 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12185 "Actual size of all VM disks combined.");
12186 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12187 "Network receive rate.");
12188 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12189 "Network transmit rate.");
12190 /* Create and register base metrics */
12191 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12192 cpuLoadUser, cpuLoadKernel);
12193 aCollector->registerBaseMetric(cpuLoad);
12194 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12195 ramUsageUsed);
12196 aCollector->registerBaseMetric(ramUsage);
12197 MediaList disks;
12198 i_getDiskList(disks);
12199 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12200 diskUsageUsed);
12201 aCollector->registerBaseMetric(diskUsage);
12202
12203 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12204 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12205 new pm::AggregateAvg()));
12206 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12207 new pm::AggregateMin()));
12208 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12209 new pm::AggregateMax()));
12210 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12211 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12212 new pm::AggregateAvg()));
12213 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12214 new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12216 new pm::AggregateMax()));
12217
12218 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12219 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12220 new pm::AggregateAvg()));
12221 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12222 new pm::AggregateMin()));
12223 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12224 new pm::AggregateMax()));
12225
12226 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12227 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12228 new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12230 new pm::AggregateMin()));
12231 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12232 new pm::AggregateMax()));
12233
12234
12235 /* Guest metrics collector */
12236 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12237 aCollector->registerGuest(mCollectorGuest);
12238 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12239
12240 /* Create sub metrics */
12241 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12242 "Percentage of processor time spent in user mode as seen by the guest.");
12243 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12244 "Percentage of processor time spent in kernel mode as seen by the guest.");
12245 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12246 "Percentage of processor time spent idling as seen by the guest.");
12247
12248 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12249 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12250 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12251 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12252 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12253 pm::SubMetric *guestMemCache = new pm::SubMetric(
12254 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12255
12256 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12257 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12258
12259 /* Create and register base metrics */
12260 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12261 machineNetRx, machineNetTx);
12262 aCollector->registerBaseMetric(machineNetRate);
12263
12264 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12265 guestLoadUser, guestLoadKernel, guestLoadIdle);
12266 aCollector->registerBaseMetric(guestCpuLoad);
12267
12268 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12269 guestMemTotal, guestMemFree,
12270 guestMemBalloon, guestMemShared,
12271 guestMemCache, guestPagedTotal);
12272 aCollector->registerBaseMetric(guestCpuMem);
12273
12274 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12275 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12276 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12280 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12282 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12283
12284 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12286 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12288
12289 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12290 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12291 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12293
12294 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12295 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12296 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12297 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12298
12299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12301 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12303
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12306 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12308
12309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12311 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12313
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12318
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12323
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12328}
12329
12330void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12331{
12332 AssertReturnVoid(isWriteLockOnCurrentThread());
12333
12334 if (aCollector)
12335 {
12336 aCollector->unregisterMetricsFor(aMachine);
12337 aCollector->unregisterBaseMetricsFor(aMachine);
12338 }
12339}
12340
12341#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12342
12343
12344////////////////////////////////////////////////////////////////////////////////
12345
12346DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12347
12348HRESULT SessionMachine::FinalConstruct()
12349{
12350 LogFlowThisFunc(("\n"));
12351
12352 mClientToken = NULL;
12353
12354 return BaseFinalConstruct();
12355}
12356
12357void SessionMachine::FinalRelease()
12358{
12359 LogFlowThisFunc(("\n"));
12360
12361 Assert(!mClientToken);
12362 /* paranoia, should not hang around any more */
12363 if (mClientToken)
12364 {
12365 delete mClientToken;
12366 mClientToken = NULL;
12367 }
12368
12369 uninit(Uninit::Unexpected);
12370
12371 BaseFinalRelease();
12372}
12373
12374/**
12375 * @note Must be called only by Machine::LockMachine() from its own write lock.
12376 */
12377HRESULT SessionMachine::init(Machine *aMachine)
12378{
12379 LogFlowThisFuncEnter();
12380 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12381
12382 AssertReturn(aMachine, E_INVALIDARG);
12383
12384 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12385
12386 /* Enclose the state transition NotReady->InInit->Ready */
12387 AutoInitSpan autoInitSpan(this);
12388 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12389
12390 HRESULT rc = S_OK;
12391
12392 RT_ZERO(mAuthLibCtx);
12393
12394 /* create the machine client token */
12395 try
12396 {
12397 mClientToken = new ClientToken(aMachine, this);
12398 if (!mClientToken->isReady())
12399 {
12400 delete mClientToken;
12401 mClientToken = NULL;
12402 rc = E_FAIL;
12403 }
12404 }
12405 catch (std::bad_alloc &)
12406 {
12407 rc = E_OUTOFMEMORY;
12408 }
12409 if (FAILED(rc))
12410 return rc;
12411
12412 /* memorize the peer Machine */
12413 unconst(mPeer) = aMachine;
12414 /* share the parent pointer */
12415 unconst(mParent) = aMachine->mParent;
12416
12417 /* take the pointers to data to share */
12418 mData.share(aMachine->mData);
12419 mSSData.share(aMachine->mSSData);
12420
12421 mUserData.share(aMachine->mUserData);
12422 mHWData.share(aMachine->mHWData);
12423 mMediaData.share(aMachine->mMediaData);
12424
12425 mStorageControllers.allocate();
12426 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12427 it != aMachine->mStorageControllers->end();
12428 ++it)
12429 {
12430 ComObjPtr<StorageController> ctl;
12431 ctl.createObject();
12432 ctl->init(this, *it);
12433 mStorageControllers->push_back(ctl);
12434 }
12435
12436 mUSBControllers.allocate();
12437 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12438 it != aMachine->mUSBControllers->end();
12439 ++it)
12440 {
12441 ComObjPtr<USBController> ctl;
12442 ctl.createObject();
12443 ctl->init(this, *it);
12444 mUSBControllers->push_back(ctl);
12445 }
12446
12447 unconst(mBIOSSettings).createObject();
12448 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12449 /* create another VRDEServer object that will be mutable */
12450 unconst(mVRDEServer).createObject();
12451 mVRDEServer->init(this, aMachine->mVRDEServer);
12452 /* create another audio adapter object that will be mutable */
12453 unconst(mAudioAdapter).createObject();
12454 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12455 /* create a list of serial ports that will be mutable */
12456 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12457 {
12458 unconst(mSerialPorts[slot]).createObject();
12459 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12460 }
12461 /* create a list of parallel ports that will be mutable */
12462 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12463 {
12464 unconst(mParallelPorts[slot]).createObject();
12465 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12466 }
12467
12468 /* create another USB device filters object that will be mutable */
12469 unconst(mUSBDeviceFilters).createObject();
12470 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12471
12472 /* create a list of network adapters that will be mutable */
12473 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12474 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12475 {
12476 unconst(mNetworkAdapters[slot]).createObject();
12477 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12478 }
12479
12480 /* create another bandwidth control object that will be mutable */
12481 unconst(mBandwidthControl).createObject();
12482 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12483
12484 /* default is to delete saved state on Saved -> PoweredOff transition */
12485 mRemoveSavedState = true;
12486
12487 /* Confirm a successful initialization when it's the case */
12488 autoInitSpan.setSucceeded();
12489
12490 miNATNetworksStarted = 0;
12491
12492 LogFlowThisFuncLeave();
12493 return rc;
12494}
12495
12496/**
12497 * Uninitializes this session object. If the reason is other than
12498 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12499 * or the client watcher code.
12500 *
12501 * @param aReason uninitialization reason
12502 *
12503 * @note Locks mParent + this object for writing.
12504 */
12505void SessionMachine::uninit(Uninit::Reason aReason)
12506{
12507 LogFlowThisFuncEnter();
12508 LogFlowThisFunc(("reason=%d\n", aReason));
12509
12510 /*
12511 * Strongly reference ourselves to prevent this object deletion after
12512 * mData->mSession.mMachine.setNull() below (which can release the last
12513 * reference and call the destructor). Important: this must be done before
12514 * accessing any members (and before AutoUninitSpan that does it as well).
12515 * This self reference will be released as the very last step on return.
12516 */
12517 ComObjPtr<SessionMachine> selfRef = this;
12518
12519 /* Enclose the state transition Ready->InUninit->NotReady */
12520 AutoUninitSpan autoUninitSpan(this);
12521 if (autoUninitSpan.uninitDone())
12522 {
12523 LogFlowThisFunc(("Already uninitialized\n"));
12524 LogFlowThisFuncLeave();
12525 return;
12526 }
12527
12528 if (autoUninitSpan.initFailed())
12529 {
12530 /* We've been called by init() because it's failed. It's not really
12531 * necessary (nor it's safe) to perform the regular uninit sequence
12532 * below, the following is enough.
12533 */
12534 LogFlowThisFunc(("Initialization failed.\n"));
12535 /* destroy the machine client token */
12536 if (mClientToken)
12537 {
12538 delete mClientToken;
12539 mClientToken = NULL;
12540 }
12541 uninitDataAndChildObjects();
12542 mData.free();
12543 unconst(mParent) = NULL;
12544 unconst(mPeer) = NULL;
12545 LogFlowThisFuncLeave();
12546 return;
12547 }
12548
12549 MachineState_T lastState;
12550 {
12551 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12552 lastState = mData->mMachineState;
12553 }
12554 NOREF(lastState);
12555
12556#ifdef VBOX_WITH_USB
12557 // release all captured USB devices, but do this before requesting the locks below
12558 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12559 {
12560 /* Console::captureUSBDevices() is called in the VM process only after
12561 * setting the machine state to Starting or Restoring.
12562 * Console::detachAllUSBDevices() will be called upon successful
12563 * termination. So, we need to release USB devices only if there was
12564 * an abnormal termination of a running VM.
12565 *
12566 * This is identical to SessionMachine::DetachAllUSBDevices except
12567 * for the aAbnormal argument. */
12568 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12569 AssertComRC(rc);
12570 NOREF(rc);
12571
12572 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12573 if (service)
12574 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12575 }
12576#endif /* VBOX_WITH_USB */
12577
12578 // we need to lock this object in uninit() because the lock is shared
12579 // with mPeer (as well as data we modify below). mParent lock is needed
12580 // by several calls to it, and USB needs host lock.
12581 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12582
12583#ifdef VBOX_WITH_RESOURCE_USAGE_API
12584 /*
12585 * It is safe to call Machine::i_unregisterMetrics() here because
12586 * PerformanceCollector::samplerCallback no longer accesses guest methods
12587 * holding the lock.
12588 */
12589 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12590 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12591 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12592 if (mCollectorGuest)
12593 {
12594 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12595 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12596 mCollectorGuest = NULL;
12597 }
12598#endif
12599
12600 if (aReason == Uninit::Abnormal)
12601 {
12602 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12603
12604 /* reset the state to Aborted */
12605 if (mData->mMachineState != MachineState_Aborted)
12606 i_setMachineState(MachineState_Aborted);
12607 }
12608
12609 // any machine settings modified?
12610 if (mData->flModifications)
12611 {
12612 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12613 i_rollback(false /* aNotify */);
12614 }
12615
12616 mData->mSession.mPID = NIL_RTPROCESS;
12617
12618 if (aReason == Uninit::Unexpected)
12619 {
12620 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12621 * client watcher thread to update the set of machines that have open
12622 * sessions. */
12623 mParent->i_updateClientWatcher();
12624 }
12625
12626 /* uninitialize all remote controls */
12627 if (mData->mSession.mRemoteControls.size())
12628 {
12629 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12630 mData->mSession.mRemoteControls.size()));
12631
12632 Data::Session::RemoteControlList::iterator it =
12633 mData->mSession.mRemoteControls.begin();
12634 while (it != mData->mSession.mRemoteControls.end())
12635 {
12636 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12637 HRESULT rc = (*it)->Uninitialize();
12638 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12639 if (FAILED(rc))
12640 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12641 ++it;
12642 }
12643 mData->mSession.mRemoteControls.clear();
12644 }
12645
12646 /* Remove all references to the NAT network service. The service will stop
12647 * if all references (also from other VMs) are removed. */
12648 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12649 {
12650 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12651 {
12652 BOOL enabled;
12653 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12654 if ( FAILED(hrc)
12655 || !enabled)
12656 continue;
12657
12658 NetworkAttachmentType_T type;
12659 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12660 if ( SUCCEEDED(hrc)
12661 && type == NetworkAttachmentType_NATNetwork)
12662 {
12663 Bstr name;
12664 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12665 if (SUCCEEDED(hrc))
12666 {
12667 multilock.release();
12668 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12669 mUserData->s.strName.c_str(), name.raw()));
12670 mParent->i_natNetworkRefDec(name.raw());
12671 multilock.acquire();
12672 }
12673 }
12674 }
12675 }
12676
12677 /*
12678 * An expected uninitialization can come only from #i_checkForDeath().
12679 * Otherwise it means that something's gone really wrong (for example,
12680 * the Session implementation has released the VirtualBox reference
12681 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12682 * etc). However, it's also possible, that the client releases the IPC
12683 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12684 * but the VirtualBox release event comes first to the server process.
12685 * This case is practically possible, so we should not assert on an
12686 * unexpected uninit, just log a warning.
12687 */
12688
12689 if ((aReason == Uninit::Unexpected))
12690 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12691
12692 if (aReason != Uninit::Normal)
12693 {
12694 mData->mSession.mDirectControl.setNull();
12695 }
12696 else
12697 {
12698 /* this must be null here (see #OnSessionEnd()) */
12699 Assert(mData->mSession.mDirectControl.isNull());
12700 Assert(mData->mSession.mState == SessionState_Unlocking);
12701 Assert(!mData->mSession.mProgress.isNull());
12702 }
12703 if (mData->mSession.mProgress)
12704 {
12705 if (aReason == Uninit::Normal)
12706 mData->mSession.mProgress->i_notifyComplete(S_OK);
12707 else
12708 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12709 COM_IIDOF(ISession),
12710 getComponentName(),
12711 tr("The VM session was aborted"));
12712 mData->mSession.mProgress.setNull();
12713 }
12714
12715 if (mConsoleTaskData.mProgress)
12716 {
12717 Assert(aReason == Uninit::Abnormal);
12718 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12719 COM_IIDOF(ISession),
12720 getComponentName(),
12721 tr("The VM session was aborted"));
12722 mConsoleTaskData.mProgress.setNull();
12723 }
12724
12725 /* remove the association between the peer machine and this session machine */
12726 Assert( (SessionMachine*)mData->mSession.mMachine == this
12727 || aReason == Uninit::Unexpected);
12728
12729 /* reset the rest of session data */
12730 mData->mSession.mLockType = LockType_Null;
12731 mData->mSession.mMachine.setNull();
12732 mData->mSession.mState = SessionState_Unlocked;
12733 mData->mSession.mName.setNull();
12734
12735 /* destroy the machine client token before leaving the exclusive lock */
12736 if (mClientToken)
12737 {
12738 delete mClientToken;
12739 mClientToken = NULL;
12740 }
12741
12742 /* fire an event */
12743 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12744
12745 uninitDataAndChildObjects();
12746
12747 /* free the essential data structure last */
12748 mData.free();
12749
12750 /* release the exclusive lock before setting the below two to NULL */
12751 multilock.release();
12752
12753 unconst(mParent) = NULL;
12754 unconst(mPeer) = NULL;
12755
12756 AuthLibUnload(&mAuthLibCtx);
12757
12758 LogFlowThisFuncLeave();
12759}
12760
12761// util::Lockable interface
12762////////////////////////////////////////////////////////////////////////////////
12763
12764/**
12765 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12766 * with the primary Machine instance (mPeer).
12767 */
12768RWLockHandle *SessionMachine::lockHandle() const
12769{
12770 AssertReturn(mPeer != NULL, NULL);
12771 return mPeer->lockHandle();
12772}
12773
12774// IInternalMachineControl methods
12775////////////////////////////////////////////////////////////////////////////////
12776
12777/**
12778 * Passes collected guest statistics to performance collector object
12779 */
12780HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12781 ULONG aCpuKernel, ULONG aCpuIdle,
12782 ULONG aMemTotal, ULONG aMemFree,
12783 ULONG aMemBalloon, ULONG aMemShared,
12784 ULONG aMemCache, ULONG aPageTotal,
12785 ULONG aAllocVMM, ULONG aFreeVMM,
12786 ULONG aBalloonedVMM, ULONG aSharedVMM,
12787 ULONG aVmNetRx, ULONG aVmNetTx)
12788{
12789#ifdef VBOX_WITH_RESOURCE_USAGE_API
12790 if (mCollectorGuest)
12791 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12792 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12793 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12794 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12795
12796 return S_OK;
12797#else
12798 NOREF(aValidStats);
12799 NOREF(aCpuUser);
12800 NOREF(aCpuKernel);
12801 NOREF(aCpuIdle);
12802 NOREF(aMemTotal);
12803 NOREF(aMemFree);
12804 NOREF(aMemBalloon);
12805 NOREF(aMemShared);
12806 NOREF(aMemCache);
12807 NOREF(aPageTotal);
12808 NOREF(aAllocVMM);
12809 NOREF(aFreeVMM);
12810 NOREF(aBalloonedVMM);
12811 NOREF(aSharedVMM);
12812 NOREF(aVmNetRx);
12813 NOREF(aVmNetTx);
12814 return E_NOTIMPL;
12815#endif
12816}
12817
12818////////////////////////////////////////////////////////////////////////////////
12819//
12820// SessionMachine task records
12821//
12822////////////////////////////////////////////////////////////////////////////////
12823
12824/**
12825 * Task record for saving the machine state.
12826 */
12827struct SessionMachine::SaveStateTask
12828 : public Machine::Task
12829{
12830 SaveStateTask(SessionMachine *m,
12831 Progress *p,
12832 const Utf8Str &t,
12833 Reason_T enmReason,
12834 const Utf8Str &strStateFilePath)
12835 : Task(m, p, t),
12836 m_enmReason(enmReason),
12837 m_strStateFilePath(strStateFilePath)
12838 {}
12839
12840 void handler()
12841 {
12842 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12843 }
12844
12845 Reason_T m_enmReason;
12846 Utf8Str m_strStateFilePath;
12847};
12848
12849/**
12850 * Task thread implementation for SessionMachine::SaveState(), called from
12851 * SessionMachine::taskHandler().
12852 *
12853 * @note Locks this object for writing.
12854 *
12855 * @param task
12856 * @return
12857 */
12858void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12859{
12860 LogFlowThisFuncEnter();
12861
12862 AutoCaller autoCaller(this);
12863 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12864 if (FAILED(autoCaller.rc()))
12865 {
12866 /* we might have been uninitialized because the session was accidentally
12867 * closed by the client, so don't assert */
12868 HRESULT rc = setError(E_FAIL,
12869 tr("The session has been accidentally closed"));
12870 task.m_pProgress->i_notifyComplete(rc);
12871 LogFlowThisFuncLeave();
12872 return;
12873 }
12874
12875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12876
12877 HRESULT rc = S_OK;
12878
12879 try
12880 {
12881 ComPtr<IInternalSessionControl> directControl;
12882 if (mData->mSession.mLockType == LockType_VM)
12883 directControl = mData->mSession.mDirectControl;
12884 if (directControl.isNull())
12885 throw setError(VBOX_E_INVALID_VM_STATE,
12886 tr("Trying to save state without a running VM"));
12887 alock.release();
12888 BOOL fSuspendedBySave;
12889 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12890 Assert(!fSuspendedBySave);
12891 alock.acquire();
12892
12893 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12894 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12895 throw E_FAIL);
12896
12897 if (SUCCEEDED(rc))
12898 {
12899 mSSData->strStateFilePath = task.m_strStateFilePath;
12900
12901 /* save all VM settings */
12902 rc = i_saveSettings(NULL);
12903 // no need to check whether VirtualBox.xml needs saving also since
12904 // we can't have a name change pending at this point
12905 }
12906 else
12907 {
12908 // On failure, set the state to the state we had at the beginning.
12909 i_setMachineState(task.m_machineStateBackup);
12910 i_updateMachineStateOnClient();
12911
12912 // Delete the saved state file (might have been already created).
12913 // No need to check whether this is shared with a snapshot here
12914 // because we certainly created a fresh saved state file here.
12915 RTFileDelete(task.m_strStateFilePath.c_str());
12916 }
12917 }
12918 catch (HRESULT aRC) { rc = aRC; }
12919
12920 task.m_pProgress->i_notifyComplete(rc);
12921
12922 LogFlowThisFuncLeave();
12923}
12924
12925/**
12926 * @note Locks this object for writing.
12927 */
12928HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12929{
12930 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12931}
12932
12933HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12934{
12935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12936
12937 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12938 if (FAILED(rc)) return rc;
12939
12940 if ( mData->mMachineState != MachineState_Running
12941 && mData->mMachineState != MachineState_Paused
12942 )
12943 return setError(VBOX_E_INVALID_VM_STATE,
12944 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12945 Global::stringifyMachineState(mData->mMachineState));
12946
12947 ComObjPtr<Progress> pProgress;
12948 pProgress.createObject();
12949 rc = pProgress->init(i_getVirtualBox(),
12950 static_cast<IMachine *>(this) /* aInitiator */,
12951 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12952 FALSE /* aCancelable */);
12953 if (FAILED(rc))
12954 return rc;
12955
12956 Utf8Str strStateFilePath;
12957 i_composeSavedStateFilename(strStateFilePath);
12958
12959 /* create and start the task on a separate thread (note that it will not
12960 * start working until we release alock) */
12961 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12962 rc = pTask->createThread();
12963 if (FAILED(rc))
12964 return rc;
12965
12966 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12967 i_setMachineState(MachineState_Saving);
12968 i_updateMachineStateOnClient();
12969
12970 pProgress.queryInterfaceTo(aProgress.asOutParam());
12971
12972 return S_OK;
12973}
12974
12975/**
12976 * @note Locks this object for writing.
12977 */
12978HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12979{
12980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12981
12982 HRESULT rc = i_checkStateDependency(MutableStateDep);
12983 if (FAILED(rc)) return rc;
12984
12985 if ( mData->mMachineState != MachineState_PoweredOff
12986 && mData->mMachineState != MachineState_Teleported
12987 && mData->mMachineState != MachineState_Aborted
12988 )
12989 return setError(VBOX_E_INVALID_VM_STATE,
12990 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12991 Global::stringifyMachineState(mData->mMachineState));
12992
12993 com::Utf8Str stateFilePathFull;
12994 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12995 if (RT_FAILURE(vrc))
12996 return setError(VBOX_E_FILE_ERROR,
12997 tr("Invalid saved state file path '%s' (%Rrc)"),
12998 aSavedStateFile.c_str(),
12999 vrc);
13000
13001 mSSData->strStateFilePath = stateFilePathFull;
13002
13003 /* The below i_setMachineState() will detect the state transition and will
13004 * update the settings file */
13005
13006 return i_setMachineState(MachineState_Saved);
13007}
13008
13009/**
13010 * @note Locks this object for writing.
13011 */
13012HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13013{
13014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13015
13016 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13017 if (FAILED(rc)) return rc;
13018
13019 if (mData->mMachineState != MachineState_Saved)
13020 return setError(VBOX_E_INVALID_VM_STATE,
13021 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13022 Global::stringifyMachineState(mData->mMachineState));
13023
13024 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13025
13026 /*
13027 * Saved -> PoweredOff transition will be detected in the SessionMachine
13028 * and properly handled.
13029 */
13030 rc = i_setMachineState(MachineState_PoweredOff);
13031 return rc;
13032}
13033
13034
13035/**
13036 * @note Locks the same as #i_setMachineState() does.
13037 */
13038HRESULT SessionMachine::updateState(MachineState_T aState)
13039{
13040 return i_setMachineState(aState);
13041}
13042
13043/**
13044 * @note Locks this object for writing.
13045 */
13046HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13047{
13048 IProgress* pProgress(aProgress);
13049
13050 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13051
13052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13053
13054 if (mData->mSession.mState != SessionState_Locked)
13055 return VBOX_E_INVALID_OBJECT_STATE;
13056
13057 if (!mData->mSession.mProgress.isNull())
13058 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13059
13060 /* If we didn't reference the NAT network service yet, add a reference to
13061 * force a start */
13062 if (miNATNetworksStarted < 1)
13063 {
13064 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13065 {
13066 BOOL enabled;
13067 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13068 if ( FAILED(hrc)
13069 || !enabled)
13070 continue;
13071
13072 NetworkAttachmentType_T type;
13073 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13074 if ( SUCCEEDED(hrc)
13075 && type == NetworkAttachmentType_NATNetwork)
13076 {
13077 Bstr name;
13078 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13079 if (SUCCEEDED(hrc))
13080 {
13081 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13082 mUserData->s.strName.c_str(), name.raw()));
13083 mPeer->lockHandle()->unlockWrite();
13084 mParent->i_natNetworkRefInc(name.raw());
13085#ifdef RT_LOCK_STRICT
13086 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13087#else
13088 mPeer->lockHandle()->lockWrite();
13089#endif
13090 }
13091 }
13092 }
13093 miNATNetworksStarted++;
13094 }
13095
13096 LogFlowThisFunc(("returns S_OK.\n"));
13097 return S_OK;
13098}
13099
13100/**
13101 * @note Locks this object for writing.
13102 */
13103HRESULT SessionMachine::endPowerUp(LONG aResult)
13104{
13105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13106
13107 if (mData->mSession.mState != SessionState_Locked)
13108 return VBOX_E_INVALID_OBJECT_STATE;
13109
13110 /* Finalize the LaunchVMProcess progress object. */
13111 if (mData->mSession.mProgress)
13112 {
13113 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13114 mData->mSession.mProgress.setNull();
13115 }
13116
13117 if (SUCCEEDED((HRESULT)aResult))
13118 {
13119#ifdef VBOX_WITH_RESOURCE_USAGE_API
13120 /* The VM has been powered up successfully, so it makes sense
13121 * now to offer the performance metrics for a running machine
13122 * object. Doing it earlier wouldn't be safe. */
13123 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13124 mData->mSession.mPID);
13125#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13126 }
13127
13128 return S_OK;
13129}
13130
13131/**
13132 * @note Locks this object for writing.
13133 */
13134HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13135{
13136 LogFlowThisFuncEnter();
13137
13138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13139
13140 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13141 E_FAIL);
13142
13143 /* create a progress object to track operation completion */
13144 ComObjPtr<Progress> pProgress;
13145 pProgress.createObject();
13146 pProgress->init(i_getVirtualBox(),
13147 static_cast<IMachine *>(this) /* aInitiator */,
13148 Bstr(tr("Stopping the virtual machine")).raw(),
13149 FALSE /* aCancelable */);
13150
13151 /* fill in the console task data */
13152 mConsoleTaskData.mLastState = mData->mMachineState;
13153 mConsoleTaskData.mProgress = pProgress;
13154
13155 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13156 i_setMachineState(MachineState_Stopping);
13157
13158 pProgress.queryInterfaceTo(aProgress.asOutParam());
13159
13160 return S_OK;
13161}
13162
13163/**
13164 * @note Locks this object for writing.
13165 */
13166HRESULT SessionMachine::endPoweringDown(LONG aResult,
13167 const com::Utf8Str &aErrMsg)
13168{
13169 LogFlowThisFuncEnter();
13170
13171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13172
13173 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13174 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13175 && mConsoleTaskData.mLastState != MachineState_Null,
13176 E_FAIL);
13177
13178 /*
13179 * On failure, set the state to the state we had when BeginPoweringDown()
13180 * was called (this is expected by Console::PowerDown() and the associated
13181 * task). On success the VM process already changed the state to
13182 * MachineState_PoweredOff, so no need to do anything.
13183 */
13184 if (FAILED(aResult))
13185 i_setMachineState(mConsoleTaskData.mLastState);
13186
13187 /* notify the progress object about operation completion */
13188 Assert(mConsoleTaskData.mProgress);
13189 if (SUCCEEDED(aResult))
13190 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13191 else
13192 {
13193 if (aErrMsg.length())
13194 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13195 COM_IIDOF(ISession),
13196 getComponentName(),
13197 aErrMsg.c_str());
13198 else
13199 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13200 }
13201
13202 /* clear out the temporary saved state data */
13203 mConsoleTaskData.mLastState = MachineState_Null;
13204 mConsoleTaskData.mProgress.setNull();
13205
13206 LogFlowThisFuncLeave();
13207 return S_OK;
13208}
13209
13210
13211/**
13212 * Goes through the USB filters of the given machine to see if the given
13213 * device matches any filter or not.
13214 *
13215 * @note Locks the same as USBController::hasMatchingFilter() does.
13216 */
13217HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13218 BOOL *aMatched,
13219 ULONG *aMaskedInterfaces)
13220{
13221 LogFlowThisFunc(("\n"));
13222
13223#ifdef VBOX_WITH_USB
13224 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13225#else
13226 NOREF(aDevice);
13227 NOREF(aMaskedInterfaces);
13228 *aMatched = FALSE;
13229#endif
13230
13231 return S_OK;
13232}
13233
13234/**
13235 * @note Locks the same as Host::captureUSBDevice() does.
13236 */
13237HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13238{
13239 LogFlowThisFunc(("\n"));
13240
13241#ifdef VBOX_WITH_USB
13242 /* if captureDeviceForVM() fails, it must have set extended error info */
13243 clearError();
13244 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13245 if (FAILED(rc)) return rc;
13246
13247 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13248 AssertReturn(service, E_FAIL);
13249 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13250#else
13251 NOREF(aId);
13252 return E_NOTIMPL;
13253#endif
13254}
13255
13256/**
13257 * @note Locks the same as Host::detachUSBDevice() does.
13258 */
13259HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13260 BOOL aDone)
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264#ifdef VBOX_WITH_USB
13265 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13266 AssertReturn(service, E_FAIL);
13267 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13268#else
13269 NOREF(aId);
13270 NOREF(aDone);
13271 return E_NOTIMPL;
13272#endif
13273}
13274
13275/**
13276 * Inserts all machine filters to the USB proxy service and then calls
13277 * Host::autoCaptureUSBDevices().
13278 *
13279 * Called by Console from the VM process upon VM startup.
13280 *
13281 * @note Locks what called methods lock.
13282 */
13283HRESULT SessionMachine::autoCaptureUSBDevices()
13284{
13285 LogFlowThisFunc(("\n"));
13286
13287#ifdef VBOX_WITH_USB
13288 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13289 AssertComRC(rc);
13290 NOREF(rc);
13291
13292 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13293 AssertReturn(service, E_FAIL);
13294 return service->autoCaptureDevicesForVM(this);
13295#else
13296 return S_OK;
13297#endif
13298}
13299
13300/**
13301 * Removes all machine filters from the USB proxy service and then calls
13302 * Host::detachAllUSBDevices().
13303 *
13304 * Called by Console from the VM process upon normal VM termination or by
13305 * SessionMachine::uninit() upon abnormal VM termination (from under the
13306 * Machine/SessionMachine lock).
13307 *
13308 * @note Locks what called methods lock.
13309 */
13310HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13311{
13312 LogFlowThisFunc(("\n"));
13313
13314#ifdef VBOX_WITH_USB
13315 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13316 AssertComRC(rc);
13317 NOREF(rc);
13318
13319 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13320 AssertReturn(service, E_FAIL);
13321 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13322#else
13323 NOREF(aDone);
13324 return S_OK;
13325#endif
13326}
13327
13328/**
13329 * @note Locks this object for writing.
13330 */
13331HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13332 ComPtr<IProgress> &aProgress)
13333{
13334 LogFlowThisFuncEnter();
13335
13336 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13337 /*
13338 * We don't assert below because it might happen that a non-direct session
13339 * informs us it is closed right after we've been uninitialized -- it's ok.
13340 */
13341
13342 /* get IInternalSessionControl interface */
13343 ComPtr<IInternalSessionControl> control(aSession);
13344
13345 ComAssertRet(!control.isNull(), E_INVALIDARG);
13346
13347 /* Creating a Progress object requires the VirtualBox lock, and
13348 * thus locking it here is required by the lock order rules. */
13349 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13350
13351 if (control == mData->mSession.mDirectControl)
13352 {
13353 /* The direct session is being normally closed by the client process
13354 * ----------------------------------------------------------------- */
13355
13356 /* go to the closing state (essential for all open*Session() calls and
13357 * for #i_checkForDeath()) */
13358 Assert(mData->mSession.mState == SessionState_Locked);
13359 mData->mSession.mState = SessionState_Unlocking;
13360
13361 /* set direct control to NULL to release the remote instance */
13362 mData->mSession.mDirectControl.setNull();
13363 LogFlowThisFunc(("Direct control is set to NULL\n"));
13364
13365 if (mData->mSession.mProgress)
13366 {
13367 /* finalize the progress, someone might wait if a frontend
13368 * closes the session before powering on the VM. */
13369 mData->mSession.mProgress->notifyComplete(E_FAIL,
13370 COM_IIDOF(ISession),
13371 getComponentName(),
13372 tr("The VM session was closed before any attempt to power it on"));
13373 mData->mSession.mProgress.setNull();
13374 }
13375
13376 /* Create the progress object the client will use to wait until
13377 * #i_checkForDeath() is called to uninitialize this session object after
13378 * it releases the IPC semaphore.
13379 * Note! Because we're "reusing" mProgress here, this must be a proxy
13380 * object just like for LaunchVMProcess. */
13381 Assert(mData->mSession.mProgress.isNull());
13382 ComObjPtr<ProgressProxy> progress;
13383 progress.createObject();
13384 ComPtr<IUnknown> pPeer(mPeer);
13385 progress->init(mParent, pPeer,
13386 Bstr(tr("Closing session")).raw(),
13387 FALSE /* aCancelable */);
13388 progress.queryInterfaceTo(aProgress.asOutParam());
13389 mData->mSession.mProgress = progress;
13390 }
13391 else
13392 {
13393 /* the remote session is being normally closed */
13394 Data::Session::RemoteControlList::iterator it =
13395 mData->mSession.mRemoteControls.begin();
13396 while (it != mData->mSession.mRemoteControls.end())
13397 {
13398 if (control == *it)
13399 break;
13400 ++it;
13401 }
13402 BOOL found = it != mData->mSession.mRemoteControls.end();
13403 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13404 E_INVALIDARG);
13405 // This MUST be erase(it), not remove(*it) as the latter triggers a
13406 // very nasty use after free due to the place where the value "lives".
13407 mData->mSession.mRemoteControls.erase(it);
13408 }
13409
13410 /* signal the client watcher thread, because the client is going away */
13411 mParent->i_updateClientWatcher();
13412
13413 LogFlowThisFuncLeave();
13414 return S_OK;
13415}
13416
13417HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13418 std::vector<com::Utf8Str> &aValues,
13419 std::vector<LONG64> &aTimestamps,
13420 std::vector<com::Utf8Str> &aFlags)
13421{
13422 LogFlowThisFunc(("\n"));
13423
13424#ifdef VBOX_WITH_GUEST_PROPS
13425 using namespace guestProp;
13426
13427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13428
13429 size_t cEntries = mHWData->mGuestProperties.size();
13430 aNames.resize(cEntries);
13431 aValues.resize(cEntries);
13432 aTimestamps.resize(cEntries);
13433 aFlags.resize(cEntries);
13434
13435 size_t i = 0;
13436 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13437 it != mHWData->mGuestProperties.end();
13438 ++it, ++i)
13439 {
13440 char szFlags[MAX_FLAGS_LEN + 1];
13441 aNames[i] = it->first;
13442 aValues[i] = it->second.strValue;
13443 aTimestamps[i] = it->second.mTimestamp;
13444
13445 /* If it is NULL, keep it NULL. */
13446 if (it->second.mFlags)
13447 {
13448 writeFlags(it->second.mFlags, szFlags);
13449 aFlags[i] = szFlags;
13450 }
13451 else
13452 aFlags[i] = "";
13453 }
13454 return S_OK;
13455#else
13456 ReturnComNotImplemented();
13457#endif
13458}
13459
13460HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13461 const com::Utf8Str &aValue,
13462 LONG64 aTimestamp,
13463 const com::Utf8Str &aFlags)
13464{
13465 LogFlowThisFunc(("\n"));
13466
13467#ifdef VBOX_WITH_GUEST_PROPS
13468 using namespace guestProp;
13469
13470 try
13471 {
13472 /*
13473 * Convert input up front.
13474 */
13475 uint32_t fFlags = NILFLAG;
13476 if (aFlags.length())
13477 {
13478 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13479 AssertRCReturn(vrc, E_INVALIDARG);
13480 }
13481
13482 /*
13483 * Now grab the object lock, validate the state and do the update.
13484 */
13485
13486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13487
13488 if (!Global::IsOnline(mData->mMachineState))
13489 {
13490 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13491 VBOX_E_INVALID_VM_STATE);
13492 }
13493
13494 i_setModified(IsModified_MachineData);
13495 mHWData.backup();
13496
13497 bool fDelete = !aValue.length();
13498 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13499 if (it != mHWData->mGuestProperties.end())
13500 {
13501 if (!fDelete)
13502 {
13503 it->second.strValue = aValue;
13504 it->second.mTimestamp = aTimestamp;
13505 it->second.mFlags = fFlags;
13506 }
13507 else
13508 mHWData->mGuestProperties.erase(it);
13509
13510 mData->mGuestPropertiesModified = TRUE;
13511 }
13512 else if (!fDelete)
13513 {
13514 HWData::GuestProperty prop;
13515 prop.strValue = aValue;
13516 prop.mTimestamp = aTimestamp;
13517 prop.mFlags = fFlags;
13518
13519 mHWData->mGuestProperties[aName] = prop;
13520 mData->mGuestPropertiesModified = TRUE;
13521 }
13522
13523 alock.release();
13524
13525 mParent->i_onGuestPropertyChange(mData->mUuid,
13526 Bstr(aName).raw(),
13527 Bstr(aValue).raw(),
13528 Bstr(aFlags).raw());
13529 }
13530 catch (...)
13531 {
13532 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13533 }
13534 return S_OK;
13535#else
13536 ReturnComNotImplemented();
13537#endif
13538}
13539
13540
13541HRESULT SessionMachine::lockMedia()
13542{
13543 AutoMultiWriteLock2 alock(this->lockHandle(),
13544 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13545
13546 AssertReturn( mData->mMachineState == MachineState_Starting
13547 || mData->mMachineState == MachineState_Restoring
13548 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13549
13550 clearError();
13551 alock.release();
13552 return i_lockMedia();
13553}
13554
13555HRESULT SessionMachine::unlockMedia()
13556{
13557 HRESULT hrc = i_unlockMedia();
13558 return hrc;
13559}
13560
13561HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13562 ComPtr<IMediumAttachment> &aNewAttachment)
13563{
13564 // request the host lock first, since might be calling Host methods for getting host drives;
13565 // next, protect the media tree all the while we're in here, as well as our member variables
13566 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13567 this->lockHandle(),
13568 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13569
13570 IMediumAttachment *iAttach = aAttachment;
13571 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13572
13573 Bstr ctrlName;
13574 LONG lPort;
13575 LONG lDevice;
13576 bool fTempEject;
13577 {
13578 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13579
13580 /* Need to query the details first, as the IMediumAttachment reference
13581 * might be to the original settings, which we are going to change. */
13582 ctrlName = pAttach->i_getControllerName();
13583 lPort = pAttach->i_getPort();
13584 lDevice = pAttach->i_getDevice();
13585 fTempEject = pAttach->i_getTempEject();
13586 }
13587
13588 if (!fTempEject)
13589 {
13590 /* Remember previously mounted medium. The medium before taking the
13591 * backup is not necessarily the same thing. */
13592 ComObjPtr<Medium> oldmedium;
13593 oldmedium = pAttach->i_getMedium();
13594
13595 i_setModified(IsModified_Storage);
13596 mMediaData.backup();
13597
13598 // The backup operation makes the pAttach reference point to the
13599 // old settings. Re-get the correct reference.
13600 pAttach = i_findAttachment(mMediaData->mAttachments,
13601 ctrlName.raw(),
13602 lPort,
13603 lDevice);
13604
13605 {
13606 AutoCaller autoAttachCaller(this);
13607 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13608
13609 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13610 if (!oldmedium.isNull())
13611 oldmedium->i_removeBackReference(mData->mUuid);
13612
13613 pAttach->i_updateMedium(NULL);
13614 pAttach->i_updateEjected();
13615 }
13616
13617 i_setModified(IsModified_Storage);
13618 }
13619 else
13620 {
13621 {
13622 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13623 pAttach->i_updateEjected();
13624 }
13625 }
13626
13627 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13628
13629 return S_OK;
13630}
13631
13632HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13633 com::Utf8Str &aResult)
13634{
13635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13636
13637 HRESULT hr = S_OK;
13638
13639 if (!mAuthLibCtx.hAuthLibrary)
13640 {
13641 /* Load the external authentication library. */
13642 Bstr authLibrary;
13643 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13644
13645 Utf8Str filename = authLibrary;
13646
13647 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13648 if (RT_FAILURE(rc))
13649 {
13650 hr = setError(E_FAIL,
13651 tr("Could not load the external authentication library '%s' (%Rrc)"),
13652 filename.c_str(), rc);
13653 }
13654 }
13655
13656 /* The auth library might need the machine lock. */
13657 alock.release();
13658
13659 if (FAILED(hr))
13660 return hr;
13661
13662 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13663 {
13664 enum VRDEAuthParams
13665 {
13666 parmUuid = 1,
13667 parmGuestJudgement,
13668 parmUser,
13669 parmPassword,
13670 parmDomain,
13671 parmClientId
13672 };
13673
13674 AuthResult result = AuthResultAccessDenied;
13675
13676 Guid uuid(aAuthParams[parmUuid]);
13677 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13678 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13679
13680 result = AuthLibAuthenticate(&mAuthLibCtx,
13681 uuid.raw(), guestJudgement,
13682 aAuthParams[parmUser].c_str(),
13683 aAuthParams[parmPassword].c_str(),
13684 aAuthParams[parmDomain].c_str(),
13685 u32ClientId);
13686
13687 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13688 size_t cbPassword = aAuthParams[parmPassword].length();
13689 if (cbPassword)
13690 {
13691 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13692 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13693 }
13694
13695 if (result == AuthResultAccessGranted)
13696 aResult = "granted";
13697 else
13698 aResult = "denied";
13699
13700 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13701 aAuthParams[parmUser].c_str(), aResult.c_str()));
13702 }
13703 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13704 {
13705 enum VRDEAuthDisconnectParams
13706 {
13707 parmUuid = 1,
13708 parmClientId
13709 };
13710
13711 Guid uuid(aAuthParams[parmUuid]);
13712 uint32_t u32ClientId = 0;
13713 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13714 }
13715 else
13716 {
13717 hr = E_INVALIDARG;
13718 }
13719
13720 return hr;
13721}
13722
13723// public methods only for internal purposes
13724/////////////////////////////////////////////////////////////////////////////
13725
13726#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13727/**
13728 * Called from the client watcher thread to check for expected or unexpected
13729 * death of the client process that has a direct session to this machine.
13730 *
13731 * On Win32 and on OS/2, this method is called only when we've got the
13732 * mutex (i.e. the client has either died or terminated normally) so it always
13733 * returns @c true (the client is terminated, the session machine is
13734 * uninitialized).
13735 *
13736 * On other platforms, the method returns @c true if the client process has
13737 * terminated normally or abnormally and the session machine was uninitialized,
13738 * and @c false if the client process is still alive.
13739 *
13740 * @note Locks this object for writing.
13741 */
13742bool SessionMachine::i_checkForDeath()
13743{
13744 Uninit::Reason reason;
13745 bool terminated = false;
13746
13747 /* Enclose autoCaller with a block because calling uninit() from under it
13748 * will deadlock. */
13749 {
13750 AutoCaller autoCaller(this);
13751 if (!autoCaller.isOk())
13752 {
13753 /* return true if not ready, to cause the client watcher to exclude
13754 * the corresponding session from watching */
13755 LogFlowThisFunc(("Already uninitialized!\n"));
13756 return true;
13757 }
13758
13759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13760
13761 /* Determine the reason of death: if the session state is Closing here,
13762 * everything is fine. Otherwise it means that the client did not call
13763 * OnSessionEnd() before it released the IPC semaphore. This may happen
13764 * either because the client process has abnormally terminated, or
13765 * because it simply forgot to call ISession::Close() before exiting. We
13766 * threat the latter also as an abnormal termination (see
13767 * Session::uninit() for details). */
13768 reason = mData->mSession.mState == SessionState_Unlocking ?
13769 Uninit::Normal :
13770 Uninit::Abnormal;
13771
13772 if (mClientToken)
13773 terminated = mClientToken->release();
13774 } /* AutoCaller block */
13775
13776 if (terminated)
13777 uninit(reason);
13778
13779 return terminated;
13780}
13781
13782void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13783{
13784 LogFlowThisFunc(("\n"));
13785
13786 strTokenId.setNull();
13787
13788 AutoCaller autoCaller(this);
13789 AssertComRCReturnVoid(autoCaller.rc());
13790
13791 Assert(mClientToken);
13792 if (mClientToken)
13793 mClientToken->getId(strTokenId);
13794}
13795#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13796IToken *SessionMachine::i_getToken()
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 AutoCaller autoCaller(this);
13801 AssertComRCReturn(autoCaller.rc(), NULL);
13802
13803 Assert(mClientToken);
13804 if (mClientToken)
13805 return mClientToken->getToken();
13806 else
13807 return NULL;
13808}
13809#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13810
13811Machine::ClientToken *SessionMachine::i_getClientToken()
13812{
13813 LogFlowThisFunc(("\n"));
13814
13815 AutoCaller autoCaller(this);
13816 AssertComRCReturn(autoCaller.rc(), NULL);
13817
13818 return mClientToken;
13819}
13820
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 if (mData->mSession.mLockType == LockType_VM)
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842
13843 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13844}
13845
13846/**
13847 * @note Locks this object for reading.
13848 */
13849HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13850 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13851 IN_BSTR aGuestIp, LONG aGuestPort)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13857
13858 ComPtr<IInternalSessionControl> directControl;
13859 {
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861 if (mData->mSession.mLockType == LockType_VM)
13862 directControl = mData->mSession.mDirectControl;
13863 }
13864
13865 /* ignore notifications sent after #OnSessionEnd() is called */
13866 if (!directControl)
13867 return S_OK;
13868 /*
13869 * instead acting like callback we ask IVirtualBox deliver corresponding event
13870 */
13871
13872 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13873 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13874 return S_OK;
13875}
13876
13877/**
13878 * @note Locks this object for reading.
13879 */
13880HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13881{
13882 LogFlowThisFunc(("\n"));
13883
13884 AutoCaller autoCaller(this);
13885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13886
13887 ComPtr<IInternalSessionControl> directControl;
13888 {
13889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13890 if (mData->mSession.mLockType == LockType_VM)
13891 directControl = mData->mSession.mDirectControl;
13892 }
13893
13894 /* ignore notifications sent after #OnSessionEnd() is called */
13895 if (!directControl)
13896 return S_OK;
13897
13898 return directControl->OnSerialPortChange(serialPort);
13899}
13900
13901/**
13902 * @note Locks this object for reading.
13903 */
13904HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13905{
13906 LogFlowThisFunc(("\n"));
13907
13908 AutoCaller autoCaller(this);
13909 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13910
13911 ComPtr<IInternalSessionControl> directControl;
13912 {
13913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13914 if (mData->mSession.mLockType == LockType_VM)
13915 directControl = mData->mSession.mDirectControl;
13916 }
13917
13918 /* ignore notifications sent after #OnSessionEnd() is called */
13919 if (!directControl)
13920 return S_OK;
13921
13922 return directControl->OnParallelPortChange(parallelPort);
13923}
13924
13925/**
13926 * @note Locks this object for reading.
13927 */
13928HRESULT SessionMachine::i_onStorageControllerChange()
13929{
13930 LogFlowThisFunc(("\n"));
13931
13932 AutoCaller autoCaller(this);
13933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13934
13935 ComPtr<IInternalSessionControl> directControl;
13936 {
13937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13938 if (mData->mSession.mLockType == LockType_VM)
13939 directControl = mData->mSession.mDirectControl;
13940 }
13941
13942 /* ignore notifications sent after #OnSessionEnd() is called */
13943 if (!directControl)
13944 return S_OK;
13945
13946 return directControl->OnStorageControllerChange();
13947}
13948
13949/**
13950 * @note Locks this object for reading.
13951 */
13952HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956 AutoCaller autoCaller(this);
13957 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13958
13959 ComPtr<IInternalSessionControl> directControl;
13960 {
13961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13962 if (mData->mSession.mLockType == LockType_VM)
13963 directControl = mData->mSession.mDirectControl;
13964 }
13965
13966 /* ignore notifications sent after #OnSessionEnd() is called */
13967 if (!directControl)
13968 return S_OK;
13969
13970 return directControl->OnMediumChange(aAttachment, aForce);
13971}
13972
13973/**
13974 * @note Locks this object for reading.
13975 */
13976HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13977{
13978 LogFlowThisFunc(("\n"));
13979
13980 AutoCaller autoCaller(this);
13981 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13982
13983 ComPtr<IInternalSessionControl> directControl;
13984 {
13985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13986 if (mData->mSession.mLockType == LockType_VM)
13987 directControl = mData->mSession.mDirectControl;
13988 }
13989
13990 /* ignore notifications sent after #OnSessionEnd() is called */
13991 if (!directControl)
13992 return S_OK;
13993
13994 return directControl->OnCPUChange(aCPU, aRemove);
13995}
13996
13997HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13998{
13999 LogFlowThisFunc(("\n"));
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14003
14004 ComPtr<IInternalSessionControl> directControl;
14005 {
14006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14007 if (mData->mSession.mLockType == LockType_VM)
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 /* ignore notifications sent after #OnSessionEnd() is called */
14012 if (!directControl)
14013 return S_OK;
14014
14015 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14016}
14017
14018/**
14019 * @note Locks this object for reading.
14020 */
14021HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 if (mData->mSession.mLockType == LockType_VM)
14032 directControl = mData->mSession.mDirectControl;
14033 }
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnVRDEServerChange(aRestart);
14040}
14041
14042/**
14043 * @note Locks this object for reading.
14044 */
14045HRESULT SessionMachine::i_onVideoCaptureChange()
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 /* ignore notifications sent after #OnSessionEnd() is called */
14060 if (!directControl)
14061 return S_OK;
14062
14063 return directControl->OnVideoCaptureChange();
14064}
14065
14066/**
14067 * @note Locks this object for reading.
14068 */
14069HRESULT SessionMachine::i_onUSBControllerChange()
14070{
14071 LogFlowThisFunc(("\n"));
14072
14073 AutoCaller autoCaller(this);
14074 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14075
14076 ComPtr<IInternalSessionControl> directControl;
14077 {
14078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14079 if (mData->mSession.mLockType == LockType_VM)
14080 directControl = mData->mSession.mDirectControl;
14081 }
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnUSBControllerChange();
14088}
14089
14090/**
14091 * @note Locks this object for reading.
14092 */
14093HRESULT SessionMachine::i_onSharedFolderChange()
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 AutoCaller autoCaller(this);
14098 AssertComRCReturnRC(autoCaller.rc());
14099
14100 ComPtr<IInternalSessionControl> directControl;
14101 {
14102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14103 if (mData->mSession.mLockType == LockType_VM)
14104 directControl = mData->mSession.mDirectControl;
14105 }
14106
14107 /* ignore notifications sent after #OnSessionEnd() is called */
14108 if (!directControl)
14109 return S_OK;
14110
14111 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14112}
14113
14114/**
14115 * @note Locks this object for reading.
14116 */
14117HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14118{
14119 LogFlowThisFunc(("\n"));
14120
14121 AutoCaller autoCaller(this);
14122 AssertComRCReturnRC(autoCaller.rc());
14123
14124 ComPtr<IInternalSessionControl> directControl;
14125 {
14126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14127 if (mData->mSession.mLockType == LockType_VM)
14128 directControl = mData->mSession.mDirectControl;
14129 }
14130
14131 /* ignore notifications sent after #OnSessionEnd() is called */
14132 if (!directControl)
14133 return S_OK;
14134
14135 return directControl->OnClipboardModeChange(aClipboardMode);
14136}
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146 AssertComRCReturnRC(autoCaller.rc());
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 if (mData->mSession.mLockType == LockType_VM)
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158
14159 return directControl->OnDnDModeChange(aDnDMode);
14160}
14161
14162/**
14163 * @note Locks this object for reading.
14164 */
14165HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14166{
14167 LogFlowThisFunc(("\n"));
14168
14169 AutoCaller autoCaller(this);
14170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14171
14172 ComPtr<IInternalSessionControl> directControl;
14173 {
14174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14175 if (mData->mSession.mLockType == LockType_VM)
14176 directControl = mData->mSession.mDirectControl;
14177 }
14178
14179 /* ignore notifications sent after #OnSessionEnd() is called */
14180 if (!directControl)
14181 return S_OK;
14182
14183 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14184}
14185
14186/**
14187 * @note Locks this object for reading.
14188 */
14189HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14190{
14191 LogFlowThisFunc(("\n"));
14192
14193 AutoCaller autoCaller(this);
14194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14195
14196 ComPtr<IInternalSessionControl> directControl;
14197 {
14198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14199 if (mData->mSession.mLockType == LockType_VM)
14200 directControl = mData->mSession.mDirectControl;
14201 }
14202
14203 /* ignore notifications sent after #OnSessionEnd() is called */
14204 if (!directControl)
14205 return S_OK;
14206
14207 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14208}
14209
14210/**
14211 * Returns @c true if this machine's USB controller reports it has a matching
14212 * filter for the given USB device and @c false otherwise.
14213 *
14214 * @note locks this object for reading.
14215 */
14216bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14217{
14218 AutoCaller autoCaller(this);
14219 /* silently return if not ready -- this method may be called after the
14220 * direct machine session has been called */
14221 if (!autoCaller.isOk())
14222 return false;
14223
14224#ifdef VBOX_WITH_USB
14225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14226
14227 switch (mData->mMachineState)
14228 {
14229 case MachineState_Starting:
14230 case MachineState_Restoring:
14231 case MachineState_TeleportingIn:
14232 case MachineState_Paused:
14233 case MachineState_Running:
14234 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14235 * elsewhere... */
14236 alock.release();
14237 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14238 default: break;
14239 }
14240#else
14241 NOREF(aDevice);
14242 NOREF(aMaskedIfs);
14243#endif
14244 return false;
14245}
14246
14247/**
14248 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14249 */
14250HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14251 IVirtualBoxErrorInfo *aError,
14252 ULONG aMaskedIfs,
14253 const com::Utf8Str &aCaptureFilename)
14254{
14255 LogFlowThisFunc(("\n"));
14256
14257 AutoCaller autoCaller(this);
14258
14259 /* This notification may happen after the machine object has been
14260 * uninitialized (the session was closed), so don't assert. */
14261 if (FAILED(autoCaller.rc())) return 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 /* fail on notifications sent after #OnSessionEnd() is called, it is
14271 * expected by the caller */
14272 if (!directControl)
14273 return E_FAIL;
14274
14275 /* No locks should be held at this point. */
14276 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14277 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14278
14279 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14280}
14281
14282/**
14283 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14284 */
14285HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14286 IVirtualBoxErrorInfo *aError)
14287{
14288 LogFlowThisFunc(("\n"));
14289
14290 AutoCaller autoCaller(this);
14291
14292 /* This notification may happen after the machine object has been
14293 * uninitialized (the session was closed), so don't assert. */
14294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14295
14296 ComPtr<IInternalSessionControl> directControl;
14297 {
14298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14299 if (mData->mSession.mLockType == LockType_VM)
14300 directControl = mData->mSession.mDirectControl;
14301 }
14302
14303 /* fail on notifications sent after #OnSessionEnd() is called, it is
14304 * expected by the caller */
14305 if (!directControl)
14306 return E_FAIL;
14307
14308 /* No locks should be held at this point. */
14309 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14310 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14311
14312 return directControl->OnUSBDeviceDetach(aId, aError);
14313}
14314
14315// protected methods
14316/////////////////////////////////////////////////////////////////////////////
14317
14318/**
14319 * Deletes the given file if it is no longer in use by either the current machine state
14320 * (if the machine is "saved") or any of the machine's snapshots.
14321 *
14322 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14323 * but is different for each SnapshotMachine. When calling this, the order of calling this
14324 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14325 * is therefore critical. I know, it's all rather messy.
14326 *
14327 * @param strStateFile
14328 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14329 * the test for whether the saved state file is in use.
14330 */
14331void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14332 Snapshot *pSnapshotToIgnore)
14333{
14334 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14335 if ( (strStateFile.isNotEmpty())
14336 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14337 )
14338 // ... and it must also not be shared with other snapshots
14339 if ( !mData->mFirstSnapshot
14340 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14341 // this checks the SnapshotMachine's state file paths
14342 )
14343 RTFileDelete(strStateFile.c_str());
14344}
14345
14346/**
14347 * Locks the attached media.
14348 *
14349 * All attached hard disks are locked for writing and DVD/floppy are locked for
14350 * reading. Parents of attached hard disks (if any) are locked for reading.
14351 *
14352 * This method also performs accessibility check of all media it locks: if some
14353 * media is inaccessible, the method will return a failure and a bunch of
14354 * extended error info objects per each inaccessible medium.
14355 *
14356 * Note that this method is atomic: if it returns a success, all media are
14357 * locked as described above; on failure no media is locked at all (all
14358 * succeeded individual locks will be undone).
14359 *
14360 * The caller is responsible for doing the necessary state sanity checks.
14361 *
14362 * The locks made by this method must be undone by calling #unlockMedia() when
14363 * no more needed.
14364 */
14365HRESULT SessionMachine::i_lockMedia()
14366{
14367 AutoCaller autoCaller(this);
14368 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14369
14370 AutoMultiWriteLock2 alock(this->lockHandle(),
14371 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14372
14373 /* bail out if trying to lock things with already set up locking */
14374 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14375
14376 MultiResult mrc(S_OK);
14377
14378 /* Collect locking information for all medium objects attached to the VM. */
14379 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14380 it != mMediaData->mAttachments.end();
14381 ++it)
14382 {
14383 MediumAttachment* pAtt = *it;
14384 DeviceType_T devType = pAtt->i_getType();
14385 Medium *pMedium = pAtt->i_getMedium();
14386
14387 MediumLockList *pMediumLockList(new MediumLockList());
14388 // There can be attachments without a medium (floppy/dvd), and thus
14389 // it's impossible to create a medium lock list. It still makes sense
14390 // to have the empty medium lock list in the map in case a medium is
14391 // attached later.
14392 if (pMedium != NULL)
14393 {
14394 MediumType_T mediumType = pMedium->i_getType();
14395 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14396 || mediumType == MediumType_Shareable;
14397 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14398
14399 alock.release();
14400 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14401 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14402 false /* fMediumLockWriteAll */,
14403 NULL,
14404 *pMediumLockList);
14405 alock.acquire();
14406 if (FAILED(mrc))
14407 {
14408 delete pMediumLockList;
14409 mData->mSession.mLockedMedia.Clear();
14410 break;
14411 }
14412 }
14413
14414 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14415 if (FAILED(rc))
14416 {
14417 mData->mSession.mLockedMedia.Clear();
14418 mrc = setError(rc,
14419 tr("Collecting locking information for all attached media failed"));
14420 break;
14421 }
14422 }
14423
14424 if (SUCCEEDED(mrc))
14425 {
14426 /* Now lock all media. If this fails, nothing is locked. */
14427 alock.release();
14428 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14429 alock.acquire();
14430 if (FAILED(rc))
14431 {
14432 mrc = setError(rc,
14433 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14434 }
14435 }
14436
14437 return mrc;
14438}
14439
14440/**
14441 * Undoes the locks made by by #lockMedia().
14442 */
14443HRESULT SessionMachine::i_unlockMedia()
14444{
14445 AutoCaller autoCaller(this);
14446 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14447
14448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14449
14450 /* we may be holding important error info on the current thread;
14451 * preserve it */
14452 ErrorInfoKeeper eik;
14453
14454 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14455 AssertComRC(rc);
14456 return rc;
14457}
14458
14459/**
14460 * Helper to change the machine state (reimplementation).
14461 *
14462 * @note Locks this object for writing.
14463 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14464 * it can cause crashes in random places due to unexpectedly committing
14465 * the current settings. The caller is responsible for that. The call
14466 * to saveStateSettings is fine, because this method does not commit.
14467 */
14468HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14469{
14470 LogFlowThisFuncEnter();
14471 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14472
14473 AutoCaller autoCaller(this);
14474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14475
14476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14477
14478 MachineState_T oldMachineState = mData->mMachineState;
14479
14480 AssertMsgReturn(oldMachineState != aMachineState,
14481 ("oldMachineState=%s, aMachineState=%s\n",
14482 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14483 E_FAIL);
14484
14485 HRESULT rc = S_OK;
14486
14487 int stsFlags = 0;
14488 bool deleteSavedState = false;
14489
14490 /* detect some state transitions */
14491
14492 if ( ( oldMachineState == MachineState_Saved
14493 && aMachineState == MachineState_Restoring)
14494 || ( ( oldMachineState == MachineState_PoweredOff
14495 || oldMachineState == MachineState_Teleported
14496 || oldMachineState == MachineState_Aborted
14497 )
14498 && ( aMachineState == MachineState_TeleportingIn
14499 || aMachineState == MachineState_Starting
14500 )
14501 )
14502 )
14503 {
14504 /* The EMT thread is about to start */
14505
14506 /* Nothing to do here for now... */
14507
14508 /// @todo NEWMEDIA don't let mDVDDrive and other children
14509 /// change anything when in the Starting/Restoring state
14510 }
14511 else if ( ( oldMachineState == MachineState_Running
14512 || oldMachineState == MachineState_Paused
14513 || oldMachineState == MachineState_Teleporting
14514 || oldMachineState == MachineState_OnlineSnapshotting
14515 || oldMachineState == MachineState_LiveSnapshotting
14516 || oldMachineState == MachineState_Stuck
14517 || oldMachineState == MachineState_Starting
14518 || oldMachineState == MachineState_Stopping
14519 || oldMachineState == MachineState_Saving
14520 || oldMachineState == MachineState_Restoring
14521 || oldMachineState == MachineState_TeleportingPausedVM
14522 || oldMachineState == MachineState_TeleportingIn
14523 )
14524 && ( aMachineState == MachineState_PoweredOff
14525 || aMachineState == MachineState_Saved
14526 || aMachineState == MachineState_Teleported
14527 || aMachineState == MachineState_Aborted
14528 )
14529 )
14530 {
14531 /* The EMT thread has just stopped, unlock attached media. Note that as
14532 * opposed to locking that is done from Console, we do unlocking here
14533 * because the VM process may have aborted before having a chance to
14534 * properly unlock all media it locked. */
14535
14536 unlockMedia();
14537 }
14538
14539 if (oldMachineState == MachineState_Restoring)
14540 {
14541 if (aMachineState != MachineState_Saved)
14542 {
14543 /*
14544 * delete the saved state file once the machine has finished
14545 * restoring from it (note that Console sets the state from
14546 * Restoring to Saved if the VM couldn't restore successfully,
14547 * to give the user an ability to fix an error and retry --
14548 * we keep the saved state file in this case)
14549 */
14550 deleteSavedState = true;
14551 }
14552 }
14553 else if ( oldMachineState == MachineState_Saved
14554 && ( aMachineState == MachineState_PoweredOff
14555 || aMachineState == MachineState_Aborted
14556 || aMachineState == MachineState_Teleported
14557 )
14558 )
14559 {
14560 /*
14561 * delete the saved state after SessionMachine::ForgetSavedState() is called
14562 * or if the VM process (owning a direct VM session) crashed while the
14563 * VM was Saved
14564 */
14565
14566 /// @todo (dmik)
14567 // Not sure that deleting the saved state file just because of the
14568 // client death before it attempted to restore the VM is a good
14569 // thing. But when it crashes we need to go to the Aborted state
14570 // which cannot have the saved state file associated... The only
14571 // way to fix this is to make the Aborted condition not a VM state
14572 // but a bool flag: i.e., when a crash occurs, set it to true and
14573 // change the state to PoweredOff or Saved depending on the
14574 // saved state presence.
14575
14576 deleteSavedState = true;
14577 mData->mCurrentStateModified = TRUE;
14578 stsFlags |= SaveSTS_CurStateModified;
14579 }
14580
14581 if ( aMachineState == MachineState_Starting
14582 || aMachineState == MachineState_Restoring
14583 || aMachineState == MachineState_TeleportingIn
14584 )
14585 {
14586 /* set the current state modified flag to indicate that the current
14587 * state is no more identical to the state in the
14588 * current snapshot */
14589 if (!mData->mCurrentSnapshot.isNull())
14590 {
14591 mData->mCurrentStateModified = TRUE;
14592 stsFlags |= SaveSTS_CurStateModified;
14593 }
14594 }
14595
14596 if (deleteSavedState)
14597 {
14598 if (mRemoveSavedState)
14599 {
14600 Assert(!mSSData->strStateFilePath.isEmpty());
14601
14602 // it is safe to delete the saved state file if ...
14603 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14604 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14605 // ... none of the snapshots share the saved state file
14606 )
14607 RTFileDelete(mSSData->strStateFilePath.c_str());
14608 }
14609
14610 mSSData->strStateFilePath.setNull();
14611 stsFlags |= SaveSTS_StateFilePath;
14612 }
14613
14614 /* redirect to the underlying peer machine */
14615 mPeer->i_setMachineState(aMachineState);
14616
14617 if ( oldMachineState != MachineState_RestoringSnapshot
14618 && ( aMachineState == MachineState_PoweredOff
14619 || aMachineState == MachineState_Teleported
14620 || aMachineState == MachineState_Aborted
14621 || aMachineState == MachineState_Saved))
14622 {
14623 /* the machine has stopped execution
14624 * (or the saved state file was adopted) */
14625 stsFlags |= SaveSTS_StateTimeStamp;
14626 }
14627
14628 if ( ( oldMachineState == MachineState_PoweredOff
14629 || oldMachineState == MachineState_Aborted
14630 || oldMachineState == MachineState_Teleported
14631 )
14632 && aMachineState == MachineState_Saved)
14633 {
14634 /* the saved state file was adopted */
14635 Assert(!mSSData->strStateFilePath.isEmpty());
14636 stsFlags |= SaveSTS_StateFilePath;
14637 }
14638
14639#ifdef VBOX_WITH_GUEST_PROPS
14640 if ( aMachineState == MachineState_PoweredOff
14641 || aMachineState == MachineState_Aborted
14642 || aMachineState == MachineState_Teleported)
14643 {
14644 /* Make sure any transient guest properties get removed from the
14645 * property store on shutdown. */
14646 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14647
14648 /* remove it from the settings representation */
14649 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14650 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14651 it != llGuestProperties.end();
14652 /*nothing*/)
14653 {
14654 const settings::GuestProperty &prop = *it;
14655 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14656 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14657 {
14658 it = llGuestProperties.erase(it);
14659 fNeedsSaving = true;
14660 }
14661 else
14662 {
14663 ++it;
14664 }
14665 }
14666
14667 /* Additionally remove it from the HWData representation. Required to
14668 * keep everything in sync, as this is what the API keeps using. */
14669 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14670 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14671 it != llHWGuestProperties.end();
14672 /*nothing*/)
14673 {
14674 uint32_t fFlags = it->second.mFlags;
14675 if ( fFlags & guestProp::TRANSIENT
14676 || fFlags & guestProp::TRANSRESET)
14677 {
14678 /* iterator where we need to continue after the erase call
14679 * (C++03 is a fact still, and it doesn't return the iterator
14680 * which would allow continuing) */
14681 HWData::GuestPropertyMap::iterator it2 = it;
14682 ++it2;
14683 llHWGuestProperties.erase(it);
14684 it = it2;
14685 fNeedsSaving = true;
14686 }
14687 else
14688 {
14689 ++it;
14690 }
14691 }
14692
14693 if (fNeedsSaving)
14694 {
14695 mData->mCurrentStateModified = TRUE;
14696 stsFlags |= SaveSTS_CurStateModified;
14697 }
14698 }
14699#endif /* VBOX_WITH_GUEST_PROPS */
14700
14701 rc = i_saveStateSettings(stsFlags);
14702
14703 if ( ( oldMachineState != MachineState_PoweredOff
14704 && oldMachineState != MachineState_Aborted
14705 && oldMachineState != MachineState_Teleported
14706 )
14707 && ( aMachineState == MachineState_PoweredOff
14708 || aMachineState == MachineState_Aborted
14709 || aMachineState == MachineState_Teleported
14710 )
14711 )
14712 {
14713 /* we've been shut down for any reason */
14714 /* no special action so far */
14715 }
14716
14717 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14718 LogFlowThisFuncLeave();
14719 return rc;
14720}
14721
14722/**
14723 * Sends the current machine state value to the VM process.
14724 *
14725 * @note Locks this object for reading, then calls a client process.
14726 */
14727HRESULT SessionMachine::i_updateMachineStateOnClient()
14728{
14729 AutoCaller autoCaller(this);
14730 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14731
14732 ComPtr<IInternalSessionControl> directControl;
14733 {
14734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14735 AssertReturn(!!mData, E_FAIL);
14736 if (mData->mSession.mLockType == LockType_VM)
14737 directControl = mData->mSession.mDirectControl;
14738
14739 /* directControl may be already set to NULL here in #OnSessionEnd()
14740 * called too early by the direct session process while there is still
14741 * some operation (like deleting the snapshot) in progress. The client
14742 * process in this case is waiting inside Session::close() for the
14743 * "end session" process object to complete, while #uninit() called by
14744 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14745 * operation to complete. For now, we accept this inconsistent behavior
14746 * and simply do nothing here. */
14747
14748 if (mData->mSession.mState == SessionState_Unlocking)
14749 return S_OK;
14750 }
14751
14752 /* ignore notifications sent after #OnSessionEnd() is called */
14753 if (!directControl)
14754 return S_OK;
14755
14756 return directControl->UpdateMachineState(mData->mMachineState);
14757}
14758
14759
14760/**
14761 * Static Machine method that can get passed to RTThreadCreate to
14762 * have a thread started for a Task. See Machine::Task.
14763 */
14764/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14765{
14766 AssertReturn(pvUser, VERR_INVALID_POINTER);
14767
14768 Task *pTask = static_cast<Task *>(pvUser);
14769 pTask->handler();
14770 /** @todo r=klaus it would be safer to update the progress object here,
14771 * as it avoids possible races due to scoping issues/tricks in the handler */
14772 // it's our responsibility to delete the task
14773 delete pTask;
14774
14775 return 0;
14776}
14777
14778/*static*/
14779HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14780{
14781 va_list args;
14782 va_start(args, pcszMsg);
14783 HRESULT rc = setErrorInternal(aResultCode,
14784 getStaticClassIID(),
14785 getStaticComponentName(),
14786 Utf8Str(pcszMsg, args),
14787 false /* aWarning */,
14788 true /* aLogIt */);
14789 va_end(args);
14790 return rc;
14791}
14792
14793
14794HRESULT Machine::updateState(MachineState_T aState)
14795{
14796 NOREF(aState);
14797 ReturnComNotImplemented();
14798}
14799
14800HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14801{
14802 NOREF(aProgress);
14803 ReturnComNotImplemented();
14804}
14805
14806HRESULT Machine::endPowerUp(LONG aResult)
14807{
14808 NOREF(aResult);
14809 ReturnComNotImplemented();
14810}
14811
14812HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14813{
14814 NOREF(aProgress);
14815 ReturnComNotImplemented();
14816}
14817
14818HRESULT Machine::endPoweringDown(LONG aResult,
14819 const com::Utf8Str &aErrMsg)
14820{
14821 NOREF(aResult);
14822 NOREF(aErrMsg);
14823 ReturnComNotImplemented();
14824}
14825
14826HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14827 BOOL *aMatched,
14828 ULONG *aMaskedInterfaces)
14829{
14830 NOREF(aDevice);
14831 NOREF(aMatched);
14832 NOREF(aMaskedInterfaces);
14833 ReturnComNotImplemented();
14834
14835}
14836
14837HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14838{
14839 NOREF(aId); NOREF(aCaptureFilename);
14840 ReturnComNotImplemented();
14841}
14842
14843HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14844 BOOL aDone)
14845{
14846 NOREF(aId);
14847 NOREF(aDone);
14848 ReturnComNotImplemented();
14849}
14850
14851HRESULT Machine::autoCaptureUSBDevices()
14852{
14853 ReturnComNotImplemented();
14854}
14855
14856HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14857{
14858 NOREF(aDone);
14859 ReturnComNotImplemented();
14860}
14861
14862HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14863 ComPtr<IProgress> &aProgress)
14864{
14865 NOREF(aSession);
14866 NOREF(aProgress);
14867 ReturnComNotImplemented();
14868}
14869
14870HRESULT Machine::finishOnlineMergeMedium()
14871{
14872 ReturnComNotImplemented();
14873}
14874
14875HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14876 std::vector<com::Utf8Str> &aValues,
14877 std::vector<LONG64> &aTimestamps,
14878 std::vector<com::Utf8Str> &aFlags)
14879{
14880 NOREF(aNames);
14881 NOREF(aValues);
14882 NOREF(aTimestamps);
14883 NOREF(aFlags);
14884 ReturnComNotImplemented();
14885}
14886
14887HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14888 const com::Utf8Str &aValue,
14889 LONG64 aTimestamp,
14890 const com::Utf8Str &aFlags)
14891{
14892 NOREF(aName);
14893 NOREF(aValue);
14894 NOREF(aTimestamp);
14895 NOREF(aFlags);
14896 ReturnComNotImplemented();
14897}
14898
14899HRESULT Machine::lockMedia()
14900{
14901 ReturnComNotImplemented();
14902}
14903
14904HRESULT Machine::unlockMedia()
14905{
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14910 ComPtr<IMediumAttachment> &aNewAttachment)
14911{
14912 NOREF(aAttachment);
14913 NOREF(aNewAttachment);
14914 ReturnComNotImplemented();
14915}
14916
14917HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14918 ULONG aCpuUser,
14919 ULONG aCpuKernel,
14920 ULONG aCpuIdle,
14921 ULONG aMemTotal,
14922 ULONG aMemFree,
14923 ULONG aMemBalloon,
14924 ULONG aMemShared,
14925 ULONG aMemCache,
14926 ULONG aPagedTotal,
14927 ULONG aMemAllocTotal,
14928 ULONG aMemFreeTotal,
14929 ULONG aMemBalloonTotal,
14930 ULONG aMemSharedTotal,
14931 ULONG aVmNetRx,
14932 ULONG aVmNetTx)
14933{
14934 NOREF(aValidStats);
14935 NOREF(aCpuUser);
14936 NOREF(aCpuKernel);
14937 NOREF(aCpuIdle);
14938 NOREF(aMemTotal);
14939 NOREF(aMemFree);
14940 NOREF(aMemBalloon);
14941 NOREF(aMemShared);
14942 NOREF(aMemCache);
14943 NOREF(aPagedTotal);
14944 NOREF(aMemAllocTotal);
14945 NOREF(aMemFreeTotal);
14946 NOREF(aMemBalloonTotal);
14947 NOREF(aMemSharedTotal);
14948 NOREF(aVmNetRx);
14949 NOREF(aVmNetTx);
14950 ReturnComNotImplemented();
14951}
14952
14953HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14954 com::Utf8Str &aResult)
14955{
14956 NOREF(aAuthParams);
14957 NOREF(aResult);
14958 ReturnComNotImplemented();
14959}
14960
14961HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14962{
14963 NOREF(aFlags);
14964 ReturnComNotImplemented();
14965}
14966
14967/* This isn't handled entirely by the wrapper generator yet. */
14968#ifdef VBOX_WITH_XPCOM
14969NS_DECL_CLASSINFO(SessionMachine)
14970NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14971
14972NS_DECL_CLASSINFO(SnapshotMachine)
14973NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14974#endif
Note: See TracBrowser for help on using the repository browser.

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