VirtualBox

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

Last change on this file since 72332 was 72332, checked in by vboxsync, 7 years ago

Main: Added HWVirtExPropertyType::UseNativeApi for use with IMachine::getHWVirtExProperty and IMachine::setHWVirtExProperty. bugref:9044

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 527.2 KB
Line 
1/* $Id: MachineImpl.cpp 72332 2018-05-24 20:51:23Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189 mHWVirtExUseNativeApi = false;
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mTripleFaultReset = false;
197 mAPIC = true;
198 mX2APIC = false;
199 mIBPBOnVMExit = false;
200 mIBPBOnVMEntry = false;
201 mSpecCtrl = false;
202 mSpecCtrlByHost = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Apply BIOS defaults */
345 mBIOSSettings->i_applyDefaults(aOsType);
346
347 /* Let the OS type select 64-bit ness. */
348 mHWData->mLongMode = aOsType->i_is64Bit()
349 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
350
351 /* Let the OS type enable the X2APIC */
352 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
353 }
354
355 /* Apply network adapters defaults */
356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
357 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
358
359 /* Apply serial port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
361 mSerialPorts[slot]->i_applyDefaults(aOsType);
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 strConfigFile 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 aParent Associated parent object.
518 * @param strName Name for the new machine; this overrides what is specified in config and is used
519 * for the settings file as well.
520 * @param config Machine configuration loaded and parsed from XML.
521 *
522 * @return Success indicator. if not S_OK, the machine object is invalid
523 */
524HRESULT Machine::init(VirtualBox *aParent,
525 const Utf8Str &strName,
526 const settings::MachineConfigFile &config)
527{
528 LogFlowThisFuncEnter();
529
530 /* Enclose the state transition NotReady->InInit->Ready */
531 AutoInitSpan autoInitSpan(this);
532 AssertReturn(autoInitSpan.isOk(), E_FAIL);
533
534 Utf8Str strConfigFile;
535 aParent->i_getDefaultMachineFolder(strConfigFile);
536 strConfigFile.append(RTPATH_DELIMITER);
537 strConfigFile.append(strName);
538 strConfigFile.append(RTPATH_DELIMITER);
539 strConfigFile.append(strName);
540 strConfigFile.append(".vbox");
541
542 HRESULT rc = initImpl(aParent, strConfigFile);
543 if (FAILED(rc)) return rc;
544
545 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
546 if (FAILED(rc)) return rc;
547
548 rc = initDataAndChildObjects();
549
550 if (SUCCEEDED(rc))
551 {
552 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
553 mData->mAccessible = TRUE;
554
555 // create empty machine config for instance data
556 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
557
558 // generate fresh UUID, ignore machine config
559 unconst(mData->mUuid).create();
560
561 rc = i_loadMachineDataFromSettings(config,
562 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
563
564 // override VM name as well, it may be different
565 mUserData->s.strName = strName;
566
567 if (SUCCEEDED(rc))
568 {
569 /* At this point the changing of the current state modification
570 * flag is allowed. */
571 i_allowStateModification();
572
573 /* commit all changes made during the initialization */
574 i_commit();
575 }
576 }
577
578 /* Confirm a successful initialization when it's the case */
579 if (SUCCEEDED(rc))
580 {
581 if (mData->mAccessible)
582 autoInitSpan.setSucceeded();
583 else
584 {
585 /* Ignore all errors from unregistering, they would destroy
586- * the more interesting error information we already have,
587- * pinpointing the issue with the VM config. */
588 ErrorInfoKeeper eik;
589
590 autoInitSpan.setLimited();
591
592 // uninit media from this machine's media registry, or else
593 // reloading the settings will fail
594 mParent->i_unregisterMachineMedia(i_getId());
595 }
596 }
597
598 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
599 "rc=%08X\n",
600 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
601 mData->mRegistered, mData->mAccessible, rc));
602
603 LogFlowThisFuncLeave();
604
605 return rc;
606}
607
608/**
609 * Shared code between the various init() implementations.
610 * @param aParent The VirtualBox object.
611 * @param strConfigFile Settings file.
612 * @return
613 */
614HRESULT Machine::initImpl(VirtualBox *aParent,
615 const Utf8Str &strConfigFile)
616{
617 LogFlowThisFuncEnter();
618
619 AssertReturn(aParent, E_INVALIDARG);
620 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
621
622 HRESULT rc = S_OK;
623
624 /* share the parent weakly */
625 unconst(mParent) = aParent;
626
627 /* allocate the essential machine data structure (the rest will be
628 * allocated later by initDataAndChildObjects() */
629 mData.allocate();
630
631 /* memorize the config file name (as provided) */
632 mData->m_strConfigFile = strConfigFile;
633
634 /* get the full file name */
635 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
636 if (RT_FAILURE(vrc1))
637 return setError(VBOX_E_FILE_ERROR,
638 tr("Invalid machine settings file name '%s' (%Rrc)"),
639 strConfigFile.c_str(),
640 vrc1);
641
642 LogFlowThisFuncLeave();
643
644 return rc;
645}
646
647/**
648 * Tries to create a machine settings file in the path stored in the machine
649 * instance data. Used when a new machine is created to fail gracefully if
650 * the settings file could not be written (e.g. because machine dir is read-only).
651 * @return
652 */
653HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
654{
655 HRESULT rc = S_OK;
656
657 // when we create a new machine, we must be able to create the settings file
658 RTFILE f = NIL_RTFILE;
659 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
660 if ( RT_SUCCESS(vrc)
661 || vrc == VERR_SHARING_VIOLATION
662 )
663 {
664 if (RT_SUCCESS(vrc))
665 RTFileClose(f);
666 if (!fForceOverwrite)
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Machine settings file '%s' already exists"),
669 mData->m_strConfigFileFull.c_str());
670 else
671 {
672 /* try to delete the config file, as otherwise the creation
673 * of a new settings file will fail. */
674 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
675 if (RT_FAILURE(vrc2))
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Could not delete the existing settings file '%s' (%Rrc)"),
678 mData->m_strConfigFileFull.c_str(), vrc2);
679 }
680 }
681 else if ( vrc != VERR_FILE_NOT_FOUND
682 && vrc != VERR_PATH_NOT_FOUND
683 )
684 rc = setError(VBOX_E_FILE_ERROR,
685 tr("Invalid machine settings file name '%s' (%Rrc)"),
686 mData->m_strConfigFileFull.c_str(),
687 vrc);
688 return rc;
689}
690
691/**
692 * Initializes the registered machine by loading the settings file.
693 * This method is separated from #init() in order to make it possible to
694 * retry the operation after VirtualBox startup instead of refusing to
695 * startup the whole VirtualBox server in case if the settings file of some
696 * registered VM is invalid or inaccessible.
697 *
698 * @note Must be always called from this object's write lock
699 * (unless called from #init() that doesn't need any locking).
700 * @note Locks the mUSBController method for writing.
701 * @note Subclasses must not call this method.
702 */
703HRESULT Machine::i_registeredInit()
704{
705 AssertReturn(!i_isSessionMachine(), E_FAIL);
706 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
707 AssertReturn(mData->mUuid.isValid(), E_FAIL);
708 AssertReturn(!mData->mAccessible, E_FAIL);
709
710 HRESULT rc = initDataAndChildObjects();
711
712 if (SUCCEEDED(rc))
713 {
714 /* Temporarily reset the registered flag in order to let setters
715 * potentially called from loadSettings() succeed (isMutable() used in
716 * all setters will return FALSE for a Machine instance if mRegistered
717 * is TRUE). */
718 mData->mRegistered = FALSE;
719
720 try
721 {
722 // load and parse machine XML; this will throw on XML or logic errors
723 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
724
725 if (mData->mUuid != mData->pMachineConfigFile->uuid)
726 throw setError(E_FAIL,
727 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
728 mData->pMachineConfigFile->uuid.raw(),
729 mData->m_strConfigFileFull.c_str(),
730 mData->mUuid.toString().c_str(),
731 mParent->i_settingsFilePath().c_str());
732
733 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
734 NULL /* const Guid *puuidRegistry */);
735 if (FAILED(rc)) throw rc;
736 }
737 catch (HRESULT err)
738 {
739 /* we assume that error info is set by the thrower */
740 rc = err;
741 }
742 catch (...)
743 {
744 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
745 }
746
747 /* Restore the registered flag (even on failure) */
748 mData->mRegistered = TRUE;
749 }
750
751 if (SUCCEEDED(rc))
752 {
753 /* Set mAccessible to TRUE only if we successfully locked and loaded
754 * the settings file */
755 mData->mAccessible = TRUE;
756
757 /* commit all changes made during loading the settings file */
758 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
759 /// @todo r=klaus for some reason the settings loading logic backs up
760 // the settings, and therefore a commit is needed. Should probably be changed.
761 }
762 else
763 {
764 /* If the machine is registered, then, instead of returning a
765 * failure, we mark it as inaccessible and set the result to
766 * success to give it a try later */
767
768 /* fetch the current error info */
769 mData->mAccessError = com::ErrorInfo();
770 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
771
772 /* rollback all changes */
773 i_rollback(false /* aNotify */);
774
775 // uninit media from this machine's media registry, or else
776 // reloading the settings will fail
777 mParent->i_unregisterMachineMedia(i_getId());
778
779 /* uninitialize the common part to make sure all data is reset to
780 * default (null) values */
781 uninitDataAndChildObjects();
782
783 rc = S_OK;
784 }
785
786 return rc;
787}
788
789/**
790 * Uninitializes the instance.
791 * Called either from FinalRelease() or by the parent when it gets destroyed.
792 *
793 * @note The caller of this method must make sure that this object
794 * a) doesn't have active callers on the current thread and b) is not locked
795 * by the current thread; otherwise uninit() will hang either a) due to
796 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
797 * a dead-lock caused by this thread waiting for all callers on the other
798 * threads are done but preventing them from doing so by holding a lock.
799 */
800void Machine::uninit()
801{
802 LogFlowThisFuncEnter();
803
804 Assert(!isWriteLockOnCurrentThread());
805
806 Assert(!uRegistryNeedsSaving);
807 if (uRegistryNeedsSaving)
808 {
809 AutoCaller autoCaller(this);
810 if (SUCCEEDED(autoCaller.rc()))
811 {
812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
813 i_saveSettings(NULL, Machine::SaveS_Force);
814 }
815 }
816
817 /* Enclose the state transition Ready->InUninit->NotReady */
818 AutoUninitSpan autoUninitSpan(this);
819 if (autoUninitSpan.uninitDone())
820 return;
821
822 Assert(!i_isSnapshotMachine());
823 Assert(!i_isSessionMachine());
824 Assert(!!mData);
825
826 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
827 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
828
829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
830
831 if (!mData->mSession.mMachine.isNull())
832 {
833 /* Theoretically, this can only happen if the VirtualBox server has been
834 * terminated while there were clients running that owned open direct
835 * sessions. Since in this case we are definitely called by
836 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
837 * won't happen on the client watcher thread (because it has a
838 * VirtualBox caller for the duration of the
839 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
840 * cannot happen until the VirtualBox caller is released). This is
841 * important, because SessionMachine::uninit() cannot correctly operate
842 * after we return from this method (it expects the Machine instance is
843 * still valid). We'll call it ourselves below.
844 */
845 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
846 (SessionMachine*)mData->mSession.mMachine));
847
848 if (Global::IsOnlineOrTransient(mData->mMachineState))
849 {
850 Log1WarningThisFunc(("Setting state to Aborted!\n"));
851 /* set machine state using SessionMachine reimplementation */
852 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
853 }
854
855 /*
856 * Uninitialize SessionMachine using public uninit() to indicate
857 * an unexpected uninitialization.
858 */
859 mData->mSession.mMachine->uninit();
860 /* SessionMachine::uninit() must set mSession.mMachine to null */
861 Assert(mData->mSession.mMachine.isNull());
862 }
863
864 // uninit media from this machine's media registry, if they're still there
865 Guid uuidMachine(i_getId());
866
867 /* the lock is no more necessary (SessionMachine is uninitialized) */
868 alock.release();
869
870 /* XXX This will fail with
871 * "cannot be closed because it is still attached to 1 virtual machines"
872 * because at this point we did not call uninitDataAndChildObjects() yet
873 * and therefore also removeBackReference() for all these mediums was not called! */
874
875 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
876 mParent->i_unregisterMachineMedia(uuidMachine);
877
878 // has machine been modified?
879 if (mData->flModifications)
880 {
881 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
882 i_rollback(false /* aNotify */);
883 }
884
885 if (mData->mAccessible)
886 uninitDataAndChildObjects();
887
888 /* free the essential data structure last */
889 mData.free();
890
891 LogFlowThisFuncLeave();
892}
893
894// Wrapped IMachine properties
895/////////////////////////////////////////////////////////////////////////////
896HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
897{
898 /* mParent is constant during life time, no need to lock */
899 ComObjPtr<VirtualBox> pVirtualBox(mParent);
900 aParent = pVirtualBox;
901
902 return S_OK;
903}
904
905
906HRESULT Machine::getAccessible(BOOL *aAccessible)
907{
908 /* In some cases (medium registry related), it is necessary to be able to
909 * go through the list of all machines. Happens when an inaccessible VM
910 * has a sensible medium registry. */
911 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
913
914 HRESULT rc = S_OK;
915
916 if (!mData->mAccessible)
917 {
918 /* try to initialize the VM once more if not accessible */
919
920 AutoReinitSpan autoReinitSpan(this);
921 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
922
923#ifdef DEBUG
924 LogFlowThisFunc(("Dumping media backreferences\n"));
925 mParent->i_dumpAllBackRefs();
926#endif
927
928 if (mData->pMachineConfigFile)
929 {
930 // reset the XML file to force loadSettings() (called from i_registeredInit())
931 // to parse it again; the file might have changed
932 delete mData->pMachineConfigFile;
933 mData->pMachineConfigFile = NULL;
934 }
935
936 rc = i_registeredInit();
937
938 if (SUCCEEDED(rc) && mData->mAccessible)
939 {
940 autoReinitSpan.setSucceeded();
941
942 /* make sure interesting parties will notice the accessibility
943 * state change */
944 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
945 mParent->i_onMachineDataChange(mData->mUuid);
946 }
947 }
948
949 if (SUCCEEDED(rc))
950 *aAccessible = mData->mAccessible;
951
952 LogFlowThisFuncLeave();
953
954 return rc;
955}
956
957HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
958{
959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
960
961 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
962 {
963 /* return shortly */
964 aAccessError = NULL;
965 return S_OK;
966 }
967
968 HRESULT rc = S_OK;
969
970 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
971 rc = errorInfo.createObject();
972 if (SUCCEEDED(rc))
973 {
974 errorInfo->init(mData->mAccessError.getResultCode(),
975 mData->mAccessError.getInterfaceID().ref(),
976 Utf8Str(mData->mAccessError.getComponent()).c_str(),
977 Utf8Str(mData->mAccessError.getText()));
978 aAccessError = errorInfo;
979 }
980
981 return rc;
982}
983
984HRESULT Machine::getName(com::Utf8Str &aName)
985{
986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
987
988 aName = mUserData->s.strName;
989
990 return S_OK;
991}
992
993HRESULT Machine::setName(const com::Utf8Str &aName)
994{
995 // prohibit setting a UUID only as the machine name, or else it can
996 // never be found by findMachine()
997 Guid test(aName);
998
999 if (test.isValid())
1000 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1001
1002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1003
1004 HRESULT rc = i_checkStateDependency(MutableStateDep);
1005 if (FAILED(rc)) return rc;
1006
1007 i_setModified(IsModified_MachineData);
1008 mUserData.backup();
1009 mUserData->s.strName = aName;
1010
1011 return S_OK;
1012}
1013
1014HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1015{
1016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 aDescription = mUserData->s.strDescription;
1019
1020 return S_OK;
1021}
1022
1023HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1024{
1025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1026
1027 // this can be done in principle in any state as it doesn't affect the VM
1028 // significantly, but play safe by not messing around while complex
1029 // activities are going on
1030 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1031 if (FAILED(rc)) return rc;
1032
1033 i_setModified(IsModified_MachineData);
1034 mUserData.backup();
1035 mUserData->s.strDescription = aDescription;
1036
1037 return S_OK;
1038}
1039
1040HRESULT Machine::getId(com::Guid &aId)
1041{
1042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1043
1044 aId = mData->mUuid;
1045
1046 return S_OK;
1047}
1048
1049HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1050{
1051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1052 aGroups.resize(mUserData->s.llGroups.size());
1053 size_t i = 0;
1054 for (StringsList::const_iterator
1055 it = mUserData->s.llGroups.begin();
1056 it != mUserData->s.llGroups.end();
1057 ++it, ++i)
1058 aGroups[i] = (*it);
1059
1060 return S_OK;
1061}
1062
1063HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1064{
1065 StringsList llGroups;
1066 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1067 if (FAILED(rc))
1068 return rc;
1069
1070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1071
1072 rc = i_checkStateDependency(MutableOrSavedStateDep);
1073 if (FAILED(rc)) return rc;
1074
1075 i_setModified(IsModified_MachineData);
1076 mUserData.backup();
1077 mUserData->s.llGroups = llGroups;
1078
1079 return S_OK;
1080}
1081
1082HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1083{
1084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1085
1086 aOSTypeId = mUserData->s.strOsType;
1087
1088 return S_OK;
1089}
1090
1091HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1092{
1093 /* look up the object by Id to check it is valid */
1094 ComObjPtr<GuestOSType> pGuestOSType;
1095 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1096 pGuestOSType);
1097 if (FAILED(rc)) return rc;
1098
1099 /* when setting, always use the "etalon" value for consistency -- lookup
1100 * by ID is case-insensitive and the input value may have different case */
1101 Utf8Str osTypeId = pGuestOSType->i_id();
1102
1103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1104
1105 rc = i_checkStateDependency(MutableStateDep);
1106 if (FAILED(rc)) return rc;
1107
1108 i_setModified(IsModified_MachineData);
1109 mUserData.backup();
1110 mUserData->s.strOsType = osTypeId;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1116{
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 *aFirmwareType = mHWData->mFirmwareType;
1120
1121 return S_OK;
1122}
1123
1124HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1125{
1126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 HRESULT rc = i_checkStateDependency(MutableStateDep);
1129 if (FAILED(rc)) return rc;
1130
1131 i_setModified(IsModified_MachineData);
1132 mHWData.backup();
1133 mHWData->mFirmwareType = aFirmwareType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1139{
1140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1143
1144 return S_OK;
1145}
1146
1147HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1148{
1149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1150
1151 HRESULT rc = i_checkStateDependency(MutableStateDep);
1152 if (FAILED(rc)) return rc;
1153
1154 i_setModified(IsModified_MachineData);
1155 mHWData.backup();
1156 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1162{
1163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 *aPointingHIDType = mHWData->mPointingHIDType;
1166
1167 return S_OK;
1168}
1169
1170HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1171{
1172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 HRESULT rc = i_checkStateDependency(MutableStateDep);
1175 if (FAILED(rc)) return rc;
1176
1177 i_setModified(IsModified_MachineData);
1178 mHWData.backup();
1179 mHWData->mPointingHIDType = aPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1185{
1186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 *aChipsetType = mHWData->mChipsetType;
1189
1190 return S_OK;
1191}
1192
1193HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1194{
1195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 HRESULT rc = i_checkStateDependency(MutableStateDep);
1198 if (FAILED(rc)) return rc;
1199
1200 if (aChipsetType != mHWData->mChipsetType)
1201 {
1202 i_setModified(IsModified_MachineData);
1203 mHWData.backup();
1204 mHWData->mChipsetType = aChipsetType;
1205
1206 // Resize network adapter array, to be finalized on commit/rollback.
1207 // We must not throw away entries yet, otherwise settings are lost
1208 // without a way to roll back.
1209 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1210 size_t oldCount = mNetworkAdapters.size();
1211 if (newCount > oldCount)
1212 {
1213 mNetworkAdapters.resize(newCount);
1214 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1215 {
1216 unconst(mNetworkAdapters[slot]).createObject();
1217 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1218 }
1219 }
1220 }
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1226{
1227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 aParavirtDebug = mHWData->mParavirtDebug;
1230 return S_OK;
1231}
1232
1233HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1234{
1235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1236
1237 HRESULT rc = i_checkStateDependency(MutableStateDep);
1238 if (FAILED(rc)) return rc;
1239
1240 /** @todo Parse/validate options? */
1241 if (aParavirtDebug != mHWData->mParavirtDebug)
1242 {
1243 i_setModified(IsModified_MachineData);
1244 mHWData.backup();
1245 mHWData->mParavirtDebug = aParavirtDebug;
1246 }
1247
1248 return S_OK;
1249}
1250
1251HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 *aParavirtProvider = mHWData->mParavirtProvider;
1256
1257 return S_OK;
1258}
1259
1260HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1261{
1262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 HRESULT rc = i_checkStateDependency(MutableStateDep);
1265 if (FAILED(rc)) return rc;
1266
1267 if (aParavirtProvider != mHWData->mParavirtProvider)
1268 {
1269 i_setModified(IsModified_MachineData);
1270 mHWData.backup();
1271 mHWData->mParavirtProvider = aParavirtProvider;
1272 }
1273
1274 return S_OK;
1275}
1276
1277HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1278{
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 *aParavirtProvider = mHWData->mParavirtProvider;
1282 switch (mHWData->mParavirtProvider)
1283 {
1284 case ParavirtProvider_None:
1285 case ParavirtProvider_HyperV:
1286 case ParavirtProvider_KVM:
1287 case ParavirtProvider_Minimal:
1288 break;
1289
1290 /* Resolve dynamic provider types to the effective types. */
1291 default:
1292 {
1293 ComObjPtr<GuestOSType> pGuestOSType;
1294 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1295 pGuestOSType);
1296 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1297
1298 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1299 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1300
1301 switch (mHWData->mParavirtProvider)
1302 {
1303 case ParavirtProvider_Legacy:
1304 {
1305 if (fOsXGuest)
1306 *aParavirtProvider = ParavirtProvider_Minimal;
1307 else
1308 *aParavirtProvider = ParavirtProvider_None;
1309 break;
1310 }
1311
1312 case ParavirtProvider_Default:
1313 {
1314 if (fOsXGuest)
1315 *aParavirtProvider = ParavirtProvider_Minimal;
1316 else if ( mUserData->s.strOsType == "Windows10"
1317 || mUserData->s.strOsType == "Windows10_64"
1318 || mUserData->s.strOsType == "Windows81"
1319 || mUserData->s.strOsType == "Windows81_64"
1320 || mUserData->s.strOsType == "Windows8"
1321 || mUserData->s.strOsType == "Windows8_64"
1322 || mUserData->s.strOsType == "Windows7"
1323 || mUserData->s.strOsType == "Windows7_64"
1324 || mUserData->s.strOsType == "WindowsVista"
1325 || mUserData->s.strOsType == "WindowsVista_64"
1326 || mUserData->s.strOsType == "Windows2012"
1327 || mUserData->s.strOsType == "Windows2012_64"
1328 || mUserData->s.strOsType == "Windows2008"
1329 || mUserData->s.strOsType == "Windows2008_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_HyperV;
1332 }
1333 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1334 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1335 || mUserData->s.strOsType == "Linux"
1336 || mUserData->s.strOsType == "Linux_64"
1337 || mUserData->s.strOsType == "ArchLinux"
1338 || mUserData->s.strOsType == "ArchLinux_64"
1339 || mUserData->s.strOsType == "Debian"
1340 || mUserData->s.strOsType == "Debian_64"
1341 || mUserData->s.strOsType == "Fedora"
1342 || mUserData->s.strOsType == "Fedora_64"
1343 || mUserData->s.strOsType == "Gentoo"
1344 || mUserData->s.strOsType == "Gentoo_64"
1345 || mUserData->s.strOsType == "Mandriva"
1346 || mUserData->s.strOsType == "Mandriva_64"
1347 || mUserData->s.strOsType == "OpenSUSE"
1348 || mUserData->s.strOsType == "OpenSUSE_64"
1349 || mUserData->s.strOsType == "Oracle"
1350 || mUserData->s.strOsType == "Oracle_64"
1351 || mUserData->s.strOsType == "RedHat"
1352 || mUserData->s.strOsType == "RedHat_64"
1353 || mUserData->s.strOsType == "Turbolinux"
1354 || mUserData->s.strOsType == "Turbolinux_64"
1355 || mUserData->s.strOsType == "Ubuntu"
1356 || mUserData->s.strOsType == "Ubuntu_64"
1357 || mUserData->s.strOsType == "Xandros"
1358 || mUserData->s.strOsType == "Xandros_64")
1359 {
1360 *aParavirtProvider = ParavirtProvider_KVM;
1361 }
1362 else
1363 *aParavirtProvider = ParavirtProvider_None;
1364 break;
1365 }
1366
1367 default: AssertFailedBreak(); /* Shut up MSC. */
1368 }
1369 break;
1370 }
1371 }
1372
1373 Assert( *aParavirtProvider == ParavirtProvider_None
1374 || *aParavirtProvider == ParavirtProvider_Minimal
1375 || *aParavirtProvider == ParavirtProvider_HyperV
1376 || *aParavirtProvider == ParavirtProvider_KVM);
1377 return S_OK;
1378}
1379
1380HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1381{
1382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1383
1384 aHardwareVersion = mHWData->mHWVersion;
1385
1386 return S_OK;
1387}
1388
1389HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1390{
1391 /* check known version */
1392 Utf8Str hwVersion = aHardwareVersion;
1393 if ( hwVersion.compare("1") != 0
1394 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1395 return setError(E_INVALIDARG,
1396 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1397
1398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1399
1400 HRESULT rc = i_checkStateDependency(MutableStateDep);
1401 if (FAILED(rc)) return rc;
1402
1403 i_setModified(IsModified_MachineData);
1404 mHWData.backup();
1405 mHWData->mHWVersion = aHardwareVersion;
1406
1407 return S_OK;
1408}
1409
1410HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1411{
1412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1413
1414 if (!mHWData->mHardwareUUID.isZero())
1415 aHardwareUUID = mHWData->mHardwareUUID;
1416 else
1417 aHardwareUUID = mData->mUuid;
1418
1419 return S_OK;
1420}
1421
1422HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1423{
1424 if (!aHardwareUUID.isValid())
1425 return E_INVALIDARG;
1426
1427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 HRESULT rc = i_checkStateDependency(MutableStateDep);
1430 if (FAILED(rc)) return rc;
1431
1432 i_setModified(IsModified_MachineData);
1433 mHWData.backup();
1434 if (aHardwareUUID == mData->mUuid)
1435 mHWData->mHardwareUUID.clear();
1436 else
1437 mHWData->mHardwareUUID = aHardwareUUID;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aMemorySize = mHWData->mMemorySize;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setMemorySize(ULONG aMemorySize)
1452{
1453 /* check RAM limits */
1454 if ( aMemorySize < MM_RAM_MIN_IN_MB
1455 || aMemorySize > MM_RAM_MAX_IN_MB
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1459 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 HRESULT rc = i_checkStateDependency(MutableStateDep);
1464 if (FAILED(rc)) return rc;
1465
1466 i_setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mMemorySize = aMemorySize;
1469
1470 return S_OK;
1471}
1472
1473HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1474{
1475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1476
1477 *aCPUCount = mHWData->mCPUCount;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::setCPUCount(ULONG aCPUCount)
1483{
1484 /* check CPU limits */
1485 if ( aCPUCount < SchemaDefs::MinCPUCount
1486 || aCPUCount > SchemaDefs::MaxCPUCount
1487 )
1488 return setError(E_INVALIDARG,
1489 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1490 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1491
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1495 if (mHWData->mCPUHotPlugEnabled)
1496 {
1497 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1498 {
1499 if (mHWData->mCPUAttached[idx])
1500 return setError(E_INVALIDARG,
1501 tr("There is still a CPU attached to socket %lu."
1502 "Detach the CPU before removing the socket"),
1503 aCPUCount, idx+1);
1504 }
1505 }
1506
1507 HRESULT rc = i_checkStateDependency(MutableStateDep);
1508 if (FAILED(rc)) return rc;
1509
1510 i_setModified(IsModified_MachineData);
1511 mHWData.backup();
1512 mHWData->mCPUCount = aCPUCount;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1518{
1519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1527{
1528 HRESULT rc = S_OK;
1529
1530 /* check throttle limits */
1531 if ( aCPUExecutionCap < 1
1532 || aCPUExecutionCap > 100
1533 )
1534 return setError(E_INVALIDARG,
1535 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1536 aCPUExecutionCap, 1, 100);
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 alock.release();
1541 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1542 alock.acquire();
1543 if (FAILED(rc)) return rc;
1544
1545 i_setModified(IsModified_MachineData);
1546 mHWData.backup();
1547 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1548
1549 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1550 if (Global::IsOnline(mData->mMachineState))
1551 i_saveSettings(NULL);
1552
1553 return S_OK;
1554}
1555
1556HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1557{
1558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1559
1560 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1561
1562 return S_OK;
1563}
1564
1565HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1566{
1567 HRESULT rc = S_OK;
1568
1569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1570
1571 rc = i_checkStateDependency(MutableStateDep);
1572 if (FAILED(rc)) return rc;
1573
1574 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1575 {
1576 if (aCPUHotPlugEnabled)
1577 {
1578 i_setModified(IsModified_MachineData);
1579 mHWData.backup();
1580
1581 /* Add the amount of CPUs currently attached */
1582 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1583 mHWData->mCPUAttached[i] = true;
1584 }
1585 else
1586 {
1587 /*
1588 * We can disable hotplug only if the amount of maximum CPUs is equal
1589 * to the amount of attached CPUs
1590 */
1591 unsigned cCpusAttached = 0;
1592 unsigned iHighestId = 0;
1593
1594 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1595 {
1596 if (mHWData->mCPUAttached[i])
1597 {
1598 cCpusAttached++;
1599 iHighestId = i;
1600 }
1601 }
1602
1603 if ( (cCpusAttached != mHWData->mCPUCount)
1604 || (iHighestId >= mHWData->mCPUCount))
1605 return setError(E_INVALIDARG,
1606 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1607
1608 i_setModified(IsModified_MachineData);
1609 mHWData.backup();
1610 }
1611 }
1612
1613 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1614
1615 return rc;
1616}
1617
1618HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1619{
1620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1621
1622 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1623
1624 return S_OK;
1625}
1626
1627HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1628{
1629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1632 if (SUCCEEDED(hrc))
1633 {
1634 i_setModified(IsModified_MachineData);
1635 mHWData.backup();
1636 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1637 }
1638 return hrc;
1639}
1640
1641HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1642{
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644 aCPUProfile = mHWData->mCpuProfile;
1645 return S_OK;
1646}
1647
1648HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1649{
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1652 if (SUCCEEDED(hrc))
1653 {
1654 i_setModified(IsModified_MachineData);
1655 mHWData.backup();
1656 /* Empty equals 'host'. */
1657 if (aCPUProfile.isNotEmpty())
1658 mHWData->mCpuProfile = aCPUProfile;
1659 else
1660 mHWData->mCpuProfile = "host";
1661 }
1662 return hrc;
1663}
1664
1665HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1666{
1667#ifdef VBOX_WITH_USB_CARDREADER
1668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1669
1670 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1671
1672 return S_OK;
1673#else
1674 NOREF(aEmulatedUSBCardReaderEnabled);
1675 return E_NOTIMPL;
1676#endif
1677}
1678
1679HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1680{
1681#ifdef VBOX_WITH_USB_CARDREADER
1682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1683
1684 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1685 if (FAILED(rc)) return rc;
1686
1687 i_setModified(IsModified_MachineData);
1688 mHWData.backup();
1689 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1690
1691 return S_OK;
1692#else
1693 NOREF(aEmulatedUSBCardReaderEnabled);
1694 return E_NOTIMPL;
1695#endif
1696}
1697
1698HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1699{
1700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 *aHPETEnabled = mHWData->mHPETEnabled;
1703
1704 return S_OK;
1705}
1706
1707HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1708{
1709 HRESULT rc = S_OK;
1710
1711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 rc = i_checkStateDependency(MutableStateDep);
1714 if (FAILED(rc)) return rc;
1715
1716 i_setModified(IsModified_MachineData);
1717 mHWData.backup();
1718
1719 mHWData->mHPETEnabled = aHPETEnabled;
1720
1721 return rc;
1722}
1723
1724HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1725{
1726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1727
1728 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1729 return S_OK;
1730}
1731
1732HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1733{
1734 HRESULT rc = S_OK;
1735
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 i_setModified(IsModified_MachineData);
1739 mHWData.backup();
1740 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1741
1742 alock.release();
1743 rc = i_onVideoCaptureChange();
1744 alock.acquire();
1745 if (FAILED(rc))
1746 {
1747 /*
1748 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1749 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1750 * determine if it should start or stop capturing. Therefore we need to manually
1751 * undo change.
1752 */
1753 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1754 return rc;
1755 }
1756
1757 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1758 if (Global::IsOnline(mData->mMachineState))
1759 i_saveSettings(NULL);
1760
1761 return rc;
1762}
1763
1764HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1765{
1766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1767 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1768 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1769 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1770 return S_OK;
1771}
1772
1773HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1774{
1775 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1776 bool fChanged = false;
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1781 {
1782 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1783 {
1784 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1785 fChanged = true;
1786 }
1787 }
1788 if (fChanged)
1789 {
1790 alock.release();
1791 HRESULT rc = i_onVideoCaptureChange();
1792 alock.acquire();
1793 if (FAILED(rc)) return rc;
1794 i_setModified(IsModified_MachineData);
1795
1796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1797 if (Global::IsOnline(mData->mMachineState))
1798 i_saveSettings(NULL);
1799 }
1800
1801 return S_OK;
1802}
1803
1804HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1805{
1806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1807 if (mHWData->mVideoCaptureFile.isEmpty())
1808 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1809 else
1810 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1815{
1816 Utf8Str strFile(aVideoCaptureFile);
1817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 if ( Global::IsOnline(mData->mMachineState)
1820 && mHWData->mVideoCaptureEnabled)
1821 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1822
1823 if (!RTPathStartsWithRoot(strFile.c_str()))
1824 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1825
1826 if (!strFile.isEmpty())
1827 {
1828 Utf8Str defaultFile;
1829 i_getDefaultVideoCaptureFile(defaultFile);
1830 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1831 strFile.setNull();
1832 }
1833
1834 i_setModified(IsModified_MachineData);
1835 mHWData.backup();
1836 mHWData->mVideoCaptureFile = strFile;
1837
1838 return S_OK;
1839}
1840
1841HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1842{
1843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1844 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1845 return S_OK;
1846}
1847
1848HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1849{
1850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 if ( Global::IsOnline(mData->mMachineState)
1853 && mHWData->mVideoCaptureEnabled)
1854 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1855
1856 i_setModified(IsModified_MachineData);
1857 mHWData.backup();
1858 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1859
1860 return S_OK;
1861}
1862
1863HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1864{
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1867 return S_OK;
1868}
1869
1870HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1871{
1872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1873
1874 if ( Global::IsOnline(mData->mMachineState)
1875 && mHWData->mVideoCaptureEnabled)
1876 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1886{
1887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1888 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1889 return S_OK;
1890}
1891
1892HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1893{
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 if ( Global::IsOnline(mData->mMachineState)
1897 && mHWData->mVideoCaptureEnabled)
1898 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1899
1900 i_setModified(IsModified_MachineData);
1901 mHWData.backup();
1902 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1903
1904 return S_OK;
1905}
1906
1907HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1908{
1909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1910 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1911 return S_OK;
1912}
1913
1914HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1915{
1916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 if ( Global::IsOnline(mData->mMachineState)
1919 && mHWData->mVideoCaptureEnabled)
1920 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1921
1922 i_setModified(IsModified_MachineData);
1923 mHWData.backup();
1924 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1925
1926 return S_OK;
1927}
1928
1929HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1930{
1931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1932 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1933 return S_OK;
1934}
1935
1936HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1937{
1938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1939
1940 if ( Global::IsOnline(mData->mMachineState)
1941 && mHWData->mVideoCaptureEnabled)
1942 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1943
1944 i_setModified(IsModified_MachineData);
1945 mHWData.backup();
1946 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1947
1948 return S_OK;
1949}
1950
1951HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1952{
1953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1954 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1955 return S_OK;
1956}
1957
1958HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1959{
1960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1961
1962 if ( Global::IsOnline(mData->mMachineState)
1963 && mHWData->mVideoCaptureEnabled)
1964 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1965
1966 i_setModified(IsModified_MachineData);
1967 mHWData.backup();
1968 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1969
1970 return S_OK;
1971}
1972
1973HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1974{
1975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1976
1977 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1978 return S_OK;
1979}
1980
1981HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1982{
1983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 if ( Global::IsOnline(mData->mMachineState)
1986 && mHWData->mVideoCaptureEnabled)
1987 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1988
1989 i_setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1992
1993 return S_OK;
1994}
1995
1996HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1997{
1998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1999
2000 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2001
2002 return S_OK;
2003}
2004
2005HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2006{
2007 switch (aGraphicsControllerType)
2008 {
2009 case GraphicsControllerType_Null:
2010 case GraphicsControllerType_VBoxVGA:
2011#ifdef VBOX_WITH_VMSVGA
2012 case GraphicsControllerType_VMSVGA:
2013#endif
2014 break;
2015 default:
2016 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2017 }
2018
2019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2020
2021 HRESULT rc = i_checkStateDependency(MutableStateDep);
2022 if (FAILED(rc)) return rc;
2023
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2027
2028 return S_OK;
2029}
2030
2031HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2032{
2033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 *aVRAMSize = mHWData->mVRAMSize;
2036
2037 return S_OK;
2038}
2039
2040HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2041{
2042 /* check VRAM limits */
2043 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2044 return setError(E_INVALIDARG,
2045 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2046 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2047
2048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 HRESULT rc = i_checkStateDependency(MutableStateDep);
2051 if (FAILED(rc)) return rc;
2052
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mVRAMSize = aVRAMSize;
2056
2057 return S_OK;
2058}
2059
2060/** @todo this method should not be public */
2061HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2062{
2063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2066
2067 return S_OK;
2068}
2069
2070/**
2071 * Set the memory balloon size.
2072 *
2073 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2074 * we have to make sure that we never call IGuest from here.
2075 */
2076HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2077{
2078 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2079#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2080 /* check limits */
2081 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2082 return setError(E_INVALIDARG,
2083 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2084 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2085
2086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2087
2088 i_setModified(IsModified_MachineData);
2089 mHWData.backup();
2090 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2091
2092 return S_OK;
2093#else
2094 NOREF(aMemoryBalloonSize);
2095 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2096#endif
2097}
2098
2099HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2100{
2101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2104 return S_OK;
2105}
2106
2107HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2108{
2109#ifdef VBOX_WITH_PAGE_SHARING
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2113 i_setModified(IsModified_MachineData);
2114 mHWData.backup();
2115 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2116 return S_OK;
2117#else
2118 NOREF(aPageFusionEnabled);
2119 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2120#endif
2121}
2122
2123HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2124{
2125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2128
2129 return S_OK;
2130}
2131
2132HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2133{
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 /** @todo check validity! */
2140
2141 i_setModified(IsModified_MachineData);
2142 mHWData.backup();
2143 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2144
2145 return S_OK;
2146}
2147
2148
2149HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2150{
2151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2152
2153 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2154
2155 return S_OK;
2156}
2157
2158HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2159{
2160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2161
2162 HRESULT rc = i_checkStateDependency(MutableStateDep);
2163 if (FAILED(rc)) return rc;
2164
2165 /** @todo check validity! */
2166 i_setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2169
2170 return S_OK;
2171}
2172
2173HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2174{
2175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2176
2177 *aMonitorCount = mHWData->mMonitorCount;
2178
2179 return S_OK;
2180}
2181
2182HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2183{
2184 /* make sure monitor count is a sensible number */
2185 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2186 return setError(E_INVALIDARG,
2187 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2188 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2189
2190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2191
2192 HRESULT rc = i_checkStateDependency(MutableStateDep);
2193 if (FAILED(rc)) return rc;
2194
2195 i_setModified(IsModified_MachineData);
2196 mHWData.backup();
2197 mHWData->mMonitorCount = aMonitorCount;
2198
2199 return S_OK;
2200}
2201
2202HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2203{
2204 /* mBIOSSettings is constant during life time, no need to lock */
2205 aBIOSSettings = mBIOSSettings;
2206
2207 return S_OK;
2208}
2209
2210HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2211{
2212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2213
2214 switch (aProperty)
2215 {
2216 case CPUPropertyType_PAE:
2217 *aValue = mHWData->mPAEEnabled;
2218 break;
2219
2220 case CPUPropertyType_LongMode:
2221 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2222 *aValue = TRUE;
2223 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2224 *aValue = FALSE;
2225#if HC_ARCH_BITS == 64
2226 else
2227 *aValue = TRUE;
2228#else
2229 else
2230 {
2231 *aValue = FALSE;
2232
2233 ComObjPtr<GuestOSType> pGuestOSType;
2234 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2235 pGuestOSType);
2236 if (SUCCEEDED(hrc2))
2237 {
2238 if (pGuestOSType->i_is64Bit())
2239 {
2240 ComObjPtr<Host> pHost = mParent->i_host();
2241 alock.release();
2242
2243 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2244 if (FAILED(hrc2))
2245 *aValue = FALSE;
2246 }
2247 }
2248 }
2249#endif
2250 break;
2251
2252 case CPUPropertyType_TripleFaultReset:
2253 *aValue = mHWData->mTripleFaultReset;
2254 break;
2255
2256 case CPUPropertyType_APIC:
2257 *aValue = mHWData->mAPIC;
2258 break;
2259
2260 case CPUPropertyType_X2APIC:
2261 *aValue = mHWData->mX2APIC;
2262 break;
2263
2264 case CPUPropertyType_IBPBOnVMExit:
2265 *aValue = mHWData->mIBPBOnVMExit;
2266 break;
2267
2268 case CPUPropertyType_IBPBOnVMEntry:
2269 *aValue = mHWData->mIBPBOnVMEntry;
2270 break;
2271
2272 case CPUPropertyType_SpecCtrl:
2273 *aValue = mHWData->mSpecCtrl;
2274 break;
2275
2276 case CPUPropertyType_SpecCtrlByHost:
2277 *aValue = mHWData->mSpecCtrlByHost;
2278 break;
2279
2280 case CPUPropertyType_HWVirt:
2281 *aValue = mHWData->mNestedHWVirt;
2282 break;
2283
2284 default:
2285 return E_INVALIDARG;
2286 }
2287 return S_OK;
2288}
2289
2290HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2291{
2292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2293
2294 HRESULT rc = i_checkStateDependency(MutableStateDep);
2295 if (FAILED(rc)) return rc;
2296
2297 switch (aProperty)
2298 {
2299 case CPUPropertyType_PAE:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mPAEEnabled = !!aValue;
2303 break;
2304
2305 case CPUPropertyType_LongMode:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2309 break;
2310
2311 case CPUPropertyType_TripleFaultReset:
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mTripleFaultReset = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_APIC:
2318 if (mHWData->mX2APIC)
2319 aValue = TRUE;
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mAPIC = !!aValue;
2323 break;
2324
2325 case CPUPropertyType_X2APIC:
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mX2APIC = !!aValue;
2329 if (aValue)
2330 mHWData->mAPIC = !!aValue;
2331 break;
2332
2333 case CPUPropertyType_IBPBOnVMExit:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mIBPBOnVMExit = !!aValue;
2337 break;
2338
2339 case CPUPropertyType_IBPBOnVMEntry:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mIBPBOnVMEntry = !!aValue;
2343 break;
2344
2345 case CPUPropertyType_SpecCtrl:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mSpecCtrl = !!aValue;
2349 break;
2350
2351 case CPUPropertyType_SpecCtrlByHost:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mSpecCtrlByHost = !!aValue;
2355 break;
2356
2357 case CPUPropertyType_HWVirt:
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mNestedHWVirt = !!aValue;
2361 break;
2362
2363 default:
2364 return E_INVALIDARG;
2365 }
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2370 ULONG *aValEcx, ULONG *aValEdx)
2371{
2372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2373 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2374 {
2375 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2376 it != mHWData->mCpuIdLeafList.end();
2377 ++it)
2378 {
2379 if (aOrdinal == 0)
2380 {
2381 const settings::CpuIdLeaf &rLeaf= *it;
2382 *aIdx = rLeaf.idx;
2383 *aSubIdx = rLeaf.idxSub;
2384 *aValEax = rLeaf.uEax;
2385 *aValEbx = rLeaf.uEbx;
2386 *aValEcx = rLeaf.uEcx;
2387 *aValEdx = rLeaf.uEdx;
2388 return S_OK;
2389 }
2390 aOrdinal--;
2391 }
2392 }
2393 return E_INVALIDARG;
2394}
2395
2396HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2397{
2398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2399
2400 /*
2401 * Search the list.
2402 */
2403 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2404 {
2405 const settings::CpuIdLeaf &rLeaf= *it;
2406 if ( rLeaf.idx == aIdx
2407 && ( aSubIdx == UINT32_MAX
2408 || rLeaf.idxSub == aSubIdx) )
2409 {
2410 *aValEax = rLeaf.uEax;
2411 *aValEbx = rLeaf.uEbx;
2412 *aValEcx = rLeaf.uEcx;
2413 *aValEdx = rLeaf.uEdx;
2414 return S_OK;
2415 }
2416 }
2417
2418 return E_INVALIDARG;
2419}
2420
2421
2422HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2423{
2424 /*
2425 * Validate input before taking locks and checking state.
2426 */
2427 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2428 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2429 if ( aIdx >= UINT32_C(0x20)
2430 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2431 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2432 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2433
2434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2435 HRESULT rc = i_checkStateDependency(MutableStateDep);
2436 if (FAILED(rc)) return rc;
2437
2438 /*
2439 * Impose a maximum number of leaves.
2440 */
2441 if (mHWData->mCpuIdLeafList.size() > 256)
2442 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2443
2444 /*
2445 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2446 */
2447 i_setModified(IsModified_MachineData);
2448 mHWData.backup();
2449
2450 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2451 {
2452 settings::CpuIdLeaf &rLeaf= *it;
2453 if ( rLeaf.idx == aIdx
2454 && ( aSubIdx == UINT32_MAX
2455 || rLeaf.idxSub == aSubIdx) )
2456 it = mHWData->mCpuIdLeafList.erase(it);
2457 else
2458 ++it;
2459 }
2460
2461 settings::CpuIdLeaf NewLeaf;
2462 NewLeaf.idx = aIdx;
2463 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2464 NewLeaf.uEax = aValEax;
2465 NewLeaf.uEbx = aValEbx;
2466 NewLeaf.uEcx = aValEcx;
2467 NewLeaf.uEdx = aValEdx;
2468 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2469 return S_OK;
2470}
2471
2472HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2473{
2474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2475
2476 HRESULT rc = i_checkStateDependency(MutableStateDep);
2477 if (FAILED(rc)) return rc;
2478
2479 /*
2480 * Do the removal.
2481 */
2482 bool fModified = false;
2483 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2484 {
2485 settings::CpuIdLeaf &rLeaf= *it;
2486 if ( rLeaf.idx == aIdx
2487 && ( aSubIdx == UINT32_MAX
2488 || rLeaf.idxSub == aSubIdx) )
2489 {
2490 if (!fModified)
2491 {
2492 fModified = true;
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 }
2496 it = mHWData->mCpuIdLeafList.erase(it);
2497 }
2498 else
2499 ++it;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::removeAllCPUIDLeaves()
2506{
2507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 HRESULT rc = i_checkStateDependency(MutableStateDep);
2510 if (FAILED(rc)) return rc;
2511
2512 if (mHWData->mCpuIdLeafList.size() > 0)
2513 {
2514 i_setModified(IsModified_MachineData);
2515 mHWData.backup();
2516
2517 mHWData->mCpuIdLeafList.clear();
2518 }
2519
2520 return S_OK;
2521}
2522HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2523{
2524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 switch(aProperty)
2527 {
2528 case HWVirtExPropertyType_Enabled:
2529 *aValue = mHWData->mHWVirtExEnabled;
2530 break;
2531
2532 case HWVirtExPropertyType_VPID:
2533 *aValue = mHWData->mHWVirtExVPIDEnabled;
2534 break;
2535
2536 case HWVirtExPropertyType_NestedPaging:
2537 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2538 break;
2539
2540 case HWVirtExPropertyType_UnrestrictedExecution:
2541 *aValue = mHWData->mHWVirtExUXEnabled;
2542 break;
2543
2544 case HWVirtExPropertyType_LargePages:
2545 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2546#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2547 *aValue = FALSE;
2548#endif
2549 break;
2550
2551 case HWVirtExPropertyType_Force:
2552 *aValue = mHWData->mHWVirtExForceEnabled;
2553 break;
2554
2555 case HWVirtExPropertyType_UseNativeApi:
2556 *aValue = mHWData->mHWVirtExUseNativeApi;
2557 break;
2558
2559 default:
2560 return E_INVALIDARG;
2561 }
2562 return S_OK;
2563}
2564
2565HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2566{
2567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 HRESULT rc = i_checkStateDependency(MutableStateDep);
2570 if (FAILED(rc)) return rc;
2571
2572 switch (aProperty)
2573 {
2574 case HWVirtExPropertyType_Enabled:
2575 i_setModified(IsModified_MachineData);
2576 mHWData.backup();
2577 mHWData->mHWVirtExEnabled = !!aValue;
2578 break;
2579
2580 case HWVirtExPropertyType_VPID:
2581 i_setModified(IsModified_MachineData);
2582 mHWData.backup();
2583 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2584 break;
2585
2586 case HWVirtExPropertyType_NestedPaging:
2587 i_setModified(IsModified_MachineData);
2588 mHWData.backup();
2589 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2590 break;
2591
2592 case HWVirtExPropertyType_UnrestrictedExecution:
2593 i_setModified(IsModified_MachineData);
2594 mHWData.backup();
2595 mHWData->mHWVirtExUXEnabled = !!aValue;
2596 break;
2597
2598 case HWVirtExPropertyType_LargePages:
2599 i_setModified(IsModified_MachineData);
2600 mHWData.backup();
2601 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2602 break;
2603
2604 case HWVirtExPropertyType_Force:
2605 i_setModified(IsModified_MachineData);
2606 mHWData.backup();
2607 mHWData->mHWVirtExForceEnabled = !!aValue;
2608 break;
2609
2610 case HWVirtExPropertyType_UseNativeApi:
2611 i_setModified(IsModified_MachineData);
2612 mHWData.backup();
2613 mHWData->mHWVirtExUseNativeApi = !!aValue;
2614 break;
2615
2616 default:
2617 return E_INVALIDARG;
2618 }
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2633{
2634 /** @todo (r=dmik):
2635 * 1. Allow to change the name of the snapshot folder containing snapshots
2636 * 2. Rename the folder on disk instead of just changing the property
2637 * value (to be smart and not to leave garbage). Note that it cannot be
2638 * done here because the change may be rolled back. Thus, the right
2639 * place is #saveSettings().
2640 */
2641
2642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 HRESULT rc = i_checkStateDependency(MutableStateDep);
2645 if (FAILED(rc)) return rc;
2646
2647 if (!mData->mCurrentSnapshot.isNull())
2648 return setError(E_FAIL,
2649 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2650
2651 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2652
2653 if (strSnapshotFolder.isEmpty())
2654 strSnapshotFolder = "Snapshots";
2655 int vrc = i_calculateFullPath(strSnapshotFolder,
2656 strSnapshotFolder);
2657 if (RT_FAILURE(vrc))
2658 return setError(E_FAIL,
2659 tr("Invalid snapshot folder '%s' (%Rrc)"),
2660 strSnapshotFolder.c_str(), vrc);
2661
2662 i_setModified(IsModified_MachineData);
2663 mUserData.backup();
2664
2665 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 aMediumAttachments.resize(mMediumAttachments->size());
2675 size_t i = 0;
2676 for (MediumAttachmentList::const_iterator
2677 it = mMediumAttachments->begin();
2678 it != mMediumAttachments->end();
2679 ++it, ++i)
2680 aMediumAttachments[i] = *it;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 Assert(!!mVRDEServer);
2690
2691 aVRDEServer = mVRDEServer;
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 aAudioAdapter = mAudioAdapter;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2706{
2707#ifdef VBOX_WITH_VUSB
2708 clearError();
2709 MultiResult rc(S_OK);
2710
2711# ifdef VBOX_WITH_USB
2712 rc = mParent->i_host()->i_checkUSBProxyService();
2713 if (FAILED(rc)) return rc;
2714# endif
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 aUSBControllers.resize(mUSBControllers->size());
2719 size_t i = 0;
2720 for (USBControllerList::const_iterator
2721 it = mUSBControllers->begin();
2722 it != mUSBControllers->end();
2723 ++it, ++i)
2724 aUSBControllers[i] = *it;
2725
2726 return S_OK;
2727#else
2728 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2729 * extended error info to indicate that USB is simply not available
2730 * (w/o treating it as a failure), for example, as in OSE */
2731 NOREF(aUSBControllers);
2732 ReturnComNotImplemented();
2733#endif /* VBOX_WITH_VUSB */
2734}
2735
2736HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2737{
2738#ifdef VBOX_WITH_VUSB
2739 clearError();
2740 MultiResult rc(S_OK);
2741
2742# ifdef VBOX_WITH_USB
2743 rc = mParent->i_host()->i_checkUSBProxyService();
2744 if (FAILED(rc)) return rc;
2745# endif
2746
2747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2748
2749 aUSBDeviceFilters = mUSBDeviceFilters;
2750 return rc;
2751#else
2752 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2753 * extended error info to indicate that USB is simply not available
2754 * (w/o treating it as a failure), for example, as in OSE */
2755 NOREF(aUSBDeviceFilters);
2756 ReturnComNotImplemented();
2757#endif /* VBOX_WITH_VUSB */
2758}
2759
2760HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2761{
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 aSettingsFilePath = mData->m_strConfigFileFull;
2765
2766 return S_OK;
2767}
2768
2769HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2770{
2771 RT_NOREF(aSettingsFilePath);
2772 ReturnComNotImplemented();
2773}
2774
2775HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 if (!mData->pMachineConfigFile->fileExists())
2783 // this is a new machine, and no config file exists yet:
2784 *aSettingsModified = TRUE;
2785 else
2786 *aSettingsModified = (mData->flModifications != 0);
2787
2788 return S_OK;
2789}
2790
2791HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2792{
2793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2794
2795 *aSessionState = mData->mSession.mState;
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 aSessionName = mData->mSession.mName;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 *aSessionPID = mData->mSession.mPID;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getState(MachineState_T *aState)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aState = mData->mMachineState;
2823 Assert(mData->mMachineState != MachineState_Null);
2824
2825 return S_OK;
2826}
2827
2828HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2829{
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2833
2834 return S_OK;
2835}
2836
2837HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 aStateFilePath = mSSData->strStateFilePath;
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2847{
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 i_getLogFolder(aLogFolder);
2851
2852 return S_OK;
2853}
2854
2855HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2856{
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 aCurrentSnapshot = mData->mCurrentSnapshot;
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2869 ? 0
2870 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2871
2872 return S_OK;
2873}
2874
2875HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2876{
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 /* Note: for machines with no snapshots, we always return FALSE
2880 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2881 * reasons :) */
2882
2883 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2884 ? FALSE
2885 : mData->mCurrentStateModified;
2886
2887 return S_OK;
2888}
2889
2890HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2891{
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 aSharedFolders.resize(mHWData->mSharedFolders.size());
2895 size_t i = 0;
2896 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2897 it = mHWData->mSharedFolders.begin();
2898 it != mHWData->mSharedFolders.end();
2899 ++it, ++i)
2900 aSharedFolders[i] = *it;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2906{
2907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 *aClipboardMode = mHWData->mClipboardMode;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2915{
2916 HRESULT rc = S_OK;
2917
2918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 alock.release();
2921 rc = i_onClipboardModeChange(aClipboardMode);
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mClipboardMode = aClipboardMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 *aDnDMode = mHWData->mDnDMode;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2946{
2947 HRESULT rc = S_OK;
2948
2949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 alock.release();
2952 rc = i_onDnDModeChange(aDnDMode);
2953
2954 alock.acquire();
2955 if (FAILED(rc)) return rc;
2956
2957 i_setModified(IsModified_MachineData);
2958 mHWData.backup();
2959 mHWData->mDnDMode = aDnDMode;
2960
2961 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2962 if (Global::IsOnline(mData->mMachineState))
2963 i_saveSettings(NULL);
2964
2965 return S_OK;
2966}
2967
2968HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2969{
2970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2971
2972 aStorageControllers.resize(mStorageControllers->size());
2973 size_t i = 0;
2974 for (StorageControllerList::const_iterator
2975 it = mStorageControllers->begin();
2976 it != mStorageControllers->end();
2977 ++it, ++i)
2978 aStorageControllers[i] = *it;
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2984{
2985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2986
2987 *aEnabled = mUserData->s.fTeleporterEnabled;
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2993{
2994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 /* Only allow it to be set to true when PoweredOff or Aborted.
2997 (Clearing it is always permitted.) */
2998 if ( aTeleporterEnabled
2999 && mData->mRegistered
3000 && ( !i_isSessionMachine()
3001 || ( mData->mMachineState != MachineState_PoweredOff
3002 && mData->mMachineState != MachineState_Teleported
3003 && mData->mMachineState != MachineState_Aborted
3004 )
3005 )
3006 )
3007 return setError(VBOX_E_INVALID_VM_STATE,
3008 tr("The machine is not powered off (state is %s)"),
3009 Global::stringifyMachineState(mData->mMachineState));
3010
3011 i_setModified(IsModified_MachineData);
3012 mUserData.backup();
3013 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3014
3015 return S_OK;
3016}
3017
3018HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3019{
3020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3023
3024 return S_OK;
3025}
3026
3027HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3028{
3029 if (aTeleporterPort >= _64K)
3030 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3031
3032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3033
3034 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3040
3041 return S_OK;
3042}
3043
3044HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3045{
3046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3054{
3055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3058 if (FAILED(rc)) return rc;
3059
3060 i_setModified(IsModified_MachineData);
3061 mUserData.backup();
3062 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3063
3064 return S_OK;
3065}
3066
3067HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3068{
3069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3070 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3076{
3077 /*
3078 * Hash the password first.
3079 */
3080 com::Utf8Str aT = aTeleporterPassword;
3081
3082 if (!aT.isEmpty())
3083 {
3084 if (VBoxIsPasswordHashed(&aT))
3085 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3086 VBoxHashPassword(&aT);
3087 }
3088
3089 /*
3090 * Do the update.
3091 */
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3094 if (SUCCEEDED(hrc))
3095 {
3096 i_setModified(IsModified_MachineData);
3097 mUserData.backup();
3098 mUserData->s.strTeleporterPassword = aT;
3099 }
3100
3101 return hrc;
3102}
3103
3104HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3113{
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 /** @todo deal with running state change. */
3117 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3123 return S_OK;
3124}
3125
3126HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3127{
3128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3129
3130 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3131 return S_OK;
3132}
3133
3134HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3135{
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 /** @todo deal with running state change. */
3139 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3140 if (FAILED(rc)) return rc;
3141
3142 i_setModified(IsModified_MachineData);
3143 mUserData.backup();
3144 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3149{
3150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3153 return S_OK;
3154}
3155
3156HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3157{
3158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 /** @todo deal with running state change. */
3161 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3162 if (FAILED(rc)) return rc;
3163
3164 i_setModified(IsModified_MachineData);
3165 mUserData.backup();
3166 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3167 return S_OK;
3168}
3169
3170HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3171{
3172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3175
3176 return S_OK;
3177}
3178
3179HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3180{
3181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 /** @todo deal with running state change. */
3184 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 i_setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3190
3191 return S_OK;
3192}
3193
3194HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3195{
3196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3197
3198 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3199 return S_OK;
3200}
3201
3202HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3203{
3204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3205
3206 /** @todo deal with running state change. */
3207 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3208 if (FAILED(rc)) return rc;
3209
3210 i_setModified(IsModified_MachineData);
3211 mUserData.backup();
3212 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3213 return S_OK;
3214}
3215
3216HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3217{
3218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3221
3222 return S_OK;
3223}
3224
3225HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3226{
3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 /* Only allow it to be set to true when PoweredOff or Aborted.
3230 (Clearing it is always permitted.) */
3231 if ( aRTCUseUTC
3232 && mData->mRegistered
3233 && ( !i_isSessionMachine()
3234 || ( mData->mMachineState != MachineState_PoweredOff
3235 && mData->mMachineState != MachineState_Teleported
3236 && mData->mMachineState != MachineState_Aborted
3237 )
3238 )
3239 )
3240 return setError(VBOX_E_INVALID_VM_STATE,
3241 tr("The machine is not powered off (state is %s)"),
3242 Global::stringifyMachineState(mData->mMachineState));
3243
3244 i_setModified(IsModified_MachineData);
3245 mUserData.backup();
3246 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3247
3248 return S_OK;
3249}
3250
3251HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3252{
3253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3256
3257 return S_OK;
3258}
3259
3260HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3261{
3262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3263
3264 HRESULT rc = i_checkStateDependency(MutableStateDep);
3265 if (FAILED(rc)) return rc;
3266
3267 i_setModified(IsModified_MachineData);
3268 mHWData.backup();
3269 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3270
3271 return S_OK;
3272}
3273
3274HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3275{
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 *aIOCacheSize = mHWData->mIOCacheSize;
3279
3280 return S_OK;
3281}
3282
3283HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3284{
3285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3286
3287 HRESULT rc = i_checkStateDependency(MutableStateDep);
3288 if (FAILED(rc)) return rc;
3289
3290 i_setModified(IsModified_MachineData);
3291 mHWData.backup();
3292 mHWData->mIOCacheSize = aIOCacheSize;
3293
3294 return S_OK;
3295}
3296
3297
3298/**
3299 * @note Locks objects!
3300 */
3301HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3302 LockType_T aLockType)
3303{
3304 /* check the session state */
3305 SessionState_T state;
3306 HRESULT rc = aSession->COMGETTER(State)(&state);
3307 if (FAILED(rc)) return rc;
3308
3309 if (state != SessionState_Unlocked)
3310 return setError(VBOX_E_INVALID_OBJECT_STATE,
3311 tr("The given session is busy"));
3312
3313 // get the client's IInternalSessionControl interface
3314 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3315 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3316 E_INVALIDARG);
3317
3318 // session name (only used in some code paths)
3319 Utf8Str strSessionName;
3320
3321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3322
3323 if (!mData->mRegistered)
3324 return setError(E_UNEXPECTED,
3325 tr("The machine '%s' is not registered"),
3326 mUserData->s.strName.c_str());
3327
3328 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3329
3330 SessionState_T oldState = mData->mSession.mState;
3331 /* Hack: in case the session is closing and there is a progress object
3332 * which allows waiting for the session to be closed, take the opportunity
3333 * and do a limited wait (max. 1 second). This helps a lot when the system
3334 * is busy and thus session closing can take a little while. */
3335 if ( mData->mSession.mState == SessionState_Unlocking
3336 && mData->mSession.mProgress)
3337 {
3338 alock.release();
3339 mData->mSession.mProgress->WaitForCompletion(1000);
3340 alock.acquire();
3341 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3342 }
3343
3344 // try again now
3345 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3346 // (i.e. session machine exists)
3347 && (aLockType == LockType_Shared) // caller wants a shared link to the
3348 // existing session that holds the write lock:
3349 )
3350 {
3351 // OK, share the session... we are now dealing with three processes:
3352 // 1) VBoxSVC (where this code runs);
3353 // 2) process C: the caller's client process (who wants a shared session);
3354 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3355
3356 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3357 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3358 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3359 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3360 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3361
3362 /*
3363 * Release the lock before calling the client process. It's safe here
3364 * since the only thing to do after we get the lock again is to add
3365 * the remote control to the list (which doesn't directly influence
3366 * anything).
3367 */
3368 alock.release();
3369
3370 // get the console of the session holding the write lock (this is a remote call)
3371 ComPtr<IConsole> pConsoleW;
3372 if (mData->mSession.mLockType == LockType_VM)
3373 {
3374 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3375 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3376 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3377 if (FAILED(rc))
3378 // the failure may occur w/o any error info (from RPC), so provide one
3379 return setError(VBOX_E_VM_ERROR,
3380 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3381 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3382 }
3383
3384 // share the session machine and W's console with the caller's session
3385 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3386 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3387 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3388
3389 if (FAILED(rc))
3390 // the failure may occur w/o any error info (from RPC), so provide one
3391 return setError(VBOX_E_VM_ERROR,
3392 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3393 alock.acquire();
3394
3395 // need to revalidate the state after acquiring the lock again
3396 if (mData->mSession.mState != SessionState_Locked)
3397 {
3398 pSessionControl->Uninitialize();
3399 return setError(VBOX_E_INVALID_SESSION_STATE,
3400 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3401 mUserData->s.strName.c_str());
3402 }
3403
3404 // add the caller's session to the list
3405 mData->mSession.mRemoteControls.push_back(pSessionControl);
3406 }
3407 else if ( mData->mSession.mState == SessionState_Locked
3408 || mData->mSession.mState == SessionState_Unlocking
3409 )
3410 {
3411 // sharing not permitted, or machine still unlocking:
3412 return setError(VBOX_E_INVALID_OBJECT_STATE,
3413 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3414 mUserData->s.strName.c_str());
3415 }
3416 else
3417 {
3418 // machine is not locked: then write-lock the machine (create the session machine)
3419
3420 // must not be busy
3421 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3422
3423 // get the caller's session PID
3424 RTPROCESS pid = NIL_RTPROCESS;
3425 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3426 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3427 Assert(pid != NIL_RTPROCESS);
3428
3429 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3430
3431 if (fLaunchingVMProcess)
3432 {
3433 if (mData->mSession.mPID == NIL_RTPROCESS)
3434 {
3435 // two or more clients racing for a lock, the one which set the
3436 // session state to Spawning will win, the others will get an
3437 // error as we can't decide here if waiting a little would help
3438 // (only for shared locks this would avoid an error)
3439 return setError(VBOX_E_INVALID_OBJECT_STATE,
3440 tr("The machine '%s' already has a lock request pending"),
3441 mUserData->s.strName.c_str());
3442 }
3443
3444 // this machine is awaiting for a spawning session to be opened:
3445 // then the calling process must be the one that got started by
3446 // LaunchVMProcess()
3447
3448 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3449 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3450
3451#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3452 /* Hardened windows builds spawns three processes when a VM is
3453 launched, the 3rd one is the one that will end up here. */
3454 RTPROCESS ppid;
3455 int rc = RTProcQueryParent(pid, &ppid);
3456 if (RT_SUCCESS(rc))
3457 rc = RTProcQueryParent(ppid, &ppid);
3458 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3459 || rc == VERR_ACCESS_DENIED)
3460 {
3461 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3462 mData->mSession.mPID = pid;
3463 }
3464#endif
3465
3466 if (mData->mSession.mPID != pid)
3467 return setError(E_ACCESSDENIED,
3468 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3469 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3470 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3471 }
3472
3473 // create the mutable SessionMachine from the current machine
3474 ComObjPtr<SessionMachine> sessionMachine;
3475 sessionMachine.createObject();
3476 rc = sessionMachine->init(this);
3477 AssertComRC(rc);
3478
3479 /* NOTE: doing return from this function after this point but
3480 * before the end is forbidden since it may call SessionMachine::uninit()
3481 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3482 * lock while still holding the Machine lock in alock so that a deadlock
3483 * is possible due to the wrong lock order. */
3484
3485 if (SUCCEEDED(rc))
3486 {
3487 /*
3488 * Set the session state to Spawning to protect against subsequent
3489 * attempts to open a session and to unregister the machine after
3490 * we release the lock.
3491 */
3492 SessionState_T origState = mData->mSession.mState;
3493 mData->mSession.mState = SessionState_Spawning;
3494
3495#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3496 /* Get the client token ID to be passed to the client process */
3497 Utf8Str strTokenId;
3498 sessionMachine->i_getTokenId(strTokenId);
3499 Assert(!strTokenId.isEmpty());
3500#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3501 /* Get the client token to be passed to the client process */
3502 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3503 /* The token is now "owned" by pToken, fix refcount */
3504 if (!pToken.isNull())
3505 pToken->Release();
3506#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3507
3508 /*
3509 * Release the lock before calling the client process -- it will call
3510 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3511 * because the state is Spawning, so that LaunchVMProcess() and
3512 * LockMachine() calls will fail. This method, called before we
3513 * acquire the lock again, will fail because of the wrong PID.
3514 *
3515 * Note that mData->mSession.mRemoteControls accessed outside
3516 * the lock may not be modified when state is Spawning, so it's safe.
3517 */
3518 alock.release();
3519
3520 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3521#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3522 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3523#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3524 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3525 /* Now the token is owned by the client process. */
3526 pToken.setNull();
3527#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3528 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3529
3530 /* The failure may occur w/o any error info (from RPC), so provide one */
3531 if (FAILED(rc))
3532 setError(VBOX_E_VM_ERROR,
3533 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3534
3535 // get session name, either to remember or to compare against
3536 // the already known session name.
3537 {
3538 Bstr bstrSessionName;
3539 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3540 if (SUCCEEDED(rc2))
3541 strSessionName = bstrSessionName;
3542 }
3543
3544 if ( SUCCEEDED(rc)
3545 && fLaunchingVMProcess
3546 )
3547 {
3548 /* complete the remote session initialization */
3549
3550 /* get the console from the direct session */
3551 ComPtr<IConsole> console;
3552 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3553 ComAssertComRC(rc);
3554
3555 if (SUCCEEDED(rc) && !console)
3556 {
3557 ComAssert(!!console);
3558 rc = E_FAIL;
3559 }
3560
3561 /* assign machine & console to the remote session */
3562 if (SUCCEEDED(rc))
3563 {
3564 /*
3565 * after LaunchVMProcess(), the first and the only
3566 * entry in remoteControls is that remote session
3567 */
3568 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3569 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3570 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3571
3572 /* The failure may occur w/o any error info (from RPC), so provide one */
3573 if (FAILED(rc))
3574 setError(VBOX_E_VM_ERROR,
3575 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3576 }
3577
3578 if (FAILED(rc))
3579 pSessionControl->Uninitialize();
3580 }
3581
3582 /* acquire the lock again */
3583 alock.acquire();
3584
3585 /* Restore the session state */
3586 mData->mSession.mState = origState;
3587 }
3588
3589 // finalize spawning anyway (this is why we don't return on errors above)
3590 if (fLaunchingVMProcess)
3591 {
3592 Assert(mData->mSession.mName == strSessionName);
3593 /* Note that the progress object is finalized later */
3594 /** @todo Consider checking mData->mSession.mProgress for cancellation
3595 * around here. */
3596
3597 /* We don't reset mSession.mPID here because it is necessary for
3598 * SessionMachine::uninit() to reap the child process later. */
3599
3600 if (FAILED(rc))
3601 {
3602 /* Close the remote session, remove the remote control from the list
3603 * and reset session state to Closed (@note keep the code in sync
3604 * with the relevant part in checkForSpawnFailure()). */
3605
3606 Assert(mData->mSession.mRemoteControls.size() == 1);
3607 if (mData->mSession.mRemoteControls.size() == 1)
3608 {
3609 ErrorInfoKeeper eik;
3610 mData->mSession.mRemoteControls.front()->Uninitialize();
3611 }
3612
3613 mData->mSession.mRemoteControls.clear();
3614 mData->mSession.mState = SessionState_Unlocked;
3615 }
3616 }
3617 else
3618 {
3619 /* memorize PID of the directly opened session */
3620 if (SUCCEEDED(rc))
3621 mData->mSession.mPID = pid;
3622 }
3623
3624 if (SUCCEEDED(rc))
3625 {
3626 mData->mSession.mLockType = aLockType;
3627 /* memorize the direct session control and cache IUnknown for it */
3628 mData->mSession.mDirectControl = pSessionControl;
3629 mData->mSession.mState = SessionState_Locked;
3630 if (!fLaunchingVMProcess)
3631 mData->mSession.mName = strSessionName;
3632 /* associate the SessionMachine with this Machine */
3633 mData->mSession.mMachine = sessionMachine;
3634
3635 /* request an IUnknown pointer early from the remote party for later
3636 * identity checks (it will be internally cached within mDirectControl
3637 * at least on XPCOM) */
3638 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3639 NOREF(unk);
3640 }
3641
3642 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3643 * would break the lock order */
3644 alock.release();
3645
3646 /* uninitialize the created session machine on failure */
3647 if (FAILED(rc))
3648 sessionMachine->uninit();
3649 }
3650
3651 if (SUCCEEDED(rc))
3652 {
3653 /*
3654 * tell the client watcher thread to update the set of
3655 * machines that have open sessions
3656 */
3657 mParent->i_updateClientWatcher();
3658
3659 if (oldState != SessionState_Locked)
3660 /* fire an event */
3661 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3662 }
3663
3664 return rc;
3665}
3666
3667/**
3668 * @note Locks objects!
3669 */
3670HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3671 const com::Utf8Str &aName,
3672 const com::Utf8Str &aEnvironment,
3673 ComPtr<IProgress> &aProgress)
3674{
3675 Utf8Str strFrontend(aName);
3676 /* "emergencystop" doesn't need the session, so skip the checks/interface
3677 * retrieval. This code doesn't quite fit in here, but introducing a
3678 * special API method would be even more effort, and would require explicit
3679 * support by every API client. It's better to hide the feature a bit. */
3680 if (strFrontend != "emergencystop")
3681 CheckComArgNotNull(aSession);
3682
3683 HRESULT rc = S_OK;
3684 if (strFrontend.isEmpty())
3685 {
3686 Bstr bstrFrontend;
3687 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3688 if (FAILED(rc))
3689 return rc;
3690 strFrontend = bstrFrontend;
3691 if (strFrontend.isEmpty())
3692 {
3693 ComPtr<ISystemProperties> systemProperties;
3694 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3695 if (FAILED(rc))
3696 return rc;
3697 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3698 if (FAILED(rc))
3699 return rc;
3700 strFrontend = bstrFrontend;
3701 }
3702 /* paranoia - emergencystop is not a valid default */
3703 if (strFrontend == "emergencystop")
3704 strFrontend = Utf8Str::Empty;
3705 }
3706 /* default frontend: Qt GUI */
3707 if (strFrontend.isEmpty())
3708 strFrontend = "GUI/Qt";
3709
3710 if (strFrontend != "emergencystop")
3711 {
3712 /* check the session state */
3713 SessionState_T state;
3714 rc = aSession->COMGETTER(State)(&state);
3715 if (FAILED(rc))
3716 return rc;
3717
3718 if (state != SessionState_Unlocked)
3719 return setError(VBOX_E_INVALID_OBJECT_STATE,
3720 tr("The given session is busy"));
3721
3722 /* get the IInternalSessionControl interface */
3723 ComPtr<IInternalSessionControl> control(aSession);
3724 ComAssertMsgRet(!control.isNull(),
3725 ("No IInternalSessionControl interface"),
3726 E_INVALIDARG);
3727
3728 /* get the teleporter enable state for the progress object init. */
3729 BOOL fTeleporterEnabled;
3730 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3731 if (FAILED(rc))
3732 return rc;
3733
3734 /* create a progress object */
3735 ComObjPtr<ProgressProxy> progress;
3736 progress.createObject();
3737 rc = progress->init(mParent,
3738 static_cast<IMachine*>(this),
3739 Bstr(tr("Starting VM")).raw(),
3740 TRUE /* aCancelable */,
3741 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3742 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3743 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3744 2 /* uFirstOperationWeight */,
3745 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3746
3747 if (SUCCEEDED(rc))
3748 {
3749 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3750 if (SUCCEEDED(rc))
3751 {
3752 aProgress = progress;
3753
3754 /* signal the client watcher thread */
3755 mParent->i_updateClientWatcher();
3756
3757 /* fire an event */
3758 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3759 }
3760 }
3761 }
3762 else
3763 {
3764 /* no progress object - either instant success or failure */
3765 aProgress = NULL;
3766
3767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3768
3769 if (mData->mSession.mState != SessionState_Locked)
3770 return setError(VBOX_E_INVALID_OBJECT_STATE,
3771 tr("The machine '%s' is not locked by a session"),
3772 mUserData->s.strName.c_str());
3773
3774 /* must have a VM process associated - do not kill normal API clients
3775 * with an open session */
3776 if (!Global::IsOnline(mData->mMachineState))
3777 return setError(VBOX_E_INVALID_OBJECT_STATE,
3778 tr("The machine '%s' does not have a VM process"),
3779 mUserData->s.strName.c_str());
3780
3781 /* forcibly terminate the VM process */
3782 if (mData->mSession.mPID != NIL_RTPROCESS)
3783 RTProcTerminate(mData->mSession.mPID);
3784
3785 /* signal the client watcher thread, as most likely the client has
3786 * been terminated */
3787 mParent->i_updateClientWatcher();
3788 }
3789
3790 return rc;
3791}
3792
3793HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3794{
3795 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3796 return setError(E_INVALIDARG,
3797 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3798 aPosition, SchemaDefs::MaxBootPosition);
3799
3800 if (aDevice == DeviceType_USB)
3801 return setError(E_NOTIMPL,
3802 tr("Booting from USB device is currently not supported"));
3803
3804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3805
3806 HRESULT rc = i_checkStateDependency(MutableStateDep);
3807 if (FAILED(rc)) return rc;
3808
3809 i_setModified(IsModified_MachineData);
3810 mHWData.backup();
3811 mHWData->mBootOrder[aPosition - 1] = aDevice;
3812
3813 return S_OK;
3814}
3815
3816HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3817{
3818 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3819 return setError(E_INVALIDARG,
3820 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3821 aPosition, SchemaDefs::MaxBootPosition);
3822
3823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3824
3825 *aDevice = mHWData->mBootOrder[aPosition - 1];
3826
3827 return S_OK;
3828}
3829
3830HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3831 LONG aControllerPort,
3832 LONG aDevice,
3833 DeviceType_T aType,
3834 const ComPtr<IMedium> &aMedium)
3835{
3836 IMedium *aM = aMedium;
3837 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3838 aName.c_str(), aControllerPort, aDevice, aType, aM));
3839
3840 // request the host lock first, since might be calling Host methods for getting host drives;
3841 // next, protect the media tree all the while we're in here, as well as our member variables
3842 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3843 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3844
3845 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3846 if (FAILED(rc)) return rc;
3847
3848 /// @todo NEWMEDIA implicit machine registration
3849 if (!mData->mRegistered)
3850 return setError(VBOX_E_INVALID_OBJECT_STATE,
3851 tr("Cannot attach storage devices to an unregistered machine"));
3852
3853 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3854
3855 /* Check for an existing controller. */
3856 ComObjPtr<StorageController> ctl;
3857 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3858 if (FAILED(rc)) return rc;
3859
3860 StorageControllerType_T ctrlType;
3861 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3862 if (FAILED(rc))
3863 return setError(E_FAIL,
3864 tr("Could not get type of controller '%s'"),
3865 aName.c_str());
3866
3867 bool fSilent = false;
3868 Utf8Str strReconfig;
3869
3870 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3871 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3872 if ( mData->mMachineState == MachineState_Paused
3873 && strReconfig == "1")
3874 fSilent = true;
3875
3876 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3877 bool fHotplug = false;
3878 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3879 fHotplug = true;
3880
3881 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3882 return setError(VBOX_E_INVALID_VM_STATE,
3883 tr("Controller '%s' does not support hotplugging"),
3884 aName.c_str());
3885
3886 // check that the port and device are not out of range
3887 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3888 if (FAILED(rc)) return rc;
3889
3890 /* check if the device slot is already busy */
3891 MediumAttachment *pAttachTemp;
3892 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3893 aName,
3894 aControllerPort,
3895 aDevice)))
3896 {
3897 Medium *pMedium = pAttachTemp->i_getMedium();
3898 if (pMedium)
3899 {
3900 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3901 return setError(VBOX_E_OBJECT_IN_USE,
3902 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3903 pMedium->i_getLocationFull().c_str(),
3904 aControllerPort,
3905 aDevice,
3906 aName.c_str());
3907 }
3908 else
3909 return setError(VBOX_E_OBJECT_IN_USE,
3910 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3911 aControllerPort, aDevice, aName.c_str());
3912 }
3913
3914 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3915 if (aMedium && medium.isNull())
3916 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3917
3918 AutoCaller mediumCaller(medium);
3919 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3920
3921 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3922
3923 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3924 && !medium.isNull()
3925 )
3926 return setError(VBOX_E_OBJECT_IN_USE,
3927 tr("Medium '%s' is already attached to this virtual machine"),
3928 medium->i_getLocationFull().c_str());
3929
3930 if (!medium.isNull())
3931 {
3932 MediumType_T mtype = medium->i_getType();
3933 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3934 // For DVDs it's not written to the config file, so needs no global config
3935 // version bump. For floppies it's a new attribute "type", which is ignored
3936 // by older VirtualBox version, so needs no global config version bump either.
3937 // For hard disks this type is not accepted.
3938 if (mtype == MediumType_MultiAttach)
3939 {
3940 // This type is new with VirtualBox 4.0 and therefore requires settings
3941 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3942 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3943 // two reasons: The medium type is a property of the media registry tree, which
3944 // can reside in the global config file (for pre-4.0 media); we would therefore
3945 // possibly need to bump the global config version. We don't want to do that though
3946 // because that might make downgrading to pre-4.0 impossible.
3947 // As a result, we can only use these two new types if the medium is NOT in the
3948 // global registry:
3949 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3950 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3951 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3952 )
3953 return setError(VBOX_E_INVALID_OBJECT_STATE,
3954 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3955 "to machines that were created with VirtualBox 4.0 or later"),
3956 medium->i_getLocationFull().c_str());
3957 }
3958 }
3959
3960 bool fIndirect = false;
3961 if (!medium.isNull())
3962 fIndirect = medium->i_isReadOnly();
3963 bool associate = true;
3964
3965 do
3966 {
3967 if ( aType == DeviceType_HardDisk
3968 && mMediumAttachments.isBackedUp())
3969 {
3970 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3971
3972 /* check if the medium was attached to the VM before we started
3973 * changing attachments in which case the attachment just needs to
3974 * be restored */
3975 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3976 {
3977 AssertReturn(!fIndirect, E_FAIL);
3978
3979 /* see if it's the same bus/channel/device */
3980 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3981 {
3982 /* the simplest case: restore the whole attachment
3983 * and return, nothing else to do */
3984 mMediumAttachments->push_back(pAttachTemp);
3985
3986 /* Reattach the medium to the VM. */
3987 if (fHotplug || fSilent)
3988 {
3989 mediumLock.release();
3990 treeLock.release();
3991 alock.release();
3992
3993 MediumLockList *pMediumLockList(new MediumLockList());
3994
3995 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3996 medium /* pToLockWrite */,
3997 false /* fMediumLockWriteAll */,
3998 NULL,
3999 *pMediumLockList);
4000 alock.acquire();
4001 if (FAILED(rc))
4002 delete pMediumLockList;
4003 else
4004 {
4005 mData->mSession.mLockedMedia.Unlock();
4006 alock.release();
4007 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4008 mData->mSession.mLockedMedia.Lock();
4009 alock.acquire();
4010 }
4011 alock.release();
4012
4013 if (SUCCEEDED(rc))
4014 {
4015 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4016 /* Remove lock list in case of error. */
4017 if (FAILED(rc))
4018 {
4019 mData->mSession.mLockedMedia.Unlock();
4020 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4021 mData->mSession.mLockedMedia.Lock();
4022 }
4023 }
4024 }
4025
4026 return S_OK;
4027 }
4028
4029 /* bus/channel/device differ; we need a new attachment object,
4030 * but don't try to associate it again */
4031 associate = false;
4032 break;
4033 }
4034 }
4035
4036 /* go further only if the attachment is to be indirect */
4037 if (!fIndirect)
4038 break;
4039
4040 /* perform the so called smart attachment logic for indirect
4041 * attachments. Note that smart attachment is only applicable to base
4042 * hard disks. */
4043
4044 if (medium->i_getParent().isNull())
4045 {
4046 /* first, investigate the backup copy of the current hard disk
4047 * attachments to make it possible to re-attach existing diffs to
4048 * another device slot w/o losing their contents */
4049 if (mMediumAttachments.isBackedUp())
4050 {
4051 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4052
4053 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4054 uint32_t foundLevel = 0;
4055
4056 for (MediumAttachmentList::const_iterator
4057 it = oldAtts.begin();
4058 it != oldAtts.end();
4059 ++it)
4060 {
4061 uint32_t level = 0;
4062 MediumAttachment *pAttach = *it;
4063 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4064 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4065 if (pMedium.isNull())
4066 continue;
4067
4068 if (pMedium->i_getBase(&level) == medium)
4069 {
4070 /* skip the hard disk if its currently attached (we
4071 * cannot attach the same hard disk twice) */
4072 if (i_findAttachment(*mMediumAttachments.data(),
4073 pMedium))
4074 continue;
4075
4076 /* matched device, channel and bus (i.e. attached to the
4077 * same place) will win and immediately stop the search;
4078 * otherwise the attachment that has the youngest
4079 * descendant of medium will be used
4080 */
4081 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4082 {
4083 /* the simplest case: restore the whole attachment
4084 * and return, nothing else to do */
4085 mMediumAttachments->push_back(*it);
4086
4087 /* Reattach the medium to the VM. */
4088 if (fHotplug || fSilent)
4089 {
4090 mediumLock.release();
4091 treeLock.release();
4092 alock.release();
4093
4094 MediumLockList *pMediumLockList(new MediumLockList());
4095
4096 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4097 medium /* pToLockWrite */,
4098 false /* fMediumLockWriteAll */,
4099 NULL,
4100 *pMediumLockList);
4101 alock.acquire();
4102 if (FAILED(rc))
4103 delete pMediumLockList;
4104 else
4105 {
4106 mData->mSession.mLockedMedia.Unlock();
4107 alock.release();
4108 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4109 mData->mSession.mLockedMedia.Lock();
4110 alock.acquire();
4111 }
4112 alock.release();
4113
4114 if (SUCCEEDED(rc))
4115 {
4116 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4117 /* Remove lock list in case of error. */
4118 if (FAILED(rc))
4119 {
4120 mData->mSession.mLockedMedia.Unlock();
4121 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4122 mData->mSession.mLockedMedia.Lock();
4123 }
4124 }
4125 }
4126
4127 return S_OK;
4128 }
4129 else if ( foundIt == oldAtts.end()
4130 || level > foundLevel /* prefer younger */
4131 )
4132 {
4133 foundIt = it;
4134 foundLevel = level;
4135 }
4136 }
4137 }
4138
4139 if (foundIt != oldAtts.end())
4140 {
4141 /* use the previously attached hard disk */
4142 medium = (*foundIt)->i_getMedium();
4143 mediumCaller.attach(medium);
4144 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4145 mediumLock.attach(medium);
4146 /* not implicit, doesn't require association with this VM */
4147 fIndirect = false;
4148 associate = false;
4149 /* go right to the MediumAttachment creation */
4150 break;
4151 }
4152 }
4153
4154 /* must give up the medium lock and medium tree lock as below we
4155 * go over snapshots, which needs a lock with higher lock order. */
4156 mediumLock.release();
4157 treeLock.release();
4158
4159 /* then, search through snapshots for the best diff in the given
4160 * hard disk's chain to base the new diff on */
4161
4162 ComObjPtr<Medium> base;
4163 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4164 while (snap)
4165 {
4166 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4167
4168 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4169
4170 MediumAttachment *pAttachFound = NULL;
4171 uint32_t foundLevel = 0;
4172
4173 for (MediumAttachmentList::const_iterator
4174 it = snapAtts.begin();
4175 it != snapAtts.end();
4176 ++it)
4177 {
4178 MediumAttachment *pAttach = *it;
4179 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4180 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4181 if (pMedium.isNull())
4182 continue;
4183
4184 uint32_t level = 0;
4185 if (pMedium->i_getBase(&level) == medium)
4186 {
4187 /* matched device, channel and bus (i.e. attached to the
4188 * same place) will win and immediately stop the search;
4189 * otherwise the attachment that has the youngest
4190 * descendant of medium will be used
4191 */
4192 if ( pAttach->i_getDevice() == aDevice
4193 && pAttach->i_getPort() == aControllerPort
4194 && pAttach->i_getControllerName() == aName
4195 )
4196 {
4197 pAttachFound = pAttach;
4198 break;
4199 }
4200 else if ( !pAttachFound
4201 || level > foundLevel /* prefer younger */
4202 )
4203 {
4204 pAttachFound = pAttach;
4205 foundLevel = level;
4206 }
4207 }
4208 }
4209
4210 if (pAttachFound)
4211 {
4212 base = pAttachFound->i_getMedium();
4213 break;
4214 }
4215
4216 snap = snap->i_getParent();
4217 }
4218
4219 /* re-lock medium tree and the medium, as we need it below */
4220 treeLock.acquire();
4221 mediumLock.acquire();
4222
4223 /* found a suitable diff, use it as a base */
4224 if (!base.isNull())
4225 {
4226 medium = base;
4227 mediumCaller.attach(medium);
4228 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4229 mediumLock.attach(medium);
4230 }
4231 }
4232
4233 Utf8Str strFullSnapshotFolder;
4234 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4235
4236 ComObjPtr<Medium> diff;
4237 diff.createObject();
4238 // store this diff in the same registry as the parent
4239 Guid uuidRegistryParent;
4240 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4241 {
4242 // parent image has no registry: this can happen if we're attaching a new immutable
4243 // image that has not yet been attached (medium then points to the base and we're
4244 // creating the diff image for the immutable, and the parent is not yet registered);
4245 // put the parent in the machine registry then
4246 mediumLock.release();
4247 treeLock.release();
4248 alock.release();
4249 i_addMediumToRegistry(medium);
4250 alock.acquire();
4251 treeLock.acquire();
4252 mediumLock.acquire();
4253 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4254 }
4255 rc = diff->init(mParent,
4256 medium->i_getPreferredDiffFormat(),
4257 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4258 uuidRegistryParent,
4259 DeviceType_HardDisk);
4260 if (FAILED(rc)) return rc;
4261
4262 /* Apply the normal locking logic to the entire chain. */
4263 MediumLockList *pMediumLockList(new MediumLockList());
4264 mediumLock.release();
4265 treeLock.release();
4266 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4267 diff /* pToLockWrite */,
4268 false /* fMediumLockWriteAll */,
4269 medium,
4270 *pMediumLockList);
4271 treeLock.acquire();
4272 mediumLock.acquire();
4273 if (SUCCEEDED(rc))
4274 {
4275 mediumLock.release();
4276 treeLock.release();
4277 rc = pMediumLockList->Lock();
4278 treeLock.acquire();
4279 mediumLock.acquire();
4280 if (FAILED(rc))
4281 setError(rc,
4282 tr("Could not lock medium when creating diff '%s'"),
4283 diff->i_getLocationFull().c_str());
4284 else
4285 {
4286 /* will release the lock before the potentially lengthy
4287 * operation, so protect with the special state */
4288 MachineState_T oldState = mData->mMachineState;
4289 i_setMachineState(MachineState_SettingUp);
4290
4291 mediumLock.release();
4292 treeLock.release();
4293 alock.release();
4294
4295 rc = medium->i_createDiffStorage(diff,
4296 medium->i_getPreferredDiffVariant(),
4297 pMediumLockList,
4298 NULL /* aProgress */,
4299 true /* aWait */);
4300
4301 alock.acquire();
4302 treeLock.acquire();
4303 mediumLock.acquire();
4304
4305 i_setMachineState(oldState);
4306 }
4307 }
4308
4309 /* Unlock the media and free the associated memory. */
4310 delete pMediumLockList;
4311
4312 if (FAILED(rc)) return rc;
4313
4314 /* use the created diff for the actual attachment */
4315 medium = diff;
4316 mediumCaller.attach(medium);
4317 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4318 mediumLock.attach(medium);
4319 }
4320 while (0);
4321
4322 ComObjPtr<MediumAttachment> attachment;
4323 attachment.createObject();
4324 rc = attachment->init(this,
4325 medium,
4326 aName,
4327 aControllerPort,
4328 aDevice,
4329 aType,
4330 fIndirect,
4331 false /* fPassthrough */,
4332 false /* fTempEject */,
4333 false /* fNonRotational */,
4334 false /* fDiscard */,
4335 fHotplug /* fHotPluggable */,
4336 Utf8Str::Empty);
4337 if (FAILED(rc)) return rc;
4338
4339 if (associate && !medium.isNull())
4340 {
4341 // as the last step, associate the medium to the VM
4342 rc = medium->i_addBackReference(mData->mUuid);
4343 // here we can fail because of Deleting, or being in process of creating a Diff
4344 if (FAILED(rc)) return rc;
4345
4346 mediumLock.release();
4347 treeLock.release();
4348 alock.release();
4349 i_addMediumToRegistry(medium);
4350 alock.acquire();
4351 treeLock.acquire();
4352 mediumLock.acquire();
4353 }
4354
4355 /* success: finally remember the attachment */
4356 i_setModified(IsModified_Storage);
4357 mMediumAttachments.backup();
4358 mMediumAttachments->push_back(attachment);
4359
4360 mediumLock.release();
4361 treeLock.release();
4362 alock.release();
4363
4364 if (fHotplug || fSilent)
4365 {
4366 if (!medium.isNull())
4367 {
4368 MediumLockList *pMediumLockList(new MediumLockList());
4369
4370 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4371 medium /* pToLockWrite */,
4372 false /* fMediumLockWriteAll */,
4373 NULL,
4374 *pMediumLockList);
4375 alock.acquire();
4376 if (FAILED(rc))
4377 delete pMediumLockList;
4378 else
4379 {
4380 mData->mSession.mLockedMedia.Unlock();
4381 alock.release();
4382 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4383 mData->mSession.mLockedMedia.Lock();
4384 alock.acquire();
4385 }
4386 alock.release();
4387 }
4388
4389 if (SUCCEEDED(rc))
4390 {
4391 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4392 /* Remove lock list in case of error. */
4393 if (FAILED(rc))
4394 {
4395 mData->mSession.mLockedMedia.Unlock();
4396 mData->mSession.mLockedMedia.Remove(attachment);
4397 mData->mSession.mLockedMedia.Lock();
4398 }
4399 }
4400 }
4401
4402 /* Save modified registries, but skip this machine as it's the caller's
4403 * job to save its settings like all other settings changes. */
4404 mParent->i_unmarkRegistryModified(i_getId());
4405 mParent->i_saveModifiedRegistries();
4406
4407 return rc;
4408}
4409
4410HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4411 LONG aDevice)
4412{
4413 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4414 aName.c_str(), aControllerPort, aDevice));
4415
4416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4417
4418 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4419 if (FAILED(rc)) return rc;
4420
4421 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4422
4423 /* Check for an existing controller. */
4424 ComObjPtr<StorageController> ctl;
4425 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4426 if (FAILED(rc)) return rc;
4427
4428 StorageControllerType_T ctrlType;
4429 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4430 if (FAILED(rc))
4431 return setError(E_FAIL,
4432 tr("Could not get type of controller '%s'"),
4433 aName.c_str());
4434
4435 bool fSilent = false;
4436 Utf8Str strReconfig;
4437
4438 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4439 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4440 if ( mData->mMachineState == MachineState_Paused
4441 && strReconfig == "1")
4442 fSilent = true;
4443
4444 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4445 bool fHotplug = false;
4446 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4447 fHotplug = true;
4448
4449 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4450 return setError(VBOX_E_INVALID_VM_STATE,
4451 tr("Controller '%s' does not support hotplugging"),
4452 aName.c_str());
4453
4454 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4455 aName,
4456 aControllerPort,
4457 aDevice);
4458 if (!pAttach)
4459 return setError(VBOX_E_OBJECT_NOT_FOUND,
4460 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4461 aDevice, aControllerPort, aName.c_str());
4462
4463 if (fHotplug && !pAttach->i_getHotPluggable())
4464 return setError(VBOX_E_NOT_SUPPORTED,
4465 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4466 aDevice, aControllerPort, aName.c_str());
4467
4468 /*
4469 * The VM has to detach the device before we delete any implicit diffs.
4470 * If this fails we can roll back without loosing data.
4471 */
4472 if (fHotplug || fSilent)
4473 {
4474 alock.release();
4475 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4476 alock.acquire();
4477 }
4478 if (FAILED(rc)) return rc;
4479
4480 /* If we are here everything went well and we can delete the implicit now. */
4481 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4482
4483 alock.release();
4484
4485 /* Save modified registries, but skip this machine as it's the caller's
4486 * job to save its settings like all other settings changes. */
4487 mParent->i_unmarkRegistryModified(i_getId());
4488 mParent->i_saveModifiedRegistries();
4489
4490 return rc;
4491}
4492
4493HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4494 LONG aDevice, BOOL aPassthrough)
4495{
4496 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4497 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4498
4499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4500
4501 HRESULT rc = i_checkStateDependency(MutableStateDep);
4502 if (FAILED(rc)) return rc;
4503
4504 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4505
4506 if (Global::IsOnlineOrTransient(mData->mMachineState))
4507 return setError(VBOX_E_INVALID_VM_STATE,
4508 tr("Invalid machine state: %s"),
4509 Global::stringifyMachineState(mData->mMachineState));
4510
4511 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4512 aName,
4513 aControllerPort,
4514 aDevice);
4515 if (!pAttach)
4516 return setError(VBOX_E_OBJECT_NOT_FOUND,
4517 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4518 aDevice, aControllerPort, aName.c_str());
4519
4520
4521 i_setModified(IsModified_Storage);
4522 mMediumAttachments.backup();
4523
4524 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4525
4526 if (pAttach->i_getType() != DeviceType_DVD)
4527 return setError(E_INVALIDARG,
4528 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4529 aDevice, aControllerPort, aName.c_str());
4530 pAttach->i_updatePassthrough(!!aPassthrough);
4531
4532 return S_OK;
4533}
4534
4535HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4536 LONG aDevice, BOOL aTemporaryEject)
4537{
4538
4539 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4540 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4541
4542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4543
4544 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4545 if (FAILED(rc)) return rc;
4546
4547 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4548 aName,
4549 aControllerPort,
4550 aDevice);
4551 if (!pAttach)
4552 return setError(VBOX_E_OBJECT_NOT_FOUND,
4553 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4554 aDevice, aControllerPort, aName.c_str());
4555
4556
4557 i_setModified(IsModified_Storage);
4558 mMediumAttachments.backup();
4559
4560 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4561
4562 if (pAttach->i_getType() != DeviceType_DVD)
4563 return setError(E_INVALIDARG,
4564 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4565 aDevice, aControllerPort, aName.c_str());
4566 pAttach->i_updateTempEject(!!aTemporaryEject);
4567
4568 return S_OK;
4569}
4570
4571HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4572 LONG aDevice, BOOL aNonRotational)
4573{
4574
4575 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4576 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4577
4578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4579
4580 HRESULT rc = i_checkStateDependency(MutableStateDep);
4581 if (FAILED(rc)) return rc;
4582
4583 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4584
4585 if (Global::IsOnlineOrTransient(mData->mMachineState))
4586 return setError(VBOX_E_INVALID_VM_STATE,
4587 tr("Invalid machine state: %s"),
4588 Global::stringifyMachineState(mData->mMachineState));
4589
4590 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4591 aName,
4592 aControllerPort,
4593 aDevice);
4594 if (!pAttach)
4595 return setError(VBOX_E_OBJECT_NOT_FOUND,
4596 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4597 aDevice, aControllerPort, aName.c_str());
4598
4599
4600 i_setModified(IsModified_Storage);
4601 mMediumAttachments.backup();
4602
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604
4605 if (pAttach->i_getType() != DeviceType_HardDisk)
4606 return setError(E_INVALIDARG,
4607 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"),
4608 aDevice, aControllerPort, aName.c_str());
4609 pAttach->i_updateNonRotational(!!aNonRotational);
4610
4611 return S_OK;
4612}
4613
4614HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4615 LONG aDevice, BOOL aDiscard)
4616{
4617
4618 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4619 aName.c_str(), aControllerPort, aDevice, aDiscard));
4620
4621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4622
4623 HRESULT rc = i_checkStateDependency(MutableStateDep);
4624 if (FAILED(rc)) return rc;
4625
4626 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4627
4628 if (Global::IsOnlineOrTransient(mData->mMachineState))
4629 return setError(VBOX_E_INVALID_VM_STATE,
4630 tr("Invalid machine state: %s"),
4631 Global::stringifyMachineState(mData->mMachineState));
4632
4633 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4634 aName,
4635 aControllerPort,
4636 aDevice);
4637 if (!pAttach)
4638 return setError(VBOX_E_OBJECT_NOT_FOUND,
4639 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4640 aDevice, aControllerPort, aName.c_str());
4641
4642
4643 i_setModified(IsModified_Storage);
4644 mMediumAttachments.backup();
4645
4646 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4647
4648 if (pAttach->i_getType() != DeviceType_HardDisk)
4649 return setError(E_INVALIDARG,
4650 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"),
4651 aDevice, aControllerPort, aName.c_str());
4652 pAttach->i_updateDiscard(!!aDiscard);
4653
4654 return S_OK;
4655}
4656
4657HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4658 LONG aDevice, BOOL aHotPluggable)
4659{
4660 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4661 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4662
4663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4664
4665 HRESULT rc = i_checkStateDependency(MutableStateDep);
4666 if (FAILED(rc)) return rc;
4667
4668 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4669
4670 if (Global::IsOnlineOrTransient(mData->mMachineState))
4671 return setError(VBOX_E_INVALID_VM_STATE,
4672 tr("Invalid machine state: %s"),
4673 Global::stringifyMachineState(mData->mMachineState));
4674
4675 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4676 aName,
4677 aControllerPort,
4678 aDevice);
4679 if (!pAttach)
4680 return setError(VBOX_E_OBJECT_NOT_FOUND,
4681 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4682 aDevice, aControllerPort, aName.c_str());
4683
4684 /* Check for an existing controller. */
4685 ComObjPtr<StorageController> ctl;
4686 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4687 if (FAILED(rc)) return rc;
4688
4689 StorageControllerType_T ctrlType;
4690 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4691 if (FAILED(rc))
4692 return setError(E_FAIL,
4693 tr("Could not get type of controller '%s'"),
4694 aName.c_str());
4695
4696 if (!i_isControllerHotplugCapable(ctrlType))
4697 return setError(VBOX_E_NOT_SUPPORTED,
4698 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4699 aName.c_str());
4700
4701 i_setModified(IsModified_Storage);
4702 mMediumAttachments.backup();
4703
4704 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4705
4706 if (pAttach->i_getType() == DeviceType_Floppy)
4707 return setError(E_INVALIDARG,
4708 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"),
4709 aDevice, aControllerPort, aName.c_str());
4710 pAttach->i_updateHotPluggable(!!aHotPluggable);
4711
4712 return S_OK;
4713}
4714
4715HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4716 LONG aDevice)
4717{
4718 int rc = S_OK;
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4720 aName.c_str(), aControllerPort, aDevice));
4721
4722 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4723
4724 return rc;
4725}
4726
4727HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4728 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4729{
4730 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4731 aName.c_str(), aControllerPort, aDevice));
4732
4733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4734
4735 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4736 if (FAILED(rc)) return rc;
4737
4738 if (Global::IsOnlineOrTransient(mData->mMachineState))
4739 return setError(VBOX_E_INVALID_VM_STATE,
4740 tr("Invalid machine state: %s"),
4741 Global::stringifyMachineState(mData->mMachineState));
4742
4743 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4744 aName,
4745 aControllerPort,
4746 aDevice);
4747 if (!pAttach)
4748 return setError(VBOX_E_OBJECT_NOT_FOUND,
4749 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4750 aDevice, aControllerPort, aName.c_str());
4751
4752
4753 i_setModified(IsModified_Storage);
4754 mMediumAttachments.backup();
4755
4756 IBandwidthGroup *iB = aBandwidthGroup;
4757 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4758 if (aBandwidthGroup && group.isNull())
4759 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4760
4761 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4762
4763 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4764 if (strBandwidthGroupOld.isNotEmpty())
4765 {
4766 /* Get the bandwidth group object and release it - this must not fail. */
4767 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4768 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4769 Assert(SUCCEEDED(rc));
4770
4771 pBandwidthGroupOld->i_release();
4772 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4773 }
4774
4775 if (!group.isNull())
4776 {
4777 group->i_reference();
4778 pAttach->i_updateBandwidthGroup(group->i_getName());
4779 }
4780
4781 return S_OK;
4782}
4783
4784HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4785 LONG aControllerPort,
4786 LONG aDevice,
4787 DeviceType_T aType)
4788{
4789 HRESULT rc = S_OK;
4790
4791 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4792 aName.c_str(), aControllerPort, aDevice, aType));
4793
4794 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4795
4796 return rc;
4797}
4798
4799
4800HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4801 LONG aControllerPort,
4802 LONG aDevice,
4803 BOOL aForce)
4804{
4805 int rc = S_OK;
4806 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4807 aName.c_str(), aControllerPort, aForce));
4808
4809 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4810
4811 return rc;
4812}
4813
4814HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4815 LONG aControllerPort,
4816 LONG aDevice,
4817 const ComPtr<IMedium> &aMedium,
4818 BOOL aForce)
4819{
4820 int rc = S_OK;
4821 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4822 aName.c_str(), aControllerPort, aDevice, aForce));
4823
4824 // request the host lock first, since might be calling Host methods for getting host drives;
4825 // next, protect the media tree all the while we're in here, as well as our member variables
4826 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4827 this->lockHandle(),
4828 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4829
4830 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4831 aName,
4832 aControllerPort,
4833 aDevice);
4834 if (pAttach.isNull())
4835 return setError(VBOX_E_OBJECT_NOT_FOUND,
4836 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4837 aDevice, aControllerPort, aName.c_str());
4838
4839 /* Remember previously mounted medium. The medium before taking the
4840 * backup is not necessarily the same thing. */
4841 ComObjPtr<Medium> oldmedium;
4842 oldmedium = pAttach->i_getMedium();
4843
4844 IMedium *iM = aMedium;
4845 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4846 if (aMedium && pMedium.isNull())
4847 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4848
4849 AutoCaller mediumCaller(pMedium);
4850 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4851
4852 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4853 if (pMedium)
4854 {
4855 DeviceType_T mediumType = pAttach->i_getType();
4856 switch (mediumType)
4857 {
4858 case DeviceType_DVD:
4859 case DeviceType_Floppy:
4860 break;
4861
4862 default:
4863 return setError(VBOX_E_INVALID_OBJECT_STATE,
4864 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4865 aControllerPort,
4866 aDevice,
4867 aName.c_str());
4868 }
4869 }
4870
4871 i_setModified(IsModified_Storage);
4872 mMediumAttachments.backup();
4873
4874 {
4875 // The backup operation makes the pAttach reference point to the
4876 // old settings. Re-get the correct reference.
4877 pAttach = i_findAttachment(*mMediumAttachments.data(),
4878 aName,
4879 aControllerPort,
4880 aDevice);
4881 if (!oldmedium.isNull())
4882 oldmedium->i_removeBackReference(mData->mUuid);
4883 if (!pMedium.isNull())
4884 {
4885 pMedium->i_addBackReference(mData->mUuid);
4886
4887 mediumLock.release();
4888 multiLock.release();
4889 i_addMediumToRegistry(pMedium);
4890 multiLock.acquire();
4891 mediumLock.acquire();
4892 }
4893
4894 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4895 pAttach->i_updateMedium(pMedium);
4896 }
4897
4898 i_setModified(IsModified_Storage);
4899
4900 mediumLock.release();
4901 multiLock.release();
4902 rc = i_onMediumChange(pAttach, aForce);
4903 multiLock.acquire();
4904 mediumLock.acquire();
4905
4906 /* On error roll back this change only. */
4907 if (FAILED(rc))
4908 {
4909 if (!pMedium.isNull())
4910 pMedium->i_removeBackReference(mData->mUuid);
4911 pAttach = i_findAttachment(*mMediumAttachments.data(),
4912 aName,
4913 aControllerPort,
4914 aDevice);
4915 /* If the attachment is gone in the meantime, bail out. */
4916 if (pAttach.isNull())
4917 return rc;
4918 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4919 if (!oldmedium.isNull())
4920 oldmedium->i_addBackReference(mData->mUuid);
4921 pAttach->i_updateMedium(oldmedium);
4922 }
4923
4924 mediumLock.release();
4925 multiLock.release();
4926
4927 /* Save modified registries, but skip this machine as it's the caller's
4928 * job to save its settings like all other settings changes. */
4929 mParent->i_unmarkRegistryModified(i_getId());
4930 mParent->i_saveModifiedRegistries();
4931
4932 return rc;
4933}
4934HRESULT Machine::getMedium(const com::Utf8Str &aName,
4935 LONG aControllerPort,
4936 LONG aDevice,
4937 ComPtr<IMedium> &aMedium)
4938{
4939 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4940 aName.c_str(), aControllerPort, aDevice));
4941
4942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4943
4944 aMedium = NULL;
4945
4946 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4947 aName,
4948 aControllerPort,
4949 aDevice);
4950 if (pAttach.isNull())
4951 return setError(VBOX_E_OBJECT_NOT_FOUND,
4952 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4953 aDevice, aControllerPort, aName.c_str());
4954
4955 aMedium = pAttach->i_getMedium();
4956
4957 return S_OK;
4958}
4959
4960HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4961{
4962
4963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4964
4965 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4966
4967 return S_OK;
4968}
4969
4970HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4971{
4972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4973
4974 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4975
4976 return S_OK;
4977}
4978
4979HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4980{
4981 /* Do not assert if slot is out of range, just return the advertised
4982 status. testdriver/vbox.py triggers this in logVmInfo. */
4983 if (aSlot >= mNetworkAdapters.size())
4984 return setError(E_INVALIDARG,
4985 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4986 aSlot, mNetworkAdapters.size());
4987
4988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4989
4990 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4991
4992 return S_OK;
4993}
4994
4995HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4996{
4997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4998
4999 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5000 size_t i = 0;
5001 for (settings::StringsMap::const_iterator
5002 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5003 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5004 ++it, ++i)
5005 aKeys[i] = it->first;
5006
5007 return S_OK;
5008}
5009
5010 /**
5011 * @note Locks this object for reading.
5012 */
5013HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5014 com::Utf8Str &aValue)
5015{
5016 /* start with nothing found */
5017 aValue = "";
5018
5019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5020
5021 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5022 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5023 // found:
5024 aValue = it->second; // source is a Utf8Str
5025
5026 /* return the result to caller (may be empty) */
5027 return S_OK;
5028}
5029
5030 /**
5031 * @note Locks mParent for writing + this object for writing.
5032 */
5033HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5034{
5035 Utf8Str strOldValue; // empty
5036
5037 // locking note: we only hold the read lock briefly to look up the old value,
5038 // then release it and call the onExtraCanChange callbacks. There is a small
5039 // chance of a race insofar as the callback might be called twice if two callers
5040 // change the same key at the same time, but that's a much better solution
5041 // than the deadlock we had here before. The actual changing of the extradata
5042 // is then performed under the write lock and race-free.
5043
5044 // look up the old value first; if nothing has changed then we need not do anything
5045 {
5046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5047
5048 // For snapshots don't even think about allowing changes, extradata
5049 // is global for a machine, so there is nothing snapshot specific.
5050 if (i_isSnapshotMachine())
5051 return setError(VBOX_E_INVALID_VM_STATE,
5052 tr("Cannot set extradata for a snapshot"));
5053
5054 // check if the right IMachine instance is used
5055 if (mData->mRegistered && !i_isSessionMachine())
5056 return setError(VBOX_E_INVALID_VM_STATE,
5057 tr("Cannot set extradata for an immutable machine"));
5058
5059 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5060 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5061 strOldValue = it->second;
5062 }
5063
5064 bool fChanged;
5065 if ((fChanged = (strOldValue != aValue)))
5066 {
5067 // ask for permission from all listeners outside the locks;
5068 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5069 // lock to copy the list of callbacks to invoke
5070 Bstr error;
5071 Bstr bstrValue(aValue);
5072
5073 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5074 {
5075 const char *sep = error.isEmpty() ? "" : ": ";
5076 CBSTR err = error.raw();
5077 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5078 return setError(E_ACCESSDENIED,
5079 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5080 aKey.c_str(),
5081 aValue.c_str(),
5082 sep,
5083 err);
5084 }
5085
5086 // data is changing and change not vetoed: then write it out under the lock
5087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5088
5089 if (aValue.isEmpty())
5090 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5091 else
5092 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5093 // creates a new key if needed
5094
5095 bool fNeedsGlobalSaveSettings = false;
5096 // This saving of settings is tricky: there is no "old state" for the
5097 // extradata items at all (unlike all other settings), so the old/new
5098 // settings comparison would give a wrong result!
5099 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5100
5101 if (fNeedsGlobalSaveSettings)
5102 {
5103 // save the global settings; for that we should hold only the VirtualBox lock
5104 alock.release();
5105 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5106 mParent->i_saveSettings();
5107 }
5108 }
5109
5110 // fire notification outside the lock
5111 if (fChanged)
5112 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5113
5114 return S_OK;
5115}
5116
5117HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5118{
5119 aProgress = NULL;
5120 NOREF(aSettingsFilePath);
5121 ReturnComNotImplemented();
5122}
5123
5124HRESULT Machine::saveSettings()
5125{
5126 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5127
5128 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5129 if (FAILED(rc)) return rc;
5130
5131 /* the settings file path may never be null */
5132 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5133
5134 /* save all VM data excluding snapshots */
5135 bool fNeedsGlobalSaveSettings = false;
5136 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5137 mlock.release();
5138
5139 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5140 {
5141 // save the global settings; for that we should hold only the VirtualBox lock
5142 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5143 rc = mParent->i_saveSettings();
5144 }
5145
5146 return rc;
5147}
5148
5149
5150HRESULT Machine::discardSettings()
5151{
5152 /*
5153 * We need to take the machine list lock here as well as the machine one
5154 * or we'll get into trouble should any media stuff require rolling back.
5155 *
5156 * Details:
5157 *
5158 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5159 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5160 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5161 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5162 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5163 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5164 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5165 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5166 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5167 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5168 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5169 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5170 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5171 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5172 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5173 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5174 * 0:005> k
5175 * # Child-SP RetAddr Call Site
5176 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5177 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5178 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5179 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5180 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5181 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5182 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5183 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5184 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5185 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5186 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5187 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5188 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5189 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5190 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5191 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5192 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5193 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5194 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5195 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5196 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5197 *
5198 */
5199 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5201
5202 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5203 if (FAILED(rc)) return rc;
5204
5205 /*
5206 * during this rollback, the session will be notified if data has
5207 * been actually changed
5208 */
5209 i_rollback(true /* aNotify */);
5210
5211 return S_OK;
5212}
5213
5214/** @note Locks objects! */
5215HRESULT Machine::unregister(AutoCaller &autoCaller,
5216 CleanupMode_T aCleanupMode,
5217 std::vector<ComPtr<IMedium> > &aMedia)
5218{
5219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5220
5221 Guid id(i_getId());
5222
5223 if (mData->mSession.mState != SessionState_Unlocked)
5224 return setError(VBOX_E_INVALID_OBJECT_STATE,
5225 tr("Cannot unregister the machine '%s' while it is locked"),
5226 mUserData->s.strName.c_str());
5227
5228 // wait for state dependents to drop to zero
5229 i_ensureNoStateDependencies();
5230
5231 if (!mData->mAccessible)
5232 {
5233 // inaccessible maschines can only be unregistered; uninitialize ourselves
5234 // here because currently there may be no unregistered that are inaccessible
5235 // (this state combination is not supported). Note releasing the caller and
5236 // leaving the lock before calling uninit()
5237 alock.release();
5238 autoCaller.release();
5239
5240 uninit();
5241
5242 mParent->i_unregisterMachine(this, id);
5243 // calls VirtualBox::i_saveSettings()
5244
5245 return S_OK;
5246 }
5247
5248 HRESULT rc = S_OK;
5249
5250 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5251 // discard saved state
5252 if (mData->mMachineState == MachineState_Saved)
5253 {
5254 // add the saved state file to the list of files the caller should delete
5255 Assert(!mSSData->strStateFilePath.isEmpty());
5256 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5257
5258 mSSData->strStateFilePath.setNull();
5259
5260 // unconditionally set the machine state to powered off, we now
5261 // know no session has locked the machine
5262 mData->mMachineState = MachineState_PoweredOff;
5263 }
5264
5265 size_t cSnapshots = 0;
5266 if (mData->mFirstSnapshot)
5267 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5268 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5269 // fail now before we start detaching media
5270 return setError(VBOX_E_INVALID_OBJECT_STATE,
5271 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5272 mUserData->s.strName.c_str(), cSnapshots);
5273
5274 // This list collects the medium objects from all medium attachments
5275 // which we will detach from the machine and its snapshots, in a specific
5276 // order which allows for closing all media without getting "media in use"
5277 // errors, simply by going through the list from the front to the back:
5278 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5279 // and must be closed before the parent media from the snapshots, or closing the parents
5280 // will fail because they still have children);
5281 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5282 // the root ("first") snapshot of the machine.
5283 MediaList llMedia;
5284
5285 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5286 && mMediumAttachments->size()
5287 )
5288 {
5289 // we have media attachments: detach them all and add the Medium objects to our list
5290 if (aCleanupMode != CleanupMode_UnregisterOnly)
5291 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5292 else
5293 return setError(VBOX_E_INVALID_OBJECT_STATE,
5294 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5295 mUserData->s.strName.c_str(), mMediumAttachments->size());
5296 }
5297
5298 if (cSnapshots)
5299 {
5300 // add the media from the medium attachments of the snapshots to llMedia
5301 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5302 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5303 // into the children first
5304
5305 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5306 MachineState_T oldState = mData->mMachineState;
5307 mData->mMachineState = MachineState_DeletingSnapshot;
5308
5309 // make a copy of the first snapshot so the refcount does not drop to 0
5310 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5311 // because of the AutoCaller voodoo)
5312 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5313
5314 // GO!
5315 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5316
5317 mData->mMachineState = oldState;
5318 }
5319
5320 if (FAILED(rc))
5321 {
5322 i_rollbackMedia();
5323 return rc;
5324 }
5325
5326 // commit all the media changes made above
5327 i_commitMedia();
5328
5329 mData->mRegistered = false;
5330
5331 // machine lock no longer needed
5332 alock.release();
5333
5334 // return media to caller
5335 aMedia.resize(llMedia.size());
5336 size_t i = 0;
5337 for (MediaList::const_iterator
5338 it = llMedia.begin();
5339 it != llMedia.end();
5340 ++it, ++i)
5341 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5342
5343 mParent->i_unregisterMachine(this, id);
5344 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5345
5346 return S_OK;
5347}
5348
5349/**
5350 * Task record for deleting a machine config.
5351 */
5352class Machine::DeleteConfigTask
5353 : public Machine::Task
5354{
5355public:
5356 DeleteConfigTask(Machine *m,
5357 Progress *p,
5358 const Utf8Str &t,
5359 const RTCList<ComPtr<IMedium> > &llMediums,
5360 const StringsList &llFilesToDelete)
5361 : Task(m, p, t),
5362 m_llMediums(llMediums),
5363 m_llFilesToDelete(llFilesToDelete)
5364 {}
5365
5366private:
5367 void handler()
5368 {
5369 try
5370 {
5371 m_pMachine->i_deleteConfigHandler(*this);
5372 }
5373 catch (...)
5374 {
5375 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5376 }
5377 }
5378
5379 RTCList<ComPtr<IMedium> > m_llMediums;
5380 StringsList m_llFilesToDelete;
5381
5382 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5383};
5384
5385/**
5386 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5387 * SessionMachine::taskHandler().
5388 *
5389 * @note Locks this object for writing.
5390 *
5391 * @param task
5392 * @return
5393 */
5394void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5395{
5396 LogFlowThisFuncEnter();
5397
5398 AutoCaller autoCaller(this);
5399 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5400 if (FAILED(autoCaller.rc()))
5401 {
5402 /* we might have been uninitialized because the session was accidentally
5403 * closed by the client, so don't assert */
5404 HRESULT rc = setError(E_FAIL,
5405 tr("The session has been accidentally closed"));
5406 task.m_pProgress->i_notifyComplete(rc);
5407 LogFlowThisFuncLeave();
5408 return;
5409 }
5410
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = S_OK;
5414
5415 try
5416 {
5417 ULONG uLogHistoryCount = 3;
5418 ComPtr<ISystemProperties> systemProperties;
5419 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5420 if (FAILED(rc)) throw rc;
5421
5422 if (!systemProperties.isNull())
5423 {
5424 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5425 if (FAILED(rc)) throw rc;
5426 }
5427
5428 MachineState_T oldState = mData->mMachineState;
5429 i_setMachineState(MachineState_SettingUp);
5430 alock.release();
5431 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5432 {
5433 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5434 {
5435 AutoCaller mac(pMedium);
5436 if (FAILED(mac.rc())) throw mac.rc();
5437 Utf8Str strLocation = pMedium->i_getLocationFull();
5438 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5439 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5440 if (FAILED(rc)) throw rc;
5441 }
5442 if (pMedium->i_isMediumFormatFile())
5443 {
5444 ComPtr<IProgress> pProgress2;
5445 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5446 if (FAILED(rc)) throw rc;
5447 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5448 if (FAILED(rc)) throw rc;
5449 /* Check the result of the asynchronous process. */
5450 LONG iRc;
5451 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5452 if (FAILED(rc)) throw rc;
5453 /* If the thread of the progress object has an error, then
5454 * retrieve the error info from there, or it'll be lost. */
5455 if (FAILED(iRc))
5456 throw setError(ProgressErrorInfo(pProgress2));
5457 }
5458
5459 /* Close the medium, deliberately without checking the return
5460 * code, and without leaving any trace in the error info, as
5461 * a failure here is a very minor issue, which shouldn't happen
5462 * as above we even managed to delete the medium. */
5463 {
5464 ErrorInfoKeeper eik;
5465 pMedium->Close();
5466 }
5467 }
5468 i_setMachineState(oldState);
5469 alock.acquire();
5470
5471 // delete the files pushed on the task list by Machine::Delete()
5472 // (this includes saved states of the machine and snapshots and
5473 // medium storage files from the IMedium list passed in, and the
5474 // machine XML file)
5475 for (StringsList::const_iterator
5476 it = task.m_llFilesToDelete.begin();
5477 it != task.m_llFilesToDelete.end();
5478 ++it)
5479 {
5480 const Utf8Str &strFile = *it;
5481 LogFunc(("Deleting file %s\n", strFile.c_str()));
5482 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5483 if (FAILED(rc)) throw rc;
5484
5485 int vrc = RTFileDelete(strFile.c_str());
5486 if (RT_FAILURE(vrc))
5487 throw setError(VBOX_E_IPRT_ERROR,
5488 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5489 }
5490
5491 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5492 if (FAILED(rc)) throw rc;
5493
5494 /* delete the settings only when the file actually exists */
5495 if (mData->pMachineConfigFile->fileExists())
5496 {
5497 /* Delete any backup or uncommitted XML files. Ignore failures.
5498 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5499 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5500 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5501 RTFileDelete(otherXml.c_str());
5502 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5503 RTFileDelete(otherXml.c_str());
5504
5505 /* delete the Logs folder, nothing important should be left
5506 * there (we don't check for errors because the user might have
5507 * some private files there that we don't want to delete) */
5508 Utf8Str logFolder;
5509 getLogFolder(logFolder);
5510 Assert(logFolder.length());
5511 if (RTDirExists(logFolder.c_str()))
5512 {
5513 /* Delete all VBox.log[.N] files from the Logs folder
5514 * (this must be in sync with the rotation logic in
5515 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5516 * files that may have been created by the GUI. */
5517 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5518 logFolder.c_str(), RTPATH_DELIMITER);
5519 RTFileDelete(log.c_str());
5520 log = Utf8StrFmt("%s%cVBox.png",
5521 logFolder.c_str(), RTPATH_DELIMITER);
5522 RTFileDelete(log.c_str());
5523 for (int i = uLogHistoryCount; i > 0; i--)
5524 {
5525 log = Utf8StrFmt("%s%cVBox.log.%d",
5526 logFolder.c_str(), RTPATH_DELIMITER, i);
5527 RTFileDelete(log.c_str());
5528 log = Utf8StrFmt("%s%cVBox.png.%d",
5529 logFolder.c_str(), RTPATH_DELIMITER, i);
5530 RTFileDelete(log.c_str());
5531 }
5532#if defined(RT_OS_WINDOWS)
5533 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5534 RTFileDelete(log.c_str());
5535 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5536 RTFileDelete(log.c_str());
5537#endif
5538
5539 RTDirRemove(logFolder.c_str());
5540 }
5541
5542 /* delete the Snapshots folder, nothing important should be left
5543 * there (we don't check for errors because the user might have
5544 * some private files there that we don't want to delete) */
5545 Utf8Str strFullSnapshotFolder;
5546 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5547 Assert(!strFullSnapshotFolder.isEmpty());
5548 if (RTDirExists(strFullSnapshotFolder.c_str()))
5549 RTDirRemove(strFullSnapshotFolder.c_str());
5550
5551 // delete the directory that contains the settings file, but only
5552 // if it matches the VM name
5553 Utf8Str settingsDir;
5554 if (i_isInOwnDir(&settingsDir))
5555 RTDirRemove(settingsDir.c_str());
5556 }
5557
5558 alock.release();
5559
5560 mParent->i_saveModifiedRegistries();
5561 }
5562 catch (HRESULT aRC) { rc = aRC; }
5563
5564 task.m_pProgress->i_notifyComplete(rc);
5565
5566 LogFlowThisFuncLeave();
5567}
5568
5569HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5570{
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572
5573 HRESULT rc = i_checkStateDependency(MutableStateDep);
5574 if (FAILED(rc)) return rc;
5575
5576 if (mData->mRegistered)
5577 return setError(VBOX_E_INVALID_VM_STATE,
5578 tr("Cannot delete settings of a registered machine"));
5579
5580 // collect files to delete
5581 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5582 if (mData->pMachineConfigFile->fileExists())
5583 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5584
5585 RTCList<ComPtr<IMedium> > llMediums;
5586 for (size_t i = 0; i < aMedia.size(); ++i)
5587 {
5588 IMedium *pIMedium(aMedia[i]);
5589 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5590 if (pMedium.isNull())
5591 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5592 SafeArray<BSTR> ids;
5593 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5594 if (FAILED(rc)) return rc;
5595 /* At this point the medium should not have any back references
5596 * anymore. If it has it is attached to another VM and *must* not
5597 * deleted. */
5598 if (ids.size() < 1)
5599 llMediums.append(pMedium);
5600 }
5601
5602 ComObjPtr<Progress> pProgress;
5603 pProgress.createObject();
5604 rc = pProgress->init(i_getVirtualBox(),
5605 static_cast<IMachine*>(this) /* aInitiator */,
5606 tr("Deleting files"),
5607 true /* fCancellable */,
5608 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5609 tr("Collecting file inventory"));
5610 if (FAILED(rc))
5611 return rc;
5612
5613 /* create and start the task on a separate thread (note that it will not
5614 * start working until we release alock) */
5615 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5616 rc = pTask->createThread();
5617 if (FAILED(rc))
5618 return rc;
5619
5620 pProgress.queryInterfaceTo(aProgress.asOutParam());
5621
5622 LogFlowFuncLeave();
5623
5624 return S_OK;
5625}
5626
5627HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5628{
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630
5631 ComObjPtr<Snapshot> pSnapshot;
5632 HRESULT rc;
5633
5634 if (aNameOrId.isEmpty())
5635 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5636 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5637 else
5638 {
5639 Guid uuid(aNameOrId);
5640 if (uuid.isValid())
5641 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5642 else
5643 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5644 }
5645 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5646
5647 return rc;
5648}
5649
5650HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5651{
5652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5653
5654 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5655 if (FAILED(rc)) return rc;
5656
5657 ComObjPtr<SharedFolder> sharedFolder;
5658 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5659 if (SUCCEEDED(rc))
5660 return setError(VBOX_E_OBJECT_IN_USE,
5661 tr("Shared folder named '%s' already exists"),
5662 aName.c_str());
5663
5664 sharedFolder.createObject();
5665 rc = sharedFolder->init(i_getMachine(),
5666 aName,
5667 aHostPath,
5668 !!aWritable,
5669 !!aAutomount,
5670 true /* fFailOnError */);
5671 if (FAILED(rc)) return rc;
5672
5673 i_setModified(IsModified_SharedFolders);
5674 mHWData.backup();
5675 mHWData->mSharedFolders.push_back(sharedFolder);
5676
5677 /* inform the direct session if any */
5678 alock.release();
5679 i_onSharedFolderChange();
5680
5681 return S_OK;
5682}
5683
5684HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5685{
5686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5687
5688 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5689 if (FAILED(rc)) return rc;
5690
5691 ComObjPtr<SharedFolder> sharedFolder;
5692 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5693 if (FAILED(rc)) return rc;
5694
5695 i_setModified(IsModified_SharedFolders);
5696 mHWData.backup();
5697 mHWData->mSharedFolders.remove(sharedFolder);
5698
5699 /* inform the direct session if any */
5700 alock.release();
5701 i_onSharedFolderChange();
5702
5703 return S_OK;
5704}
5705
5706HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5707{
5708 /* start with No */
5709 *aCanShow = FALSE;
5710
5711 ComPtr<IInternalSessionControl> directControl;
5712 {
5713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5714
5715 if (mData->mSession.mState != SessionState_Locked)
5716 return setError(VBOX_E_INVALID_VM_STATE,
5717 tr("Machine is not locked for session (session state: %s)"),
5718 Global::stringifySessionState(mData->mSession.mState));
5719
5720 if (mData->mSession.mLockType == LockType_VM)
5721 directControl = mData->mSession.mDirectControl;
5722 }
5723
5724 /* ignore calls made after #OnSessionEnd() is called */
5725 if (!directControl)
5726 return S_OK;
5727
5728 LONG64 dummy;
5729 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5730}
5731
5732HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5733{
5734 ComPtr<IInternalSessionControl> directControl;
5735 {
5736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5737
5738 if (mData->mSession.mState != SessionState_Locked)
5739 return setError(E_FAIL,
5740 tr("Machine is not locked for session (session state: %s)"),
5741 Global::stringifySessionState(mData->mSession.mState));
5742
5743 if (mData->mSession.mLockType == LockType_VM)
5744 directControl = mData->mSession.mDirectControl;
5745 }
5746
5747 /* ignore calls made after #OnSessionEnd() is called */
5748 if (!directControl)
5749 return S_OK;
5750
5751 BOOL dummy;
5752 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5753}
5754
5755#ifdef VBOX_WITH_GUEST_PROPS
5756/**
5757 * Look up a guest property in VBoxSVC's internal structures.
5758 */
5759HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5760 com::Utf8Str &aValue,
5761 LONG64 *aTimestamp,
5762 com::Utf8Str &aFlags) const
5763{
5764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5765
5766 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5767 if (it != mHWData->mGuestProperties.end())
5768 {
5769 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5770 aValue = it->second.strValue;
5771 *aTimestamp = it->second.mTimestamp;
5772 GuestPropWriteFlags(it->second.mFlags, szFlags);
5773 aFlags = Utf8Str(szFlags);
5774 }
5775
5776 return S_OK;
5777}
5778
5779/**
5780 * Query the VM that a guest property belongs to for the property.
5781 * @returns E_ACCESSDENIED if the VM process is not available or not
5782 * currently handling queries and the lookup should then be done in
5783 * VBoxSVC.
5784 */
5785HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5786 com::Utf8Str &aValue,
5787 LONG64 *aTimestamp,
5788 com::Utf8Str &aFlags) const
5789{
5790 HRESULT rc = S_OK;
5791 BSTR bValue = NULL;
5792 BSTR bFlags = NULL;
5793
5794 ComPtr<IInternalSessionControl> directControl;
5795 {
5796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5797 if (mData->mSession.mLockType == LockType_VM)
5798 directControl = mData->mSession.mDirectControl;
5799 }
5800
5801 /* ignore calls made after #OnSessionEnd() is called */
5802 if (!directControl)
5803 rc = E_ACCESSDENIED;
5804 else
5805 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5806 0 /* accessMode */,
5807 &bValue, aTimestamp, &bFlags);
5808
5809 aValue = bValue;
5810 aFlags = bFlags;
5811
5812 return rc;
5813}
5814#endif // VBOX_WITH_GUEST_PROPS
5815
5816HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5817 com::Utf8Str &aValue,
5818 LONG64 *aTimestamp,
5819 com::Utf8Str &aFlags)
5820{
5821#ifndef VBOX_WITH_GUEST_PROPS
5822 ReturnComNotImplemented();
5823#else // VBOX_WITH_GUEST_PROPS
5824
5825 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5826
5827 if (rc == E_ACCESSDENIED)
5828 /* The VM is not running or the service is not (yet) accessible */
5829 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5830 return rc;
5831#endif // VBOX_WITH_GUEST_PROPS
5832}
5833
5834HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5835{
5836 LONG64 dummyTimestamp;
5837 com::Utf8Str dummyFlags;
5838 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5839 return rc;
5840
5841}
5842HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5843{
5844 com::Utf8Str dummyFlags;
5845 com::Utf8Str dummyValue;
5846 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5847 return rc;
5848}
5849
5850#ifdef VBOX_WITH_GUEST_PROPS
5851/**
5852 * Set a guest property in VBoxSVC's internal structures.
5853 */
5854HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5855 const com::Utf8Str &aFlags, bool fDelete)
5856{
5857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5858 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5859 if (FAILED(rc)) return rc;
5860
5861 try
5862 {
5863 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5864 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5865 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5866
5867 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5868 if (it == mHWData->mGuestProperties.end())
5869 {
5870 if (!fDelete)
5871 {
5872 i_setModified(IsModified_MachineData);
5873 mHWData.backupEx();
5874
5875 RTTIMESPEC time;
5876 HWData::GuestProperty prop;
5877 prop.strValue = Bstr(aValue).raw();
5878 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5879 prop.mFlags = fFlags;
5880 mHWData->mGuestProperties[aName] = prop;
5881 }
5882 }
5883 else
5884 {
5885 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5886 {
5887 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5888 }
5889 else
5890 {
5891 i_setModified(IsModified_MachineData);
5892 mHWData.backupEx();
5893
5894 /* The backupEx() operation invalidates our iterator,
5895 * so get a new one. */
5896 it = mHWData->mGuestProperties.find(aName);
5897 Assert(it != mHWData->mGuestProperties.end());
5898
5899 if (!fDelete)
5900 {
5901 RTTIMESPEC time;
5902 it->second.strValue = aValue;
5903 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5904 it->second.mFlags = fFlags;
5905 }
5906 else
5907 mHWData->mGuestProperties.erase(it);
5908 }
5909 }
5910
5911 if (SUCCEEDED(rc))
5912 {
5913 alock.release();
5914
5915 mParent->i_onGuestPropertyChange(mData->mUuid,
5916 Bstr(aName).raw(),
5917 Bstr(aValue).raw(),
5918 Bstr(aFlags).raw());
5919 }
5920 }
5921 catch (std::bad_alloc &)
5922 {
5923 rc = E_OUTOFMEMORY;
5924 }
5925
5926 return rc;
5927}
5928
5929/**
5930 * Set a property on the VM that that property belongs to.
5931 * @returns E_ACCESSDENIED if the VM process is not available or not
5932 * currently handling queries and the setting should then be done in
5933 * VBoxSVC.
5934 */
5935HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5936 const com::Utf8Str &aFlags, bool fDelete)
5937{
5938 HRESULT rc;
5939
5940 try
5941 {
5942 ComPtr<IInternalSessionControl> directControl;
5943 {
5944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5945 if (mData->mSession.mLockType == LockType_VM)
5946 directControl = mData->mSession.mDirectControl;
5947 }
5948
5949 BSTR dummy = NULL; /* will not be changed (setter) */
5950 LONG64 dummy64;
5951 if (!directControl)
5952 rc = E_ACCESSDENIED;
5953 else
5954 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5955 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5956 fDelete? 2: 1 /* accessMode */,
5957 &dummy, &dummy64, &dummy);
5958 }
5959 catch (std::bad_alloc &)
5960 {
5961 rc = E_OUTOFMEMORY;
5962 }
5963
5964 return rc;
5965}
5966#endif // VBOX_WITH_GUEST_PROPS
5967
5968HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5969 const com::Utf8Str &aFlags)
5970{
5971#ifndef VBOX_WITH_GUEST_PROPS
5972 ReturnComNotImplemented();
5973#else // VBOX_WITH_GUEST_PROPS
5974 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5975 if (rc == E_ACCESSDENIED)
5976 /* The VM is not running or the service is not (yet) accessible */
5977 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5978 return rc;
5979#endif // VBOX_WITH_GUEST_PROPS
5980}
5981
5982HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5983{
5984 return setGuestProperty(aProperty, aValue, "");
5985}
5986
5987HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5988{
5989#ifndef VBOX_WITH_GUEST_PROPS
5990 ReturnComNotImplemented();
5991#else // VBOX_WITH_GUEST_PROPS
5992 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5993 if (rc == E_ACCESSDENIED)
5994 /* The VM is not running or the service is not (yet) accessible */
5995 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5996 return rc;
5997#endif // VBOX_WITH_GUEST_PROPS
5998}
5999
6000#ifdef VBOX_WITH_GUEST_PROPS
6001/**
6002 * Enumerate the guest properties in VBoxSVC's internal structures.
6003 */
6004HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6005 std::vector<com::Utf8Str> &aNames,
6006 std::vector<com::Utf8Str> &aValues,
6007 std::vector<LONG64> &aTimestamps,
6008 std::vector<com::Utf8Str> &aFlags)
6009{
6010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6011 Utf8Str strPatterns(aPatterns);
6012
6013 /*
6014 * Look for matching patterns and build up a list.
6015 */
6016 HWData::GuestPropertyMap propMap;
6017 for (HWData::GuestPropertyMap::const_iterator
6018 it = mHWData->mGuestProperties.begin();
6019 it != mHWData->mGuestProperties.end();
6020 ++it)
6021 {
6022 if ( strPatterns.isEmpty()
6023 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6024 RTSTR_MAX,
6025 it->first.c_str(),
6026 RTSTR_MAX,
6027 NULL)
6028 )
6029 propMap.insert(*it);
6030 }
6031
6032 alock.release();
6033
6034 /*
6035 * And build up the arrays for returning the property information.
6036 */
6037 size_t cEntries = propMap.size();
6038
6039 aNames.resize(cEntries);
6040 aValues.resize(cEntries);
6041 aTimestamps.resize(cEntries);
6042 aFlags.resize(cEntries);
6043
6044 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6045 size_t i = 0;
6046 for (HWData::GuestPropertyMap::const_iterator
6047 it = propMap.begin();
6048 it != propMap.end();
6049 ++it, ++i)
6050 {
6051 aNames[i] = it->first;
6052 aValues[i] = it->second.strValue;
6053 aTimestamps[i] = it->second.mTimestamp;
6054 GuestPropWriteFlags(it->second.mFlags, szFlags);
6055 aFlags[i] = Utf8Str(szFlags);
6056 }
6057
6058 return S_OK;
6059}
6060
6061/**
6062 * Enumerate the properties managed by a VM.
6063 * @returns E_ACCESSDENIED if the VM process is not available or not
6064 * currently handling queries and the setting should then be done in
6065 * VBoxSVC.
6066 */
6067HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6068 std::vector<com::Utf8Str> &aNames,
6069 std::vector<com::Utf8Str> &aValues,
6070 std::vector<LONG64> &aTimestamps,
6071 std::vector<com::Utf8Str> &aFlags)
6072{
6073 HRESULT rc;
6074 ComPtr<IInternalSessionControl> directControl;
6075 {
6076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6077 if (mData->mSession.mLockType == LockType_VM)
6078 directControl = mData->mSession.mDirectControl;
6079 }
6080
6081 com::SafeArray<BSTR> bNames;
6082 com::SafeArray<BSTR> bValues;
6083 com::SafeArray<LONG64> bTimestamps;
6084 com::SafeArray<BSTR> bFlags;
6085
6086 if (!directControl)
6087 rc = E_ACCESSDENIED;
6088 else
6089 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6090 ComSafeArrayAsOutParam(bNames),
6091 ComSafeArrayAsOutParam(bValues),
6092 ComSafeArrayAsOutParam(bTimestamps),
6093 ComSafeArrayAsOutParam(bFlags));
6094 size_t i;
6095 aNames.resize(bNames.size());
6096 for (i = 0; i < bNames.size(); ++i)
6097 aNames[i] = Utf8Str(bNames[i]);
6098 aValues.resize(bValues.size());
6099 for (i = 0; i < bValues.size(); ++i)
6100 aValues[i] = Utf8Str(bValues[i]);
6101 aTimestamps.resize(bTimestamps.size());
6102 for (i = 0; i < bTimestamps.size(); ++i)
6103 aTimestamps[i] = bTimestamps[i];
6104 aFlags.resize(bFlags.size());
6105 for (i = 0; i < bFlags.size(); ++i)
6106 aFlags[i] = Utf8Str(bFlags[i]);
6107
6108 return rc;
6109}
6110#endif // VBOX_WITH_GUEST_PROPS
6111HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6112 std::vector<com::Utf8Str> &aNames,
6113 std::vector<com::Utf8Str> &aValues,
6114 std::vector<LONG64> &aTimestamps,
6115 std::vector<com::Utf8Str> &aFlags)
6116{
6117#ifndef VBOX_WITH_GUEST_PROPS
6118 ReturnComNotImplemented();
6119#else // VBOX_WITH_GUEST_PROPS
6120
6121 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6122
6123 if (rc == E_ACCESSDENIED)
6124 /* The VM is not running or the service is not (yet) accessible */
6125 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6126 return rc;
6127#endif // VBOX_WITH_GUEST_PROPS
6128}
6129
6130HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6131 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6132{
6133 MediumAttachmentList atts;
6134
6135 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6136 if (FAILED(rc)) return rc;
6137
6138 aMediumAttachments.resize(atts.size());
6139 size_t i = 0;
6140 for (MediumAttachmentList::const_iterator
6141 it = atts.begin();
6142 it != atts.end();
6143 ++it, ++i)
6144 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6145
6146 return S_OK;
6147}
6148
6149HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6150 LONG aControllerPort,
6151 LONG aDevice,
6152 ComPtr<IMediumAttachment> &aAttachment)
6153{
6154 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6155 aName.c_str(), aControllerPort, aDevice));
6156
6157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6158
6159 aAttachment = NULL;
6160
6161 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6162 aName,
6163 aControllerPort,
6164 aDevice);
6165 if (pAttach.isNull())
6166 return setError(VBOX_E_OBJECT_NOT_FOUND,
6167 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6168 aDevice, aControllerPort, aName.c_str());
6169
6170 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6171
6172 return S_OK;
6173}
6174
6175
6176HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6177 StorageBus_T aConnectionType,
6178 ComPtr<IStorageController> &aController)
6179{
6180 if ( (aConnectionType <= StorageBus_Null)
6181 || (aConnectionType > StorageBus_PCIe))
6182 return setError(E_INVALIDARG,
6183 tr("Invalid connection type: %d"),
6184 aConnectionType);
6185
6186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6187
6188 HRESULT rc = i_checkStateDependency(MutableStateDep);
6189 if (FAILED(rc)) return rc;
6190
6191 /* try to find one with the name first. */
6192 ComObjPtr<StorageController> ctrl;
6193
6194 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6195 if (SUCCEEDED(rc))
6196 return setError(VBOX_E_OBJECT_IN_USE,
6197 tr("Storage controller named '%s' already exists"),
6198 aName.c_str());
6199
6200 ctrl.createObject();
6201
6202 /* get a new instance number for the storage controller */
6203 ULONG ulInstance = 0;
6204 bool fBootable = true;
6205 for (StorageControllerList::const_iterator
6206 it = mStorageControllers->begin();
6207 it != mStorageControllers->end();
6208 ++it)
6209 {
6210 if ((*it)->i_getStorageBus() == aConnectionType)
6211 {
6212 ULONG ulCurInst = (*it)->i_getInstance();
6213
6214 if (ulCurInst >= ulInstance)
6215 ulInstance = ulCurInst + 1;
6216
6217 /* Only one controller of each type can be marked as bootable. */
6218 if ((*it)->i_getBootable())
6219 fBootable = false;
6220 }
6221 }
6222
6223 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6224 if (FAILED(rc)) return rc;
6225
6226 i_setModified(IsModified_Storage);
6227 mStorageControllers.backup();
6228 mStorageControllers->push_back(ctrl);
6229
6230 ctrl.queryInterfaceTo(aController.asOutParam());
6231
6232 /* inform the direct session if any */
6233 alock.release();
6234 i_onStorageControllerChange();
6235
6236 return S_OK;
6237}
6238
6239HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6240 ComPtr<IStorageController> &aStorageController)
6241{
6242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6243
6244 ComObjPtr<StorageController> ctrl;
6245
6246 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6247 if (SUCCEEDED(rc))
6248 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6249
6250 return rc;
6251}
6252
6253HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6254 ULONG aInstance,
6255 ComPtr<IStorageController> &aStorageController)
6256{
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 for (StorageControllerList::const_iterator
6260 it = mStorageControllers->begin();
6261 it != mStorageControllers->end();
6262 ++it)
6263 {
6264 if ( (*it)->i_getStorageBus() == aConnectionType
6265 && (*it)->i_getInstance() == aInstance)
6266 {
6267 (*it).queryInterfaceTo(aStorageController.asOutParam());
6268 return S_OK;
6269 }
6270 }
6271
6272 return setError(VBOX_E_OBJECT_NOT_FOUND,
6273 tr("Could not find a storage controller with instance number '%lu'"),
6274 aInstance);
6275}
6276
6277HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6278{
6279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6280
6281 HRESULT rc = i_checkStateDependency(MutableStateDep);
6282 if (FAILED(rc)) return rc;
6283
6284 ComObjPtr<StorageController> ctrl;
6285
6286 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6287 if (SUCCEEDED(rc))
6288 {
6289 /* Ensure that only one controller of each type is marked as bootable. */
6290 if (aBootable == TRUE)
6291 {
6292 for (StorageControllerList::const_iterator
6293 it = mStorageControllers->begin();
6294 it != mStorageControllers->end();
6295 ++it)
6296 {
6297 ComObjPtr<StorageController> aCtrl = (*it);
6298
6299 if ( (aCtrl->i_getName() != aName)
6300 && aCtrl->i_getBootable() == TRUE
6301 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6302 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6303 {
6304 aCtrl->i_setBootable(FALSE);
6305 break;
6306 }
6307 }
6308 }
6309
6310 if (SUCCEEDED(rc))
6311 {
6312 ctrl->i_setBootable(aBootable);
6313 i_setModified(IsModified_Storage);
6314 }
6315 }
6316
6317 if (SUCCEEDED(rc))
6318 {
6319 /* inform the direct session if any */
6320 alock.release();
6321 i_onStorageControllerChange();
6322 }
6323
6324 return rc;
6325}
6326
6327HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6328{
6329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6330
6331 HRESULT rc = i_checkStateDependency(MutableStateDep);
6332 if (FAILED(rc)) return rc;
6333
6334 ComObjPtr<StorageController> ctrl;
6335 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6336 if (FAILED(rc)) return rc;
6337
6338 {
6339 /* find all attached devices to the appropriate storage controller and detach them all */
6340 // make a temporary list because detachDevice invalidates iterators into
6341 // mMediumAttachments
6342 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6343
6344 for (MediumAttachmentList::const_iterator
6345 it = llAttachments2.begin();
6346 it != llAttachments2.end();
6347 ++it)
6348 {
6349 MediumAttachment *pAttachTemp = *it;
6350
6351 AutoCaller localAutoCaller(pAttachTemp);
6352 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6353
6354 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6355
6356 if (pAttachTemp->i_getControllerName() == aName)
6357 {
6358 rc = i_detachDevice(pAttachTemp, alock, NULL);
6359 if (FAILED(rc)) return rc;
6360 }
6361 }
6362 }
6363
6364 /* We can remove it now. */
6365 i_setModified(IsModified_Storage);
6366 mStorageControllers.backup();
6367
6368 ctrl->i_unshare();
6369
6370 mStorageControllers->remove(ctrl);
6371
6372 /* inform the direct session if any */
6373 alock.release();
6374 i_onStorageControllerChange();
6375
6376 return S_OK;
6377}
6378
6379HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6380 ComPtr<IUSBController> &aController)
6381{
6382 if ( (aType <= USBControllerType_Null)
6383 || (aType >= USBControllerType_Last))
6384 return setError(E_INVALIDARG,
6385 tr("Invalid USB controller type: %d"),
6386 aType);
6387
6388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6389
6390 HRESULT rc = i_checkStateDependency(MutableStateDep);
6391 if (FAILED(rc)) return rc;
6392
6393 /* try to find one with the same type first. */
6394 ComObjPtr<USBController> ctrl;
6395
6396 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6397 if (SUCCEEDED(rc))
6398 return setError(VBOX_E_OBJECT_IN_USE,
6399 tr("USB controller named '%s' already exists"),
6400 aName.c_str());
6401
6402 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6403 ULONG maxInstances;
6404 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6405 if (FAILED(rc))
6406 return rc;
6407
6408 ULONG cInstances = i_getUSBControllerCountByType(aType);
6409 if (cInstances >= maxInstances)
6410 return setError(E_INVALIDARG,
6411 tr("Too many USB controllers of this type"));
6412
6413 ctrl.createObject();
6414
6415 rc = ctrl->init(this, aName, aType);
6416 if (FAILED(rc)) return rc;
6417
6418 i_setModified(IsModified_USB);
6419 mUSBControllers.backup();
6420 mUSBControllers->push_back(ctrl);
6421
6422 ctrl.queryInterfaceTo(aController.asOutParam());
6423
6424 /* inform the direct session if any */
6425 alock.release();
6426 i_onUSBControllerChange();
6427
6428 return S_OK;
6429}
6430
6431HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6432{
6433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 ComObjPtr<USBController> ctrl;
6436
6437 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6438 if (SUCCEEDED(rc))
6439 ctrl.queryInterfaceTo(aController.asOutParam());
6440
6441 return rc;
6442}
6443
6444HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6445 ULONG *aControllers)
6446{
6447 if ( (aType <= USBControllerType_Null)
6448 || (aType >= USBControllerType_Last))
6449 return setError(E_INVALIDARG,
6450 tr("Invalid USB controller type: %d"),
6451 aType);
6452
6453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 ComObjPtr<USBController> ctrl;
6456
6457 *aControllers = i_getUSBControllerCountByType(aType);
6458
6459 return S_OK;
6460}
6461
6462HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6463{
6464
6465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6466
6467 HRESULT rc = i_checkStateDependency(MutableStateDep);
6468 if (FAILED(rc)) return rc;
6469
6470 ComObjPtr<USBController> ctrl;
6471 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6472 if (FAILED(rc)) return rc;
6473
6474 i_setModified(IsModified_USB);
6475 mUSBControllers.backup();
6476
6477 ctrl->i_unshare();
6478
6479 mUSBControllers->remove(ctrl);
6480
6481 /* inform the direct session if any */
6482 alock.release();
6483 i_onUSBControllerChange();
6484
6485 return S_OK;
6486}
6487
6488HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6489 ULONG *aOriginX,
6490 ULONG *aOriginY,
6491 ULONG *aWidth,
6492 ULONG *aHeight,
6493 BOOL *aEnabled)
6494{
6495 uint32_t u32OriginX= 0;
6496 uint32_t u32OriginY= 0;
6497 uint32_t u32Width = 0;
6498 uint32_t u32Height = 0;
6499 uint16_t u16Flags = 0;
6500
6501 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6502 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6503 if (RT_FAILURE(vrc))
6504 {
6505#ifdef RT_OS_WINDOWS
6506 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6507 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6508 * So just assign fEnable to TRUE again.
6509 * The right fix would be to change GUI API wrappers to make sure that parameters
6510 * are changed only if API succeeds.
6511 */
6512 *aEnabled = TRUE;
6513#endif
6514 return setError(VBOX_E_IPRT_ERROR,
6515 tr("Saved guest size is not available (%Rrc)"),
6516 vrc);
6517 }
6518
6519 *aOriginX = u32OriginX;
6520 *aOriginY = u32OriginY;
6521 *aWidth = u32Width;
6522 *aHeight = u32Height;
6523 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6524
6525 return S_OK;
6526}
6527
6528HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6529 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6530{
6531 if (aScreenId != 0)
6532 return E_NOTIMPL;
6533
6534 if ( aBitmapFormat != BitmapFormat_BGR0
6535 && aBitmapFormat != BitmapFormat_BGRA
6536 && aBitmapFormat != BitmapFormat_RGBA
6537 && aBitmapFormat != BitmapFormat_PNG)
6538 return setError(E_NOTIMPL,
6539 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6540
6541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6542
6543 uint8_t *pu8Data = NULL;
6544 uint32_t cbData = 0;
6545 uint32_t u32Width = 0;
6546 uint32_t u32Height = 0;
6547
6548 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6549
6550 if (RT_FAILURE(vrc))
6551 return setError(VBOX_E_IPRT_ERROR,
6552 tr("Saved thumbnail data is not available (%Rrc)"),
6553 vrc);
6554
6555 HRESULT hr = S_OK;
6556
6557 *aWidth = u32Width;
6558 *aHeight = u32Height;
6559
6560 if (cbData > 0)
6561 {
6562 /* Convert pixels to the format expected by the API caller. */
6563 if (aBitmapFormat == BitmapFormat_BGR0)
6564 {
6565 /* [0] B, [1] G, [2] R, [3] 0. */
6566 aData.resize(cbData);
6567 memcpy(&aData.front(), pu8Data, cbData);
6568 }
6569 else if (aBitmapFormat == BitmapFormat_BGRA)
6570 {
6571 /* [0] B, [1] G, [2] R, [3] A. */
6572 aData.resize(cbData);
6573 for (uint32_t i = 0; i < cbData; i += 4)
6574 {
6575 aData[i] = pu8Data[i];
6576 aData[i + 1] = pu8Data[i + 1];
6577 aData[i + 2] = pu8Data[i + 2];
6578 aData[i + 3] = 0xff;
6579 }
6580 }
6581 else if (aBitmapFormat == BitmapFormat_RGBA)
6582 {
6583 /* [0] R, [1] G, [2] B, [3] A. */
6584 aData.resize(cbData);
6585 for (uint32_t i = 0; i < cbData; i += 4)
6586 {
6587 aData[i] = pu8Data[i + 2];
6588 aData[i + 1] = pu8Data[i + 1];
6589 aData[i + 2] = pu8Data[i];
6590 aData[i + 3] = 0xff;
6591 }
6592 }
6593 else if (aBitmapFormat == BitmapFormat_PNG)
6594 {
6595 uint8_t *pu8PNG = NULL;
6596 uint32_t cbPNG = 0;
6597 uint32_t cxPNG = 0;
6598 uint32_t cyPNG = 0;
6599
6600 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6601
6602 if (RT_SUCCESS(vrc))
6603 {
6604 aData.resize(cbPNG);
6605 if (cbPNG)
6606 memcpy(&aData.front(), pu8PNG, cbPNG);
6607 }
6608 else
6609 hr = setError(VBOX_E_IPRT_ERROR,
6610 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6611 vrc);
6612
6613 RTMemFree(pu8PNG);
6614 }
6615 }
6616
6617 freeSavedDisplayScreenshot(pu8Data);
6618
6619 return hr;
6620}
6621
6622HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6623 ULONG *aWidth,
6624 ULONG *aHeight,
6625 std::vector<BitmapFormat_T> &aBitmapFormats)
6626{
6627 if (aScreenId != 0)
6628 return E_NOTIMPL;
6629
6630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6631
6632 uint8_t *pu8Data = NULL;
6633 uint32_t cbData = 0;
6634 uint32_t u32Width = 0;
6635 uint32_t u32Height = 0;
6636
6637 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6638
6639 if (RT_FAILURE(vrc))
6640 return setError(VBOX_E_IPRT_ERROR,
6641 tr("Saved screenshot data is not available (%Rrc)"),
6642 vrc);
6643
6644 *aWidth = u32Width;
6645 *aHeight = u32Height;
6646 aBitmapFormats.resize(1);
6647 aBitmapFormats[0] = BitmapFormat_PNG;
6648
6649 freeSavedDisplayScreenshot(pu8Data);
6650
6651 return S_OK;
6652}
6653
6654HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6655 BitmapFormat_T aBitmapFormat,
6656 ULONG *aWidth,
6657 ULONG *aHeight,
6658 std::vector<BYTE> &aData)
6659{
6660 if (aScreenId != 0)
6661 return E_NOTIMPL;
6662
6663 if (aBitmapFormat != BitmapFormat_PNG)
6664 return E_NOTIMPL;
6665
6666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6667
6668 uint8_t *pu8Data = NULL;
6669 uint32_t cbData = 0;
6670 uint32_t u32Width = 0;
6671 uint32_t u32Height = 0;
6672
6673 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6674
6675 if (RT_FAILURE(vrc))
6676 return setError(VBOX_E_IPRT_ERROR,
6677 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6678 vrc);
6679
6680 *aWidth = u32Width;
6681 *aHeight = u32Height;
6682
6683 aData.resize(cbData);
6684 if (cbData)
6685 memcpy(&aData.front(), pu8Data, cbData);
6686
6687 freeSavedDisplayScreenshot(pu8Data);
6688
6689 return S_OK;
6690}
6691
6692HRESULT Machine::hotPlugCPU(ULONG aCpu)
6693{
6694 HRESULT rc = S_OK;
6695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 if (!mHWData->mCPUHotPlugEnabled)
6698 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6699
6700 if (aCpu >= mHWData->mCPUCount)
6701 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6702
6703 if (mHWData->mCPUAttached[aCpu])
6704 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6705
6706 alock.release();
6707 rc = i_onCPUChange(aCpu, false);
6708 alock.acquire();
6709 if (FAILED(rc)) return rc;
6710
6711 i_setModified(IsModified_MachineData);
6712 mHWData.backup();
6713 mHWData->mCPUAttached[aCpu] = true;
6714
6715 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6716 if (Global::IsOnline(mData->mMachineState))
6717 i_saveSettings(NULL);
6718
6719 return S_OK;
6720}
6721
6722HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6723{
6724 HRESULT rc = S_OK;
6725
6726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6727
6728 if (!mHWData->mCPUHotPlugEnabled)
6729 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6730
6731 if (aCpu >= SchemaDefs::MaxCPUCount)
6732 return setError(E_INVALIDARG,
6733 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6734 SchemaDefs::MaxCPUCount);
6735
6736 if (!mHWData->mCPUAttached[aCpu])
6737 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6738
6739 /* CPU 0 can't be detached */
6740 if (aCpu == 0)
6741 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6742
6743 alock.release();
6744 rc = i_onCPUChange(aCpu, true);
6745 alock.acquire();
6746 if (FAILED(rc)) return rc;
6747
6748 i_setModified(IsModified_MachineData);
6749 mHWData.backup();
6750 mHWData->mCPUAttached[aCpu] = false;
6751
6752 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6753 if (Global::IsOnline(mData->mMachineState))
6754 i_saveSettings(NULL);
6755
6756 return S_OK;
6757}
6758
6759HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6760{
6761 *aAttached = false;
6762
6763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 /* If hotplug is enabled the CPU is always enabled. */
6766 if (!mHWData->mCPUHotPlugEnabled)
6767 {
6768 if (aCpu < mHWData->mCPUCount)
6769 *aAttached = true;
6770 }
6771 else
6772 {
6773 if (aCpu < SchemaDefs::MaxCPUCount)
6774 *aAttached = mHWData->mCPUAttached[aCpu];
6775 }
6776
6777 return S_OK;
6778}
6779
6780HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6781{
6782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6783
6784 Utf8Str log = i_getLogFilename(aIdx);
6785 if (!RTFileExists(log.c_str()))
6786 log.setNull();
6787 aFilename = log;
6788
6789 return S_OK;
6790}
6791
6792HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6793{
6794 if (aSize < 0)
6795 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6796
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 HRESULT rc = S_OK;
6800 Utf8Str log = i_getLogFilename(aIdx);
6801
6802 /* do not unnecessarily hold the lock while doing something which does
6803 * not need the lock and potentially takes a long time. */
6804 alock.release();
6805
6806 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6807 * keeps the SOAP reply size under 1M for the webservice (we're using
6808 * base64 encoded strings for binary data for years now, avoiding the
6809 * expansion of each byte array element to approx. 25 bytes of XML. */
6810 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6811 aData.resize(cbData);
6812
6813 RTFILE LogFile;
6814 int vrc = RTFileOpen(&LogFile, log.c_str(),
6815 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6816 if (RT_SUCCESS(vrc))
6817 {
6818 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6819 if (RT_SUCCESS(vrc))
6820 aData.resize(cbData);
6821 else
6822 rc = setError(VBOX_E_IPRT_ERROR,
6823 tr("Could not read log file '%s' (%Rrc)"),
6824 log.c_str(), vrc);
6825 RTFileClose(LogFile);
6826 }
6827 else
6828 rc = setError(VBOX_E_IPRT_ERROR,
6829 tr("Could not open log file '%s' (%Rrc)"),
6830 log.c_str(), vrc);
6831
6832 if (FAILED(rc))
6833 aData.resize(0);
6834
6835 return rc;
6836}
6837
6838
6839/**
6840 * Currently this method doesn't attach device to the running VM,
6841 * just makes sure it's plugged on next VM start.
6842 */
6843HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6844{
6845 // lock scope
6846 {
6847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6848
6849 HRESULT rc = i_checkStateDependency(MutableStateDep);
6850 if (FAILED(rc)) return rc;
6851
6852 ChipsetType_T aChipset = ChipsetType_PIIX3;
6853 COMGETTER(ChipsetType)(&aChipset);
6854
6855 if (aChipset != ChipsetType_ICH9)
6856 {
6857 return setError(E_INVALIDARG,
6858 tr("Host PCI attachment only supported with ICH9 chipset"));
6859 }
6860
6861 // check if device with this host PCI address already attached
6862 for (HWData::PCIDeviceAssignmentList::const_iterator
6863 it = mHWData->mPCIDeviceAssignments.begin();
6864 it != mHWData->mPCIDeviceAssignments.end();
6865 ++it)
6866 {
6867 LONG iHostAddress = -1;
6868 ComPtr<PCIDeviceAttachment> pAttach;
6869 pAttach = *it;
6870 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6871 if (iHostAddress == aHostAddress)
6872 return setError(E_INVALIDARG,
6873 tr("Device with host PCI address already attached to this VM"));
6874 }
6875
6876 ComObjPtr<PCIDeviceAttachment> pda;
6877 char name[32];
6878
6879 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6880 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6881 pda.createObject();
6882 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6883 i_setModified(IsModified_MachineData);
6884 mHWData.backup();
6885 mHWData->mPCIDeviceAssignments.push_back(pda);
6886 }
6887
6888 return S_OK;
6889}
6890
6891/**
6892 * Currently this method doesn't detach device from the running VM,
6893 * just makes sure it's not plugged on next VM start.
6894 */
6895HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6896{
6897 ComObjPtr<PCIDeviceAttachment> pAttach;
6898 bool fRemoved = false;
6899 HRESULT rc;
6900
6901 // lock scope
6902 {
6903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6904
6905 rc = i_checkStateDependency(MutableStateDep);
6906 if (FAILED(rc)) return rc;
6907
6908 for (HWData::PCIDeviceAssignmentList::const_iterator
6909 it = mHWData->mPCIDeviceAssignments.begin();
6910 it != mHWData->mPCIDeviceAssignments.end();
6911 ++it)
6912 {
6913 LONG iHostAddress = -1;
6914 pAttach = *it;
6915 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6916 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6917 {
6918 i_setModified(IsModified_MachineData);
6919 mHWData.backup();
6920 mHWData->mPCIDeviceAssignments.remove(pAttach);
6921 fRemoved = true;
6922 break;
6923 }
6924 }
6925 }
6926
6927
6928 /* Fire event outside of the lock */
6929 if (fRemoved)
6930 {
6931 Assert(!pAttach.isNull());
6932 ComPtr<IEventSource> es;
6933 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6934 Assert(SUCCEEDED(rc));
6935 Bstr mid;
6936 rc = this->COMGETTER(Id)(mid.asOutParam());
6937 Assert(SUCCEEDED(rc));
6938 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6939 }
6940
6941 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6942 tr("No host PCI device %08x attached"),
6943 aHostAddress
6944 );
6945}
6946
6947HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6948{
6949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6950
6951 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6952 size_t i = 0;
6953 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6954 it = mHWData->mPCIDeviceAssignments.begin();
6955 it != mHWData->mPCIDeviceAssignments.end();
6956 ++it, ++i)
6957 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6958
6959 return S_OK;
6960}
6961
6962HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6963{
6964 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6965
6966 return S_OK;
6967}
6968
6969HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6970{
6971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6974
6975 return S_OK;
6976}
6977
6978HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6979{
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6982 if (SUCCEEDED(hrc))
6983 {
6984 hrc = mHWData.backupEx();
6985 if (SUCCEEDED(hrc))
6986 {
6987 i_setModified(IsModified_MachineData);
6988 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6989 }
6990 }
6991 return hrc;
6992}
6993
6994HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6995{
6996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6998 return S_OK;
6999}
7000
7001HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7002{
7003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7005 if (SUCCEEDED(hrc))
7006 {
7007 hrc = mHWData.backupEx();
7008 if (SUCCEEDED(hrc))
7009 {
7010 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7011 if (SUCCEEDED(hrc))
7012 i_setModified(IsModified_MachineData);
7013 }
7014 }
7015 return hrc;
7016}
7017
7018HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7019{
7020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7021
7022 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7023
7024 return S_OK;
7025}
7026
7027HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7028{
7029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7030 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7031 if (SUCCEEDED(hrc))
7032 {
7033 hrc = mHWData.backupEx();
7034 if (SUCCEEDED(hrc))
7035 {
7036 i_setModified(IsModified_MachineData);
7037 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7038 }
7039 }
7040 return hrc;
7041}
7042
7043HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7044{
7045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7046
7047 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7048
7049 return S_OK;
7050}
7051
7052HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7053{
7054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7055
7056 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7057 if ( SUCCEEDED(hrc)
7058 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7059 {
7060 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7061 int vrc;
7062
7063 if (aAutostartEnabled)
7064 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7065 else
7066 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7067
7068 if (RT_SUCCESS(vrc))
7069 {
7070 hrc = mHWData.backupEx();
7071 if (SUCCEEDED(hrc))
7072 {
7073 i_setModified(IsModified_MachineData);
7074 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7075 }
7076 }
7077 else if (vrc == VERR_NOT_SUPPORTED)
7078 hrc = setError(VBOX_E_NOT_SUPPORTED,
7079 tr("The VM autostart feature is not supported on this platform"));
7080 else if (vrc == VERR_PATH_NOT_FOUND)
7081 hrc = setError(E_FAIL,
7082 tr("The path to the autostart database is not set"));
7083 else
7084 hrc = setError(E_UNEXPECTED,
7085 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7086 aAutostartEnabled ? "Adding" : "Removing",
7087 mUserData->s.strName.c_str(), vrc);
7088 }
7089 return hrc;
7090}
7091
7092HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7093{
7094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7095
7096 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7097
7098 return S_OK;
7099}
7100
7101HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7102{
7103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7104 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7105 if (SUCCEEDED(hrc))
7106 {
7107 hrc = mHWData.backupEx();
7108 if (SUCCEEDED(hrc))
7109 {
7110 i_setModified(IsModified_MachineData);
7111 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7112 }
7113 }
7114 return hrc;
7115}
7116
7117HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7118{
7119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7120
7121 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7122
7123 return S_OK;
7124}
7125
7126HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7127{
7128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7129 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7130 if ( SUCCEEDED(hrc)
7131 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7132 {
7133 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7134 int vrc;
7135
7136 if (aAutostopType != AutostopType_Disabled)
7137 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7138 else
7139 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7140
7141 if (RT_SUCCESS(vrc))
7142 {
7143 hrc = mHWData.backupEx();
7144 if (SUCCEEDED(hrc))
7145 {
7146 i_setModified(IsModified_MachineData);
7147 mHWData->mAutostart.enmAutostopType = aAutostopType;
7148 }
7149 }
7150 else if (vrc == VERR_NOT_SUPPORTED)
7151 hrc = setError(VBOX_E_NOT_SUPPORTED,
7152 tr("The VM autostop feature is not supported on this platform"));
7153 else if (vrc == VERR_PATH_NOT_FOUND)
7154 hrc = setError(E_FAIL,
7155 tr("The path to the autostart database is not set"));
7156 else
7157 hrc = setError(E_UNEXPECTED,
7158 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7159 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7160 mUserData->s.strName.c_str(), vrc);
7161 }
7162 return hrc;
7163}
7164
7165HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7166{
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168
7169 aDefaultFrontend = mHWData->mDefaultFrontend;
7170
7171 return S_OK;
7172}
7173
7174HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7175{
7176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7177 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7178 if (SUCCEEDED(hrc))
7179 {
7180 hrc = mHWData.backupEx();
7181 if (SUCCEEDED(hrc))
7182 {
7183 i_setModified(IsModified_MachineData);
7184 mHWData->mDefaultFrontend = aDefaultFrontend;
7185 }
7186 }
7187 return hrc;
7188}
7189
7190HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7191{
7192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7193 size_t cbIcon = mUserData->s.ovIcon.size();
7194 aIcon.resize(cbIcon);
7195 if (cbIcon)
7196 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7197 return S_OK;
7198}
7199
7200HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7201{
7202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7203 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7204 if (SUCCEEDED(hrc))
7205 {
7206 i_setModified(IsModified_MachineData);
7207 mUserData.backup();
7208 size_t cbIcon = aIcon.size();
7209 mUserData->s.ovIcon.resize(cbIcon);
7210 if (cbIcon)
7211 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7212 }
7213 return hrc;
7214}
7215
7216HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7217{
7218#ifdef VBOX_WITH_USB
7219 *aUSBProxyAvailable = true;
7220#else
7221 *aUSBProxyAvailable = false;
7222#endif
7223 return S_OK;
7224}
7225
7226HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7227{
7228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7229
7230 aVMProcessPriority = mUserData->s.strVMPriority;
7231
7232 return S_OK;
7233}
7234
7235HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7236{
7237 RT_NOREF(aVMProcessPriority);
7238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7239 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7240 if (SUCCEEDED(hrc))
7241 {
7242 /** @todo r=klaus: currently this is marked as not implemented, as
7243 * the code for setting the priority of the process is not there
7244 * (neither when starting the VM nor at runtime). */
7245 ReturnComNotImplemented();
7246#if 0
7247 hrc = mUserData.backupEx();
7248 if (SUCCEEDED(hrc))
7249 {
7250 i_setModified(IsModified_MachineData);
7251 mUserData->s.strVMPriority = aVMProcessPriority;
7252 }
7253#endif
7254 }
7255 return hrc;
7256}
7257
7258HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7259 ComPtr<IProgress> &aProgress)
7260{
7261 ComObjPtr<Progress> pP;
7262 Progress *ppP = pP;
7263 IProgress *iP = static_cast<IProgress *>(ppP);
7264 IProgress **pProgress = &iP;
7265
7266 IMachine *pTarget = aTarget;
7267
7268 /* Convert the options. */
7269 RTCList<CloneOptions_T> optList;
7270 if (aOptions.size())
7271 for (size_t i = 0; i < aOptions.size(); ++i)
7272 optList.append(aOptions[i]);
7273
7274 if (optList.contains(CloneOptions_Link))
7275 {
7276 if (!i_isSnapshotMachine())
7277 return setError(E_INVALIDARG,
7278 tr("Linked clone can only be created from a snapshot"));
7279 if (aMode != CloneMode_MachineState)
7280 return setError(E_INVALIDARG,
7281 tr("Linked clone can only be created for a single machine state"));
7282 }
7283 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7284
7285 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7286
7287 HRESULT rc = pWorker->start(pProgress);
7288
7289 pP = static_cast<Progress *>(*pProgress);
7290 pP.queryInterfaceTo(aProgress.asOutParam());
7291
7292 return rc;
7293
7294}
7295
7296HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7297 const com::Utf8Str &aType,
7298 ComPtr<IProgress> &aProgress)
7299{
7300 LogFlowThisFuncEnter();
7301
7302 ComObjPtr<Progress> progress;
7303
7304 progress.createObject();
7305
7306 HRESULT rc = S_OK;
7307 Utf8Str targetPath = aTargetPath;
7308 Utf8Str type = aType;
7309
7310 /* Initialize our worker task */
7311 MachineMoveVM* task = NULL;
7312 try
7313 {
7314 task = new MachineMoveVM(this, targetPath, type, progress);
7315 }
7316 catch(...)
7317 {
7318 delete task;
7319 return rc;
7320 }
7321
7322 /*
7323 * task pointer will be owned by the ThreadTask class.
7324 * There is no need to call operator "delete" in the end.
7325 */
7326 rc = task->init();
7327 if (SUCCEEDED(rc))
7328 {
7329 rc = task->createThread();
7330 if (FAILED(rc))
7331 {
7332 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7333 }
7334
7335 /* Return progress to the caller */
7336 progress.queryInterfaceTo(aProgress.asOutParam());
7337 }
7338
7339 LogFlowThisFuncLeave();
7340 return rc;
7341
7342}
7343
7344HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7345{
7346 NOREF(aProgress);
7347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7348
7349 // This check should always fail.
7350 HRESULT rc = i_checkStateDependency(MutableStateDep);
7351 if (FAILED(rc)) return rc;
7352
7353 AssertFailedReturn(E_NOTIMPL);
7354}
7355
7356HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7357{
7358 NOREF(aSavedStateFile);
7359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7360
7361 // This check should always fail.
7362 HRESULT rc = i_checkStateDependency(MutableStateDep);
7363 if (FAILED(rc)) return rc;
7364
7365 AssertFailedReturn(E_NOTIMPL);
7366}
7367
7368HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7369{
7370 NOREF(aFRemoveFile);
7371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7372
7373 // This check should always fail.
7374 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7375 if (FAILED(rc)) return rc;
7376
7377 AssertFailedReturn(E_NOTIMPL);
7378}
7379
7380// public methods for internal purposes
7381/////////////////////////////////////////////////////////////////////////////
7382
7383/**
7384 * Adds the given IsModified_* flag to the dirty flags of the machine.
7385 * This must be called either during i_loadSettings or under the machine write lock.
7386 * @param fl Flag
7387 * @param fAllowStateModification If state modifications are allowed.
7388 */
7389void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7390{
7391 mData->flModifications |= fl;
7392 if (fAllowStateModification && i_isStateModificationAllowed())
7393 mData->mCurrentStateModified = true;
7394}
7395
7396/**
7397 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7398 * care of the write locking.
7399 *
7400 * @param fModification The flag to add.
7401 * @param fAllowStateModification If state modifications are allowed.
7402 */
7403void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7404{
7405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7406 i_setModified(fModification, fAllowStateModification);
7407}
7408
7409/**
7410 * Saves the registry entry of this machine to the given configuration node.
7411 *
7412 * @param data Machine registry data.
7413 *
7414 * @note locks this object for reading.
7415 */
7416HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7417{
7418 AutoLimitedCaller autoCaller(this);
7419 AssertComRCReturnRC(autoCaller.rc());
7420
7421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7422
7423 data.uuid = mData->mUuid;
7424 data.strSettingsFile = mData->m_strConfigFile;
7425
7426 return S_OK;
7427}
7428
7429/**
7430 * Calculates the absolute path of the given path taking the directory of the
7431 * machine settings file as the current directory.
7432 *
7433 * @param strPath Path to calculate the absolute path for.
7434 * @param aResult Where to put the result (used only on success, can be the
7435 * same Utf8Str instance as passed in @a aPath).
7436 * @return IPRT result.
7437 *
7438 * @note Locks this object for reading.
7439 */
7440int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7441{
7442 AutoCaller autoCaller(this);
7443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7444
7445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7446
7447 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7448
7449 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7450
7451 strSettingsDir.stripFilename();
7452 char folder[RTPATH_MAX];
7453 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7454 if (RT_SUCCESS(vrc))
7455 aResult = folder;
7456
7457 return vrc;
7458}
7459
7460/**
7461 * Copies strSource to strTarget, making it relative to the machine folder
7462 * if it is a subdirectory thereof, or simply copying it otherwise.
7463 *
7464 * @param strSource Path to evaluate and copy.
7465 * @param strTarget Buffer to receive target path.
7466 *
7467 * @note Locks this object for reading.
7468 */
7469void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7470 Utf8Str &strTarget)
7471{
7472 AutoCaller autoCaller(this);
7473 AssertComRCReturn(autoCaller.rc(), (void)0);
7474
7475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7476
7477 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7478 // use strTarget as a temporary buffer to hold the machine settings dir
7479 strTarget = mData->m_strConfigFileFull;
7480 strTarget.stripFilename();
7481 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7482 {
7483 // is relative: then append what's left
7484 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7485 // for empty paths (only possible for subdirs) use "." to avoid
7486 // triggering default settings for not present config attributes.
7487 if (strTarget.isEmpty())
7488 strTarget = ".";
7489 }
7490 else
7491 // is not relative: then overwrite
7492 strTarget = strSource;
7493}
7494
7495/**
7496 * Returns the full path to the machine's log folder in the
7497 * \a aLogFolder argument.
7498 */
7499void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7500{
7501 AutoCaller autoCaller(this);
7502 AssertComRCReturnVoid(autoCaller.rc());
7503
7504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7505
7506 char szTmp[RTPATH_MAX];
7507 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7508 if (RT_SUCCESS(vrc))
7509 {
7510 if (szTmp[0] && !mUserData.isNull())
7511 {
7512 char szTmp2[RTPATH_MAX];
7513 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7514 if (RT_SUCCESS(vrc))
7515 aLogFolder = Utf8StrFmt("%s%c%s",
7516 szTmp2,
7517 RTPATH_DELIMITER,
7518 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7519 }
7520 else
7521 vrc = VERR_PATH_IS_RELATIVE;
7522 }
7523
7524 if (RT_FAILURE(vrc))
7525 {
7526 // fallback if VBOX_USER_LOGHOME is not set or invalid
7527 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7528 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7529 aLogFolder.append(RTPATH_DELIMITER);
7530 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7531 }
7532}
7533
7534/**
7535 * Returns the full path to the machine's log file for an given index.
7536 */
7537Utf8Str Machine::i_getLogFilename(ULONG idx)
7538{
7539 Utf8Str logFolder;
7540 getLogFolder(logFolder);
7541 Assert(logFolder.length());
7542
7543 Utf8Str log;
7544 if (idx == 0)
7545 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7546#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7547 else if (idx == 1)
7548 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7549 else
7550 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7551#else
7552 else
7553 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7554#endif
7555 return log;
7556}
7557
7558/**
7559 * Returns the full path to the machine's hardened log file.
7560 */
7561Utf8Str Machine::i_getHardeningLogFilename(void)
7562{
7563 Utf8Str strFilename;
7564 getLogFolder(strFilename);
7565 Assert(strFilename.length());
7566 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7567 return strFilename;
7568}
7569
7570
7571/**
7572 * Composes a unique saved state filename based on the current system time. The filename is
7573 * granular to the second so this will work so long as no more than one snapshot is taken on
7574 * a machine per second.
7575 *
7576 * Before version 4.1, we used this formula for saved state files:
7577 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7578 * which no longer works because saved state files can now be shared between the saved state of the
7579 * "saved" machine and an online snapshot, and the following would cause problems:
7580 * 1) save machine
7581 * 2) create online snapshot from that machine state --> reusing saved state file
7582 * 3) save machine again --> filename would be reused, breaking the online snapshot
7583 *
7584 * So instead we now use a timestamp.
7585 *
7586 * @param strStateFilePath
7587 */
7588
7589void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7590{
7591 AutoCaller autoCaller(this);
7592 AssertComRCReturnVoid(autoCaller.rc());
7593
7594 {
7595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7596 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7597 }
7598
7599 RTTIMESPEC ts;
7600 RTTimeNow(&ts);
7601 RTTIME time;
7602 RTTimeExplode(&time, &ts);
7603
7604 strStateFilePath += RTPATH_DELIMITER;
7605 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7606 time.i32Year, time.u8Month, time.u8MonthDay,
7607 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7608}
7609
7610/**
7611 * Returns the full path to the default video capture file.
7612 */
7613void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7614{
7615 AutoCaller autoCaller(this);
7616 AssertComRCReturnVoid(autoCaller.rc());
7617
7618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7619
7620 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7621 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7622 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7623}
7624
7625/**
7626 * Returns whether at least one USB controller is present for the VM.
7627 */
7628bool Machine::i_isUSBControllerPresent()
7629{
7630 AutoCaller autoCaller(this);
7631 AssertComRCReturn(autoCaller.rc(), false);
7632
7633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7634
7635 return (mUSBControllers->size() > 0);
7636}
7637
7638/**
7639 * @note Locks this object for writing, calls the client process
7640 * (inside the lock).
7641 */
7642HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7643 const Utf8Str &strFrontend,
7644 const Utf8Str &strEnvironment,
7645 ProgressProxy *aProgress)
7646{
7647 LogFlowThisFuncEnter();
7648
7649 AssertReturn(aControl, E_FAIL);
7650 AssertReturn(aProgress, E_FAIL);
7651 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7652
7653 AutoCaller autoCaller(this);
7654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7655
7656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7657
7658 if (!mData->mRegistered)
7659 return setError(E_UNEXPECTED,
7660 tr("The machine '%s' is not registered"),
7661 mUserData->s.strName.c_str());
7662
7663 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7664
7665 /* The process started when launching a VM with separate UI/VM processes is always
7666 * the UI process, i.e. needs special handling as it won't claim the session. */
7667 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7668
7669 if (fSeparate)
7670 {
7671 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7672 return setError(VBOX_E_INVALID_OBJECT_STATE,
7673 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7674 mUserData->s.strName.c_str());
7675 }
7676 else
7677 {
7678 if ( mData->mSession.mState == SessionState_Locked
7679 || mData->mSession.mState == SessionState_Spawning
7680 || mData->mSession.mState == SessionState_Unlocking)
7681 return setError(VBOX_E_INVALID_OBJECT_STATE,
7682 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7683 mUserData->s.strName.c_str());
7684
7685 /* may not be busy */
7686 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7687 }
7688
7689 /* get the path to the executable */
7690 char szPath[RTPATH_MAX];
7691 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7692 size_t cchBufLeft = strlen(szPath);
7693 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7694 szPath[cchBufLeft] = 0;
7695 char *pszNamePart = szPath + cchBufLeft;
7696 cchBufLeft = sizeof(szPath) - cchBufLeft;
7697
7698 int vrc = VINF_SUCCESS;
7699 RTPROCESS pid = NIL_RTPROCESS;
7700
7701 RTENV env = RTENV_DEFAULT;
7702
7703 if (!strEnvironment.isEmpty())
7704 {
7705 char *newEnvStr = NULL;
7706
7707 do
7708 {
7709 /* clone the current environment */
7710 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7711 AssertRCBreakStmt(vrc2, vrc = vrc2);
7712
7713 newEnvStr = RTStrDup(strEnvironment.c_str());
7714 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7715
7716 /* put new variables to the environment
7717 * (ignore empty variable names here since RTEnv API
7718 * intentionally doesn't do that) */
7719 char *var = newEnvStr;
7720 for (char *p = newEnvStr; *p; ++p)
7721 {
7722 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7723 {
7724 *p = '\0';
7725 if (*var)
7726 {
7727 char *val = strchr(var, '=');
7728 if (val)
7729 {
7730 *val++ = '\0';
7731 vrc2 = RTEnvSetEx(env, var, val);
7732 }
7733 else
7734 vrc2 = RTEnvUnsetEx(env, var);
7735 if (RT_FAILURE(vrc2))
7736 break;
7737 }
7738 var = p + 1;
7739 }
7740 }
7741 if (RT_SUCCESS(vrc2) && *var)
7742 vrc2 = RTEnvPutEx(env, var);
7743
7744 AssertRCBreakStmt(vrc2, vrc = vrc2);
7745 }
7746 while (0);
7747
7748 if (newEnvStr != NULL)
7749 RTStrFree(newEnvStr);
7750 }
7751
7752 /* Hardening logging */
7753#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7754 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7755 {
7756 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7757 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7758 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7759 {
7760 Utf8Str strStartupLogDir = strHardeningLogFile;
7761 strStartupLogDir.stripFilename();
7762 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7763 file without stripping the file. */
7764 }
7765 strSupHardeningLogArg.append(strHardeningLogFile);
7766
7767 /* Remove legacy log filename to avoid confusion. */
7768 Utf8Str strOldStartupLogFile;
7769 getLogFolder(strOldStartupLogFile);
7770 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7771 RTFileDelete(strOldStartupLogFile.c_str());
7772 }
7773 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7774#else
7775 const char *pszSupHardeningLogArg = NULL;
7776#endif
7777
7778 Utf8Str strCanonicalName;
7779
7780#ifdef VBOX_WITH_QTGUI
7781 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7782 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7783 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7784 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7785 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7786 {
7787 strCanonicalName = "GUI/Qt";
7788# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7789 /* Modify the base path so that we don't need to use ".." below. */
7790 RTPathStripTrailingSlash(szPath);
7791 RTPathStripFilename(szPath);
7792 cchBufLeft = strlen(szPath);
7793 pszNamePart = szPath + cchBufLeft;
7794 cchBufLeft = sizeof(szPath) - cchBufLeft;
7795
7796# define OSX_APP_NAME "VirtualBoxVM"
7797# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7798
7799 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7800 if ( strAppOverride.contains(".")
7801 || strAppOverride.contains("/")
7802 || strAppOverride.contains("\\")
7803 || strAppOverride.contains(":"))
7804 strAppOverride.setNull();
7805 Utf8Str strAppPath;
7806 if (!strAppOverride.isEmpty())
7807 {
7808 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7809 Utf8Str strFullPath(szPath);
7810 strFullPath.append(strAppPath);
7811 /* there is a race, but people using this deserve the failure */
7812 if (!RTFileExists(strFullPath.c_str()))
7813 strAppOverride.setNull();
7814 }
7815 if (strAppOverride.isEmpty())
7816 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7817 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7818 strcpy(pszNamePart, strAppPath.c_str());
7819# else
7820# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7821 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7822# else
7823 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7824# endif
7825 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7826 strcpy(pszNamePart, s_szVirtualBox_exe);
7827# endif
7828
7829 Utf8Str idStr = mData->mUuid.toString();
7830 const char *apszArgs[] =
7831 {
7832 szPath,
7833 "--comment", mUserData->s.strName.c_str(),
7834 "--startvm", idStr.c_str(),
7835 "--no-startvm-errormsgbox",
7836 NULL, /* For "--separate". */
7837 NULL, /* For "--sup-startup-log". */
7838 NULL
7839 };
7840 unsigned iArg = 6;
7841 if (fSeparate)
7842 apszArgs[iArg++] = "--separate";
7843 apszArgs[iArg++] = pszSupHardeningLogArg;
7844
7845 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7846 }
7847#else /* !VBOX_WITH_QTGUI */
7848 if (0)
7849 ;
7850#endif /* VBOX_WITH_QTGUI */
7851
7852 else
7853
7854#ifdef VBOX_WITH_VBOXSDL
7855 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7856 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7857 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7858 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7859 {
7860 strCanonicalName = "GUI/SDL";
7861 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7862 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7863 strcpy(pszNamePart, s_szVBoxSDL_exe);
7864
7865 Utf8Str idStr = mData->mUuid.toString();
7866 const char *apszArgs[] =
7867 {
7868 szPath,
7869 "--comment", mUserData->s.strName.c_str(),
7870 "--startvm", idStr.c_str(),
7871 NULL, /* For "--separate". */
7872 NULL, /* For "--sup-startup-log". */
7873 NULL
7874 };
7875 unsigned iArg = 5;
7876 if (fSeparate)
7877 apszArgs[iArg++] = "--separate";
7878 apszArgs[iArg++] = pszSupHardeningLogArg;
7879
7880 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7881 }
7882#else /* !VBOX_WITH_VBOXSDL */
7883 if (0)
7884 ;
7885#endif /* !VBOX_WITH_VBOXSDL */
7886
7887 else
7888
7889#ifdef VBOX_WITH_HEADLESS
7890 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7891 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7892 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7893 )
7894 {
7895 strCanonicalName = "headless";
7896 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7897 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7898 * and a VM works even if the server has not been installed.
7899 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7900 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7901 * differently in 4.0 and 3.x.
7902 */
7903 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7904 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7905 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7906
7907 Utf8Str idStr = mData->mUuid.toString();
7908 const char *apszArgs[] =
7909 {
7910 szPath,
7911 "--comment", mUserData->s.strName.c_str(),
7912 "--startvm", idStr.c_str(),
7913 "--vrde", "config",
7914 NULL, /* For "--capture". */
7915 NULL, /* For "--sup-startup-log". */
7916 NULL
7917 };
7918 unsigned iArg = 7;
7919 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7920 apszArgs[iArg++] = "--capture";
7921 apszArgs[iArg++] = pszSupHardeningLogArg;
7922
7923# ifdef RT_OS_WINDOWS
7924 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7925# else
7926 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7927# endif
7928 }
7929#else /* !VBOX_WITH_HEADLESS */
7930 if (0)
7931 ;
7932#endif /* !VBOX_WITH_HEADLESS */
7933 else
7934 {
7935 RTEnvDestroy(env);
7936 return setError(E_INVALIDARG,
7937 tr("Invalid frontend name: '%s'"),
7938 strFrontend.c_str());
7939 }
7940
7941 RTEnvDestroy(env);
7942
7943 if (RT_FAILURE(vrc))
7944 return setError(VBOX_E_IPRT_ERROR,
7945 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7946 mUserData->s.strName.c_str(), vrc);
7947
7948 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7949
7950 if (!fSeparate)
7951 {
7952 /*
7953 * Note that we don't release the lock here before calling the client,
7954 * because it doesn't need to call us back if called with a NULL argument.
7955 * Releasing the lock here is dangerous because we didn't prepare the
7956 * launch data yet, but the client we've just started may happen to be
7957 * too fast and call LockMachine() that will fail (because of PID, etc.),
7958 * so that the Machine will never get out of the Spawning session state.
7959 */
7960
7961 /* inform the session that it will be a remote one */
7962 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7963#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7964 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7965#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7966 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7967#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7968 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7969
7970 if (FAILED(rc))
7971 {
7972 /* restore the session state */
7973 mData->mSession.mState = SessionState_Unlocked;
7974 alock.release();
7975 mParent->i_addProcessToReap(pid);
7976 /* The failure may occur w/o any error info (from RPC), so provide one */
7977 return setError(VBOX_E_VM_ERROR,
7978 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7979 }
7980
7981 /* attach launch data to the machine */
7982 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7983 mData->mSession.mRemoteControls.push_back(aControl);
7984 mData->mSession.mProgress = aProgress;
7985 mData->mSession.mPID = pid;
7986 mData->mSession.mState = SessionState_Spawning;
7987 Assert(strCanonicalName.isNotEmpty());
7988 mData->mSession.mName = strCanonicalName;
7989 }
7990 else
7991 {
7992 /* For separate UI process we declare the launch as completed instantly, as the
7993 * actual headless VM start may or may not come. No point in remembering anything
7994 * yet, as what matters for us is when the headless VM gets started. */
7995 aProgress->i_notifyComplete(S_OK);
7996 }
7997
7998 alock.release();
7999 mParent->i_addProcessToReap(pid);
8000
8001 LogFlowThisFuncLeave();
8002 return S_OK;
8003}
8004
8005/**
8006 * Returns @c true if the given session machine instance has an open direct
8007 * session (and optionally also for direct sessions which are closing) and
8008 * returns the session control machine instance if so.
8009 *
8010 * Note that when the method returns @c false, the arguments remain unchanged.
8011 *
8012 * @param aMachine Session machine object.
8013 * @param aControl Direct session control object (optional).
8014 * @param aRequireVM If true then only allow VM sessions.
8015 * @param aAllowClosing If true then additionally a session which is currently
8016 * being closed will also be allowed.
8017 *
8018 * @note locks this object for reading.
8019 */
8020bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8021 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8022 bool aRequireVM /*= false*/,
8023 bool aAllowClosing /*= false*/)
8024{
8025 AutoLimitedCaller autoCaller(this);
8026 AssertComRCReturn(autoCaller.rc(), false);
8027
8028 /* just return false for inaccessible machines */
8029 if (getObjectState().getState() != ObjectState::Ready)
8030 return false;
8031
8032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8033
8034 if ( ( mData->mSession.mState == SessionState_Locked
8035 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8036 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8037 )
8038 {
8039 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8040
8041 aMachine = mData->mSession.mMachine;
8042
8043 if (aControl != NULL)
8044 *aControl = mData->mSession.mDirectControl;
8045
8046 return true;
8047 }
8048
8049 return false;
8050}
8051
8052/**
8053 * Returns @c true if the given machine has an spawning direct session.
8054 *
8055 * @note locks this object for reading.
8056 */
8057bool Machine::i_isSessionSpawning()
8058{
8059 AutoLimitedCaller autoCaller(this);
8060 AssertComRCReturn(autoCaller.rc(), false);
8061
8062 /* just return false for inaccessible machines */
8063 if (getObjectState().getState() != ObjectState::Ready)
8064 return false;
8065
8066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8067
8068 if (mData->mSession.mState == SessionState_Spawning)
8069 return true;
8070
8071 return false;
8072}
8073
8074/**
8075 * Called from the client watcher thread to check for unexpected client process
8076 * death during Session_Spawning state (e.g. before it successfully opened a
8077 * direct session).
8078 *
8079 * On Win32 and on OS/2, this method is called only when we've got the
8080 * direct client's process termination notification, so it always returns @c
8081 * true.
8082 *
8083 * On other platforms, this method returns @c true if the client process is
8084 * terminated and @c false if it's still alive.
8085 *
8086 * @note Locks this object for writing.
8087 */
8088bool Machine::i_checkForSpawnFailure()
8089{
8090 AutoCaller autoCaller(this);
8091 if (!autoCaller.isOk())
8092 {
8093 /* nothing to do */
8094 LogFlowThisFunc(("Already uninitialized!\n"));
8095 return true;
8096 }
8097
8098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8099
8100 if (mData->mSession.mState != SessionState_Spawning)
8101 {
8102 /* nothing to do */
8103 LogFlowThisFunc(("Not spawning any more!\n"));
8104 return true;
8105 }
8106
8107 HRESULT rc = S_OK;
8108
8109 /* PID not yet initialized, skip check. */
8110 if (mData->mSession.mPID == NIL_RTPROCESS)
8111 return false;
8112
8113 RTPROCSTATUS status;
8114 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8115
8116 if (vrc != VERR_PROCESS_RUNNING)
8117 {
8118 Utf8Str strExtraInfo;
8119
8120#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8121 /* If the startup logfile exists and is of non-zero length, tell the
8122 user to look there for more details to encourage them to attach it
8123 when reporting startup issues. */
8124 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8125 uint64_t cbStartupLogFile = 0;
8126 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8127 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8128 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8129#endif
8130
8131 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8132 rc = setError(E_FAIL,
8133 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8134 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8135 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8136 rc = setError(E_FAIL,
8137 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8138 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8139 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8140 rc = setError(E_FAIL,
8141 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8142 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8143 else
8144 rc = setError(E_FAIL,
8145 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8146 i_getName().c_str(), vrc, strExtraInfo.c_str());
8147 }
8148
8149 if (FAILED(rc))
8150 {
8151 /* Close the remote session, remove the remote control from the list
8152 * and reset session state to Closed (@note keep the code in sync with
8153 * the relevant part in LockMachine()). */
8154
8155 Assert(mData->mSession.mRemoteControls.size() == 1);
8156 if (mData->mSession.mRemoteControls.size() == 1)
8157 {
8158 ErrorInfoKeeper eik;
8159 mData->mSession.mRemoteControls.front()->Uninitialize();
8160 }
8161
8162 mData->mSession.mRemoteControls.clear();
8163 mData->mSession.mState = SessionState_Unlocked;
8164
8165 /* finalize the progress after setting the state */
8166 if (!mData->mSession.mProgress.isNull())
8167 {
8168 mData->mSession.mProgress->notifyComplete(rc);
8169 mData->mSession.mProgress.setNull();
8170 }
8171
8172 mData->mSession.mPID = NIL_RTPROCESS;
8173
8174 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8175 return true;
8176 }
8177
8178 return false;
8179}
8180
8181/**
8182 * Checks whether the machine can be registered. If so, commits and saves
8183 * all settings.
8184 *
8185 * @note Must be called from mParent's write lock. Locks this object and
8186 * children for writing.
8187 */
8188HRESULT Machine::i_prepareRegister()
8189{
8190 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8191
8192 AutoLimitedCaller autoCaller(this);
8193 AssertComRCReturnRC(autoCaller.rc());
8194
8195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8196
8197 /* wait for state dependents to drop to zero */
8198 i_ensureNoStateDependencies();
8199
8200 if (!mData->mAccessible)
8201 return setError(VBOX_E_INVALID_OBJECT_STATE,
8202 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8203 mUserData->s.strName.c_str(),
8204 mData->mUuid.toString().c_str());
8205
8206 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8207
8208 if (mData->mRegistered)
8209 return setError(VBOX_E_INVALID_OBJECT_STATE,
8210 tr("The machine '%s' with UUID {%s} is already registered"),
8211 mUserData->s.strName.c_str(),
8212 mData->mUuid.toString().c_str());
8213
8214 HRESULT rc = S_OK;
8215
8216 // Ensure the settings are saved. If we are going to be registered and
8217 // no config file exists yet, create it by calling i_saveSettings() too.
8218 if ( (mData->flModifications)
8219 || (!mData->pMachineConfigFile->fileExists())
8220 )
8221 {
8222 rc = i_saveSettings(NULL);
8223 // no need to check whether VirtualBox.xml needs saving too since
8224 // we can't have a machine XML file rename pending
8225 if (FAILED(rc)) return rc;
8226 }
8227
8228 /* more config checking goes here */
8229
8230 if (SUCCEEDED(rc))
8231 {
8232 /* we may have had implicit modifications we want to fix on success */
8233 i_commit();
8234
8235 mData->mRegistered = true;
8236 }
8237 else
8238 {
8239 /* we may have had implicit modifications we want to cancel on failure*/
8240 i_rollback(false /* aNotify */);
8241 }
8242
8243 return rc;
8244}
8245
8246/**
8247 * Increases the number of objects dependent on the machine state or on the
8248 * registered state. Guarantees that these two states will not change at least
8249 * until #i_releaseStateDependency() is called.
8250 *
8251 * Depending on the @a aDepType value, additional state checks may be made.
8252 * These checks will set extended error info on failure. See
8253 * #i_checkStateDependency() for more info.
8254 *
8255 * If this method returns a failure, the dependency is not added and the caller
8256 * is not allowed to rely on any particular machine state or registration state
8257 * value and may return the failed result code to the upper level.
8258 *
8259 * @param aDepType Dependency type to add.
8260 * @param aState Current machine state (NULL if not interested).
8261 * @param aRegistered Current registered state (NULL if not interested).
8262 *
8263 * @note Locks this object for writing.
8264 */
8265HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8266 MachineState_T *aState /* = NULL */,
8267 BOOL *aRegistered /* = NULL */)
8268{
8269 AutoCaller autoCaller(this);
8270 AssertComRCReturnRC(autoCaller.rc());
8271
8272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8273
8274 HRESULT rc = i_checkStateDependency(aDepType);
8275 if (FAILED(rc)) return rc;
8276
8277 {
8278 if (mData->mMachineStateChangePending != 0)
8279 {
8280 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8281 * drop to zero so don't add more. It may make sense to wait a bit
8282 * and retry before reporting an error (since the pending state
8283 * transition should be really quick) but let's just assert for
8284 * now to see if it ever happens on practice. */
8285
8286 AssertFailed();
8287
8288 return setError(E_ACCESSDENIED,
8289 tr("Machine state change is in progress. Please retry the operation later."));
8290 }
8291
8292 ++mData->mMachineStateDeps;
8293 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8294 }
8295
8296 if (aState)
8297 *aState = mData->mMachineState;
8298 if (aRegistered)
8299 *aRegistered = mData->mRegistered;
8300
8301 return S_OK;
8302}
8303
8304/**
8305 * Decreases the number of objects dependent on the machine state.
8306 * Must always complete the #i_addStateDependency() call after the state
8307 * dependency is no more necessary.
8308 */
8309void Machine::i_releaseStateDependency()
8310{
8311 AutoCaller autoCaller(this);
8312 AssertComRCReturnVoid(autoCaller.rc());
8313
8314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8315
8316 /* releaseStateDependency() w/o addStateDependency()? */
8317 AssertReturnVoid(mData->mMachineStateDeps != 0);
8318 -- mData->mMachineStateDeps;
8319
8320 if (mData->mMachineStateDeps == 0)
8321 {
8322 /* inform i_ensureNoStateDependencies() that there are no more deps */
8323 if (mData->mMachineStateChangePending != 0)
8324 {
8325 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8326 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8327 }
8328 }
8329}
8330
8331Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8332{
8333 /* start with nothing found */
8334 Utf8Str strResult("");
8335
8336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8337
8338 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8339 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8340 // found:
8341 strResult = it->second; // source is a Utf8Str
8342
8343 return strResult;
8344}
8345
8346// protected methods
8347/////////////////////////////////////////////////////////////////////////////
8348
8349/**
8350 * Performs machine state checks based on the @a aDepType value. If a check
8351 * fails, this method will set extended error info, otherwise it will return
8352 * S_OK. It is supposed, that on failure, the caller will immediately return
8353 * the return value of this method to the upper level.
8354 *
8355 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8356 *
8357 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8358 * current state of this machine object allows to change settings of the
8359 * machine (i.e. the machine is not registered, or registered but not running
8360 * and not saved). It is useful to call this method from Machine setters
8361 * before performing any change.
8362 *
8363 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8364 * as for MutableStateDep except that if the machine is saved, S_OK is also
8365 * returned. This is useful in setters which allow changing machine
8366 * properties when it is in the saved state.
8367 *
8368 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8369 * if the current state of this machine object allows to change runtime
8370 * changeable settings of the machine (i.e. the machine is not registered, or
8371 * registered but either running or not running and not saved). It is useful
8372 * to call this method from Machine setters before performing any changes to
8373 * runtime changeable settings.
8374 *
8375 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8376 * the same as for MutableOrRunningStateDep except that if the machine is
8377 * saved, S_OK is also returned. This is useful in setters which allow
8378 * changing runtime and saved state changeable machine properties.
8379 *
8380 * @param aDepType Dependency type to check.
8381 *
8382 * @note Non Machine based classes should use #i_addStateDependency() and
8383 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8384 * template.
8385 *
8386 * @note This method must be called from under this object's read or write
8387 * lock.
8388 */
8389HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8390{
8391 switch (aDepType)
8392 {
8393 case AnyStateDep:
8394 {
8395 break;
8396 }
8397 case MutableStateDep:
8398 {
8399 if ( mData->mRegistered
8400 && ( !i_isSessionMachine()
8401 || ( mData->mMachineState != MachineState_Aborted
8402 && mData->mMachineState != MachineState_Teleported
8403 && mData->mMachineState != MachineState_PoweredOff
8404 )
8405 )
8406 )
8407 return setError(VBOX_E_INVALID_VM_STATE,
8408 tr("The machine is not mutable (state is %s)"),
8409 Global::stringifyMachineState(mData->mMachineState));
8410 break;
8411 }
8412 case MutableOrSavedStateDep:
8413 {
8414 if ( mData->mRegistered
8415 && ( !i_isSessionMachine()
8416 || ( mData->mMachineState != MachineState_Aborted
8417 && mData->mMachineState != MachineState_Teleported
8418 && mData->mMachineState != MachineState_Saved
8419 && mData->mMachineState != MachineState_PoweredOff
8420 )
8421 )
8422 )
8423 return setError(VBOX_E_INVALID_VM_STATE,
8424 tr("The machine is not mutable or saved (state is %s)"),
8425 Global::stringifyMachineState(mData->mMachineState));
8426 break;
8427 }
8428 case MutableOrRunningStateDep:
8429 {
8430 if ( mData->mRegistered
8431 && ( !i_isSessionMachine()
8432 || ( mData->mMachineState != MachineState_Aborted
8433 && mData->mMachineState != MachineState_Teleported
8434 && mData->mMachineState != MachineState_PoweredOff
8435 && !Global::IsOnline(mData->mMachineState)
8436 )
8437 )
8438 )
8439 return setError(VBOX_E_INVALID_VM_STATE,
8440 tr("The machine is not mutable or running (state is %s)"),
8441 Global::stringifyMachineState(mData->mMachineState));
8442 break;
8443 }
8444 case MutableOrSavedOrRunningStateDep:
8445 {
8446 if ( mData->mRegistered
8447 && ( !i_isSessionMachine()
8448 || ( mData->mMachineState != MachineState_Aborted
8449 && mData->mMachineState != MachineState_Teleported
8450 && mData->mMachineState != MachineState_Saved
8451 && mData->mMachineState != MachineState_PoweredOff
8452 && !Global::IsOnline(mData->mMachineState)
8453 )
8454 )
8455 )
8456 return setError(VBOX_E_INVALID_VM_STATE,
8457 tr("The machine is not mutable, saved or running (state is %s)"),
8458 Global::stringifyMachineState(mData->mMachineState));
8459 break;
8460 }
8461 }
8462
8463 return S_OK;
8464}
8465
8466/**
8467 * Helper to initialize all associated child objects and allocate data
8468 * structures.
8469 *
8470 * This method must be called as a part of the object's initialization procedure
8471 * (usually done in the #init() method).
8472 *
8473 * @note Must be called only from #init() or from #i_registeredInit().
8474 */
8475HRESULT Machine::initDataAndChildObjects()
8476{
8477 AutoCaller autoCaller(this);
8478 AssertComRCReturnRC(autoCaller.rc());
8479 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8480 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8481
8482 AssertReturn(!mData->mAccessible, E_FAIL);
8483
8484 /* allocate data structures */
8485 mSSData.allocate();
8486 mUserData.allocate();
8487 mHWData.allocate();
8488 mMediumAttachments.allocate();
8489 mStorageControllers.allocate();
8490 mUSBControllers.allocate();
8491
8492 /* initialize mOSTypeId */
8493 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8494
8495/** @todo r=bird: init() methods never fails, right? Why don't we make them
8496 * return void then! */
8497
8498 /* create associated BIOS settings object */
8499 unconst(mBIOSSettings).createObject();
8500 mBIOSSettings->init(this);
8501
8502 /* create an associated VRDE object (default is disabled) */
8503 unconst(mVRDEServer).createObject();
8504 mVRDEServer->init(this);
8505
8506 /* create associated serial port objects */
8507 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8508 {
8509 unconst(mSerialPorts[slot]).createObject();
8510 mSerialPorts[slot]->init(this, slot);
8511 }
8512
8513 /* create associated parallel port objects */
8514 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8515 {
8516 unconst(mParallelPorts[slot]).createObject();
8517 mParallelPorts[slot]->init(this, slot);
8518 }
8519
8520 /* create the audio adapter object (always present, default is disabled) */
8521 unconst(mAudioAdapter).createObject();
8522 mAudioAdapter->init(this);
8523
8524 /* create the USB device filters object (always present) */
8525 unconst(mUSBDeviceFilters).createObject();
8526 mUSBDeviceFilters->init(this);
8527
8528 /* create associated network adapter objects */
8529 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8530 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8531 {
8532 unconst(mNetworkAdapters[slot]).createObject();
8533 mNetworkAdapters[slot]->init(this, slot);
8534 }
8535
8536 /* create the bandwidth control */
8537 unconst(mBandwidthControl).createObject();
8538 mBandwidthControl->init(this);
8539
8540 return S_OK;
8541}
8542
8543/**
8544 * Helper to uninitialize all associated child objects and to free all data
8545 * structures.
8546 *
8547 * This method must be called as a part of the object's uninitialization
8548 * procedure (usually done in the #uninit() method).
8549 *
8550 * @note Must be called only from #uninit() or from #i_registeredInit().
8551 */
8552void Machine::uninitDataAndChildObjects()
8553{
8554 AutoCaller autoCaller(this);
8555 AssertComRCReturnVoid(autoCaller.rc());
8556 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8557 || getObjectState().getState() == ObjectState::Limited);
8558
8559 /* tell all our other child objects we've been uninitialized */
8560 if (mBandwidthControl)
8561 {
8562 mBandwidthControl->uninit();
8563 unconst(mBandwidthControl).setNull();
8564 }
8565
8566 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8567 {
8568 if (mNetworkAdapters[slot])
8569 {
8570 mNetworkAdapters[slot]->uninit();
8571 unconst(mNetworkAdapters[slot]).setNull();
8572 }
8573 }
8574
8575 if (mUSBDeviceFilters)
8576 {
8577 mUSBDeviceFilters->uninit();
8578 unconst(mUSBDeviceFilters).setNull();
8579 }
8580
8581 if (mAudioAdapter)
8582 {
8583 mAudioAdapter->uninit();
8584 unconst(mAudioAdapter).setNull();
8585 }
8586
8587 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8588 {
8589 if (mParallelPorts[slot])
8590 {
8591 mParallelPorts[slot]->uninit();
8592 unconst(mParallelPorts[slot]).setNull();
8593 }
8594 }
8595
8596 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8597 {
8598 if (mSerialPorts[slot])
8599 {
8600 mSerialPorts[slot]->uninit();
8601 unconst(mSerialPorts[slot]).setNull();
8602 }
8603 }
8604
8605 if (mVRDEServer)
8606 {
8607 mVRDEServer->uninit();
8608 unconst(mVRDEServer).setNull();
8609 }
8610
8611 if (mBIOSSettings)
8612 {
8613 mBIOSSettings->uninit();
8614 unconst(mBIOSSettings).setNull();
8615 }
8616
8617 /* Deassociate media (only when a real Machine or a SnapshotMachine
8618 * instance is uninitialized; SessionMachine instances refer to real
8619 * Machine media). This is necessary for a clean re-initialization of
8620 * the VM after successfully re-checking the accessibility state. Note
8621 * that in case of normal Machine or SnapshotMachine uninitialization (as
8622 * a result of unregistering or deleting the snapshot), outdated media
8623 * attachments will already be uninitialized and deleted, so this
8624 * code will not affect them. */
8625 if ( !mMediumAttachments.isNull()
8626 && !i_isSessionMachine()
8627 )
8628 {
8629 for (MediumAttachmentList::const_iterator
8630 it = mMediumAttachments->begin();
8631 it != mMediumAttachments->end();
8632 ++it)
8633 {
8634 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8635 if (pMedium.isNull())
8636 continue;
8637 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8638 AssertComRC(rc);
8639 }
8640 }
8641
8642 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8643 {
8644 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8645 if (mData->mFirstSnapshot)
8646 {
8647 // snapshots tree is protected by machine write lock; strictly
8648 // this isn't necessary here since we're deleting the entire
8649 // machine, but otherwise we assert in Snapshot::uninit()
8650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8651 mData->mFirstSnapshot->uninit();
8652 mData->mFirstSnapshot.setNull();
8653 }
8654
8655 mData->mCurrentSnapshot.setNull();
8656 }
8657
8658 /* free data structures (the essential mData structure is not freed here
8659 * since it may be still in use) */
8660 mMediumAttachments.free();
8661 mStorageControllers.free();
8662 mUSBControllers.free();
8663 mHWData.free();
8664 mUserData.free();
8665 mSSData.free();
8666}
8667
8668/**
8669 * Returns a pointer to the Machine object for this machine that acts like a
8670 * parent for complex machine data objects such as shared folders, etc.
8671 *
8672 * For primary Machine objects and for SnapshotMachine objects, returns this
8673 * object's pointer itself. For SessionMachine objects, returns the peer
8674 * (primary) machine pointer.
8675 */
8676Machine *Machine::i_getMachine()
8677{
8678 if (i_isSessionMachine())
8679 return (Machine*)mPeer;
8680 return this;
8681}
8682
8683/**
8684 * Makes sure that there are no machine state dependents. If necessary, waits
8685 * for the number of dependents to drop to zero.
8686 *
8687 * Make sure this method is called from under this object's write lock to
8688 * guarantee that no new dependents may be added when this method returns
8689 * control to the caller.
8690 *
8691 * @note Locks this object for writing. The lock will be released while waiting
8692 * (if necessary).
8693 *
8694 * @warning To be used only in methods that change the machine state!
8695 */
8696void Machine::i_ensureNoStateDependencies()
8697{
8698 AssertReturnVoid(isWriteLockOnCurrentThread());
8699
8700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8701
8702 /* Wait for all state dependents if necessary */
8703 if (mData->mMachineStateDeps != 0)
8704 {
8705 /* lazy semaphore creation */
8706 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8707 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8708
8709 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8710 mData->mMachineStateDeps));
8711
8712 ++mData->mMachineStateChangePending;
8713
8714 /* reset the semaphore before waiting, the last dependent will signal
8715 * it */
8716 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8717
8718 alock.release();
8719
8720 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8721
8722 alock.acquire();
8723
8724 -- mData->mMachineStateChangePending;
8725 }
8726}
8727
8728/**
8729 * Changes the machine state and informs callbacks.
8730 *
8731 * This method is not intended to fail so it either returns S_OK or asserts (and
8732 * returns a failure).
8733 *
8734 * @note Locks this object for writing.
8735 */
8736HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8737{
8738 LogFlowThisFuncEnter();
8739 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8740 Assert(aMachineState != MachineState_Null);
8741
8742 AutoCaller autoCaller(this);
8743 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8744
8745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8746
8747 /* wait for state dependents to drop to zero */
8748 i_ensureNoStateDependencies();
8749
8750 MachineState_T const enmOldState = mData->mMachineState;
8751 if (enmOldState != aMachineState)
8752 {
8753 mData->mMachineState = aMachineState;
8754 RTTimeNow(&mData->mLastStateChange);
8755
8756#ifdef VBOX_WITH_DTRACE_R3_MAIN
8757 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8758#endif
8759 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8760 }
8761
8762 LogFlowThisFuncLeave();
8763 return S_OK;
8764}
8765
8766/**
8767 * Searches for a shared folder with the given logical name
8768 * in the collection of shared folders.
8769 *
8770 * @param aName logical name of the shared folder
8771 * @param aSharedFolder where to return the found object
8772 * @param aSetError whether to set the error info if the folder is
8773 * not found
8774 * @return
8775 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8776 *
8777 * @note
8778 * must be called from under the object's lock!
8779 */
8780HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8781 ComObjPtr<SharedFolder> &aSharedFolder,
8782 bool aSetError /* = false */)
8783{
8784 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8785 for (HWData::SharedFolderList::const_iterator
8786 it = mHWData->mSharedFolders.begin();
8787 it != mHWData->mSharedFolders.end();
8788 ++it)
8789 {
8790 SharedFolder *pSF = *it;
8791 AutoCaller autoCaller(pSF);
8792 if (pSF->i_getName() == aName)
8793 {
8794 aSharedFolder = pSF;
8795 rc = S_OK;
8796 break;
8797 }
8798 }
8799
8800 if (aSetError && FAILED(rc))
8801 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8802
8803 return rc;
8804}
8805
8806/**
8807 * Initializes all machine instance data from the given settings structures
8808 * from XML. The exception is the machine UUID which needs special handling
8809 * depending on the caller's use case, so the caller needs to set that herself.
8810 *
8811 * This gets called in several contexts during machine initialization:
8812 *
8813 * -- When machine XML exists on disk already and needs to be loaded into memory,
8814 * for example, from #i_registeredInit() to load all registered machines on
8815 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8816 * attached to the machine should be part of some media registry already.
8817 *
8818 * -- During OVF import, when a machine config has been constructed from an
8819 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8820 * ensure that the media listed as attachments in the config (which have
8821 * been imported from the OVF) receive the correct registry ID.
8822 *
8823 * -- During VM cloning.
8824 *
8825 * @param config Machine settings from XML.
8826 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8827 * for each attached medium in the config.
8828 * @return
8829 */
8830HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8831 const Guid *puuidRegistry)
8832{
8833 // copy name, description, OS type, teleporter, UTC etc.
8834 mUserData->s = config.machineUserData;
8835
8836 // look up the object by Id to check it is valid
8837 ComObjPtr<GuestOSType> pGuestOSType;
8838 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8839 pGuestOSType);
8840 if (FAILED(rc)) return rc;
8841 mUserData->s.strOsType = pGuestOSType->i_id();
8842
8843 // stateFile (optional)
8844 if (config.strStateFile.isEmpty())
8845 mSSData->strStateFilePath.setNull();
8846 else
8847 {
8848 Utf8Str stateFilePathFull(config.strStateFile);
8849 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8850 if (RT_FAILURE(vrc))
8851 return setError(E_FAIL,
8852 tr("Invalid saved state file path '%s' (%Rrc)"),
8853 config.strStateFile.c_str(),
8854 vrc);
8855 mSSData->strStateFilePath = stateFilePathFull;
8856 }
8857
8858 // snapshot folder needs special processing so set it again
8859 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8860 if (FAILED(rc)) return rc;
8861
8862 /* Copy the extra data items (config may or may not be the same as
8863 * mData->pMachineConfigFile) if necessary. When loading the XML files
8864 * from disk they are the same, but not for OVF import. */
8865 if (mData->pMachineConfigFile != &config)
8866 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8867
8868 /* currentStateModified (optional, default is true) */
8869 mData->mCurrentStateModified = config.fCurrentStateModified;
8870
8871 mData->mLastStateChange = config.timeLastStateChange;
8872
8873 /*
8874 * note: all mUserData members must be assigned prior this point because
8875 * we need to commit changes in order to let mUserData be shared by all
8876 * snapshot machine instances.
8877 */
8878 mUserData.commitCopy();
8879
8880 // machine registry, if present (must be loaded before snapshots)
8881 if (config.canHaveOwnMediaRegistry())
8882 {
8883 // determine machine folder
8884 Utf8Str strMachineFolder = i_getSettingsFileFull();
8885 strMachineFolder.stripFilename();
8886 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8887 config.mediaRegistry,
8888 strMachineFolder);
8889 if (FAILED(rc)) return rc;
8890 }
8891
8892 /* Snapshot node (optional) */
8893 size_t cRootSnapshots;
8894 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8895 {
8896 // there must be only one root snapshot
8897 Assert(cRootSnapshots == 1);
8898
8899 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8900
8901 rc = i_loadSnapshot(snap,
8902 config.uuidCurrentSnapshot,
8903 NULL); // no parent == first snapshot
8904 if (FAILED(rc)) return rc;
8905 }
8906
8907 // hardware data
8908 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8909 if (FAILED(rc)) return rc;
8910
8911 /*
8912 * NOTE: the assignment below must be the last thing to do,
8913 * otherwise it will be not possible to change the settings
8914 * somewhere in the code above because all setters will be
8915 * blocked by i_checkStateDependency(MutableStateDep).
8916 */
8917
8918 /* set the machine state to Aborted or Saved when appropriate */
8919 if (config.fAborted)
8920 {
8921 mSSData->strStateFilePath.setNull();
8922
8923 /* no need to use i_setMachineState() during init() */
8924 mData->mMachineState = MachineState_Aborted;
8925 }
8926 else if (!mSSData->strStateFilePath.isEmpty())
8927 {
8928 /* no need to use i_setMachineState() during init() */
8929 mData->mMachineState = MachineState_Saved;
8930 }
8931
8932 // after loading settings, we are no longer different from the XML on disk
8933 mData->flModifications = 0;
8934
8935 return S_OK;
8936}
8937
8938/**
8939 * Recursively loads all snapshots starting from the given.
8940 *
8941 * @param data snapshot settings.
8942 * @param aCurSnapshotId Current snapshot ID from the settings file.
8943 * @param aParentSnapshot Parent snapshot.
8944 */
8945HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8946 const Guid &aCurSnapshotId,
8947 Snapshot *aParentSnapshot)
8948{
8949 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8950 AssertReturn(!i_isSessionMachine(), E_FAIL);
8951
8952 HRESULT rc = S_OK;
8953
8954 Utf8Str strStateFile;
8955 if (!data.strStateFile.isEmpty())
8956 {
8957 /* optional */
8958 strStateFile = data.strStateFile;
8959 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8960 if (RT_FAILURE(vrc))
8961 return setError(E_FAIL,
8962 tr("Invalid saved state file path '%s' (%Rrc)"),
8963 strStateFile.c_str(),
8964 vrc);
8965 }
8966
8967 /* create a snapshot machine object */
8968 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8969 pSnapshotMachine.createObject();
8970 rc = pSnapshotMachine->initFromSettings(this,
8971 data.hardware,
8972 &data.debugging,
8973 &data.autostart,
8974 data.uuid.ref(),
8975 strStateFile);
8976 if (FAILED(rc)) return rc;
8977
8978 /* create a snapshot object */
8979 ComObjPtr<Snapshot> pSnapshot;
8980 pSnapshot.createObject();
8981 /* initialize the snapshot */
8982 rc = pSnapshot->init(mParent, // VirtualBox object
8983 data.uuid,
8984 data.strName,
8985 data.strDescription,
8986 data.timestamp,
8987 pSnapshotMachine,
8988 aParentSnapshot);
8989 if (FAILED(rc)) return rc;
8990
8991 /* memorize the first snapshot if necessary */
8992 if (!mData->mFirstSnapshot)
8993 mData->mFirstSnapshot = pSnapshot;
8994
8995 /* memorize the current snapshot when appropriate */
8996 if ( !mData->mCurrentSnapshot
8997 && pSnapshot->i_getId() == aCurSnapshotId
8998 )
8999 mData->mCurrentSnapshot = pSnapshot;
9000
9001 // now create the children
9002 for (settings::SnapshotsList::const_iterator
9003 it = data.llChildSnapshots.begin();
9004 it != data.llChildSnapshots.end();
9005 ++it)
9006 {
9007 const settings::Snapshot &childData = *it;
9008 // recurse
9009 rc = i_loadSnapshot(childData,
9010 aCurSnapshotId,
9011 pSnapshot); // parent = the one we created above
9012 if (FAILED(rc)) return rc;
9013 }
9014
9015 return rc;
9016}
9017
9018/**
9019 * Loads settings into mHWData.
9020 *
9021 * @param puuidRegistry Registry ID.
9022 * @param puuidSnapshot Snapshot ID
9023 * @param data Reference to the hardware settings.
9024 * @param pDbg Pointer to the debugging settings.
9025 * @param pAutostart Pointer to the autostart settings.
9026 */
9027HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9028 const Guid *puuidSnapshot,
9029 const settings::Hardware &data,
9030 const settings::Debugging *pDbg,
9031 const settings::Autostart *pAutostart)
9032{
9033 AssertReturn(!i_isSessionMachine(), E_FAIL);
9034
9035 HRESULT rc = S_OK;
9036
9037 try
9038 {
9039 ComObjPtr<GuestOSType> pGuestOSType;
9040 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9041 pGuestOSType);
9042 if (FAILED(rc))
9043 return rc;
9044
9045 /* The hardware version attribute (optional). */
9046 mHWData->mHWVersion = data.strVersion;
9047 mHWData->mHardwareUUID = data.uuid;
9048
9049 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9050 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9051 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9052 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9053 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9054 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9055 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9056 mHWData->mPAEEnabled = data.fPAE;
9057 mHWData->mLongMode = data.enmLongMode;
9058 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9059 mHWData->mAPIC = data.fAPIC;
9060 mHWData->mX2APIC = data.fX2APIC;
9061 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9062 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9063 mHWData->mSpecCtrl = data.fSpecCtrl;
9064 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9065 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9066 mHWData->mCPUCount = data.cCPUs;
9067 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9068 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9069 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9070 mHWData->mCpuProfile = data.strCpuProfile;
9071
9072 // cpu
9073 if (mHWData->mCPUHotPlugEnabled)
9074 {
9075 for (settings::CpuList::const_iterator
9076 it = data.llCpus.begin();
9077 it != data.llCpus.end();
9078 ++it)
9079 {
9080 const settings::Cpu &cpu = *it;
9081
9082 mHWData->mCPUAttached[cpu.ulId] = true;
9083 }
9084 }
9085
9086 // cpuid leafs
9087 for (settings::CpuIdLeafsList::const_iterator
9088 it = data.llCpuIdLeafs.begin();
9089 it != data.llCpuIdLeafs.end();
9090 ++it)
9091 {
9092 const settings::CpuIdLeaf &rLeaf= *it;
9093 if ( rLeaf.idx < UINT32_C(0x20)
9094 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9095 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9096 mHWData->mCpuIdLeafList.push_back(rLeaf);
9097 /* else: just ignore */
9098 }
9099
9100 mHWData->mMemorySize = data.ulMemorySizeMB;
9101 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9102
9103 // boot order
9104 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9105 {
9106 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9107 if (it == data.mapBootOrder.end())
9108 mHWData->mBootOrder[i] = DeviceType_Null;
9109 else
9110 mHWData->mBootOrder[i] = it->second;
9111 }
9112
9113 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9114 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9115 mHWData->mMonitorCount = data.cMonitors;
9116 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9117 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9118 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9119 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9120 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9121 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9122 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9123 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9124 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9125 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9126 if (!data.strVideoCaptureFile.isEmpty())
9127 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9128 else
9129 mHWData->mVideoCaptureFile.setNull();
9130 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9131 mHWData->mFirmwareType = data.firmwareType;
9132 mHWData->mPointingHIDType = data.pointingHIDType;
9133 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9134 mHWData->mChipsetType = data.chipsetType;
9135 mHWData->mParavirtProvider = data.paravirtProvider;
9136 mHWData->mParavirtDebug = data.strParavirtDebug;
9137 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9138 mHWData->mHPETEnabled = data.fHPETEnabled;
9139
9140 /* VRDEServer */
9141 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9142 if (FAILED(rc)) return rc;
9143
9144 /* BIOS */
9145 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9146 if (FAILED(rc)) return rc;
9147
9148 // Bandwidth control (must come before network adapters)
9149 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9150 if (FAILED(rc)) return rc;
9151
9152 /* Shared folders */
9153 for (settings::USBControllerList::const_iterator
9154 it = data.usbSettings.llUSBControllers.begin();
9155 it != data.usbSettings.llUSBControllers.end();
9156 ++it)
9157 {
9158 const settings::USBController &settingsCtrl = *it;
9159 ComObjPtr<USBController> newCtrl;
9160
9161 newCtrl.createObject();
9162 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9163 mUSBControllers->push_back(newCtrl);
9164 }
9165
9166 /* USB device filters */
9167 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9168 if (FAILED(rc)) return rc;
9169
9170 // network adapters (establish array size first and apply defaults, to
9171 // ensure reading the same settings as we saved, since the list skips
9172 // adapters having defaults)
9173 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9174 size_t oldCount = mNetworkAdapters.size();
9175 if (newCount > oldCount)
9176 {
9177 mNetworkAdapters.resize(newCount);
9178 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9179 {
9180 unconst(mNetworkAdapters[slot]).createObject();
9181 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9182 }
9183 }
9184 else if (newCount < oldCount)
9185 mNetworkAdapters.resize(newCount);
9186 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9187 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9188 for (settings::NetworkAdaptersList::const_iterator
9189 it = data.llNetworkAdapters.begin();
9190 it != data.llNetworkAdapters.end();
9191 ++it)
9192 {
9193 const settings::NetworkAdapter &nic = *it;
9194
9195 /* slot uniqueness is guaranteed by XML Schema */
9196 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9197 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9198 if (FAILED(rc)) return rc;
9199 }
9200
9201 // serial ports (establish defaults first, to ensure reading the same
9202 // settings as we saved, since the list skips ports having defaults)
9203 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9204 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9205 for (settings::SerialPortsList::const_iterator
9206 it = data.llSerialPorts.begin();
9207 it != data.llSerialPorts.end();
9208 ++it)
9209 {
9210 const settings::SerialPort &s = *it;
9211
9212 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9213 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9214 if (FAILED(rc)) return rc;
9215 }
9216
9217 // parallel ports (establish defaults first, to ensure reading the same
9218 // settings as we saved, since the list skips ports having defaults)
9219 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9220 mParallelPorts[i]->i_applyDefaults();
9221 for (settings::ParallelPortsList::const_iterator
9222 it = data.llParallelPorts.begin();
9223 it != data.llParallelPorts.end();
9224 ++it)
9225 {
9226 const settings::ParallelPort &p = *it;
9227
9228 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9229 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9230 if (FAILED(rc)) return rc;
9231 }
9232
9233 /* AudioAdapter */
9234 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9235 if (FAILED(rc)) return rc;
9236
9237 /* storage controllers */
9238 rc = i_loadStorageControllers(data.storage,
9239 puuidRegistry,
9240 puuidSnapshot);
9241 if (FAILED(rc)) return rc;
9242
9243 /* Shared folders */
9244 for (settings::SharedFoldersList::const_iterator
9245 it = data.llSharedFolders.begin();
9246 it != data.llSharedFolders.end();
9247 ++it)
9248 {
9249 const settings::SharedFolder &sf = *it;
9250
9251 ComObjPtr<SharedFolder> sharedFolder;
9252 /* Check for double entries. Not allowed! */
9253 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9254 if (SUCCEEDED(rc))
9255 return setError(VBOX_E_OBJECT_IN_USE,
9256 tr("Shared folder named '%s' already exists"),
9257 sf.strName.c_str());
9258
9259 /* Create the new shared folder. Don't break on error. This will be
9260 * reported when the machine starts. */
9261 sharedFolder.createObject();
9262 rc = sharedFolder->init(i_getMachine(),
9263 sf.strName,
9264 sf.strHostPath,
9265 RT_BOOL(sf.fWritable),
9266 RT_BOOL(sf.fAutoMount),
9267 false /* fFailOnError */);
9268 if (FAILED(rc)) return rc;
9269 mHWData->mSharedFolders.push_back(sharedFolder);
9270 }
9271
9272 // Clipboard
9273 mHWData->mClipboardMode = data.clipboardMode;
9274
9275 // drag'n'drop
9276 mHWData->mDnDMode = data.dndMode;
9277
9278 // guest settings
9279 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9280
9281 // IO settings
9282 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9283 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9284
9285 // Host PCI devices
9286 for (settings::HostPCIDeviceAttachmentList::const_iterator
9287 it = data.pciAttachments.begin();
9288 it != data.pciAttachments.end();
9289 ++it)
9290 {
9291 const settings::HostPCIDeviceAttachment &hpda = *it;
9292 ComObjPtr<PCIDeviceAttachment> pda;
9293
9294 pda.createObject();
9295 pda->i_loadSettings(this, hpda);
9296 mHWData->mPCIDeviceAssignments.push_back(pda);
9297 }
9298
9299 /*
9300 * (The following isn't really real hardware, but it lives in HWData
9301 * for reasons of convenience.)
9302 */
9303
9304#ifdef VBOX_WITH_GUEST_PROPS
9305 /* Guest properties (optional) */
9306
9307 /* Only load transient guest properties for configs which have saved
9308 * state, because there shouldn't be any for powered off VMs. The same
9309 * logic applies for snapshots, as offline snapshots shouldn't have
9310 * any such properties. They confuse the code in various places.
9311 * Note: can't rely on the machine state, as it isn't set yet. */
9312 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9313 /* apologies for the hacky unconst() usage, but this needs hacking
9314 * actually inconsistent settings into consistency, otherwise there
9315 * will be some corner cases where the inconsistency survives
9316 * surprisingly long without getting fixed, especially for snapshots
9317 * as there are no config changes. */
9318 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9319 for (settings::GuestPropertiesList::iterator
9320 it = llGuestProperties.begin();
9321 it != llGuestProperties.end();
9322 /*nothing*/)
9323 {
9324 const settings::GuestProperty &prop = *it;
9325 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9326 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9327 if ( fSkipTransientGuestProperties
9328 && ( fFlags & GUEST_PROP_F_TRANSIENT
9329 || fFlags & GUEST_PROP_F_TRANSRESET))
9330 {
9331 it = llGuestProperties.erase(it);
9332 continue;
9333 }
9334 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9335 mHWData->mGuestProperties[prop.strName] = property;
9336 ++it;
9337 }
9338#endif /* VBOX_WITH_GUEST_PROPS defined */
9339
9340 rc = i_loadDebugging(pDbg);
9341 if (FAILED(rc))
9342 return rc;
9343
9344 mHWData->mAutostart = *pAutostart;
9345
9346 /* default frontend */
9347 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9348 }
9349 catch (std::bad_alloc &)
9350 {
9351 return E_OUTOFMEMORY;
9352 }
9353
9354 AssertComRC(rc);
9355 return rc;
9356}
9357
9358/**
9359 * Called from i_loadHardware() to load the debugging settings of the
9360 * machine.
9361 *
9362 * @param pDbg Pointer to the settings.
9363 */
9364HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9365{
9366 mHWData->mDebugging = *pDbg;
9367 /* no more processing currently required, this will probably change. */
9368 return S_OK;
9369}
9370
9371/**
9372 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9373 *
9374 * @param data storage settings.
9375 * @param puuidRegistry media registry ID to set media to or NULL;
9376 * see Machine::i_loadMachineDataFromSettings()
9377 * @param puuidSnapshot snapshot ID
9378 * @return
9379 */
9380HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9381 const Guid *puuidRegistry,
9382 const Guid *puuidSnapshot)
9383{
9384 AssertReturn(!i_isSessionMachine(), E_FAIL);
9385
9386 HRESULT rc = S_OK;
9387
9388 for (settings::StorageControllersList::const_iterator
9389 it = data.llStorageControllers.begin();
9390 it != data.llStorageControllers.end();
9391 ++it)
9392 {
9393 const settings::StorageController &ctlData = *it;
9394
9395 ComObjPtr<StorageController> pCtl;
9396 /* Try to find one with the name first. */
9397 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9398 if (SUCCEEDED(rc))
9399 return setError(VBOX_E_OBJECT_IN_USE,
9400 tr("Storage controller named '%s' already exists"),
9401 ctlData.strName.c_str());
9402
9403 pCtl.createObject();
9404 rc = pCtl->init(this,
9405 ctlData.strName,
9406 ctlData.storageBus,
9407 ctlData.ulInstance,
9408 ctlData.fBootable);
9409 if (FAILED(rc)) return rc;
9410
9411 mStorageControllers->push_back(pCtl);
9412
9413 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9414 if (FAILED(rc)) return rc;
9415
9416 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9417 if (FAILED(rc)) return rc;
9418
9419 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9420 if (FAILED(rc)) return rc;
9421
9422 /* Load the attached devices now. */
9423 rc = i_loadStorageDevices(pCtl,
9424 ctlData,
9425 puuidRegistry,
9426 puuidSnapshot);
9427 if (FAILED(rc)) return rc;
9428 }
9429
9430 return S_OK;
9431}
9432
9433/**
9434 * Called from i_loadStorageControllers for a controller's devices.
9435 *
9436 * @param aStorageController
9437 * @param data
9438 * @param puuidRegistry media registry ID to set media to or NULL; see
9439 * Machine::i_loadMachineDataFromSettings()
9440 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9441 * @return
9442 */
9443HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9444 const settings::StorageController &data,
9445 const Guid *puuidRegistry,
9446 const Guid *puuidSnapshot)
9447{
9448 HRESULT rc = S_OK;
9449
9450 /* paranoia: detect duplicate attachments */
9451 for (settings::AttachedDevicesList::const_iterator
9452 it = data.llAttachedDevices.begin();
9453 it != data.llAttachedDevices.end();
9454 ++it)
9455 {
9456 const settings::AttachedDevice &ad = *it;
9457
9458 for (settings::AttachedDevicesList::const_iterator it2 = it;
9459 it2 != data.llAttachedDevices.end();
9460 ++it2)
9461 {
9462 if (it == it2)
9463 continue;
9464
9465 const settings::AttachedDevice &ad2 = *it2;
9466
9467 if ( ad.lPort == ad2.lPort
9468 && ad.lDevice == ad2.lDevice)
9469 {
9470 return setError(E_FAIL,
9471 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9472 aStorageController->i_getName().c_str(),
9473 ad.lPort,
9474 ad.lDevice,
9475 mUserData->s.strName.c_str());
9476 }
9477 }
9478 }
9479
9480 for (settings::AttachedDevicesList::const_iterator
9481 it = data.llAttachedDevices.begin();
9482 it != data.llAttachedDevices.end();
9483 ++it)
9484 {
9485 const settings::AttachedDevice &dev = *it;
9486 ComObjPtr<Medium> medium;
9487
9488 switch (dev.deviceType)
9489 {
9490 case DeviceType_Floppy:
9491 case DeviceType_DVD:
9492 if (dev.strHostDriveSrc.isNotEmpty())
9493 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9494 false /* fRefresh */, medium);
9495 else
9496 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9497 dev.uuid,
9498 false /* fRefresh */,
9499 false /* aSetError */,
9500 medium);
9501 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9502 // This is not an error. The host drive or UUID might have vanished, so just go
9503 // ahead without this removeable medium attachment
9504 rc = S_OK;
9505 break;
9506
9507 case DeviceType_HardDisk:
9508 {
9509 /* find a hard disk by UUID */
9510 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9511 if (FAILED(rc))
9512 {
9513 if (i_isSnapshotMachine())
9514 {
9515 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9516 // so the user knows that the bad disk is in a snapshot somewhere
9517 com::ErrorInfo info;
9518 return setError(E_FAIL,
9519 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9520 puuidSnapshot->raw(),
9521 info.getText().raw());
9522 }
9523 else
9524 return rc;
9525 }
9526
9527 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9528
9529 if (medium->i_getType() == MediumType_Immutable)
9530 {
9531 if (i_isSnapshotMachine())
9532 return setError(E_FAIL,
9533 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9534 "of the virtual machine '%s' ('%s')"),
9535 medium->i_getLocationFull().c_str(),
9536 dev.uuid.raw(),
9537 puuidSnapshot->raw(),
9538 mUserData->s.strName.c_str(),
9539 mData->m_strConfigFileFull.c_str());
9540
9541 return setError(E_FAIL,
9542 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9543 medium->i_getLocationFull().c_str(),
9544 dev.uuid.raw(),
9545 mUserData->s.strName.c_str(),
9546 mData->m_strConfigFileFull.c_str());
9547 }
9548
9549 if (medium->i_getType() == MediumType_MultiAttach)
9550 {
9551 if (i_isSnapshotMachine())
9552 return setError(E_FAIL,
9553 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9554 "of the virtual machine '%s' ('%s')"),
9555 medium->i_getLocationFull().c_str(),
9556 dev.uuid.raw(),
9557 puuidSnapshot->raw(),
9558 mUserData->s.strName.c_str(),
9559 mData->m_strConfigFileFull.c_str());
9560
9561 return setError(E_FAIL,
9562 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9563 medium->i_getLocationFull().c_str(),
9564 dev.uuid.raw(),
9565 mUserData->s.strName.c_str(),
9566 mData->m_strConfigFileFull.c_str());
9567 }
9568
9569 if ( !i_isSnapshotMachine()
9570 && medium->i_getChildren().size() != 0
9571 )
9572 return setError(E_FAIL,
9573 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9574 "because it has %d differencing child hard disks"),
9575 medium->i_getLocationFull().c_str(),
9576 dev.uuid.raw(),
9577 mUserData->s.strName.c_str(),
9578 mData->m_strConfigFileFull.c_str(),
9579 medium->i_getChildren().size());
9580
9581 if (i_findAttachment(*mMediumAttachments.data(),
9582 medium))
9583 return setError(E_FAIL,
9584 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9585 medium->i_getLocationFull().c_str(),
9586 dev.uuid.raw(),
9587 mUserData->s.strName.c_str(),
9588 mData->m_strConfigFileFull.c_str());
9589
9590 break;
9591 }
9592
9593 default:
9594 return setError(E_FAIL,
9595 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9596 medium->i_getLocationFull().c_str(),
9597 mUserData->s.strName.c_str(),
9598 mData->m_strConfigFileFull.c_str());
9599 }
9600
9601 if (FAILED(rc))
9602 break;
9603
9604 /* Bandwidth groups are loaded at this point. */
9605 ComObjPtr<BandwidthGroup> pBwGroup;
9606
9607 if (!dev.strBwGroup.isEmpty())
9608 {
9609 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9610 if (FAILED(rc))
9611 return setError(E_FAIL,
9612 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9613 medium->i_getLocationFull().c_str(),
9614 dev.strBwGroup.c_str(),
9615 mUserData->s.strName.c_str(),
9616 mData->m_strConfigFileFull.c_str());
9617 pBwGroup->i_reference();
9618 }
9619
9620 const Utf8Str controllerName = aStorageController->i_getName();
9621 ComObjPtr<MediumAttachment> pAttachment;
9622 pAttachment.createObject();
9623 rc = pAttachment->init(this,
9624 medium,
9625 controllerName,
9626 dev.lPort,
9627 dev.lDevice,
9628 dev.deviceType,
9629 false,
9630 dev.fPassThrough,
9631 dev.fTempEject,
9632 dev.fNonRotational,
9633 dev.fDiscard,
9634 dev.fHotPluggable,
9635 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9636 if (FAILED(rc)) break;
9637
9638 /* associate the medium with this machine and snapshot */
9639 if (!medium.isNull())
9640 {
9641 AutoCaller medCaller(medium);
9642 if (FAILED(medCaller.rc())) return medCaller.rc();
9643 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9644
9645 if (i_isSnapshotMachine())
9646 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9647 else
9648 rc = medium->i_addBackReference(mData->mUuid);
9649 /* If the medium->addBackReference fails it sets an appropriate
9650 * error message, so no need to do any guesswork here. */
9651
9652 if (puuidRegistry)
9653 // caller wants registry ID to be set on all attached media (OVF import case)
9654 medium->i_addRegistry(*puuidRegistry);
9655 }
9656
9657 if (FAILED(rc))
9658 break;
9659
9660 /* back up mMediumAttachments to let registeredInit() properly rollback
9661 * on failure (= limited accessibility) */
9662 i_setModified(IsModified_Storage);
9663 mMediumAttachments.backup();
9664 mMediumAttachments->push_back(pAttachment);
9665 }
9666
9667 return rc;
9668}
9669
9670/**
9671 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9672 *
9673 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9674 * @param aSnapshot where to return the found snapshot
9675 * @param aSetError true to set extended error info on failure
9676 */
9677HRESULT Machine::i_findSnapshotById(const Guid &aId,
9678 ComObjPtr<Snapshot> &aSnapshot,
9679 bool aSetError /* = false */)
9680{
9681 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9682
9683 if (!mData->mFirstSnapshot)
9684 {
9685 if (aSetError)
9686 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9687 return E_FAIL;
9688 }
9689
9690 if (aId.isZero())
9691 aSnapshot = mData->mFirstSnapshot;
9692 else
9693 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9694
9695 if (!aSnapshot)
9696 {
9697 if (aSetError)
9698 return setError(E_FAIL,
9699 tr("Could not find a snapshot with UUID {%s}"),
9700 aId.toString().c_str());
9701 return E_FAIL;
9702 }
9703
9704 return S_OK;
9705}
9706
9707/**
9708 * Returns the snapshot with the given name or fails of no such snapshot.
9709 *
9710 * @param strName snapshot name to find
9711 * @param aSnapshot where to return the found snapshot
9712 * @param aSetError true to set extended error info on failure
9713 */
9714HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9715 ComObjPtr<Snapshot> &aSnapshot,
9716 bool aSetError /* = false */)
9717{
9718 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9719
9720 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9721
9722 if (!mData->mFirstSnapshot)
9723 {
9724 if (aSetError)
9725 return setError(VBOX_E_OBJECT_NOT_FOUND,
9726 tr("This machine does not have any snapshots"));
9727 return VBOX_E_OBJECT_NOT_FOUND;
9728 }
9729
9730 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9731
9732 if (!aSnapshot)
9733 {
9734 if (aSetError)
9735 return setError(VBOX_E_OBJECT_NOT_FOUND,
9736 tr("Could not find a snapshot named '%s'"), strName.c_str());
9737 return VBOX_E_OBJECT_NOT_FOUND;
9738 }
9739
9740 return S_OK;
9741}
9742
9743/**
9744 * Returns a storage controller object with the given name.
9745 *
9746 * @param aName storage controller name to find
9747 * @param aStorageController where to return the found storage controller
9748 * @param aSetError true to set extended error info on failure
9749 */
9750HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9751 ComObjPtr<StorageController> &aStorageController,
9752 bool aSetError /* = false */)
9753{
9754 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9755
9756 for (StorageControllerList::const_iterator
9757 it = mStorageControllers->begin();
9758 it != mStorageControllers->end();
9759 ++it)
9760 {
9761 if ((*it)->i_getName() == aName)
9762 {
9763 aStorageController = (*it);
9764 return S_OK;
9765 }
9766 }
9767
9768 if (aSetError)
9769 return setError(VBOX_E_OBJECT_NOT_FOUND,
9770 tr("Could not find a storage controller named '%s'"),
9771 aName.c_str());
9772 return VBOX_E_OBJECT_NOT_FOUND;
9773}
9774
9775/**
9776 * Returns a USB controller object with the given name.
9777 *
9778 * @param aName USB controller name to find
9779 * @param aUSBController where to return the found USB controller
9780 * @param aSetError true to set extended error info on failure
9781 */
9782HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9783 ComObjPtr<USBController> &aUSBController,
9784 bool aSetError /* = false */)
9785{
9786 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9787
9788 for (USBControllerList::const_iterator
9789 it = mUSBControllers->begin();
9790 it != mUSBControllers->end();
9791 ++it)
9792 {
9793 if ((*it)->i_getName() == aName)
9794 {
9795 aUSBController = (*it);
9796 return S_OK;
9797 }
9798 }
9799
9800 if (aSetError)
9801 return setError(VBOX_E_OBJECT_NOT_FOUND,
9802 tr("Could not find a storage controller named '%s'"),
9803 aName.c_str());
9804 return VBOX_E_OBJECT_NOT_FOUND;
9805}
9806
9807/**
9808 * Returns the number of USB controller instance of the given type.
9809 *
9810 * @param enmType USB controller type.
9811 */
9812ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9813{
9814 ULONG cCtrls = 0;
9815
9816 for (USBControllerList::const_iterator
9817 it = mUSBControllers->begin();
9818 it != mUSBControllers->end();
9819 ++it)
9820 {
9821 if ((*it)->i_getControllerType() == enmType)
9822 cCtrls++;
9823 }
9824
9825 return cCtrls;
9826}
9827
9828HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9829 MediumAttachmentList &atts)
9830{
9831 AutoCaller autoCaller(this);
9832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9833
9834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9835
9836 for (MediumAttachmentList::const_iterator
9837 it = mMediumAttachments->begin();
9838 it != mMediumAttachments->end();
9839 ++it)
9840 {
9841 const ComObjPtr<MediumAttachment> &pAtt = *it;
9842 // should never happen, but deal with NULL pointers in the list.
9843 AssertContinue(!pAtt.isNull());
9844
9845 // getControllerName() needs caller+read lock
9846 AutoCaller autoAttCaller(pAtt);
9847 if (FAILED(autoAttCaller.rc()))
9848 {
9849 atts.clear();
9850 return autoAttCaller.rc();
9851 }
9852 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9853
9854 if (pAtt->i_getControllerName() == aName)
9855 atts.push_back(pAtt);
9856 }
9857
9858 return S_OK;
9859}
9860
9861
9862/**
9863 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9864 * file if the machine name was changed and about creating a new settings file
9865 * if this is a new machine.
9866 *
9867 * @note Must be never called directly but only from #saveSettings().
9868 */
9869HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9870{
9871 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9872
9873 HRESULT rc = S_OK;
9874
9875 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9876
9877 /// @todo need to handle primary group change, too
9878
9879 /* attempt to rename the settings file if machine name is changed */
9880 if ( mUserData->s.fNameSync
9881 && mUserData.isBackedUp()
9882 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9883 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9884 )
9885 {
9886 bool dirRenamed = false;
9887 bool fileRenamed = false;
9888
9889 Utf8Str configFile, newConfigFile;
9890 Utf8Str configFilePrev, newConfigFilePrev;
9891 Utf8Str configDir, newConfigDir;
9892
9893 do
9894 {
9895 int vrc = VINF_SUCCESS;
9896
9897 Utf8Str name = mUserData.backedUpData()->s.strName;
9898 Utf8Str newName = mUserData->s.strName;
9899 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9900 if (group == "/")
9901 group.setNull();
9902 Utf8Str newGroup = mUserData->s.llGroups.front();
9903 if (newGroup == "/")
9904 newGroup.setNull();
9905
9906 configFile = mData->m_strConfigFileFull;
9907
9908 /* first, rename the directory if it matches the group and machine name */
9909 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9910 group.c_str(), RTPATH_DELIMITER, name.c_str());
9911 /** @todo hack, make somehow use of ComposeMachineFilename */
9912 if (mUserData->s.fDirectoryIncludesUUID)
9913 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9914 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9915 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9916 /** @todo hack, make somehow use of ComposeMachineFilename */
9917 if (mUserData->s.fDirectoryIncludesUUID)
9918 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9919 configDir = configFile;
9920 configDir.stripFilename();
9921 newConfigDir = configDir;
9922 if ( configDir.length() >= groupPlusName.length()
9923 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9924 groupPlusName.c_str()))
9925 {
9926 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9927 Utf8Str newConfigBaseDir(newConfigDir);
9928 newConfigDir.append(newGroupPlusName);
9929 /* consistency: use \ if appropriate on the platform */
9930 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9931 /* new dir and old dir cannot be equal here because of 'if'
9932 * above and because name != newName */
9933 Assert(configDir != newConfigDir);
9934 if (!fSettingsFileIsNew)
9935 {
9936 /* perform real rename only if the machine is not new */
9937 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9938 if ( vrc == VERR_FILE_NOT_FOUND
9939 || vrc == VERR_PATH_NOT_FOUND)
9940 {
9941 /* create the parent directory, then retry renaming */
9942 Utf8Str parent(newConfigDir);
9943 parent.stripFilename();
9944 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9945 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9946 }
9947 if (RT_FAILURE(vrc))
9948 {
9949 rc = setError(E_FAIL,
9950 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9951 configDir.c_str(),
9952 newConfigDir.c_str(),
9953 vrc);
9954 break;
9955 }
9956 /* delete subdirectories which are no longer needed */
9957 Utf8Str dir(configDir);
9958 dir.stripFilename();
9959 while (dir != newConfigBaseDir && dir != ".")
9960 {
9961 vrc = RTDirRemove(dir.c_str());
9962 if (RT_FAILURE(vrc))
9963 break;
9964 dir.stripFilename();
9965 }
9966 dirRenamed = true;
9967 }
9968 }
9969
9970 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9971 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9972
9973 /* then try to rename the settings file itself */
9974 if (newConfigFile != configFile)
9975 {
9976 /* get the path to old settings file in renamed directory */
9977 configFile = Utf8StrFmt("%s%c%s",
9978 newConfigDir.c_str(),
9979 RTPATH_DELIMITER,
9980 RTPathFilename(configFile.c_str()));
9981 if (!fSettingsFileIsNew)
9982 {
9983 /* perform real rename only if the machine is not new */
9984 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9985 if (RT_FAILURE(vrc))
9986 {
9987 rc = setError(E_FAIL,
9988 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9989 configFile.c_str(),
9990 newConfigFile.c_str(),
9991 vrc);
9992 break;
9993 }
9994 fileRenamed = true;
9995 configFilePrev = configFile;
9996 configFilePrev += "-prev";
9997 newConfigFilePrev = newConfigFile;
9998 newConfigFilePrev += "-prev";
9999 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10000 }
10001 }
10002
10003 // update m_strConfigFileFull amd mConfigFile
10004 mData->m_strConfigFileFull = newConfigFile;
10005 // compute the relative path too
10006 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10007
10008 // store the old and new so that VirtualBox::i_saveSettings() can update
10009 // the media registry
10010 if ( mData->mRegistered
10011 && (configDir != newConfigDir || configFile != newConfigFile))
10012 {
10013 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10014
10015 if (pfNeedsGlobalSaveSettings)
10016 *pfNeedsGlobalSaveSettings = true;
10017 }
10018
10019 // in the saved state file path, replace the old directory with the new directory
10020 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10021 {
10022 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10023 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10024 }
10025
10026 // and do the same thing for the saved state file paths of all the online snapshots
10027 if (mData->mFirstSnapshot)
10028 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10029 newConfigDir.c_str());
10030 }
10031 while (0);
10032
10033 if (FAILED(rc))
10034 {
10035 /* silently try to rename everything back */
10036 if (fileRenamed)
10037 {
10038 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10039 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10040 }
10041 if (dirRenamed)
10042 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10043 }
10044
10045 if (FAILED(rc)) return rc;
10046 }
10047
10048 if (fSettingsFileIsNew)
10049 {
10050 /* create a virgin config file */
10051 int vrc = VINF_SUCCESS;
10052
10053 /* ensure the settings directory exists */
10054 Utf8Str path(mData->m_strConfigFileFull);
10055 path.stripFilename();
10056 if (!RTDirExists(path.c_str()))
10057 {
10058 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10059 if (RT_FAILURE(vrc))
10060 {
10061 return setError(E_FAIL,
10062 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10063 path.c_str(),
10064 vrc);
10065 }
10066 }
10067
10068 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10069 path = Utf8Str(mData->m_strConfigFileFull);
10070 RTFILE f = NIL_RTFILE;
10071 vrc = RTFileOpen(&f, path.c_str(),
10072 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10073 if (RT_FAILURE(vrc))
10074 return setError(E_FAIL,
10075 tr("Could not create the settings file '%s' (%Rrc)"),
10076 path.c_str(),
10077 vrc);
10078 RTFileClose(f);
10079 }
10080
10081 return rc;
10082}
10083
10084/**
10085 * Saves and commits machine data, user data and hardware data.
10086 *
10087 * Note that on failure, the data remains uncommitted.
10088 *
10089 * @a aFlags may combine the following flags:
10090 *
10091 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10092 * Used when saving settings after an operation that makes them 100%
10093 * correspond to the settings from the current snapshot.
10094 * - SaveS_Force: settings will be saved without doing a deep compare of the
10095 * settings structures. This is used when this is called because snapshots
10096 * have changed to avoid the overhead of the deep compare.
10097 *
10098 * @note Must be called from under this object's write lock. Locks children for
10099 * writing.
10100 *
10101 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10102 * initialized to false and that will be set to true by this function if
10103 * the caller must invoke VirtualBox::i_saveSettings() because the global
10104 * settings have changed. This will happen if a machine rename has been
10105 * saved and the global machine and media registries will therefore need
10106 * updating.
10107 * @param aFlags Flags.
10108 */
10109HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10110 int aFlags /*= 0*/)
10111{
10112 LogFlowThisFuncEnter();
10113
10114 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10115
10116 /* make sure child objects are unable to modify the settings while we are
10117 * saving them */
10118 i_ensureNoStateDependencies();
10119
10120 AssertReturn(!i_isSnapshotMachine(),
10121 E_FAIL);
10122
10123 HRESULT rc = S_OK;
10124 bool fNeedsWrite = false;
10125
10126 /* First, prepare to save settings. It will care about renaming the
10127 * settings directory and file if the machine name was changed and about
10128 * creating a new settings file if this is a new machine. */
10129 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10130 if (FAILED(rc)) return rc;
10131
10132 // keep a pointer to the current settings structures
10133 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10134 settings::MachineConfigFile *pNewConfig = NULL;
10135
10136 try
10137 {
10138 // make a fresh one to have everyone write stuff into
10139 pNewConfig = new settings::MachineConfigFile(NULL);
10140 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10141
10142 // now go and copy all the settings data from COM to the settings structures
10143 // (this calls i_saveSettings() on all the COM objects in the machine)
10144 i_copyMachineDataToSettings(*pNewConfig);
10145
10146 if (aFlags & SaveS_ResetCurStateModified)
10147 {
10148 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10149 mData->mCurrentStateModified = FALSE;
10150 fNeedsWrite = true; // always, no need to compare
10151 }
10152 else if (aFlags & SaveS_Force)
10153 {
10154 fNeedsWrite = true; // always, no need to compare
10155 }
10156 else
10157 {
10158 if (!mData->mCurrentStateModified)
10159 {
10160 // do a deep compare of the settings that we just saved with the settings
10161 // previously stored in the config file; this invokes MachineConfigFile::operator==
10162 // which does a deep compare of all the settings, which is expensive but less expensive
10163 // than writing out XML in vain
10164 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10165
10166 // could still be modified if any settings changed
10167 mData->mCurrentStateModified = fAnySettingsChanged;
10168
10169 fNeedsWrite = fAnySettingsChanged;
10170 }
10171 else
10172 fNeedsWrite = true;
10173 }
10174
10175 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10176
10177 if (fNeedsWrite)
10178 // now spit it all out!
10179 pNewConfig->write(mData->m_strConfigFileFull);
10180
10181 mData->pMachineConfigFile = pNewConfig;
10182 delete pOldConfig;
10183 i_commit();
10184
10185 // after saving settings, we are no longer different from the XML on disk
10186 mData->flModifications = 0;
10187 }
10188 catch (HRESULT err)
10189 {
10190 // we assume that error info is set by the thrower
10191 rc = err;
10192
10193 // restore old config
10194 delete pNewConfig;
10195 mData->pMachineConfigFile = pOldConfig;
10196 }
10197 catch (...)
10198 {
10199 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10200 }
10201
10202 if (fNeedsWrite)
10203 {
10204 /* Fire the data change event, even on failure (since we've already
10205 * committed all data). This is done only for SessionMachines because
10206 * mutable Machine instances are always not registered (i.e. private
10207 * to the client process that creates them) and thus don't need to
10208 * inform callbacks. */
10209 if (i_isSessionMachine())
10210 mParent->i_onMachineDataChange(mData->mUuid);
10211 }
10212
10213 LogFlowThisFunc(("rc=%08X\n", rc));
10214 LogFlowThisFuncLeave();
10215 return rc;
10216}
10217
10218/**
10219 * Implementation for saving the machine settings into the given
10220 * settings::MachineConfigFile instance. This copies machine extradata
10221 * from the previous machine config file in the instance data, if any.
10222 *
10223 * This gets called from two locations:
10224 *
10225 * -- Machine::i_saveSettings(), during the regular XML writing;
10226 *
10227 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10228 * exported to OVF and we write the VirtualBox proprietary XML
10229 * into a <vbox:Machine> tag.
10230 *
10231 * This routine fills all the fields in there, including snapshots, *except*
10232 * for the following:
10233 *
10234 * -- fCurrentStateModified. There is some special logic associated with that.
10235 *
10236 * The caller can then call MachineConfigFile::write() or do something else
10237 * with it.
10238 *
10239 * Caller must hold the machine lock!
10240 *
10241 * This throws XML errors and HRESULT, so the caller must have a catch block!
10242 */
10243void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10244{
10245 // deep copy extradata, being extra careful with self assignment (the STL
10246 // map assignment on Mac OS X clang based Xcode isn't checking)
10247 if (&config != mData->pMachineConfigFile)
10248 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10249
10250 config.uuid = mData->mUuid;
10251
10252 // copy name, description, OS type, teleport, UTC etc.
10253 config.machineUserData = mUserData->s;
10254
10255 if ( mData->mMachineState == MachineState_Saved
10256 || mData->mMachineState == MachineState_Restoring
10257 // when doing certain snapshot operations we may or may not have
10258 // a saved state in the current state, so keep everything as is
10259 || ( ( mData->mMachineState == MachineState_Snapshotting
10260 || mData->mMachineState == MachineState_DeletingSnapshot
10261 || mData->mMachineState == MachineState_RestoringSnapshot)
10262 && (!mSSData->strStateFilePath.isEmpty())
10263 )
10264 )
10265 {
10266 Assert(!mSSData->strStateFilePath.isEmpty());
10267 /* try to make the file name relative to the settings file dir */
10268 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10269 }
10270 else
10271 {
10272 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10273 config.strStateFile.setNull();
10274 }
10275
10276 if (mData->mCurrentSnapshot)
10277 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10278 else
10279 config.uuidCurrentSnapshot.clear();
10280
10281 config.timeLastStateChange = mData->mLastStateChange;
10282 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10283 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10284
10285 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10286 if (FAILED(rc)) throw rc;
10287
10288 // save machine's media registry if this is VirtualBox 4.0 or later
10289 if (config.canHaveOwnMediaRegistry())
10290 {
10291 // determine machine folder
10292 Utf8Str strMachineFolder = i_getSettingsFileFull();
10293 strMachineFolder.stripFilename();
10294 mParent->i_saveMediaRegistry(config.mediaRegistry,
10295 i_getId(), // only media with registry ID == machine UUID
10296 strMachineFolder);
10297 // this throws HRESULT
10298 }
10299
10300 // save snapshots
10301 rc = i_saveAllSnapshots(config);
10302 if (FAILED(rc)) throw rc;
10303}
10304
10305/**
10306 * Saves all snapshots of the machine into the given machine config file. Called
10307 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10308 * @param config
10309 * @return
10310 */
10311HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10312{
10313 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10314
10315 HRESULT rc = S_OK;
10316
10317 try
10318 {
10319 config.llFirstSnapshot.clear();
10320
10321 if (mData->mFirstSnapshot)
10322 {
10323 // the settings use a list for "the first snapshot"
10324 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10325
10326 // get reference to the snapshot on the list and work on that
10327 // element straight in the list to avoid excessive copying later
10328 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10329 if (FAILED(rc)) throw rc;
10330 }
10331
10332// if (mType == IsSessionMachine)
10333// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10334
10335 }
10336 catch (HRESULT err)
10337 {
10338 /* we assume that error info is set by the thrower */
10339 rc = err;
10340 }
10341 catch (...)
10342 {
10343 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10344 }
10345
10346 return rc;
10347}
10348
10349/**
10350 * Saves the VM hardware configuration. It is assumed that the
10351 * given node is empty.
10352 *
10353 * @param data Reference to the settings object for the hardware config.
10354 * @param pDbg Pointer to the settings object for the debugging config
10355 * which happens to live in mHWData.
10356 * @param pAutostart Pointer to the settings object for the autostart config
10357 * which happens to live in mHWData.
10358 */
10359HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10360 settings::Autostart *pAutostart)
10361{
10362 HRESULT rc = S_OK;
10363
10364 try
10365 {
10366 /* The hardware version attribute (optional).
10367 Automatically upgrade from 1 to current default hardware version
10368 when there is no saved state. (ugly!) */
10369 if ( mHWData->mHWVersion == "1"
10370 && mSSData->strStateFilePath.isEmpty()
10371 )
10372 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10373
10374 data.strVersion = mHWData->mHWVersion;
10375 data.uuid = mHWData->mHardwareUUID;
10376
10377 // CPU
10378 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10379 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10380 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10381 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10382 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10383 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10384 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10385 data.fPAE = !!mHWData->mPAEEnabled;
10386 data.enmLongMode = mHWData->mLongMode;
10387 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10388 data.fAPIC = !!mHWData->mAPIC;
10389 data.fX2APIC = !!mHWData->mX2APIC;
10390 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10391 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10392 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10393 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10394 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10395 data.cCPUs = mHWData->mCPUCount;
10396 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10397 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10398 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10399 data.strCpuProfile = mHWData->mCpuProfile;
10400
10401 data.llCpus.clear();
10402 if (data.fCpuHotPlug)
10403 {
10404 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10405 {
10406 if (mHWData->mCPUAttached[idx])
10407 {
10408 settings::Cpu cpu;
10409 cpu.ulId = idx;
10410 data.llCpus.push_back(cpu);
10411 }
10412 }
10413 }
10414
10415 /* Standard and Extended CPUID leafs. */
10416 data.llCpuIdLeafs.clear();
10417 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10418
10419 // memory
10420 data.ulMemorySizeMB = mHWData->mMemorySize;
10421 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10422
10423 // firmware
10424 data.firmwareType = mHWData->mFirmwareType;
10425
10426 // HID
10427 data.pointingHIDType = mHWData->mPointingHIDType;
10428 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10429
10430 // chipset
10431 data.chipsetType = mHWData->mChipsetType;
10432
10433 // paravirt
10434 data.paravirtProvider = mHWData->mParavirtProvider;
10435 data.strParavirtDebug = mHWData->mParavirtDebug;
10436
10437 // emulated USB card reader
10438 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10439
10440 // HPET
10441 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10442
10443 // boot order
10444 data.mapBootOrder.clear();
10445 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10446 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10447
10448 // display
10449 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10450 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10451 data.cMonitors = mHWData->mMonitorCount;
10452 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10453 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10454 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10455 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10456 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10457 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10458 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10459 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10460 {
10461 if (mHWData->maVideoCaptureScreens[i])
10462 ASMBitSet(&data.u64VideoCaptureScreens, i);
10463 else
10464 ASMBitClear(&data.u64VideoCaptureScreens, i);
10465 }
10466 /* store relative video capture file if possible */
10467 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10468 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10469
10470 /* VRDEServer settings (optional) */
10471 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10472 if (FAILED(rc)) throw rc;
10473
10474 /* BIOS (required) */
10475 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10476 if (FAILED(rc)) throw rc;
10477
10478 /* USB Controller (required) */
10479 data.usbSettings.llUSBControllers.clear();
10480 for (USBControllerList::const_iterator
10481 it = mUSBControllers->begin();
10482 it != mUSBControllers->end();
10483 ++it)
10484 {
10485 ComObjPtr<USBController> ctrl = *it;
10486 settings::USBController settingsCtrl;
10487
10488 settingsCtrl.strName = ctrl->i_getName();
10489 settingsCtrl.enmType = ctrl->i_getControllerType();
10490
10491 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10492 }
10493
10494 /* USB device filters (required) */
10495 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10496 if (FAILED(rc)) throw rc;
10497
10498 /* Network adapters (required) */
10499 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10500 data.llNetworkAdapters.clear();
10501 /* Write out only the nominal number of network adapters for this
10502 * chipset type. Since Machine::commit() hasn't been called there
10503 * may be extra NIC settings in the vector. */
10504 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10505 {
10506 settings::NetworkAdapter nic;
10507 nic.ulSlot = (uint32_t)slot;
10508 /* paranoia check... must not be NULL, but must not crash either. */
10509 if (mNetworkAdapters[slot])
10510 {
10511 if (mNetworkAdapters[slot]->i_hasDefaults())
10512 continue;
10513
10514 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10515 if (FAILED(rc)) throw rc;
10516
10517 data.llNetworkAdapters.push_back(nic);
10518 }
10519 }
10520
10521 /* Serial ports */
10522 data.llSerialPorts.clear();
10523 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10524 {
10525 if (mSerialPorts[slot]->i_hasDefaults())
10526 continue;
10527
10528 settings::SerialPort s;
10529 s.ulSlot = slot;
10530 rc = mSerialPorts[slot]->i_saveSettings(s);
10531 if (FAILED(rc)) return rc;
10532
10533 data.llSerialPorts.push_back(s);
10534 }
10535
10536 /* Parallel ports */
10537 data.llParallelPorts.clear();
10538 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10539 {
10540 if (mParallelPorts[slot]->i_hasDefaults())
10541 continue;
10542
10543 settings::ParallelPort p;
10544 p.ulSlot = slot;
10545 rc = mParallelPorts[slot]->i_saveSettings(p);
10546 if (FAILED(rc)) return rc;
10547
10548 data.llParallelPorts.push_back(p);
10549 }
10550
10551 /* Audio adapter */
10552 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10553 if (FAILED(rc)) return rc;
10554
10555 rc = i_saveStorageControllers(data.storage);
10556 if (FAILED(rc)) return rc;
10557
10558 /* Shared folders */
10559 data.llSharedFolders.clear();
10560 for (HWData::SharedFolderList::const_iterator
10561 it = mHWData->mSharedFolders.begin();
10562 it != mHWData->mSharedFolders.end();
10563 ++it)
10564 {
10565 SharedFolder *pSF = *it;
10566 AutoCaller sfCaller(pSF);
10567 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10568 settings::SharedFolder sf;
10569 sf.strName = pSF->i_getName();
10570 sf.strHostPath = pSF->i_getHostPath();
10571 sf.fWritable = !!pSF->i_isWritable();
10572 sf.fAutoMount = !!pSF->i_isAutoMounted();
10573
10574 data.llSharedFolders.push_back(sf);
10575 }
10576
10577 // clipboard
10578 data.clipboardMode = mHWData->mClipboardMode;
10579
10580 // drag'n'drop
10581 data.dndMode = mHWData->mDnDMode;
10582
10583 /* Guest */
10584 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10585
10586 // IO settings
10587 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10588 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10589
10590 /* BandwidthControl (required) */
10591 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10592 if (FAILED(rc)) throw rc;
10593
10594 /* Host PCI devices */
10595 data.pciAttachments.clear();
10596 for (HWData::PCIDeviceAssignmentList::const_iterator
10597 it = mHWData->mPCIDeviceAssignments.begin();
10598 it != mHWData->mPCIDeviceAssignments.end();
10599 ++it)
10600 {
10601 ComObjPtr<PCIDeviceAttachment> pda = *it;
10602 settings::HostPCIDeviceAttachment hpda;
10603
10604 rc = pda->i_saveSettings(hpda);
10605 if (FAILED(rc)) throw rc;
10606
10607 data.pciAttachments.push_back(hpda);
10608 }
10609
10610 // guest properties
10611 data.llGuestProperties.clear();
10612#ifdef VBOX_WITH_GUEST_PROPS
10613 for (HWData::GuestPropertyMap::const_iterator
10614 it = mHWData->mGuestProperties.begin();
10615 it != mHWData->mGuestProperties.end();
10616 ++it)
10617 {
10618 HWData::GuestProperty property = it->second;
10619
10620 /* Remove transient guest properties at shutdown unless we
10621 * are saving state. Note that restoring snapshot intentionally
10622 * keeps them, they will be removed if appropriate once the final
10623 * machine state is set (as crashes etc. need to work). */
10624 if ( ( mData->mMachineState == MachineState_PoweredOff
10625 || mData->mMachineState == MachineState_Aborted
10626 || mData->mMachineState == MachineState_Teleported)
10627 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10628 continue;
10629 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10630 prop.strName = it->first;
10631 prop.strValue = property.strValue;
10632 prop.timestamp = property.mTimestamp;
10633 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10634 GuestPropWriteFlags(property.mFlags, szFlags);
10635 prop.strFlags = szFlags;
10636
10637 data.llGuestProperties.push_back(prop);
10638 }
10639
10640 /* I presume this doesn't require a backup(). */
10641 mData->mGuestPropertiesModified = FALSE;
10642#endif /* VBOX_WITH_GUEST_PROPS defined */
10643
10644 *pDbg = mHWData->mDebugging;
10645 *pAutostart = mHWData->mAutostart;
10646
10647 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10648 }
10649 catch (std::bad_alloc &)
10650 {
10651 return E_OUTOFMEMORY;
10652 }
10653
10654 AssertComRC(rc);
10655 return rc;
10656}
10657
10658/**
10659 * Saves the storage controller configuration.
10660 *
10661 * @param data storage settings.
10662 */
10663HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10664{
10665 data.llStorageControllers.clear();
10666
10667 for (StorageControllerList::const_iterator
10668 it = mStorageControllers->begin();
10669 it != mStorageControllers->end();
10670 ++it)
10671 {
10672 HRESULT rc;
10673 ComObjPtr<StorageController> pCtl = *it;
10674
10675 settings::StorageController ctl;
10676 ctl.strName = pCtl->i_getName();
10677 ctl.controllerType = pCtl->i_getControllerType();
10678 ctl.storageBus = pCtl->i_getStorageBus();
10679 ctl.ulInstance = pCtl->i_getInstance();
10680 ctl.fBootable = pCtl->i_getBootable();
10681
10682 /* Save the port count. */
10683 ULONG portCount;
10684 rc = pCtl->COMGETTER(PortCount)(&portCount);
10685 ComAssertComRCRet(rc, rc);
10686 ctl.ulPortCount = portCount;
10687
10688 /* Save fUseHostIOCache */
10689 BOOL fUseHostIOCache;
10690 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10691 ComAssertComRCRet(rc, rc);
10692 ctl.fUseHostIOCache = !!fUseHostIOCache;
10693
10694 /* save the devices now. */
10695 rc = i_saveStorageDevices(pCtl, ctl);
10696 ComAssertComRCRet(rc, rc);
10697
10698 data.llStorageControllers.push_back(ctl);
10699 }
10700
10701 return S_OK;
10702}
10703
10704/**
10705 * Saves the hard disk configuration.
10706 */
10707HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10708 settings::StorageController &data)
10709{
10710 MediumAttachmentList atts;
10711
10712 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10713 if (FAILED(rc)) return rc;
10714
10715 data.llAttachedDevices.clear();
10716 for (MediumAttachmentList::const_iterator
10717 it = atts.begin();
10718 it != atts.end();
10719 ++it)
10720 {
10721 settings::AttachedDevice dev;
10722 IMediumAttachment *iA = *it;
10723 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10724 Medium *pMedium = pAttach->i_getMedium();
10725
10726 dev.deviceType = pAttach->i_getType();
10727 dev.lPort = pAttach->i_getPort();
10728 dev.lDevice = pAttach->i_getDevice();
10729 dev.fPassThrough = pAttach->i_getPassthrough();
10730 dev.fHotPluggable = pAttach->i_getHotPluggable();
10731 if (pMedium)
10732 {
10733 if (pMedium->i_isHostDrive())
10734 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10735 else
10736 dev.uuid = pMedium->i_getId();
10737 dev.fTempEject = pAttach->i_getTempEject();
10738 dev.fNonRotational = pAttach->i_getNonRotational();
10739 dev.fDiscard = pAttach->i_getDiscard();
10740 }
10741
10742 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10743
10744 data.llAttachedDevices.push_back(dev);
10745 }
10746
10747 return S_OK;
10748}
10749
10750/**
10751 * Saves machine state settings as defined by aFlags
10752 * (SaveSTS_* values).
10753 *
10754 * @param aFlags Combination of SaveSTS_* flags.
10755 *
10756 * @note Locks objects for writing.
10757 */
10758HRESULT Machine::i_saveStateSettings(int aFlags)
10759{
10760 if (aFlags == 0)
10761 return S_OK;
10762
10763 AutoCaller autoCaller(this);
10764 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10765
10766 /* This object's write lock is also necessary to serialize file access
10767 * (prevent concurrent reads and writes) */
10768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10769
10770 HRESULT rc = S_OK;
10771
10772 Assert(mData->pMachineConfigFile);
10773
10774 try
10775 {
10776 if (aFlags & SaveSTS_CurStateModified)
10777 mData->pMachineConfigFile->fCurrentStateModified = true;
10778
10779 if (aFlags & SaveSTS_StateFilePath)
10780 {
10781 if (!mSSData->strStateFilePath.isEmpty())
10782 /* try to make the file name relative to the settings file dir */
10783 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10784 else
10785 mData->pMachineConfigFile->strStateFile.setNull();
10786 }
10787
10788 if (aFlags & SaveSTS_StateTimeStamp)
10789 {
10790 Assert( mData->mMachineState != MachineState_Aborted
10791 || mSSData->strStateFilePath.isEmpty());
10792
10793 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10794
10795 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10796/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10797 }
10798
10799 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10800 }
10801 catch (...)
10802 {
10803 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10804 }
10805
10806 return rc;
10807}
10808
10809/**
10810 * Ensures that the given medium is added to a media registry. If this machine
10811 * was created with 4.0 or later, then the machine registry is used. Otherwise
10812 * the global VirtualBox media registry is used.
10813 *
10814 * Caller must NOT hold machine lock, media tree or any medium locks!
10815 *
10816 * @param pMedium
10817 */
10818void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10819{
10820 /* Paranoia checks: do not hold machine or media tree locks. */
10821 AssertReturnVoid(!isWriteLockOnCurrentThread());
10822 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10823
10824 ComObjPtr<Medium> pBase;
10825 {
10826 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10827 pBase = pMedium->i_getBase();
10828 }
10829
10830 /* Paranoia checks: do not hold medium locks. */
10831 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10832 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10833
10834 // decide which medium registry to use now that the medium is attached:
10835 Guid uuid;
10836 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10837 if (fCanHaveOwnMediaRegistry)
10838 // machine XML is VirtualBox 4.0 or higher:
10839 uuid = i_getId(); // machine UUID
10840 else
10841 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10842
10843 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10844 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10845 if (pMedium->i_addRegistry(uuid))
10846 mParent->i_markRegistryModified(uuid);
10847
10848 /* For more complex hard disk structures it can happen that the base
10849 * medium isn't yet associated with any medium registry. Do that now. */
10850 if (pMedium != pBase)
10851 {
10852 /* Tree lock needed by Medium::addRegistry when recursing. */
10853 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10854 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10855 {
10856 treeLock.release();
10857 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10858 treeLock.acquire();
10859 }
10860 if (pBase->i_addRegistryRecursive(uuid))
10861 {
10862 treeLock.release();
10863 mParent->i_markRegistryModified(uuid);
10864 }
10865 }
10866}
10867
10868/**
10869 * Creates differencing hard disks for all normal hard disks attached to this
10870 * machine and a new set of attachments to refer to created disks.
10871 *
10872 * Used when taking a snapshot or when deleting the current state. Gets called
10873 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10874 *
10875 * This method assumes that mMediumAttachments contains the original hard disk
10876 * attachments it needs to create diffs for. On success, these attachments will
10877 * be replaced with the created diffs.
10878 *
10879 * Attachments with non-normal hard disks are left as is.
10880 *
10881 * If @a aOnline is @c false then the original hard disks that require implicit
10882 * diffs will be locked for reading. Otherwise it is assumed that they are
10883 * already locked for writing (when the VM was started). Note that in the latter
10884 * case it is responsibility of the caller to lock the newly created diffs for
10885 * writing if this method succeeds.
10886 *
10887 * @param aProgress Progress object to run (must contain at least as
10888 * many operations left as the number of hard disks
10889 * attached).
10890 * @param aWeight Weight of this operation.
10891 * @param aOnline Whether the VM was online prior to this operation.
10892 *
10893 * @note The progress object is not marked as completed, neither on success nor
10894 * on failure. This is a responsibility of the caller.
10895 *
10896 * @note Locks this object and the media tree for writing.
10897 */
10898HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10899 ULONG aWeight,
10900 bool aOnline)
10901{
10902 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10903
10904 AutoCaller autoCaller(this);
10905 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10906
10907 AutoMultiWriteLock2 alock(this->lockHandle(),
10908 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10909
10910 /* must be in a protective state because we release the lock below */
10911 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10912 || mData->mMachineState == MachineState_OnlineSnapshotting
10913 || mData->mMachineState == MachineState_LiveSnapshotting
10914 || mData->mMachineState == MachineState_RestoringSnapshot
10915 || mData->mMachineState == MachineState_DeletingSnapshot
10916 , E_FAIL);
10917
10918 HRESULT rc = S_OK;
10919
10920 // use appropriate locked media map (online or offline)
10921 MediumLockListMap lockedMediaOffline;
10922 MediumLockListMap *lockedMediaMap;
10923 if (aOnline)
10924 lockedMediaMap = &mData->mSession.mLockedMedia;
10925 else
10926 lockedMediaMap = &lockedMediaOffline;
10927
10928 try
10929 {
10930 if (!aOnline)
10931 {
10932 /* lock all attached hard disks early to detect "in use"
10933 * situations before creating actual diffs */
10934 for (MediumAttachmentList::const_iterator
10935 it = mMediumAttachments->begin();
10936 it != mMediumAttachments->end();
10937 ++it)
10938 {
10939 MediumAttachment *pAtt = *it;
10940 if (pAtt->i_getType() == DeviceType_HardDisk)
10941 {
10942 Medium *pMedium = pAtt->i_getMedium();
10943 Assert(pMedium);
10944
10945 MediumLockList *pMediumLockList(new MediumLockList());
10946 alock.release();
10947 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10948 NULL /* pToLockWrite */,
10949 false /* fMediumLockWriteAll */,
10950 NULL,
10951 *pMediumLockList);
10952 alock.acquire();
10953 if (FAILED(rc))
10954 {
10955 delete pMediumLockList;
10956 throw rc;
10957 }
10958 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10959 if (FAILED(rc))
10960 {
10961 throw setError(rc,
10962 tr("Collecting locking information for all attached media failed"));
10963 }
10964 }
10965 }
10966
10967 /* Now lock all media. If this fails, nothing is locked. */
10968 alock.release();
10969 rc = lockedMediaMap->Lock();
10970 alock.acquire();
10971 if (FAILED(rc))
10972 {
10973 throw setError(rc,
10974 tr("Locking of attached media failed"));
10975 }
10976 }
10977
10978 /* remember the current list (note that we don't use backup() since
10979 * mMediumAttachments may be already backed up) */
10980 MediumAttachmentList atts = *mMediumAttachments.data();
10981
10982 /* start from scratch */
10983 mMediumAttachments->clear();
10984
10985 /* go through remembered attachments and create diffs for normal hard
10986 * disks and attach them */
10987 for (MediumAttachmentList::const_iterator
10988 it = atts.begin();
10989 it != atts.end();
10990 ++it)
10991 {
10992 MediumAttachment *pAtt = *it;
10993
10994 DeviceType_T devType = pAtt->i_getType();
10995 Medium *pMedium = pAtt->i_getMedium();
10996
10997 if ( devType != DeviceType_HardDisk
10998 || pMedium == NULL
10999 || pMedium->i_getType() != MediumType_Normal)
11000 {
11001 /* copy the attachment as is */
11002
11003 /** @todo the progress object created in SessionMachine::TakeSnaphot
11004 * only expects operations for hard disks. Later other
11005 * device types need to show up in the progress as well. */
11006 if (devType == DeviceType_HardDisk)
11007 {
11008 if (pMedium == NULL)
11009 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11010 aWeight); // weight
11011 else
11012 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11013 pMedium->i_getBase()->i_getName().c_str()).raw(),
11014 aWeight); // weight
11015 }
11016
11017 mMediumAttachments->push_back(pAtt);
11018 continue;
11019 }
11020
11021 /* need a diff */
11022 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11023 pMedium->i_getBase()->i_getName().c_str()).raw(),
11024 aWeight); // weight
11025
11026 Utf8Str strFullSnapshotFolder;
11027 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11028
11029 ComObjPtr<Medium> diff;
11030 diff.createObject();
11031 // store the diff in the same registry as the parent
11032 // (this cannot fail here because we can't create implicit diffs for
11033 // unregistered images)
11034 Guid uuidRegistryParent;
11035 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11036 Assert(fInRegistry); NOREF(fInRegistry);
11037 rc = diff->init(mParent,
11038 pMedium->i_getPreferredDiffFormat(),
11039 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11040 uuidRegistryParent,
11041 DeviceType_HardDisk);
11042 if (FAILED(rc)) throw rc;
11043
11044 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11045 * the push_back? Looks like we're going to release medium with the
11046 * wrong kind of lock (general issue with if we fail anywhere at all)
11047 * and an orphaned VDI in the snapshots folder. */
11048
11049 /* update the appropriate lock list */
11050 MediumLockList *pMediumLockList;
11051 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11052 AssertComRCThrowRC(rc);
11053 if (aOnline)
11054 {
11055 alock.release();
11056 /* The currently attached medium will be read-only, change
11057 * the lock type to read. */
11058 rc = pMediumLockList->Update(pMedium, false);
11059 alock.acquire();
11060 AssertComRCThrowRC(rc);
11061 }
11062
11063 /* release the locks before the potentially lengthy operation */
11064 alock.release();
11065 rc = pMedium->i_createDiffStorage(diff,
11066 pMedium->i_getPreferredDiffVariant(),
11067 pMediumLockList,
11068 NULL /* aProgress */,
11069 true /* aWait */);
11070 alock.acquire();
11071 if (FAILED(rc)) throw rc;
11072
11073 /* actual lock list update is done in Machine::i_commitMedia */
11074
11075 rc = diff->i_addBackReference(mData->mUuid);
11076 AssertComRCThrowRC(rc);
11077
11078 /* add a new attachment */
11079 ComObjPtr<MediumAttachment> attachment;
11080 attachment.createObject();
11081 rc = attachment->init(this,
11082 diff,
11083 pAtt->i_getControllerName(),
11084 pAtt->i_getPort(),
11085 pAtt->i_getDevice(),
11086 DeviceType_HardDisk,
11087 true /* aImplicit */,
11088 false /* aPassthrough */,
11089 false /* aTempEject */,
11090 pAtt->i_getNonRotational(),
11091 pAtt->i_getDiscard(),
11092 pAtt->i_getHotPluggable(),
11093 pAtt->i_getBandwidthGroup());
11094 if (FAILED(rc)) throw rc;
11095
11096 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11097 AssertComRCThrowRC(rc);
11098 mMediumAttachments->push_back(attachment);
11099 }
11100 }
11101 catch (HRESULT aRC) { rc = aRC; }
11102
11103 /* unlock all hard disks we locked when there is no VM */
11104 if (!aOnline)
11105 {
11106 ErrorInfoKeeper eik;
11107
11108 HRESULT rc1 = lockedMediaMap->Clear();
11109 AssertComRC(rc1);
11110 }
11111
11112 return rc;
11113}
11114
11115/**
11116 * Deletes implicit differencing hard disks created either by
11117 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11118 * mMediumAttachments.
11119 *
11120 * Note that to delete hard disks created by #attachDevice() this method is
11121 * called from #i_rollbackMedia() when the changes are rolled back.
11122 *
11123 * @note Locks this object and the media tree for writing.
11124 */
11125HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11126{
11127 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11128
11129 AutoCaller autoCaller(this);
11130 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11131
11132 AutoMultiWriteLock2 alock(this->lockHandle(),
11133 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11134
11135 /* We absolutely must have backed up state. */
11136 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11137
11138 /* Check if there are any implicitly created diff images. */
11139 bool fImplicitDiffs = false;
11140 for (MediumAttachmentList::const_iterator
11141 it = mMediumAttachments->begin();
11142 it != mMediumAttachments->end();
11143 ++it)
11144 {
11145 const ComObjPtr<MediumAttachment> &pAtt = *it;
11146 if (pAtt->i_isImplicit())
11147 {
11148 fImplicitDiffs = true;
11149 break;
11150 }
11151 }
11152 /* If there is nothing to do, leave early. This saves lots of image locking
11153 * effort. It also avoids a MachineStateChanged event without real reason.
11154 * This is important e.g. when loading a VM config, because there should be
11155 * no events. Otherwise API clients can become thoroughly confused for
11156 * inaccessible VMs (the code for loading VM configs uses this method for
11157 * cleanup if the config makes no sense), as they take such events as an
11158 * indication that the VM is alive, and they would force the VM config to
11159 * be reread, leading to an endless loop. */
11160 if (!fImplicitDiffs)
11161 return S_OK;
11162
11163 HRESULT rc = S_OK;
11164 MachineState_T oldState = mData->mMachineState;
11165
11166 /* will release the lock before the potentially lengthy operation,
11167 * so protect with the special state (unless already protected) */
11168 if ( oldState != MachineState_Snapshotting
11169 && oldState != MachineState_OnlineSnapshotting
11170 && oldState != MachineState_LiveSnapshotting
11171 && oldState != MachineState_RestoringSnapshot
11172 && oldState != MachineState_DeletingSnapshot
11173 && oldState != MachineState_DeletingSnapshotOnline
11174 && oldState != MachineState_DeletingSnapshotPaused
11175 )
11176 i_setMachineState(MachineState_SettingUp);
11177
11178 // use appropriate locked media map (online or offline)
11179 MediumLockListMap lockedMediaOffline;
11180 MediumLockListMap *lockedMediaMap;
11181 if (aOnline)
11182 lockedMediaMap = &mData->mSession.mLockedMedia;
11183 else
11184 lockedMediaMap = &lockedMediaOffline;
11185
11186 try
11187 {
11188 if (!aOnline)
11189 {
11190 /* lock all attached hard disks early to detect "in use"
11191 * situations before deleting actual diffs */
11192 for (MediumAttachmentList::const_iterator
11193 it = mMediumAttachments->begin();
11194 it != mMediumAttachments->end();
11195 ++it)
11196 {
11197 MediumAttachment *pAtt = *it;
11198 if (pAtt->i_getType() == DeviceType_HardDisk)
11199 {
11200 Medium *pMedium = pAtt->i_getMedium();
11201 Assert(pMedium);
11202
11203 MediumLockList *pMediumLockList(new MediumLockList());
11204 alock.release();
11205 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11206 NULL /* pToLockWrite */,
11207 false /* fMediumLockWriteAll */,
11208 NULL,
11209 *pMediumLockList);
11210 alock.acquire();
11211
11212 if (FAILED(rc))
11213 {
11214 delete pMediumLockList;
11215 throw rc;
11216 }
11217
11218 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11219 if (FAILED(rc))
11220 throw rc;
11221 }
11222 }
11223
11224 if (FAILED(rc))
11225 throw rc;
11226 } // end of offline
11227
11228 /* Lock lists are now up to date and include implicitly created media */
11229
11230 /* Go through remembered attachments and delete all implicitly created
11231 * diffs and fix up the attachment information */
11232 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11233 MediumAttachmentList implicitAtts;
11234 for (MediumAttachmentList::const_iterator
11235 it = mMediumAttachments->begin();
11236 it != mMediumAttachments->end();
11237 ++it)
11238 {
11239 ComObjPtr<MediumAttachment> pAtt = *it;
11240 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11241 if (pMedium.isNull())
11242 continue;
11243
11244 // Implicit attachments go on the list for deletion and back references are removed.
11245 if (pAtt->i_isImplicit())
11246 {
11247 /* Deassociate and mark for deletion */
11248 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11249 rc = pMedium->i_removeBackReference(mData->mUuid);
11250 if (FAILED(rc))
11251 throw rc;
11252 implicitAtts.push_back(pAtt);
11253 continue;
11254 }
11255
11256 /* Was this medium attached before? */
11257 if (!i_findAttachment(oldAtts, pMedium))
11258 {
11259 /* no: de-associate */
11260 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11261 rc = pMedium->i_removeBackReference(mData->mUuid);
11262 if (FAILED(rc))
11263 throw rc;
11264 continue;
11265 }
11266 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11267 }
11268
11269 /* If there are implicit attachments to delete, throw away the lock
11270 * map contents (which will unlock all media) since the medium
11271 * attachments will be rolled back. Below we need to completely
11272 * recreate the lock map anyway since it is infinitely complex to
11273 * do this incrementally (would need reconstructing each attachment
11274 * change, which would be extremely hairy). */
11275 if (implicitAtts.size() != 0)
11276 {
11277 ErrorInfoKeeper eik;
11278
11279 HRESULT rc1 = lockedMediaMap->Clear();
11280 AssertComRC(rc1);
11281 }
11282
11283 /* rollback hard disk changes */
11284 mMediumAttachments.rollback();
11285
11286 MultiResult mrc(S_OK);
11287
11288 // Delete unused implicit diffs.
11289 if (implicitAtts.size() != 0)
11290 {
11291 alock.release();
11292
11293 for (MediumAttachmentList::const_iterator
11294 it = implicitAtts.begin();
11295 it != implicitAtts.end();
11296 ++it)
11297 {
11298 // Remove medium associated with this attachment.
11299 ComObjPtr<MediumAttachment> pAtt = *it;
11300 Assert(pAtt);
11301 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11302 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11303 Assert(pMedium);
11304
11305 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11306 // continue on delete failure, just collect error messages
11307 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11308 pMedium->i_getLocationFull().c_str() ));
11309 mrc = rc;
11310 }
11311 // Clear the list of deleted implicit attachments now, while not
11312 // holding the lock, as it will ultimately trigger Medium::uninit()
11313 // calls which assume that the media tree lock isn't held.
11314 implicitAtts.clear();
11315
11316 alock.acquire();
11317
11318 /* if there is a VM recreate media lock map as mentioned above,
11319 * otherwise it is a waste of time and we leave things unlocked */
11320 if (aOnline)
11321 {
11322 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11323 /* must never be NULL, but better safe than sorry */
11324 if (!pMachine.isNull())
11325 {
11326 alock.release();
11327 rc = mData->mSession.mMachine->i_lockMedia();
11328 alock.acquire();
11329 if (FAILED(rc))
11330 throw rc;
11331 }
11332 }
11333 }
11334 }
11335 catch (HRESULT aRC) {rc = aRC;}
11336
11337 if (mData->mMachineState == MachineState_SettingUp)
11338 i_setMachineState(oldState);
11339
11340 /* unlock all hard disks we locked when there is no VM */
11341 if (!aOnline)
11342 {
11343 ErrorInfoKeeper eik;
11344
11345 HRESULT rc1 = lockedMediaMap->Clear();
11346 AssertComRC(rc1);
11347 }
11348
11349 return rc;
11350}
11351
11352
11353/**
11354 * Looks through the given list of media attachments for one with the given parameters
11355 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11356 * can be searched as well if needed.
11357 *
11358 * @param ll
11359 * @param aControllerName
11360 * @param aControllerPort
11361 * @param aDevice
11362 * @return
11363 */
11364MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11365 const Utf8Str &aControllerName,
11366 LONG aControllerPort,
11367 LONG aDevice)
11368{
11369 for (MediumAttachmentList::const_iterator
11370 it = ll.begin();
11371 it != ll.end();
11372 ++it)
11373 {
11374 MediumAttachment *pAttach = *it;
11375 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11376 return pAttach;
11377 }
11378
11379 return NULL;
11380}
11381
11382/**
11383 * Looks through the given list of media attachments for one with the given parameters
11384 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11385 * can be searched as well if needed.
11386 *
11387 * @param ll
11388 * @param pMedium
11389 * @return
11390 */
11391MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11392 ComObjPtr<Medium> pMedium)
11393{
11394 for (MediumAttachmentList::const_iterator
11395 it = ll.begin();
11396 it != ll.end();
11397 ++it)
11398 {
11399 MediumAttachment *pAttach = *it;
11400 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11401 if (pMediumThis == pMedium)
11402 return pAttach;
11403 }
11404
11405 return NULL;
11406}
11407
11408/**
11409 * Looks through the given list of media attachments for one with the given parameters
11410 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11411 * can be searched as well if needed.
11412 *
11413 * @param ll
11414 * @param id
11415 * @return
11416 */
11417MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11418 Guid &id)
11419{
11420 for (MediumAttachmentList::const_iterator
11421 it = ll.begin();
11422 it != ll.end();
11423 ++it)
11424 {
11425 MediumAttachment *pAttach = *it;
11426 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11427 if (pMediumThis->i_getId() == id)
11428 return pAttach;
11429 }
11430
11431 return NULL;
11432}
11433
11434/**
11435 * Main implementation for Machine::DetachDevice. This also gets called
11436 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11437 *
11438 * @param pAttach Medium attachment to detach.
11439 * @param writeLock Machine write lock which the caller must have locked once.
11440 * This may be released temporarily in here.
11441 * @param pSnapshot If NULL, then the detachment is for the current machine.
11442 * Otherwise this is for a SnapshotMachine, and this must be
11443 * its snapshot.
11444 * @return
11445 */
11446HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11447 AutoWriteLock &writeLock,
11448 Snapshot *pSnapshot)
11449{
11450 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11451 DeviceType_T mediumType = pAttach->i_getType();
11452
11453 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11454
11455 if (pAttach->i_isImplicit())
11456 {
11457 /* attempt to implicitly delete the implicitly created diff */
11458
11459 /// @todo move the implicit flag from MediumAttachment to Medium
11460 /// and forbid any hard disk operation when it is implicit. Or maybe
11461 /// a special media state for it to make it even more simple.
11462
11463 Assert(mMediumAttachments.isBackedUp());
11464
11465 /* will release the lock before the potentially lengthy operation, so
11466 * protect with the special state */
11467 MachineState_T oldState = mData->mMachineState;
11468 i_setMachineState(MachineState_SettingUp);
11469
11470 writeLock.release();
11471
11472 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11473 true /*aWait*/);
11474
11475 writeLock.acquire();
11476
11477 i_setMachineState(oldState);
11478
11479 if (FAILED(rc)) return rc;
11480 }
11481
11482 i_setModified(IsModified_Storage);
11483 mMediumAttachments.backup();
11484 mMediumAttachments->remove(pAttach);
11485
11486 if (!oldmedium.isNull())
11487 {
11488 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11489 if (pSnapshot)
11490 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11491 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11492 else if (mediumType != DeviceType_HardDisk)
11493 oldmedium->i_removeBackReference(mData->mUuid);
11494 }
11495
11496 return S_OK;
11497}
11498
11499/**
11500 * Goes thru all media of the given list and
11501 *
11502 * 1) calls i_detachDevice() on each of them for this machine and
11503 * 2) adds all Medium objects found in the process to the given list,
11504 * depending on cleanupMode.
11505 *
11506 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11507 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11508 * media to the list.
11509 *
11510 * This gets called from Machine::Unregister, both for the actual Machine and
11511 * the SnapshotMachine objects that might be found in the snapshots.
11512 *
11513 * Requires caller and locking. The machine lock must be passed in because it
11514 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11515 *
11516 * @param writeLock Machine lock from top-level caller; this gets passed to
11517 * i_detachDevice.
11518 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11519 * object if called for a SnapshotMachine.
11520 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11521 * added to llMedia; if Full, then all media get added;
11522 * otherwise no media get added.
11523 * @param llMedia Caller's list to receive Medium objects which got detached so
11524 * caller can close() them, depending on cleanupMode.
11525 * @return
11526 */
11527HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11528 Snapshot *pSnapshot,
11529 CleanupMode_T cleanupMode,
11530 MediaList &llMedia)
11531{
11532 Assert(isWriteLockOnCurrentThread());
11533
11534 HRESULT rc;
11535
11536 // make a temporary list because i_detachDevice invalidates iterators into
11537 // mMediumAttachments
11538 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11539
11540 for (MediumAttachmentList::iterator
11541 it = llAttachments2.begin();
11542 it != llAttachments2.end();
11543 ++it)
11544 {
11545 ComObjPtr<MediumAttachment> &pAttach = *it;
11546 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11547
11548 if (!pMedium.isNull())
11549 {
11550 AutoCaller mac(pMedium);
11551 if (FAILED(mac.rc())) return mac.rc();
11552 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11553 DeviceType_T devType = pMedium->i_getDeviceType();
11554 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11555 && devType == DeviceType_HardDisk)
11556 || (cleanupMode == CleanupMode_Full)
11557 )
11558 {
11559 llMedia.push_back(pMedium);
11560 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11561 /* Not allowed to keep this lock as below we need the parent
11562 * medium lock, and the lock order is parent to child. */
11563 lock.release();
11564 /*
11565 * Search for medias which are not attached to any machine, but
11566 * in the chain to an attached disk. Mediums are only consided
11567 * if they are:
11568 * - have only one child
11569 * - no references to any machines
11570 * - are of normal medium type
11571 */
11572 while (!pParent.isNull())
11573 {
11574 AutoCaller mac1(pParent);
11575 if (FAILED(mac1.rc())) return mac1.rc();
11576 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11577 if (pParent->i_getChildren().size() == 1)
11578 {
11579 if ( pParent->i_getMachineBackRefCount() == 0
11580 && pParent->i_getType() == MediumType_Normal
11581 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11582 llMedia.push_back(pParent);
11583 }
11584 else
11585 break;
11586 pParent = pParent->i_getParent();
11587 }
11588 }
11589 }
11590
11591 // real machine: then we need to use the proper method
11592 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11593
11594 if (FAILED(rc))
11595 return rc;
11596 }
11597
11598 return S_OK;
11599}
11600
11601/**
11602 * Perform deferred hard disk detachments.
11603 *
11604 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11605 * changed (not backed up).
11606 *
11607 * If @a aOnline is @c true then this method will also unlock the old hard
11608 * disks for which the new implicit diffs were created and will lock these new
11609 * diffs for writing.
11610 *
11611 * @param aOnline Whether the VM was online prior to this operation.
11612 *
11613 * @note Locks this object for writing!
11614 */
11615void Machine::i_commitMedia(bool aOnline /*= false*/)
11616{
11617 AutoCaller autoCaller(this);
11618 AssertComRCReturnVoid(autoCaller.rc());
11619
11620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11621
11622 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11623
11624 HRESULT rc = S_OK;
11625
11626 /* no attach/detach operations -- nothing to do */
11627 if (!mMediumAttachments.isBackedUp())
11628 return;
11629
11630 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11631 bool fMediaNeedsLocking = false;
11632
11633 /* enumerate new attachments */
11634 for (MediumAttachmentList::const_iterator
11635 it = mMediumAttachments->begin();
11636 it != mMediumAttachments->end();
11637 ++it)
11638 {
11639 MediumAttachment *pAttach = *it;
11640
11641 pAttach->i_commit();
11642
11643 Medium *pMedium = pAttach->i_getMedium();
11644 bool fImplicit = pAttach->i_isImplicit();
11645
11646 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11647 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11648 fImplicit));
11649
11650 /** @todo convert all this Machine-based voodoo to MediumAttachment
11651 * based commit logic. */
11652 if (fImplicit)
11653 {
11654 /* convert implicit attachment to normal */
11655 pAttach->i_setImplicit(false);
11656
11657 if ( aOnline
11658 && pMedium
11659 && pAttach->i_getType() == DeviceType_HardDisk
11660 )
11661 {
11662 /* update the appropriate lock list */
11663 MediumLockList *pMediumLockList;
11664 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11665 AssertComRC(rc);
11666 if (pMediumLockList)
11667 {
11668 /* unlock if there's a need to change the locking */
11669 if (!fMediaNeedsLocking)
11670 {
11671 rc = mData->mSession.mLockedMedia.Unlock();
11672 AssertComRC(rc);
11673 fMediaNeedsLocking = true;
11674 }
11675 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11676 AssertComRC(rc);
11677 rc = pMediumLockList->Append(pMedium, true);
11678 AssertComRC(rc);
11679 }
11680 }
11681
11682 continue;
11683 }
11684
11685 if (pMedium)
11686 {
11687 /* was this medium attached before? */
11688 for (MediumAttachmentList::iterator
11689 oldIt = oldAtts.begin();
11690 oldIt != oldAtts.end();
11691 ++oldIt)
11692 {
11693 MediumAttachment *pOldAttach = *oldIt;
11694 if (pOldAttach->i_getMedium() == pMedium)
11695 {
11696 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11697
11698 /* yes: remove from old to avoid de-association */
11699 oldAtts.erase(oldIt);
11700 break;
11701 }
11702 }
11703 }
11704 }
11705
11706 /* enumerate remaining old attachments and de-associate from the
11707 * current machine state */
11708 for (MediumAttachmentList::const_iterator
11709 it = oldAtts.begin();
11710 it != oldAtts.end();
11711 ++it)
11712 {
11713 MediumAttachment *pAttach = *it;
11714 Medium *pMedium = pAttach->i_getMedium();
11715
11716 /* Detach only hard disks, since DVD/floppy media is detached
11717 * instantly in MountMedium. */
11718 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11719 {
11720 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11721
11722 /* now de-associate from the current machine state */
11723 rc = pMedium->i_removeBackReference(mData->mUuid);
11724 AssertComRC(rc);
11725
11726 if (aOnline)
11727 {
11728 /* unlock since medium is not used anymore */
11729 MediumLockList *pMediumLockList;
11730 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11731 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11732 {
11733 /* this happens for online snapshots, there the attachment
11734 * is changing, but only to a diff image created under
11735 * the old one, so there is no separate lock list */
11736 Assert(!pMediumLockList);
11737 }
11738 else
11739 {
11740 AssertComRC(rc);
11741 if (pMediumLockList)
11742 {
11743 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11744 AssertComRC(rc);
11745 }
11746 }
11747 }
11748 }
11749 }
11750
11751 /* take media locks again so that the locking state is consistent */
11752 if (fMediaNeedsLocking)
11753 {
11754 Assert(aOnline);
11755 rc = mData->mSession.mLockedMedia.Lock();
11756 AssertComRC(rc);
11757 }
11758
11759 /* commit the hard disk changes */
11760 mMediumAttachments.commit();
11761
11762 if (i_isSessionMachine())
11763 {
11764 /*
11765 * Update the parent machine to point to the new owner.
11766 * This is necessary because the stored parent will point to the
11767 * session machine otherwise and cause crashes or errors later
11768 * when the session machine gets invalid.
11769 */
11770 /** @todo Change the MediumAttachment class to behave like any other
11771 * class in this regard by creating peer MediumAttachment
11772 * objects for session machines and share the data with the peer
11773 * machine.
11774 */
11775 for (MediumAttachmentList::const_iterator
11776 it = mMediumAttachments->begin();
11777 it != mMediumAttachments->end();
11778 ++it)
11779 (*it)->i_updateParentMachine(mPeer);
11780
11781 /* attach new data to the primary machine and reshare it */
11782 mPeer->mMediumAttachments.attach(mMediumAttachments);
11783 }
11784
11785 return;
11786}
11787
11788/**
11789 * Perform deferred deletion of implicitly created diffs.
11790 *
11791 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11792 * changed (not backed up).
11793 *
11794 * @note Locks this object for writing!
11795 */
11796void Machine::i_rollbackMedia()
11797{
11798 AutoCaller autoCaller(this);
11799 AssertComRCReturnVoid(autoCaller.rc());
11800
11801 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11802 LogFlowThisFunc(("Entering rollbackMedia\n"));
11803
11804 HRESULT rc = S_OK;
11805
11806 /* no attach/detach operations -- nothing to do */
11807 if (!mMediumAttachments.isBackedUp())
11808 return;
11809
11810 /* enumerate new attachments */
11811 for (MediumAttachmentList::const_iterator
11812 it = mMediumAttachments->begin();
11813 it != mMediumAttachments->end();
11814 ++it)
11815 {
11816 MediumAttachment *pAttach = *it;
11817 /* Fix up the backrefs for DVD/floppy media. */
11818 if (pAttach->i_getType() != DeviceType_HardDisk)
11819 {
11820 Medium *pMedium = pAttach->i_getMedium();
11821 if (pMedium)
11822 {
11823 rc = pMedium->i_removeBackReference(mData->mUuid);
11824 AssertComRC(rc);
11825 }
11826 }
11827
11828 (*it)->i_rollback();
11829
11830 pAttach = *it;
11831 /* Fix up the backrefs for DVD/floppy media. */
11832 if (pAttach->i_getType() != DeviceType_HardDisk)
11833 {
11834 Medium *pMedium = pAttach->i_getMedium();
11835 if (pMedium)
11836 {
11837 rc = pMedium->i_addBackReference(mData->mUuid);
11838 AssertComRC(rc);
11839 }
11840 }
11841 }
11842
11843 /** @todo convert all this Machine-based voodoo to MediumAttachment
11844 * based rollback logic. */
11845 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11846
11847 return;
11848}
11849
11850/**
11851 * Returns true if the settings file is located in the directory named exactly
11852 * as the machine; this means, among other things, that the machine directory
11853 * should be auto-renamed.
11854 *
11855 * @param aSettingsDir if not NULL, the full machine settings file directory
11856 * name will be assigned there.
11857 *
11858 * @note Doesn't lock anything.
11859 * @note Not thread safe (must be called from this object's lock).
11860 */
11861bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11862{
11863 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11864 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11865 if (aSettingsDir)
11866 *aSettingsDir = strMachineDirName;
11867 strMachineDirName.stripPath(); // vmname
11868 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11869 strConfigFileOnly.stripPath() // vmname.vbox
11870 .stripSuffix(); // vmname
11871 /** @todo hack, make somehow use of ComposeMachineFilename */
11872 if (mUserData->s.fDirectoryIncludesUUID)
11873 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11874
11875 AssertReturn(!strMachineDirName.isEmpty(), false);
11876 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11877
11878 return strMachineDirName == strConfigFileOnly;
11879}
11880
11881/**
11882 * Discards all changes to machine settings.
11883 *
11884 * @param aNotify Whether to notify the direct session about changes or not.
11885 *
11886 * @note Locks objects for writing!
11887 */
11888void Machine::i_rollback(bool aNotify)
11889{
11890 AutoCaller autoCaller(this);
11891 AssertComRCReturn(autoCaller.rc(), (void)0);
11892
11893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11894
11895 if (!mStorageControllers.isNull())
11896 {
11897 if (mStorageControllers.isBackedUp())
11898 {
11899 /* unitialize all new devices (absent in the backed up list). */
11900 StorageControllerList *backedList = mStorageControllers.backedUpData();
11901 for (StorageControllerList::const_iterator
11902 it = mStorageControllers->begin();
11903 it != mStorageControllers->end();
11904 ++it)
11905 {
11906 if ( std::find(backedList->begin(), backedList->end(), *it)
11907 == backedList->end()
11908 )
11909 {
11910 (*it)->uninit();
11911 }
11912 }
11913
11914 /* restore the list */
11915 mStorageControllers.rollback();
11916 }
11917
11918 /* rollback any changes to devices after restoring the list */
11919 if (mData->flModifications & IsModified_Storage)
11920 {
11921 for (StorageControllerList::const_iterator
11922 it = mStorageControllers->begin();
11923 it != mStorageControllers->end();
11924 ++it)
11925 {
11926 (*it)->i_rollback();
11927 }
11928 }
11929 }
11930
11931 if (!mUSBControllers.isNull())
11932 {
11933 if (mUSBControllers.isBackedUp())
11934 {
11935 /* unitialize all new devices (absent in the backed up list). */
11936 USBControllerList *backedList = mUSBControllers.backedUpData();
11937 for (USBControllerList::const_iterator
11938 it = mUSBControllers->begin();
11939 it != mUSBControllers->end();
11940 ++it)
11941 {
11942 if ( std::find(backedList->begin(), backedList->end(), *it)
11943 == backedList->end()
11944 )
11945 {
11946 (*it)->uninit();
11947 }
11948 }
11949
11950 /* restore the list */
11951 mUSBControllers.rollback();
11952 }
11953
11954 /* rollback any changes to devices after restoring the list */
11955 if (mData->flModifications & IsModified_USB)
11956 {
11957 for (USBControllerList::const_iterator
11958 it = mUSBControllers->begin();
11959 it != mUSBControllers->end();
11960 ++it)
11961 {
11962 (*it)->i_rollback();
11963 }
11964 }
11965 }
11966
11967 mUserData.rollback();
11968
11969 mHWData.rollback();
11970
11971 if (mData->flModifications & IsModified_Storage)
11972 i_rollbackMedia();
11973
11974 if (mBIOSSettings)
11975 mBIOSSettings->i_rollback();
11976
11977 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11978 mVRDEServer->i_rollback();
11979
11980 if (mAudioAdapter)
11981 mAudioAdapter->i_rollback();
11982
11983 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11984 mUSBDeviceFilters->i_rollback();
11985
11986 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11987 mBandwidthControl->i_rollback();
11988
11989 if (!mHWData.isNull())
11990 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11991 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11992 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11993 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11994
11995 if (mData->flModifications & IsModified_NetworkAdapters)
11996 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11997 if ( mNetworkAdapters[slot]
11998 && mNetworkAdapters[slot]->i_isModified())
11999 {
12000 mNetworkAdapters[slot]->i_rollback();
12001 networkAdapters[slot] = mNetworkAdapters[slot];
12002 }
12003
12004 if (mData->flModifications & IsModified_SerialPorts)
12005 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12006 if ( mSerialPorts[slot]
12007 && mSerialPorts[slot]->i_isModified())
12008 {
12009 mSerialPorts[slot]->i_rollback();
12010 serialPorts[slot] = mSerialPorts[slot];
12011 }
12012
12013 if (mData->flModifications & IsModified_ParallelPorts)
12014 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12015 if ( mParallelPorts[slot]
12016 && mParallelPorts[slot]->i_isModified())
12017 {
12018 mParallelPorts[slot]->i_rollback();
12019 parallelPorts[slot] = mParallelPorts[slot];
12020 }
12021
12022 if (aNotify)
12023 {
12024 /* inform the direct session about changes */
12025
12026 ComObjPtr<Machine> that = this;
12027 uint32_t flModifications = mData->flModifications;
12028 alock.release();
12029
12030 if (flModifications & IsModified_SharedFolders)
12031 that->i_onSharedFolderChange();
12032
12033 if (flModifications & IsModified_VRDEServer)
12034 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12035 if (flModifications & IsModified_USB)
12036 that->i_onUSBControllerChange();
12037
12038 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12039 if (networkAdapters[slot])
12040 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12041 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12042 if (serialPorts[slot])
12043 that->i_onSerialPortChange(serialPorts[slot]);
12044 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12045 if (parallelPorts[slot])
12046 that->i_onParallelPortChange(parallelPorts[slot]);
12047
12048 if (flModifications & IsModified_Storage)
12049 that->i_onStorageControllerChange();
12050
12051#if 0
12052 if (flModifications & IsModified_BandwidthControl)
12053 that->onBandwidthControlChange();
12054#endif
12055 }
12056}
12057
12058/**
12059 * Commits all the changes to machine settings.
12060 *
12061 * Note that this operation is supposed to never fail.
12062 *
12063 * @note Locks this object and children for writing.
12064 */
12065void Machine::i_commit()
12066{
12067 AutoCaller autoCaller(this);
12068 AssertComRCReturnVoid(autoCaller.rc());
12069
12070 AutoCaller peerCaller(mPeer);
12071 AssertComRCReturnVoid(peerCaller.rc());
12072
12073 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12074
12075 /*
12076 * use safe commit to ensure Snapshot machines (that share mUserData)
12077 * will still refer to a valid memory location
12078 */
12079 mUserData.commitCopy();
12080
12081 mHWData.commit();
12082
12083 if (mMediumAttachments.isBackedUp())
12084 i_commitMedia(Global::IsOnline(mData->mMachineState));
12085
12086 mBIOSSettings->i_commit();
12087 mVRDEServer->i_commit();
12088 mAudioAdapter->i_commit();
12089 mUSBDeviceFilters->i_commit();
12090 mBandwidthControl->i_commit();
12091
12092 /* Since mNetworkAdapters is a list which might have been changed (resized)
12093 * without using the Backupable<> template we need to handle the copying
12094 * of the list entries manually, including the creation of peers for the
12095 * new objects. */
12096 bool commitNetworkAdapters = false;
12097 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12098 if (mPeer)
12099 {
12100 /* commit everything, even the ones which will go away */
12101 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12102 mNetworkAdapters[slot]->i_commit();
12103 /* copy over the new entries, creating a peer and uninit the original */
12104 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12105 for (size_t slot = 0; slot < newSize; slot++)
12106 {
12107 /* look if this adapter has a peer device */
12108 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12109 if (!peer)
12110 {
12111 /* no peer means the adapter is a newly created one;
12112 * create a peer owning data this data share it with */
12113 peer.createObject();
12114 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12115 }
12116 mPeer->mNetworkAdapters[slot] = peer;
12117 }
12118 /* uninit any no longer needed network adapters */
12119 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12120 mNetworkAdapters[slot]->uninit();
12121 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12122 {
12123 if (mPeer->mNetworkAdapters[slot])
12124 mPeer->mNetworkAdapters[slot]->uninit();
12125 }
12126 /* Keep the original network adapter count until this point, so that
12127 * discarding a chipset type change will not lose settings. */
12128 mNetworkAdapters.resize(newSize);
12129 mPeer->mNetworkAdapters.resize(newSize);
12130 }
12131 else
12132 {
12133 /* we have no peer (our parent is the newly created machine);
12134 * just commit changes to the network adapters */
12135 commitNetworkAdapters = true;
12136 }
12137 if (commitNetworkAdapters)
12138 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12139 mNetworkAdapters[slot]->i_commit();
12140
12141 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12142 mSerialPorts[slot]->i_commit();
12143 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12144 mParallelPorts[slot]->i_commit();
12145
12146 bool commitStorageControllers = false;
12147
12148 if (mStorageControllers.isBackedUp())
12149 {
12150 mStorageControllers.commit();
12151
12152 if (mPeer)
12153 {
12154 /* Commit all changes to new controllers (this will reshare data with
12155 * peers for those who have peers) */
12156 StorageControllerList *newList = new StorageControllerList();
12157 for (StorageControllerList::const_iterator
12158 it = mStorageControllers->begin();
12159 it != mStorageControllers->end();
12160 ++it)
12161 {
12162 (*it)->i_commit();
12163
12164 /* look if this controller has a peer device */
12165 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12166 if (!peer)
12167 {
12168 /* no peer means the device is a newly created one;
12169 * create a peer owning data this device share it with */
12170 peer.createObject();
12171 peer->init(mPeer, *it, true /* aReshare */);
12172 }
12173 else
12174 {
12175 /* remove peer from the old list */
12176 mPeer->mStorageControllers->remove(peer);
12177 }
12178 /* and add it to the new list */
12179 newList->push_back(peer);
12180 }
12181
12182 /* uninit old peer's controllers that are left */
12183 for (StorageControllerList::const_iterator
12184 it = mPeer->mStorageControllers->begin();
12185 it != mPeer->mStorageControllers->end();
12186 ++it)
12187 {
12188 (*it)->uninit();
12189 }
12190
12191 /* attach new list of controllers to our peer */
12192 mPeer->mStorageControllers.attach(newList);
12193 }
12194 else
12195 {
12196 /* we have no peer (our parent is the newly created machine);
12197 * just commit changes to devices */
12198 commitStorageControllers = true;
12199 }
12200 }
12201 else
12202 {
12203 /* the list of controllers itself is not changed,
12204 * just commit changes to controllers themselves */
12205 commitStorageControllers = true;
12206 }
12207
12208 if (commitStorageControllers)
12209 {
12210 for (StorageControllerList::const_iterator
12211 it = mStorageControllers->begin();
12212 it != mStorageControllers->end();
12213 ++it)
12214 {
12215 (*it)->i_commit();
12216 }
12217 }
12218
12219 bool commitUSBControllers = false;
12220
12221 if (mUSBControllers.isBackedUp())
12222 {
12223 mUSBControllers.commit();
12224
12225 if (mPeer)
12226 {
12227 /* Commit all changes to new controllers (this will reshare data with
12228 * peers for those who have peers) */
12229 USBControllerList *newList = new USBControllerList();
12230 for (USBControllerList::const_iterator
12231 it = mUSBControllers->begin();
12232 it != mUSBControllers->end();
12233 ++it)
12234 {
12235 (*it)->i_commit();
12236
12237 /* look if this controller has a peer device */
12238 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12239 if (!peer)
12240 {
12241 /* no peer means the device is a newly created one;
12242 * create a peer owning data this device share it with */
12243 peer.createObject();
12244 peer->init(mPeer, *it, true /* aReshare */);
12245 }
12246 else
12247 {
12248 /* remove peer from the old list */
12249 mPeer->mUSBControllers->remove(peer);
12250 }
12251 /* and add it to the new list */
12252 newList->push_back(peer);
12253 }
12254
12255 /* uninit old peer's controllers that are left */
12256 for (USBControllerList::const_iterator
12257 it = mPeer->mUSBControllers->begin();
12258 it != mPeer->mUSBControllers->end();
12259 ++it)
12260 {
12261 (*it)->uninit();
12262 }
12263
12264 /* attach new list of controllers to our peer */
12265 mPeer->mUSBControllers.attach(newList);
12266 }
12267 else
12268 {
12269 /* we have no peer (our parent is the newly created machine);
12270 * just commit changes to devices */
12271 commitUSBControllers = true;
12272 }
12273 }
12274 else
12275 {
12276 /* the list of controllers itself is not changed,
12277 * just commit changes to controllers themselves */
12278 commitUSBControllers = true;
12279 }
12280
12281 if (commitUSBControllers)
12282 {
12283 for (USBControllerList::const_iterator
12284 it = mUSBControllers->begin();
12285 it != mUSBControllers->end();
12286 ++it)
12287 {
12288 (*it)->i_commit();
12289 }
12290 }
12291
12292 if (i_isSessionMachine())
12293 {
12294 /* attach new data to the primary machine and reshare it */
12295 mPeer->mUserData.attach(mUserData);
12296 mPeer->mHWData.attach(mHWData);
12297 /* mmMediumAttachments is reshared by fixupMedia */
12298 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12299 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12300 }
12301}
12302
12303/**
12304 * Copies all the hardware data from the given machine.
12305 *
12306 * Currently, only called when the VM is being restored from a snapshot. In
12307 * particular, this implies that the VM is not running during this method's
12308 * call.
12309 *
12310 * @note This method must be called from under this object's lock.
12311 *
12312 * @note This method doesn't call #i_commit(), so all data remains backed up and
12313 * unsaved.
12314 */
12315void Machine::i_copyFrom(Machine *aThat)
12316{
12317 AssertReturnVoid(!i_isSnapshotMachine());
12318 AssertReturnVoid(aThat->i_isSnapshotMachine());
12319
12320 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12321
12322 mHWData.assignCopy(aThat->mHWData);
12323
12324 // create copies of all shared folders (mHWData after attaching a copy
12325 // contains just references to original objects)
12326 for (HWData::SharedFolderList::iterator
12327 it = mHWData->mSharedFolders.begin();
12328 it != mHWData->mSharedFolders.end();
12329 ++it)
12330 {
12331 ComObjPtr<SharedFolder> folder;
12332 folder.createObject();
12333 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12334 AssertComRC(rc);
12335 *it = folder;
12336 }
12337
12338 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12339 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12340 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12341 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12342 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12343
12344 /* create private copies of all controllers */
12345 mStorageControllers.backup();
12346 mStorageControllers->clear();
12347 for (StorageControllerList::const_iterator
12348 it = aThat->mStorageControllers->begin();
12349 it != aThat->mStorageControllers->end();
12350 ++it)
12351 {
12352 ComObjPtr<StorageController> ctrl;
12353 ctrl.createObject();
12354 ctrl->initCopy(this, *it);
12355 mStorageControllers->push_back(ctrl);
12356 }
12357
12358 /* create private copies of all USB controllers */
12359 mUSBControllers.backup();
12360 mUSBControllers->clear();
12361 for (USBControllerList::const_iterator
12362 it = aThat->mUSBControllers->begin();
12363 it != aThat->mUSBControllers->end();
12364 ++it)
12365 {
12366 ComObjPtr<USBController> ctrl;
12367 ctrl.createObject();
12368 ctrl->initCopy(this, *it);
12369 mUSBControllers->push_back(ctrl);
12370 }
12371
12372 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12374 {
12375 if (mNetworkAdapters[slot].isNotNull())
12376 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12377 else
12378 {
12379 unconst(mNetworkAdapters[slot]).createObject();
12380 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12381 }
12382 }
12383 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12384 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12385 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12386 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12387}
12388
12389/**
12390 * Returns whether the given storage controller is hotplug capable.
12391 *
12392 * @returns true if the controller supports hotplugging
12393 * false otherwise.
12394 * @param enmCtrlType The controller type to check for.
12395 */
12396bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12397{
12398 ComPtr<ISystemProperties> systemProperties;
12399 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12400 if (FAILED(rc))
12401 return false;
12402
12403 BOOL aHotplugCapable = FALSE;
12404 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12405
12406 return RT_BOOL(aHotplugCapable);
12407}
12408
12409#ifdef VBOX_WITH_RESOURCE_USAGE_API
12410
12411void Machine::i_getDiskList(MediaList &list)
12412{
12413 for (MediumAttachmentList::const_iterator
12414 it = mMediumAttachments->begin();
12415 it != mMediumAttachments->end();
12416 ++it)
12417 {
12418 MediumAttachment *pAttach = *it;
12419 /* just in case */
12420 AssertContinue(pAttach);
12421
12422 AutoCaller localAutoCallerA(pAttach);
12423 if (FAILED(localAutoCallerA.rc())) continue;
12424
12425 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12426
12427 if (pAttach->i_getType() == DeviceType_HardDisk)
12428 list.push_back(pAttach->i_getMedium());
12429 }
12430}
12431
12432void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12433{
12434 AssertReturnVoid(isWriteLockOnCurrentThread());
12435 AssertPtrReturnVoid(aCollector);
12436
12437 pm::CollectorHAL *hal = aCollector->getHAL();
12438 /* Create sub metrics */
12439 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12440 "Percentage of processor time spent in user mode by the VM process.");
12441 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12442 "Percentage of processor time spent in kernel mode by the VM process.");
12443 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12444 "Size of resident portion of VM process in memory.");
12445 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12446 "Actual size of all VM disks combined.");
12447 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12448 "Network receive rate.");
12449 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12450 "Network transmit rate.");
12451 /* Create and register base metrics */
12452 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12453 cpuLoadUser, cpuLoadKernel);
12454 aCollector->registerBaseMetric(cpuLoad);
12455 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12456 ramUsageUsed);
12457 aCollector->registerBaseMetric(ramUsage);
12458 MediaList disks;
12459 i_getDiskList(disks);
12460 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12461 diskUsageUsed);
12462 aCollector->registerBaseMetric(diskUsage);
12463
12464 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12465 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12466 new pm::AggregateAvg()));
12467 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12468 new pm::AggregateMin()));
12469 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12470 new pm::AggregateMax()));
12471 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12472 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12473 new pm::AggregateAvg()));
12474 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12475 new pm::AggregateMin()));
12476 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12477 new pm::AggregateMax()));
12478
12479 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12480 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12481 new pm::AggregateAvg()));
12482 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12483 new pm::AggregateMin()));
12484 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12485 new pm::AggregateMax()));
12486
12487 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12488 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12489 new pm::AggregateAvg()));
12490 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12491 new pm::AggregateMin()));
12492 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12493 new pm::AggregateMax()));
12494
12495
12496 /* Guest metrics collector */
12497 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12498 aCollector->registerGuest(mCollectorGuest);
12499 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12500
12501 /* Create sub metrics */
12502 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12503 "Percentage of processor time spent in user mode as seen by the guest.");
12504 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12505 "Percentage of processor time spent in kernel mode as seen by the guest.");
12506 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12507 "Percentage of processor time spent idling as seen by the guest.");
12508
12509 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12510 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12511 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12512 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12513 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12514 pm::SubMetric *guestMemCache = new pm::SubMetric(
12515 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12516
12517 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12518 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12519
12520 /* Create and register base metrics */
12521 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12522 machineNetRx, machineNetTx);
12523 aCollector->registerBaseMetric(machineNetRate);
12524
12525 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12526 guestLoadUser, guestLoadKernel, guestLoadIdle);
12527 aCollector->registerBaseMetric(guestCpuLoad);
12528
12529 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12530 guestMemTotal, guestMemFree,
12531 guestMemBalloon, guestMemShared,
12532 guestMemCache, guestPagedTotal);
12533 aCollector->registerBaseMetric(guestCpuMem);
12534
12535 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12536 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12537 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12538 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12539
12540 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12541 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12542 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12543 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12544
12545 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12546 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12547 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12548 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12549
12550 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12551 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12552 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12553 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12554
12555 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12556 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12557 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12558 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12559
12560 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12561 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12562 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12563 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12564
12565 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12566 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12567 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12568 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12569
12570 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12571 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12572 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12573 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12574
12575 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12576 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12577 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12578 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12579
12580 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12581 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12583 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12584
12585 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12586 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12588 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12589}
12590
12591void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12592{
12593 AssertReturnVoid(isWriteLockOnCurrentThread());
12594
12595 if (aCollector)
12596 {
12597 aCollector->unregisterMetricsFor(aMachine);
12598 aCollector->unregisterBaseMetricsFor(aMachine);
12599 }
12600}
12601
12602#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12603
12604
12605////////////////////////////////////////////////////////////////////////////////
12606
12607DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12608
12609HRESULT SessionMachine::FinalConstruct()
12610{
12611 LogFlowThisFunc(("\n"));
12612
12613 mClientToken = NULL;
12614
12615 return BaseFinalConstruct();
12616}
12617
12618void SessionMachine::FinalRelease()
12619{
12620 LogFlowThisFunc(("\n"));
12621
12622 Assert(!mClientToken);
12623 /* paranoia, should not hang around any more */
12624 if (mClientToken)
12625 {
12626 delete mClientToken;
12627 mClientToken = NULL;
12628 }
12629
12630 uninit(Uninit::Unexpected);
12631
12632 BaseFinalRelease();
12633}
12634
12635/**
12636 * @note Must be called only by Machine::LockMachine() from its own write lock.
12637 */
12638HRESULT SessionMachine::init(Machine *aMachine)
12639{
12640 LogFlowThisFuncEnter();
12641 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12642
12643 AssertReturn(aMachine, E_INVALIDARG);
12644
12645 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12646
12647 /* Enclose the state transition NotReady->InInit->Ready */
12648 AutoInitSpan autoInitSpan(this);
12649 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12650
12651 HRESULT rc = S_OK;
12652
12653 RT_ZERO(mAuthLibCtx);
12654
12655 /* create the machine client token */
12656 try
12657 {
12658 mClientToken = new ClientToken(aMachine, this);
12659 if (!mClientToken->isReady())
12660 {
12661 delete mClientToken;
12662 mClientToken = NULL;
12663 rc = E_FAIL;
12664 }
12665 }
12666 catch (std::bad_alloc &)
12667 {
12668 rc = E_OUTOFMEMORY;
12669 }
12670 if (FAILED(rc))
12671 return rc;
12672
12673 /* memorize the peer Machine */
12674 unconst(mPeer) = aMachine;
12675 /* share the parent pointer */
12676 unconst(mParent) = aMachine->mParent;
12677
12678 /* take the pointers to data to share */
12679 mData.share(aMachine->mData);
12680 mSSData.share(aMachine->mSSData);
12681
12682 mUserData.share(aMachine->mUserData);
12683 mHWData.share(aMachine->mHWData);
12684 mMediumAttachments.share(aMachine->mMediumAttachments);
12685
12686 mStorageControllers.allocate();
12687 for (StorageControllerList::const_iterator
12688 it = aMachine->mStorageControllers->begin();
12689 it != aMachine->mStorageControllers->end();
12690 ++it)
12691 {
12692 ComObjPtr<StorageController> ctl;
12693 ctl.createObject();
12694 ctl->init(this, *it);
12695 mStorageControllers->push_back(ctl);
12696 }
12697
12698 mUSBControllers.allocate();
12699 for (USBControllerList::const_iterator
12700 it = aMachine->mUSBControllers->begin();
12701 it != aMachine->mUSBControllers->end();
12702 ++it)
12703 {
12704 ComObjPtr<USBController> ctl;
12705 ctl.createObject();
12706 ctl->init(this, *it);
12707 mUSBControllers->push_back(ctl);
12708 }
12709
12710 unconst(mBIOSSettings).createObject();
12711 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12712 /* create another VRDEServer object that will be mutable */
12713 unconst(mVRDEServer).createObject();
12714 mVRDEServer->init(this, aMachine->mVRDEServer);
12715 /* create another audio adapter object that will be mutable */
12716 unconst(mAudioAdapter).createObject();
12717 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12718 /* create a list of serial ports that will be mutable */
12719 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12720 {
12721 unconst(mSerialPorts[slot]).createObject();
12722 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12723 }
12724 /* create a list of parallel ports that will be mutable */
12725 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12726 {
12727 unconst(mParallelPorts[slot]).createObject();
12728 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12729 }
12730
12731 /* create another USB device filters object that will be mutable */
12732 unconst(mUSBDeviceFilters).createObject();
12733 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12734
12735 /* create a list of network adapters that will be mutable */
12736 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12737 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12738 {
12739 unconst(mNetworkAdapters[slot]).createObject();
12740 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12741 }
12742
12743 /* create another bandwidth control object that will be mutable */
12744 unconst(mBandwidthControl).createObject();
12745 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12746
12747 /* default is to delete saved state on Saved -> PoweredOff transition */
12748 mRemoveSavedState = true;
12749
12750 /* Confirm a successful initialization when it's the case */
12751 autoInitSpan.setSucceeded();
12752
12753 miNATNetworksStarted = 0;
12754
12755 LogFlowThisFuncLeave();
12756 return rc;
12757}
12758
12759/**
12760 * Uninitializes this session object. If the reason is other than
12761 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12762 * or the client watcher code.
12763 *
12764 * @param aReason uninitialization reason
12765 *
12766 * @note Locks mParent + this object for writing.
12767 */
12768void SessionMachine::uninit(Uninit::Reason aReason)
12769{
12770 LogFlowThisFuncEnter();
12771 LogFlowThisFunc(("reason=%d\n", aReason));
12772
12773 /*
12774 * Strongly reference ourselves to prevent this object deletion after
12775 * mData->mSession.mMachine.setNull() below (which can release the last
12776 * reference and call the destructor). Important: this must be done before
12777 * accessing any members (and before AutoUninitSpan that does it as well).
12778 * This self reference will be released as the very last step on return.
12779 */
12780 ComObjPtr<SessionMachine> selfRef;
12781 if (aReason != Uninit::Unexpected)
12782 selfRef = this;
12783
12784 /* Enclose the state transition Ready->InUninit->NotReady */
12785 AutoUninitSpan autoUninitSpan(this);
12786 if (autoUninitSpan.uninitDone())
12787 {
12788 LogFlowThisFunc(("Already uninitialized\n"));
12789 LogFlowThisFuncLeave();
12790 return;
12791 }
12792
12793 if (autoUninitSpan.initFailed())
12794 {
12795 /* We've been called by init() because it's failed. It's not really
12796 * necessary (nor it's safe) to perform the regular uninit sequence
12797 * below, the following is enough.
12798 */
12799 LogFlowThisFunc(("Initialization failed.\n"));
12800 /* destroy the machine client token */
12801 if (mClientToken)
12802 {
12803 delete mClientToken;
12804 mClientToken = NULL;
12805 }
12806 uninitDataAndChildObjects();
12807 mData.free();
12808 unconst(mParent) = NULL;
12809 unconst(mPeer) = NULL;
12810 LogFlowThisFuncLeave();
12811 return;
12812 }
12813
12814 MachineState_T lastState;
12815 {
12816 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12817 lastState = mData->mMachineState;
12818 }
12819 NOREF(lastState);
12820
12821#ifdef VBOX_WITH_USB
12822 // release all captured USB devices, but do this before requesting the locks below
12823 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12824 {
12825 /* Console::captureUSBDevices() is called in the VM process only after
12826 * setting the machine state to Starting or Restoring.
12827 * Console::detachAllUSBDevices() will be called upon successful
12828 * termination. So, we need to release USB devices only if there was
12829 * an abnormal termination of a running VM.
12830 *
12831 * This is identical to SessionMachine::DetachAllUSBDevices except
12832 * for the aAbnormal argument. */
12833 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12834 AssertComRC(rc);
12835 NOREF(rc);
12836
12837 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12838 if (service)
12839 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12840 }
12841#endif /* VBOX_WITH_USB */
12842
12843 // we need to lock this object in uninit() because the lock is shared
12844 // with mPeer (as well as data we modify below). mParent lock is needed
12845 // by several calls to it.
12846 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12847
12848#ifdef VBOX_WITH_RESOURCE_USAGE_API
12849 /*
12850 * It is safe to call Machine::i_unregisterMetrics() here because
12851 * PerformanceCollector::samplerCallback no longer accesses guest methods
12852 * holding the lock.
12853 */
12854 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12855 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12856 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12857 if (mCollectorGuest)
12858 {
12859 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12860 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12861 mCollectorGuest = NULL;
12862 }
12863#endif
12864
12865 if (aReason == Uninit::Abnormal)
12866 {
12867 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12868
12869 /* reset the state to Aborted */
12870 if (mData->mMachineState != MachineState_Aborted)
12871 i_setMachineState(MachineState_Aborted);
12872 }
12873
12874 // any machine settings modified?
12875 if (mData->flModifications)
12876 {
12877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12878 i_rollback(false /* aNotify */);
12879 }
12880
12881 mData->mSession.mPID = NIL_RTPROCESS;
12882
12883 if (aReason == Uninit::Unexpected)
12884 {
12885 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12886 * client watcher thread to update the set of machines that have open
12887 * sessions. */
12888 mParent->i_updateClientWatcher();
12889 }
12890
12891 /* uninitialize all remote controls */
12892 if (mData->mSession.mRemoteControls.size())
12893 {
12894 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12895 mData->mSession.mRemoteControls.size()));
12896
12897 /* Always restart a the beginning, since the iterator is invalidated
12898 * by using erase(). */
12899 for (Data::Session::RemoteControlList::iterator
12900 it = mData->mSession.mRemoteControls.begin();
12901 it != mData->mSession.mRemoteControls.end();
12902 it = mData->mSession.mRemoteControls.begin())
12903 {
12904 ComPtr<IInternalSessionControl> pControl = *it;
12905 mData->mSession.mRemoteControls.erase(it);
12906 multilock.release();
12907 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12908 HRESULT rc = pControl->Uninitialize();
12909 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12910 if (FAILED(rc))
12911 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12912 multilock.acquire();
12913 }
12914 mData->mSession.mRemoteControls.clear();
12915 }
12916
12917 /* Remove all references to the NAT network service. The service will stop
12918 * if all references (also from other VMs) are removed. */
12919 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12920 {
12921 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12922 {
12923 BOOL enabled;
12924 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12925 if ( FAILED(hrc)
12926 || !enabled)
12927 continue;
12928
12929 NetworkAttachmentType_T type;
12930 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12931 if ( SUCCEEDED(hrc)
12932 && type == NetworkAttachmentType_NATNetwork)
12933 {
12934 Bstr name;
12935 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12936 if (SUCCEEDED(hrc))
12937 {
12938 multilock.release();
12939 Utf8Str strName(name);
12940 LogRel(("VM '%s' stops using NAT network '%s'\n",
12941 mUserData->s.strName.c_str(), strName.c_str()));
12942 mParent->i_natNetworkRefDec(strName);
12943 multilock.acquire();
12944 }
12945 }
12946 }
12947 }
12948
12949 /*
12950 * An expected uninitialization can come only from #i_checkForDeath().
12951 * Otherwise it means that something's gone really wrong (for example,
12952 * the Session implementation has released the VirtualBox reference
12953 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12954 * etc). However, it's also possible, that the client releases the IPC
12955 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12956 * but the VirtualBox release event comes first to the server process.
12957 * This case is practically possible, so we should not assert on an
12958 * unexpected uninit, just log a warning.
12959 */
12960
12961 if (aReason == Uninit::Unexpected)
12962 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12963
12964 if (aReason != Uninit::Normal)
12965 {
12966 mData->mSession.mDirectControl.setNull();
12967 }
12968 else
12969 {
12970 /* this must be null here (see #OnSessionEnd()) */
12971 Assert(mData->mSession.mDirectControl.isNull());
12972 Assert(mData->mSession.mState == SessionState_Unlocking);
12973 Assert(!mData->mSession.mProgress.isNull());
12974 }
12975 if (mData->mSession.mProgress)
12976 {
12977 if (aReason == Uninit::Normal)
12978 mData->mSession.mProgress->i_notifyComplete(S_OK);
12979 else
12980 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12981 COM_IIDOF(ISession),
12982 getComponentName(),
12983 tr("The VM session was aborted"));
12984 mData->mSession.mProgress.setNull();
12985 }
12986
12987 if (mConsoleTaskData.mProgress)
12988 {
12989 Assert(aReason == Uninit::Abnormal);
12990 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12991 COM_IIDOF(ISession),
12992 getComponentName(),
12993 tr("The VM session was aborted"));
12994 mConsoleTaskData.mProgress.setNull();
12995 }
12996
12997 /* remove the association between the peer machine and this session machine */
12998 Assert( (SessionMachine*)mData->mSession.mMachine == this
12999 || aReason == Uninit::Unexpected);
13000
13001 /* reset the rest of session data */
13002 mData->mSession.mLockType = LockType_Null;
13003 mData->mSession.mMachine.setNull();
13004 mData->mSession.mState = SessionState_Unlocked;
13005 mData->mSession.mName.setNull();
13006
13007 /* destroy the machine client token before leaving the exclusive lock */
13008 if (mClientToken)
13009 {
13010 delete mClientToken;
13011 mClientToken = NULL;
13012 }
13013
13014 /* fire an event */
13015 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13016
13017 uninitDataAndChildObjects();
13018
13019 /* free the essential data structure last */
13020 mData.free();
13021
13022 /* release the exclusive lock before setting the below two to NULL */
13023 multilock.release();
13024
13025 unconst(mParent) = NULL;
13026 unconst(mPeer) = NULL;
13027
13028 AuthLibUnload(&mAuthLibCtx);
13029
13030 LogFlowThisFuncLeave();
13031}
13032
13033// util::Lockable interface
13034////////////////////////////////////////////////////////////////////////////////
13035
13036/**
13037 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13038 * with the primary Machine instance (mPeer).
13039 */
13040RWLockHandle *SessionMachine::lockHandle() const
13041{
13042 AssertReturn(mPeer != NULL, NULL);
13043 return mPeer->lockHandle();
13044}
13045
13046// IInternalMachineControl methods
13047////////////////////////////////////////////////////////////////////////////////
13048
13049/**
13050 * Passes collected guest statistics to performance collector object
13051 */
13052HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13053 ULONG aCpuKernel, ULONG aCpuIdle,
13054 ULONG aMemTotal, ULONG aMemFree,
13055 ULONG aMemBalloon, ULONG aMemShared,
13056 ULONG aMemCache, ULONG aPageTotal,
13057 ULONG aAllocVMM, ULONG aFreeVMM,
13058 ULONG aBalloonedVMM, ULONG aSharedVMM,
13059 ULONG aVmNetRx, ULONG aVmNetTx)
13060{
13061#ifdef VBOX_WITH_RESOURCE_USAGE_API
13062 if (mCollectorGuest)
13063 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13064 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13065 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13066 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13067
13068 return S_OK;
13069#else
13070 NOREF(aValidStats);
13071 NOREF(aCpuUser);
13072 NOREF(aCpuKernel);
13073 NOREF(aCpuIdle);
13074 NOREF(aMemTotal);
13075 NOREF(aMemFree);
13076 NOREF(aMemBalloon);
13077 NOREF(aMemShared);
13078 NOREF(aMemCache);
13079 NOREF(aPageTotal);
13080 NOREF(aAllocVMM);
13081 NOREF(aFreeVMM);
13082 NOREF(aBalloonedVMM);
13083 NOREF(aSharedVMM);
13084 NOREF(aVmNetRx);
13085 NOREF(aVmNetTx);
13086 return E_NOTIMPL;
13087#endif
13088}
13089
13090////////////////////////////////////////////////////////////////////////////////
13091//
13092// SessionMachine task records
13093//
13094////////////////////////////////////////////////////////////////////////////////
13095
13096/**
13097 * Task record for saving the machine state.
13098 */
13099class SessionMachine::SaveStateTask
13100 : public Machine::Task
13101{
13102public:
13103 SaveStateTask(SessionMachine *m,
13104 Progress *p,
13105 const Utf8Str &t,
13106 Reason_T enmReason,
13107 const Utf8Str &strStateFilePath)
13108 : Task(m, p, t),
13109 m_enmReason(enmReason),
13110 m_strStateFilePath(strStateFilePath)
13111 {}
13112
13113private:
13114 void handler()
13115 {
13116 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13117 }
13118
13119 Reason_T m_enmReason;
13120 Utf8Str m_strStateFilePath;
13121
13122 friend class SessionMachine;
13123};
13124
13125/**
13126 * Task thread implementation for SessionMachine::SaveState(), called from
13127 * SessionMachine::taskHandler().
13128 *
13129 * @note Locks this object for writing.
13130 *
13131 * @param task
13132 * @return
13133 */
13134void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13135{
13136 LogFlowThisFuncEnter();
13137
13138 AutoCaller autoCaller(this);
13139 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13140 if (FAILED(autoCaller.rc()))
13141 {
13142 /* we might have been uninitialized because the session was accidentally
13143 * closed by the client, so don't assert */
13144 HRESULT rc = setError(E_FAIL,
13145 tr("The session has been accidentally closed"));
13146 task.m_pProgress->i_notifyComplete(rc);
13147 LogFlowThisFuncLeave();
13148 return;
13149 }
13150
13151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13152
13153 HRESULT rc = S_OK;
13154
13155 try
13156 {
13157 ComPtr<IInternalSessionControl> directControl;
13158 if (mData->mSession.mLockType == LockType_VM)
13159 directControl = mData->mSession.mDirectControl;
13160 if (directControl.isNull())
13161 throw setError(VBOX_E_INVALID_VM_STATE,
13162 tr("Trying to save state without a running VM"));
13163 alock.release();
13164 BOOL fSuspendedBySave;
13165 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13166 Assert(!fSuspendedBySave);
13167 alock.acquire();
13168
13169 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13170 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13171 throw E_FAIL);
13172
13173 if (SUCCEEDED(rc))
13174 {
13175 mSSData->strStateFilePath = task.m_strStateFilePath;
13176
13177 /* save all VM settings */
13178 rc = i_saveSettings(NULL);
13179 // no need to check whether VirtualBox.xml needs saving also since
13180 // we can't have a name change pending at this point
13181 }
13182 else
13183 {
13184 // On failure, set the state to the state we had at the beginning.
13185 i_setMachineState(task.m_machineStateBackup);
13186 i_updateMachineStateOnClient();
13187
13188 // Delete the saved state file (might have been already created).
13189 // No need to check whether this is shared with a snapshot here
13190 // because we certainly created a fresh saved state file here.
13191 RTFileDelete(task.m_strStateFilePath.c_str());
13192 }
13193 }
13194 catch (HRESULT aRC) { rc = aRC; }
13195
13196 task.m_pProgress->i_notifyComplete(rc);
13197
13198 LogFlowThisFuncLeave();
13199}
13200
13201/**
13202 * @note Locks this object for writing.
13203 */
13204HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13205{
13206 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13207}
13208
13209HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13210{
13211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13212
13213 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13214 if (FAILED(rc)) return rc;
13215
13216 if ( mData->mMachineState != MachineState_Running
13217 && mData->mMachineState != MachineState_Paused
13218 )
13219 return setError(VBOX_E_INVALID_VM_STATE,
13220 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13221 Global::stringifyMachineState(mData->mMachineState));
13222
13223 ComObjPtr<Progress> pProgress;
13224 pProgress.createObject();
13225 rc = pProgress->init(i_getVirtualBox(),
13226 static_cast<IMachine *>(this) /* aInitiator */,
13227 tr("Saving the execution state of the virtual machine"),
13228 FALSE /* aCancelable */);
13229 if (FAILED(rc))
13230 return rc;
13231
13232 Utf8Str strStateFilePath;
13233 i_composeSavedStateFilename(strStateFilePath);
13234
13235 /* create and start the task on a separate thread (note that it will not
13236 * start working until we release alock) */
13237 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13238 rc = pTask->createThread();
13239 if (FAILED(rc))
13240 return rc;
13241
13242 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13243 i_setMachineState(MachineState_Saving);
13244 i_updateMachineStateOnClient();
13245
13246 pProgress.queryInterfaceTo(aProgress.asOutParam());
13247
13248 return S_OK;
13249}
13250
13251/**
13252 * @note Locks this object for writing.
13253 */
13254HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13255{
13256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13257
13258 HRESULT rc = i_checkStateDependency(MutableStateDep);
13259 if (FAILED(rc)) return rc;
13260
13261 if ( mData->mMachineState != MachineState_PoweredOff
13262 && mData->mMachineState != MachineState_Teleported
13263 && mData->mMachineState != MachineState_Aborted
13264 )
13265 return setError(VBOX_E_INVALID_VM_STATE,
13266 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13267 Global::stringifyMachineState(mData->mMachineState));
13268
13269 com::Utf8Str stateFilePathFull;
13270 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13271 if (RT_FAILURE(vrc))
13272 return setError(VBOX_E_FILE_ERROR,
13273 tr("Invalid saved state file path '%s' (%Rrc)"),
13274 aSavedStateFile.c_str(),
13275 vrc);
13276
13277 mSSData->strStateFilePath = stateFilePathFull;
13278
13279 /* The below i_setMachineState() will detect the state transition and will
13280 * update the settings file */
13281
13282 return i_setMachineState(MachineState_Saved);
13283}
13284
13285/**
13286 * @note Locks this object for writing.
13287 */
13288HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13289{
13290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13291
13292 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13293 if (FAILED(rc)) return rc;
13294
13295 if (mData->mMachineState != MachineState_Saved)
13296 return setError(VBOX_E_INVALID_VM_STATE,
13297 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13298 Global::stringifyMachineState(mData->mMachineState));
13299
13300 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13301
13302 /*
13303 * Saved -> PoweredOff transition will be detected in the SessionMachine
13304 * and properly handled.
13305 */
13306 rc = i_setMachineState(MachineState_PoweredOff);
13307 return rc;
13308}
13309
13310
13311/**
13312 * @note Locks the same as #i_setMachineState() does.
13313 */
13314HRESULT SessionMachine::updateState(MachineState_T aState)
13315{
13316 return i_setMachineState(aState);
13317}
13318
13319/**
13320 * @note Locks this object for writing.
13321 */
13322HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13323{
13324 IProgress *pProgress(aProgress);
13325
13326 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13327
13328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13329
13330 if (mData->mSession.mState != SessionState_Locked)
13331 return VBOX_E_INVALID_OBJECT_STATE;
13332
13333 if (!mData->mSession.mProgress.isNull())
13334 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13335
13336 /* If we didn't reference the NAT network service yet, add a reference to
13337 * force a start */
13338 if (miNATNetworksStarted < 1)
13339 {
13340 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13341 {
13342 BOOL enabled;
13343 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13344 if ( FAILED(hrc)
13345 || !enabled)
13346 continue;
13347
13348 NetworkAttachmentType_T type;
13349 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13350 if ( SUCCEEDED(hrc)
13351 && type == NetworkAttachmentType_NATNetwork)
13352 {
13353 Bstr name;
13354 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13355 if (SUCCEEDED(hrc))
13356 {
13357 Utf8Str strName(name);
13358 LogRel(("VM '%s' starts using NAT network '%s'\n",
13359 mUserData->s.strName.c_str(), strName.c_str()));
13360 mPeer->lockHandle()->unlockWrite();
13361 mParent->i_natNetworkRefInc(strName);
13362#ifdef RT_LOCK_STRICT
13363 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13364#else
13365 mPeer->lockHandle()->lockWrite();
13366#endif
13367 }
13368 }
13369 }
13370 miNATNetworksStarted++;
13371 }
13372
13373 LogFlowThisFunc(("returns S_OK.\n"));
13374 return S_OK;
13375}
13376
13377/**
13378 * @note Locks this object for writing.
13379 */
13380HRESULT SessionMachine::endPowerUp(LONG aResult)
13381{
13382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13383
13384 if (mData->mSession.mState != SessionState_Locked)
13385 return VBOX_E_INVALID_OBJECT_STATE;
13386
13387 /* Finalize the LaunchVMProcess progress object. */
13388 if (mData->mSession.mProgress)
13389 {
13390 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13391 mData->mSession.mProgress.setNull();
13392 }
13393
13394 if (SUCCEEDED((HRESULT)aResult))
13395 {
13396#ifdef VBOX_WITH_RESOURCE_USAGE_API
13397 /* The VM has been powered up successfully, so it makes sense
13398 * now to offer the performance metrics for a running machine
13399 * object. Doing it earlier wouldn't be safe. */
13400 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13401 mData->mSession.mPID);
13402#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13403 }
13404
13405 return S_OK;
13406}
13407
13408/**
13409 * @note Locks this object for writing.
13410 */
13411HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13412{
13413 LogFlowThisFuncEnter();
13414
13415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13416
13417 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13418 E_FAIL);
13419
13420 /* create a progress object to track operation completion */
13421 ComObjPtr<Progress> pProgress;
13422 pProgress.createObject();
13423 pProgress->init(i_getVirtualBox(),
13424 static_cast<IMachine *>(this) /* aInitiator */,
13425 tr("Stopping the virtual machine"),
13426 FALSE /* aCancelable */);
13427
13428 /* fill in the console task data */
13429 mConsoleTaskData.mLastState = mData->mMachineState;
13430 mConsoleTaskData.mProgress = pProgress;
13431
13432 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13433 i_setMachineState(MachineState_Stopping);
13434
13435 pProgress.queryInterfaceTo(aProgress.asOutParam());
13436
13437 return S_OK;
13438}
13439
13440/**
13441 * @note Locks this object for writing.
13442 */
13443HRESULT SessionMachine::endPoweringDown(LONG aResult,
13444 const com::Utf8Str &aErrMsg)
13445{
13446 LogFlowThisFuncEnter();
13447
13448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13449
13450 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13451 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13452 && mConsoleTaskData.mLastState != MachineState_Null,
13453 E_FAIL);
13454
13455 /*
13456 * On failure, set the state to the state we had when BeginPoweringDown()
13457 * was called (this is expected by Console::PowerDown() and the associated
13458 * task). On success the VM process already changed the state to
13459 * MachineState_PoweredOff, so no need to do anything.
13460 */
13461 if (FAILED(aResult))
13462 i_setMachineState(mConsoleTaskData.mLastState);
13463
13464 /* notify the progress object about operation completion */
13465 Assert(mConsoleTaskData.mProgress);
13466 if (SUCCEEDED(aResult))
13467 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13468 else
13469 {
13470 if (aErrMsg.length())
13471 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13472 COM_IIDOF(ISession),
13473 getComponentName(),
13474 aErrMsg.c_str());
13475 else
13476 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13477 }
13478
13479 /* clear out the temporary saved state data */
13480 mConsoleTaskData.mLastState = MachineState_Null;
13481 mConsoleTaskData.mProgress.setNull();
13482
13483 LogFlowThisFuncLeave();
13484 return S_OK;
13485}
13486
13487
13488/**
13489 * Goes through the USB filters of the given machine to see if the given
13490 * device matches any filter or not.
13491 *
13492 * @note Locks the same as USBController::hasMatchingFilter() does.
13493 */
13494HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13495 BOOL *aMatched,
13496 ULONG *aMaskedInterfaces)
13497{
13498 LogFlowThisFunc(("\n"));
13499
13500#ifdef VBOX_WITH_USB
13501 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13502#else
13503 NOREF(aDevice);
13504 NOREF(aMaskedInterfaces);
13505 *aMatched = FALSE;
13506#endif
13507
13508 return S_OK;
13509}
13510
13511/**
13512 * @note Locks the same as Host::captureUSBDevice() does.
13513 */
13514HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13515{
13516 LogFlowThisFunc(("\n"));
13517
13518#ifdef VBOX_WITH_USB
13519 /* if captureDeviceForVM() fails, it must have set extended error info */
13520 clearError();
13521 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13522 if (FAILED(rc)) return rc;
13523
13524 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13525 AssertReturn(service, E_FAIL);
13526 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13527#else
13528 NOREF(aId);
13529 return E_NOTIMPL;
13530#endif
13531}
13532
13533/**
13534 * @note Locks the same as Host::detachUSBDevice() does.
13535 */
13536HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13537 BOOL aDone)
13538{
13539 LogFlowThisFunc(("\n"));
13540
13541#ifdef VBOX_WITH_USB
13542 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13543 AssertReturn(service, E_FAIL);
13544 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13545#else
13546 NOREF(aId);
13547 NOREF(aDone);
13548 return E_NOTIMPL;
13549#endif
13550}
13551
13552/**
13553 * Inserts all machine filters to the USB proxy service and then calls
13554 * Host::autoCaptureUSBDevices().
13555 *
13556 * Called by Console from the VM process upon VM startup.
13557 *
13558 * @note Locks what called methods lock.
13559 */
13560HRESULT SessionMachine::autoCaptureUSBDevices()
13561{
13562 LogFlowThisFunc(("\n"));
13563
13564#ifdef VBOX_WITH_USB
13565 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13566 AssertComRC(rc);
13567 NOREF(rc);
13568
13569 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13570 AssertReturn(service, E_FAIL);
13571 return service->autoCaptureDevicesForVM(this);
13572#else
13573 return S_OK;
13574#endif
13575}
13576
13577/**
13578 * Removes all machine filters from the USB proxy service and then calls
13579 * Host::detachAllUSBDevices().
13580 *
13581 * Called by Console from the VM process upon normal VM termination or by
13582 * SessionMachine::uninit() upon abnormal VM termination (from under the
13583 * Machine/SessionMachine lock).
13584 *
13585 * @note Locks what called methods lock.
13586 */
13587HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13588{
13589 LogFlowThisFunc(("\n"));
13590
13591#ifdef VBOX_WITH_USB
13592 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13593 AssertComRC(rc);
13594 NOREF(rc);
13595
13596 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13597 AssertReturn(service, E_FAIL);
13598 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13599#else
13600 NOREF(aDone);
13601 return S_OK;
13602#endif
13603}
13604
13605/**
13606 * @note Locks this object for writing.
13607 */
13608HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13609 ComPtr<IProgress> &aProgress)
13610{
13611 LogFlowThisFuncEnter();
13612
13613 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13614 /*
13615 * We don't assert below because it might happen that a non-direct session
13616 * informs us it is closed right after we've been uninitialized -- it's ok.
13617 */
13618
13619 /* get IInternalSessionControl interface */
13620 ComPtr<IInternalSessionControl> control(aSession);
13621
13622 ComAssertRet(!control.isNull(), E_INVALIDARG);
13623
13624 /* Creating a Progress object requires the VirtualBox lock, and
13625 * thus locking it here is required by the lock order rules. */
13626 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13627
13628 if (control == mData->mSession.mDirectControl)
13629 {
13630 /* The direct session is being normally closed by the client process
13631 * ----------------------------------------------------------------- */
13632
13633 /* go to the closing state (essential for all open*Session() calls and
13634 * for #i_checkForDeath()) */
13635 Assert(mData->mSession.mState == SessionState_Locked);
13636 mData->mSession.mState = SessionState_Unlocking;
13637
13638 /* set direct control to NULL to release the remote instance */
13639 mData->mSession.mDirectControl.setNull();
13640 LogFlowThisFunc(("Direct control is set to NULL\n"));
13641
13642 if (mData->mSession.mProgress)
13643 {
13644 /* finalize the progress, someone might wait if a frontend
13645 * closes the session before powering on the VM. */
13646 mData->mSession.mProgress->notifyComplete(E_FAIL,
13647 COM_IIDOF(ISession),
13648 getComponentName(),
13649 tr("The VM session was closed before any attempt to power it on"));
13650 mData->mSession.mProgress.setNull();
13651 }
13652
13653 /* Create the progress object the client will use to wait until
13654 * #i_checkForDeath() is called to uninitialize this session object after
13655 * it releases the IPC semaphore.
13656 * Note! Because we're "reusing" mProgress here, this must be a proxy
13657 * object just like for LaunchVMProcess. */
13658 Assert(mData->mSession.mProgress.isNull());
13659 ComObjPtr<ProgressProxy> progress;
13660 progress.createObject();
13661 ComPtr<IUnknown> pPeer(mPeer);
13662 progress->init(mParent, pPeer,
13663 Bstr(tr("Closing session")).raw(),
13664 FALSE /* aCancelable */);
13665 progress.queryInterfaceTo(aProgress.asOutParam());
13666 mData->mSession.mProgress = progress;
13667 }
13668 else
13669 {
13670 /* the remote session is being normally closed */
13671 bool found = false;
13672 for (Data::Session::RemoteControlList::iterator
13673 it = mData->mSession.mRemoteControls.begin();
13674 it != mData->mSession.mRemoteControls.end();
13675 ++it)
13676 {
13677 if (control == *it)
13678 {
13679 found = true;
13680 // This MUST be erase(it), not remove(*it) as the latter
13681 // triggers a very nasty use after free due to the place where
13682 // the value "lives".
13683 mData->mSession.mRemoteControls.erase(it);
13684 break;
13685 }
13686 }
13687 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13688 E_INVALIDARG);
13689 }
13690
13691 /* signal the client watcher thread, because the client is going away */
13692 mParent->i_updateClientWatcher();
13693
13694 LogFlowThisFuncLeave();
13695 return S_OK;
13696}
13697
13698HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13699 std::vector<com::Utf8Str> &aValues,
13700 std::vector<LONG64> &aTimestamps,
13701 std::vector<com::Utf8Str> &aFlags)
13702{
13703 LogFlowThisFunc(("\n"));
13704
13705#ifdef VBOX_WITH_GUEST_PROPS
13706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13707
13708 size_t cEntries = mHWData->mGuestProperties.size();
13709 aNames.resize(cEntries);
13710 aValues.resize(cEntries);
13711 aTimestamps.resize(cEntries);
13712 aFlags.resize(cEntries);
13713
13714 size_t i = 0;
13715 for (HWData::GuestPropertyMap::const_iterator
13716 it = mHWData->mGuestProperties.begin();
13717 it != mHWData->mGuestProperties.end();
13718 ++it, ++i)
13719 {
13720 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13721 aNames[i] = it->first;
13722 aValues[i] = it->second.strValue;
13723 aTimestamps[i] = it->second.mTimestamp;
13724
13725 /* If it is NULL, keep it NULL. */
13726 if (it->second.mFlags)
13727 {
13728 GuestPropWriteFlags(it->second.mFlags, szFlags);
13729 aFlags[i] = szFlags;
13730 }
13731 else
13732 aFlags[i] = "";
13733 }
13734 return S_OK;
13735#else
13736 ReturnComNotImplemented();
13737#endif
13738}
13739
13740HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13741 const com::Utf8Str &aValue,
13742 LONG64 aTimestamp,
13743 const com::Utf8Str &aFlags)
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747#ifdef VBOX_WITH_GUEST_PROPS
13748 try
13749 {
13750 /*
13751 * Convert input up front.
13752 */
13753 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13754 if (aFlags.length())
13755 {
13756 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13757 AssertRCReturn(vrc, E_INVALIDARG);
13758 }
13759
13760 /*
13761 * Now grab the object lock, validate the state and do the update.
13762 */
13763
13764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13765
13766 if (!Global::IsOnline(mData->mMachineState))
13767 {
13768 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13769 VBOX_E_INVALID_VM_STATE);
13770 }
13771
13772 i_setModified(IsModified_MachineData);
13773 mHWData.backup();
13774
13775 bool fDelete = !aValue.length();
13776 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13777 if (it != mHWData->mGuestProperties.end())
13778 {
13779 if (!fDelete)
13780 {
13781 it->second.strValue = aValue;
13782 it->second.mTimestamp = aTimestamp;
13783 it->second.mFlags = fFlags;
13784 }
13785 else
13786 mHWData->mGuestProperties.erase(it);
13787
13788 mData->mGuestPropertiesModified = TRUE;
13789 }
13790 else if (!fDelete)
13791 {
13792 HWData::GuestProperty prop;
13793 prop.strValue = aValue;
13794 prop.mTimestamp = aTimestamp;
13795 prop.mFlags = fFlags;
13796
13797 mHWData->mGuestProperties[aName] = prop;
13798 mData->mGuestPropertiesModified = TRUE;
13799 }
13800
13801 alock.release();
13802
13803 mParent->i_onGuestPropertyChange(mData->mUuid,
13804 Bstr(aName).raw(),
13805 Bstr(aValue).raw(),
13806 Bstr(aFlags).raw());
13807 }
13808 catch (...)
13809 {
13810 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13811 }
13812 return S_OK;
13813#else
13814 ReturnComNotImplemented();
13815#endif
13816}
13817
13818
13819HRESULT SessionMachine::lockMedia()
13820{
13821 AutoMultiWriteLock2 alock(this->lockHandle(),
13822 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13823
13824 AssertReturn( mData->mMachineState == MachineState_Starting
13825 || mData->mMachineState == MachineState_Restoring
13826 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13827
13828 clearError();
13829 alock.release();
13830 return i_lockMedia();
13831}
13832
13833HRESULT SessionMachine::unlockMedia()
13834{
13835 HRESULT hrc = i_unlockMedia();
13836 return hrc;
13837}
13838
13839HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13840 ComPtr<IMediumAttachment> &aNewAttachment)
13841{
13842 // request the host lock first, since might be calling Host methods for getting host drives;
13843 // next, protect the media tree all the while we're in here, as well as our member variables
13844 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13845 this->lockHandle(),
13846 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13847
13848 IMediumAttachment *iAttach = aAttachment;
13849 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13850
13851 Utf8Str ctrlName;
13852 LONG lPort;
13853 LONG lDevice;
13854 bool fTempEject;
13855 {
13856 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13857
13858 /* Need to query the details first, as the IMediumAttachment reference
13859 * might be to the original settings, which we are going to change. */
13860 ctrlName = pAttach->i_getControllerName();
13861 lPort = pAttach->i_getPort();
13862 lDevice = pAttach->i_getDevice();
13863 fTempEject = pAttach->i_getTempEject();
13864 }
13865
13866 if (!fTempEject)
13867 {
13868 /* Remember previously mounted medium. The medium before taking the
13869 * backup is not necessarily the same thing. */
13870 ComObjPtr<Medium> oldmedium;
13871 oldmedium = pAttach->i_getMedium();
13872
13873 i_setModified(IsModified_Storage);
13874 mMediumAttachments.backup();
13875
13876 // The backup operation makes the pAttach reference point to the
13877 // old settings. Re-get the correct reference.
13878 pAttach = i_findAttachment(*mMediumAttachments.data(),
13879 ctrlName,
13880 lPort,
13881 lDevice);
13882
13883 {
13884 AutoCaller autoAttachCaller(this);
13885 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13886
13887 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13888 if (!oldmedium.isNull())
13889 oldmedium->i_removeBackReference(mData->mUuid);
13890
13891 pAttach->i_updateMedium(NULL);
13892 pAttach->i_updateEjected();
13893 }
13894
13895 i_setModified(IsModified_Storage);
13896 }
13897 else
13898 {
13899 {
13900 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13901 pAttach->i_updateEjected();
13902 }
13903 }
13904
13905 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13906
13907 return S_OK;
13908}
13909
13910HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13911 com::Utf8Str &aResult)
13912{
13913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13914
13915 HRESULT hr = S_OK;
13916
13917 if (!mAuthLibCtx.hAuthLibrary)
13918 {
13919 /* Load the external authentication library. */
13920 Bstr authLibrary;
13921 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13922
13923 Utf8Str filename = authLibrary;
13924
13925 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13926 if (RT_FAILURE(rc))
13927 {
13928 hr = setError(E_FAIL,
13929 tr("Could not load the external authentication library '%s' (%Rrc)"),
13930 filename.c_str(), rc);
13931 }
13932 }
13933
13934 /* The auth library might need the machine lock. */
13935 alock.release();
13936
13937 if (FAILED(hr))
13938 return hr;
13939
13940 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13941 {
13942 enum VRDEAuthParams
13943 {
13944 parmUuid = 1,
13945 parmGuestJudgement,
13946 parmUser,
13947 parmPassword,
13948 parmDomain,
13949 parmClientId
13950 };
13951
13952 AuthResult result = AuthResultAccessDenied;
13953
13954 Guid uuid(aAuthParams[parmUuid]);
13955 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13956 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13957
13958 result = AuthLibAuthenticate(&mAuthLibCtx,
13959 uuid.raw(), guestJudgement,
13960 aAuthParams[parmUser].c_str(),
13961 aAuthParams[parmPassword].c_str(),
13962 aAuthParams[parmDomain].c_str(),
13963 u32ClientId);
13964
13965 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13966 size_t cbPassword = aAuthParams[parmPassword].length();
13967 if (cbPassword)
13968 {
13969 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13970 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13971 }
13972
13973 if (result == AuthResultAccessGranted)
13974 aResult = "granted";
13975 else
13976 aResult = "denied";
13977
13978 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13979 aAuthParams[parmUser].c_str(), aResult.c_str()));
13980 }
13981 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13982 {
13983 enum VRDEAuthDisconnectParams
13984 {
13985 parmUuid = 1,
13986 parmClientId
13987 };
13988
13989 Guid uuid(aAuthParams[parmUuid]);
13990 uint32_t u32ClientId = 0;
13991 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13992 }
13993 else
13994 {
13995 hr = E_INVALIDARG;
13996 }
13997
13998 return hr;
13999}
14000
14001// public methods only for internal purposes
14002/////////////////////////////////////////////////////////////////////////////
14003
14004#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14005/**
14006 * Called from the client watcher thread to check for expected or unexpected
14007 * death of the client process that has a direct session to this machine.
14008 *
14009 * On Win32 and on OS/2, this method is called only when we've got the
14010 * mutex (i.e. the client has either died or terminated normally) so it always
14011 * returns @c true (the client is terminated, the session machine is
14012 * uninitialized).
14013 *
14014 * On other platforms, the method returns @c true if the client process has
14015 * terminated normally or abnormally and the session machine was uninitialized,
14016 * and @c false if the client process is still alive.
14017 *
14018 * @note Locks this object for writing.
14019 */
14020bool SessionMachine::i_checkForDeath()
14021{
14022 Uninit::Reason reason;
14023 bool terminated = false;
14024
14025 /* Enclose autoCaller with a block because calling uninit() from under it
14026 * will deadlock. */
14027 {
14028 AutoCaller autoCaller(this);
14029 if (!autoCaller.isOk())
14030 {
14031 /* return true if not ready, to cause the client watcher to exclude
14032 * the corresponding session from watching */
14033 LogFlowThisFunc(("Already uninitialized!\n"));
14034 return true;
14035 }
14036
14037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14038
14039 /* Determine the reason of death: if the session state is Closing here,
14040 * everything is fine. Otherwise it means that the client did not call
14041 * OnSessionEnd() before it released the IPC semaphore. This may happen
14042 * either because the client process has abnormally terminated, or
14043 * because it simply forgot to call ISession::Close() before exiting. We
14044 * threat the latter also as an abnormal termination (see
14045 * Session::uninit() for details). */
14046 reason = mData->mSession.mState == SessionState_Unlocking ?
14047 Uninit::Normal :
14048 Uninit::Abnormal;
14049
14050 if (mClientToken)
14051 terminated = mClientToken->release();
14052 } /* AutoCaller block */
14053
14054 if (terminated)
14055 uninit(reason);
14056
14057 return terminated;
14058}
14059
14060void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 strTokenId.setNull();
14065
14066 AutoCaller autoCaller(this);
14067 AssertComRCReturnVoid(autoCaller.rc());
14068
14069 Assert(mClientToken);
14070 if (mClientToken)
14071 mClientToken->getId(strTokenId);
14072}
14073#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14074IToken *SessionMachine::i_getToken()
14075{
14076 LogFlowThisFunc(("\n"));
14077
14078 AutoCaller autoCaller(this);
14079 AssertComRCReturn(autoCaller.rc(), NULL);
14080
14081 Assert(mClientToken);
14082 if (mClientToken)
14083 return mClientToken->getToken();
14084 else
14085 return NULL;
14086}
14087#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14088
14089Machine::ClientToken *SessionMachine::i_getClientToken()
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), NULL);
14095
14096 return mClientToken;
14097}
14098
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14122}
14123
14124/**
14125 * @note Locks this object for reading.
14126 */
14127HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14128 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14129 IN_BSTR aGuestIp, LONG aGuestPort)
14130{
14131 LogFlowThisFunc(("\n"));
14132
14133 AutoCaller autoCaller(this);
14134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14135
14136 ComPtr<IInternalSessionControl> directControl;
14137 {
14138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14139 if (mData->mSession.mLockType == LockType_VM)
14140 directControl = mData->mSession.mDirectControl;
14141 }
14142
14143 /* ignore notifications sent after #OnSessionEnd() is called */
14144 if (!directControl)
14145 return S_OK;
14146 /*
14147 * instead acting like callback we ask IVirtualBox deliver corresponding event
14148 */
14149
14150 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14151 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14152 return S_OK;
14153}
14154
14155/**
14156 * @note Locks this object for reading.
14157 */
14158HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnAudioAdapterChange(audioAdapter);
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 if (mData->mSession.mLockType == LockType_VM)
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnSerialPortChange(serialPort);
14201}
14202
14203/**
14204 * @note Locks this object for reading.
14205 */
14206HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14207{
14208 LogFlowThisFunc(("\n"));
14209
14210 AutoCaller autoCaller(this);
14211 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14212
14213 ComPtr<IInternalSessionControl> directControl;
14214 {
14215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14216 if (mData->mSession.mLockType == LockType_VM)
14217 directControl = mData->mSession.mDirectControl;
14218 }
14219
14220 /* ignore notifications sent after #OnSessionEnd() is called */
14221 if (!directControl)
14222 return S_OK;
14223
14224 return directControl->OnParallelPortChange(parallelPort);
14225}
14226
14227/**
14228 * @note Locks this object for reading.
14229 */
14230HRESULT SessionMachine::i_onStorageControllerChange()
14231{
14232 LogFlowThisFunc(("\n"));
14233
14234 AutoCaller autoCaller(this);
14235 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14236
14237 ComPtr<IInternalSessionControl> directControl;
14238 {
14239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14240 if (mData->mSession.mLockType == LockType_VM)
14241 directControl = mData->mSession.mDirectControl;
14242 }
14243
14244 /* ignore notifications sent after #OnSessionEnd() is called */
14245 if (!directControl)
14246 return S_OK;
14247
14248 return directControl->OnStorageControllerChange();
14249}
14250
14251/**
14252 * @note Locks this object for reading.
14253 */
14254HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14255{
14256 LogFlowThisFunc(("\n"));
14257
14258 AutoCaller autoCaller(this);
14259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14260
14261 ComPtr<IInternalSessionControl> directControl;
14262 {
14263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14264 if (mData->mSession.mLockType == LockType_VM)
14265 directControl = mData->mSession.mDirectControl;
14266 }
14267
14268 /* ignore notifications sent after #OnSessionEnd() is called */
14269 if (!directControl)
14270 return S_OK;
14271
14272 return directControl->OnMediumChange(aAttachment, aForce);
14273}
14274
14275/**
14276 * @note Locks this object for reading.
14277 */
14278HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14279{
14280 LogFlowThisFunc(("\n"));
14281
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14284
14285 ComPtr<IInternalSessionControl> directControl;
14286 {
14287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14288 if (mData->mSession.mLockType == LockType_VM)
14289 directControl = mData->mSession.mDirectControl;
14290 }
14291
14292 /* ignore notifications sent after #OnSessionEnd() is called */
14293 if (!directControl)
14294 return S_OK;
14295
14296 return directControl->OnCPUChange(aCPU, aRemove);
14297}
14298
14299HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 if (mData->mSession.mLockType == LockType_VM)
14310 directControl = mData->mSession.mDirectControl;
14311 }
14312
14313 /* ignore notifications sent after #OnSessionEnd() is called */
14314 if (!directControl)
14315 return S_OK;
14316
14317 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14318}
14319
14320/**
14321 * @note Locks this object for reading.
14322 */
14323HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14324{
14325 LogFlowThisFunc(("\n"));
14326
14327 AutoCaller autoCaller(this);
14328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14329
14330 ComPtr<IInternalSessionControl> directControl;
14331 {
14332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14333 if (mData->mSession.mLockType == LockType_VM)
14334 directControl = mData->mSession.mDirectControl;
14335 }
14336
14337 /* ignore notifications sent after #OnSessionEnd() is called */
14338 if (!directControl)
14339 return S_OK;
14340
14341 return directControl->OnVRDEServerChange(aRestart);
14342}
14343
14344/**
14345 * @note Locks this object for reading.
14346 */
14347HRESULT SessionMachine::i_onVideoCaptureChange()
14348{
14349 LogFlowThisFunc(("\n"));
14350
14351 AutoCaller autoCaller(this);
14352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14353
14354 ComPtr<IInternalSessionControl> directControl;
14355 {
14356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14357 if (mData->mSession.mLockType == LockType_VM)
14358 directControl = mData->mSession.mDirectControl;
14359 }
14360
14361 /* ignore notifications sent after #OnSessionEnd() is called */
14362 if (!directControl)
14363 return S_OK;
14364
14365 return directControl->OnVideoCaptureChange();
14366}
14367
14368/**
14369 * @note Locks this object for reading.
14370 */
14371HRESULT SessionMachine::i_onUSBControllerChange()
14372{
14373 LogFlowThisFunc(("\n"));
14374
14375 AutoCaller autoCaller(this);
14376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14377
14378 ComPtr<IInternalSessionControl> directControl;
14379 {
14380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14381 if (mData->mSession.mLockType == LockType_VM)
14382 directControl = mData->mSession.mDirectControl;
14383 }
14384
14385 /* ignore notifications sent after #OnSessionEnd() is called */
14386 if (!directControl)
14387 return S_OK;
14388
14389 return directControl->OnUSBControllerChange();
14390}
14391
14392/**
14393 * @note Locks this object for reading.
14394 */
14395HRESULT SessionMachine::i_onSharedFolderChange()
14396{
14397 LogFlowThisFunc(("\n"));
14398
14399 AutoCaller autoCaller(this);
14400 AssertComRCReturnRC(autoCaller.rc());
14401
14402 ComPtr<IInternalSessionControl> directControl;
14403 {
14404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14405 if (mData->mSession.mLockType == LockType_VM)
14406 directControl = mData->mSession.mDirectControl;
14407 }
14408
14409 /* ignore notifications sent after #OnSessionEnd() is called */
14410 if (!directControl)
14411 return S_OK;
14412
14413 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14414}
14415
14416/**
14417 * @note Locks this object for reading.
14418 */
14419HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14420{
14421 LogFlowThisFunc(("\n"));
14422
14423 AutoCaller autoCaller(this);
14424 AssertComRCReturnRC(autoCaller.rc());
14425
14426 ComPtr<IInternalSessionControl> directControl;
14427 {
14428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14429 if (mData->mSession.mLockType == LockType_VM)
14430 directControl = mData->mSession.mDirectControl;
14431 }
14432
14433 /* ignore notifications sent after #OnSessionEnd() is called */
14434 if (!directControl)
14435 return S_OK;
14436
14437 return directControl->OnClipboardModeChange(aClipboardMode);
14438}
14439
14440/**
14441 * @note Locks this object for reading.
14442 */
14443HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14444{
14445 LogFlowThisFunc(("\n"));
14446
14447 AutoCaller autoCaller(this);
14448 AssertComRCReturnRC(autoCaller.rc());
14449
14450 ComPtr<IInternalSessionControl> directControl;
14451 {
14452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14453 if (mData->mSession.mLockType == LockType_VM)
14454 directControl = mData->mSession.mDirectControl;
14455 }
14456
14457 /* ignore notifications sent after #OnSessionEnd() is called */
14458 if (!directControl)
14459 return S_OK;
14460
14461 return directControl->OnDnDModeChange(aDnDMode);
14462}
14463
14464/**
14465 * @note Locks this object for reading.
14466 */
14467HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14468{
14469 LogFlowThisFunc(("\n"));
14470
14471 AutoCaller autoCaller(this);
14472 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14473
14474 ComPtr<IInternalSessionControl> directControl;
14475 {
14476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14477 if (mData->mSession.mLockType == LockType_VM)
14478 directControl = mData->mSession.mDirectControl;
14479 }
14480
14481 /* ignore notifications sent after #OnSessionEnd() is called */
14482 if (!directControl)
14483 return S_OK;
14484
14485 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14486}
14487
14488/**
14489 * @note Locks this object for reading.
14490 */
14491HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14492{
14493 LogFlowThisFunc(("\n"));
14494
14495 AutoCaller autoCaller(this);
14496 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14497
14498 ComPtr<IInternalSessionControl> directControl;
14499 {
14500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14501 if (mData->mSession.mLockType == LockType_VM)
14502 directControl = mData->mSession.mDirectControl;
14503 }
14504
14505 /* ignore notifications sent after #OnSessionEnd() is called */
14506 if (!directControl)
14507 return S_OK;
14508
14509 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14510}
14511
14512/**
14513 * Returns @c true if this machine's USB controller reports it has a matching
14514 * filter for the given USB device and @c false otherwise.
14515 *
14516 * @note locks this object for reading.
14517 */
14518bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14519{
14520 AutoCaller autoCaller(this);
14521 /* silently return if not ready -- this method may be called after the
14522 * direct machine session has been called */
14523 if (!autoCaller.isOk())
14524 return false;
14525
14526#ifdef VBOX_WITH_USB
14527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14528
14529 switch (mData->mMachineState)
14530 {
14531 case MachineState_Starting:
14532 case MachineState_Restoring:
14533 case MachineState_TeleportingIn:
14534 case MachineState_Paused:
14535 case MachineState_Running:
14536 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14537 * elsewhere... */
14538 alock.release();
14539 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14540 default: break;
14541 }
14542#else
14543 NOREF(aDevice);
14544 NOREF(aMaskedIfs);
14545#endif
14546 return false;
14547}
14548
14549/**
14550 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14551 */
14552HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14553 IVirtualBoxErrorInfo *aError,
14554 ULONG aMaskedIfs,
14555 const com::Utf8Str &aCaptureFilename)
14556{
14557 LogFlowThisFunc(("\n"));
14558
14559 AutoCaller autoCaller(this);
14560
14561 /* This notification may happen after the machine object has been
14562 * uninitialized (the session was closed), so don't assert. */
14563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14564
14565 ComPtr<IInternalSessionControl> directControl;
14566 {
14567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14568 if (mData->mSession.mLockType == LockType_VM)
14569 directControl = mData->mSession.mDirectControl;
14570 }
14571
14572 /* fail on notifications sent after #OnSessionEnd() is called, it is
14573 * expected by the caller */
14574 if (!directControl)
14575 return E_FAIL;
14576
14577 /* No locks should be held at this point. */
14578 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14579 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14580
14581 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14582}
14583
14584/**
14585 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14586 */
14587HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14588 IVirtualBoxErrorInfo *aError)
14589{
14590 LogFlowThisFunc(("\n"));
14591
14592 AutoCaller autoCaller(this);
14593
14594 /* This notification may happen after the machine object has been
14595 * uninitialized (the session was closed), so don't assert. */
14596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14597
14598 ComPtr<IInternalSessionControl> directControl;
14599 {
14600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14601 if (mData->mSession.mLockType == LockType_VM)
14602 directControl = mData->mSession.mDirectControl;
14603 }
14604
14605 /* fail on notifications sent after #OnSessionEnd() is called, it is
14606 * expected by the caller */
14607 if (!directControl)
14608 return E_FAIL;
14609
14610 /* No locks should be held at this point. */
14611 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14612 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14613
14614 return directControl->OnUSBDeviceDetach(aId, aError);
14615}
14616
14617// protected methods
14618/////////////////////////////////////////////////////////////////////////////
14619
14620/**
14621 * Deletes the given file if it is no longer in use by either the current machine state
14622 * (if the machine is "saved") or any of the machine's snapshots.
14623 *
14624 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14625 * but is different for each SnapshotMachine. When calling this, the order of calling this
14626 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14627 * is therefore critical. I know, it's all rather messy.
14628 *
14629 * @param strStateFile
14630 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14631 * the test for whether the saved state file is in use.
14632 */
14633void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14634 Snapshot *pSnapshotToIgnore)
14635{
14636 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14637 if ( (strStateFile.isNotEmpty())
14638 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14639 )
14640 // ... and it must also not be shared with other snapshots
14641 if ( !mData->mFirstSnapshot
14642 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14643 // this checks the SnapshotMachine's state file paths
14644 )
14645 RTFileDelete(strStateFile.c_str());
14646}
14647
14648/**
14649 * Locks the attached media.
14650 *
14651 * All attached hard disks are locked for writing and DVD/floppy are locked for
14652 * reading. Parents of attached hard disks (if any) are locked for reading.
14653 *
14654 * This method also performs accessibility check of all media it locks: if some
14655 * media is inaccessible, the method will return a failure and a bunch of
14656 * extended error info objects per each inaccessible medium.
14657 *
14658 * Note that this method is atomic: if it returns a success, all media are
14659 * locked as described above; on failure no media is locked at all (all
14660 * succeeded individual locks will be undone).
14661 *
14662 * The caller is responsible for doing the necessary state sanity checks.
14663 *
14664 * The locks made by this method must be undone by calling #unlockMedia() when
14665 * no more needed.
14666 */
14667HRESULT SessionMachine::i_lockMedia()
14668{
14669 AutoCaller autoCaller(this);
14670 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14671
14672 AutoMultiWriteLock2 alock(this->lockHandle(),
14673 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14674
14675 /* bail out if trying to lock things with already set up locking */
14676 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14677
14678 MultiResult mrc(S_OK);
14679
14680 /* Collect locking information for all medium objects attached to the VM. */
14681 for (MediumAttachmentList::const_iterator
14682 it = mMediumAttachments->begin();
14683 it != mMediumAttachments->end();
14684 ++it)
14685 {
14686 MediumAttachment *pAtt = *it;
14687 DeviceType_T devType = pAtt->i_getType();
14688 Medium *pMedium = pAtt->i_getMedium();
14689
14690 MediumLockList *pMediumLockList(new MediumLockList());
14691 // There can be attachments without a medium (floppy/dvd), and thus
14692 // it's impossible to create a medium lock list. It still makes sense
14693 // to have the empty medium lock list in the map in case a medium is
14694 // attached later.
14695 if (pMedium != NULL)
14696 {
14697 MediumType_T mediumType = pMedium->i_getType();
14698 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14699 || mediumType == MediumType_Shareable;
14700 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14701
14702 alock.release();
14703 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14704 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14705 false /* fMediumLockWriteAll */,
14706 NULL,
14707 *pMediumLockList);
14708 alock.acquire();
14709 if (FAILED(mrc))
14710 {
14711 delete pMediumLockList;
14712 mData->mSession.mLockedMedia.Clear();
14713 break;
14714 }
14715 }
14716
14717 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14718 if (FAILED(rc))
14719 {
14720 mData->mSession.mLockedMedia.Clear();
14721 mrc = setError(rc,
14722 tr("Collecting locking information for all attached media failed"));
14723 break;
14724 }
14725 }
14726
14727 if (SUCCEEDED(mrc))
14728 {
14729 /* Now lock all media. If this fails, nothing is locked. */
14730 alock.release();
14731 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14732 alock.acquire();
14733 if (FAILED(rc))
14734 {
14735 mrc = setError(rc,
14736 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14737 }
14738 }
14739
14740 return mrc;
14741}
14742
14743/**
14744 * Undoes the locks made by by #lockMedia().
14745 */
14746HRESULT SessionMachine::i_unlockMedia()
14747{
14748 AutoCaller autoCaller(this);
14749 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14750
14751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14752
14753 /* we may be holding important error info on the current thread;
14754 * preserve it */
14755 ErrorInfoKeeper eik;
14756
14757 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14758 AssertComRC(rc);
14759 return rc;
14760}
14761
14762/**
14763 * Helper to change the machine state (reimplementation).
14764 *
14765 * @note Locks this object for writing.
14766 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14767 * it can cause crashes in random places due to unexpectedly committing
14768 * the current settings. The caller is responsible for that. The call
14769 * to saveStateSettings is fine, because this method does not commit.
14770 */
14771HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14772{
14773 LogFlowThisFuncEnter();
14774 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14775
14776 AutoCaller autoCaller(this);
14777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14778
14779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14780
14781 MachineState_T oldMachineState = mData->mMachineState;
14782
14783 AssertMsgReturn(oldMachineState != aMachineState,
14784 ("oldMachineState=%s, aMachineState=%s\n",
14785 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14786 E_FAIL);
14787
14788 HRESULT rc = S_OK;
14789
14790 int stsFlags = 0;
14791 bool deleteSavedState = false;
14792
14793 /* detect some state transitions */
14794
14795 if ( ( oldMachineState == MachineState_Saved
14796 && aMachineState == MachineState_Restoring)
14797 || ( ( oldMachineState == MachineState_PoweredOff
14798 || oldMachineState == MachineState_Teleported
14799 || oldMachineState == MachineState_Aborted
14800 )
14801 && ( aMachineState == MachineState_TeleportingIn
14802 || aMachineState == MachineState_Starting
14803 )
14804 )
14805 )
14806 {
14807 /* The EMT thread is about to start */
14808
14809 /* Nothing to do here for now... */
14810
14811 /// @todo NEWMEDIA don't let mDVDDrive and other children
14812 /// change anything when in the Starting/Restoring state
14813 }
14814 else if ( ( oldMachineState == MachineState_Running
14815 || oldMachineState == MachineState_Paused
14816 || oldMachineState == MachineState_Teleporting
14817 || oldMachineState == MachineState_OnlineSnapshotting
14818 || oldMachineState == MachineState_LiveSnapshotting
14819 || oldMachineState == MachineState_Stuck
14820 || oldMachineState == MachineState_Starting
14821 || oldMachineState == MachineState_Stopping
14822 || oldMachineState == MachineState_Saving
14823 || oldMachineState == MachineState_Restoring
14824 || oldMachineState == MachineState_TeleportingPausedVM
14825 || oldMachineState == MachineState_TeleportingIn
14826 )
14827 && ( aMachineState == MachineState_PoweredOff
14828 || aMachineState == MachineState_Saved
14829 || aMachineState == MachineState_Teleported
14830 || aMachineState == MachineState_Aborted
14831 )
14832 )
14833 {
14834 /* The EMT thread has just stopped, unlock attached media. Note that as
14835 * opposed to locking that is done from Console, we do unlocking here
14836 * because the VM process may have aborted before having a chance to
14837 * properly unlock all media it locked. */
14838
14839 unlockMedia();
14840 }
14841
14842 if (oldMachineState == MachineState_Restoring)
14843 {
14844 if (aMachineState != MachineState_Saved)
14845 {
14846 /*
14847 * delete the saved state file once the machine has finished
14848 * restoring from it (note that Console sets the state from
14849 * Restoring to Saved if the VM couldn't restore successfully,
14850 * to give the user an ability to fix an error and retry --
14851 * we keep the saved state file in this case)
14852 */
14853 deleteSavedState = true;
14854 }
14855 }
14856 else if ( oldMachineState == MachineState_Saved
14857 && ( aMachineState == MachineState_PoweredOff
14858 || aMachineState == MachineState_Aborted
14859 || aMachineState == MachineState_Teleported
14860 )
14861 )
14862 {
14863 /*
14864 * delete the saved state after SessionMachine::ForgetSavedState() is called
14865 * or if the VM process (owning a direct VM session) crashed while the
14866 * VM was Saved
14867 */
14868
14869 /// @todo (dmik)
14870 // Not sure that deleting the saved state file just because of the
14871 // client death before it attempted to restore the VM is a good
14872 // thing. But when it crashes we need to go to the Aborted state
14873 // which cannot have the saved state file associated... The only
14874 // way to fix this is to make the Aborted condition not a VM state
14875 // but a bool flag: i.e., when a crash occurs, set it to true and
14876 // change the state to PoweredOff or Saved depending on the
14877 // saved state presence.
14878
14879 deleteSavedState = true;
14880 mData->mCurrentStateModified = TRUE;
14881 stsFlags |= SaveSTS_CurStateModified;
14882 }
14883
14884 if ( aMachineState == MachineState_Starting
14885 || aMachineState == MachineState_Restoring
14886 || aMachineState == MachineState_TeleportingIn
14887 )
14888 {
14889 /* set the current state modified flag to indicate that the current
14890 * state is no more identical to the state in the
14891 * current snapshot */
14892 if (!mData->mCurrentSnapshot.isNull())
14893 {
14894 mData->mCurrentStateModified = TRUE;
14895 stsFlags |= SaveSTS_CurStateModified;
14896 }
14897 }
14898
14899 if (deleteSavedState)
14900 {
14901 if (mRemoveSavedState)
14902 {
14903 Assert(!mSSData->strStateFilePath.isEmpty());
14904
14905 // it is safe to delete the saved state file if ...
14906 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14907 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14908 // ... none of the snapshots share the saved state file
14909 )
14910 RTFileDelete(mSSData->strStateFilePath.c_str());
14911 }
14912
14913 mSSData->strStateFilePath.setNull();
14914 stsFlags |= SaveSTS_StateFilePath;
14915 }
14916
14917 /* redirect to the underlying peer machine */
14918 mPeer->i_setMachineState(aMachineState);
14919
14920 if ( oldMachineState != MachineState_RestoringSnapshot
14921 && ( aMachineState == MachineState_PoweredOff
14922 || aMachineState == MachineState_Teleported
14923 || aMachineState == MachineState_Aborted
14924 || aMachineState == MachineState_Saved))
14925 {
14926 /* the machine has stopped execution
14927 * (or the saved state file was adopted) */
14928 stsFlags |= SaveSTS_StateTimeStamp;
14929 }
14930
14931 if ( ( oldMachineState == MachineState_PoweredOff
14932 || oldMachineState == MachineState_Aborted
14933 || oldMachineState == MachineState_Teleported
14934 )
14935 && aMachineState == MachineState_Saved)
14936 {
14937 /* the saved state file was adopted */
14938 Assert(!mSSData->strStateFilePath.isEmpty());
14939 stsFlags |= SaveSTS_StateFilePath;
14940 }
14941
14942#ifdef VBOX_WITH_GUEST_PROPS
14943 if ( aMachineState == MachineState_PoweredOff
14944 || aMachineState == MachineState_Aborted
14945 || aMachineState == MachineState_Teleported)
14946 {
14947 /* Make sure any transient guest properties get removed from the
14948 * property store on shutdown. */
14949 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14950
14951 /* remove it from the settings representation */
14952 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14953 for (settings::GuestPropertiesList::iterator
14954 it = llGuestProperties.begin();
14955 it != llGuestProperties.end();
14956 /*nothing*/)
14957 {
14958 const settings::GuestProperty &prop = *it;
14959 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14960 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14961 {
14962 it = llGuestProperties.erase(it);
14963 fNeedsSaving = true;
14964 }
14965 else
14966 {
14967 ++it;
14968 }
14969 }
14970
14971 /* Additionally remove it from the HWData representation. Required to
14972 * keep everything in sync, as this is what the API keeps using. */
14973 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14974 for (HWData::GuestPropertyMap::iterator
14975 it = llHWGuestProperties.begin();
14976 it != llHWGuestProperties.end();
14977 /*nothing*/)
14978 {
14979 uint32_t fFlags = it->second.mFlags;
14980 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14981 {
14982 /* iterator where we need to continue after the erase call
14983 * (C++03 is a fact still, and it doesn't return the iterator
14984 * which would allow continuing) */
14985 HWData::GuestPropertyMap::iterator it2 = it;
14986 ++it2;
14987 llHWGuestProperties.erase(it);
14988 it = it2;
14989 fNeedsSaving = true;
14990 }
14991 else
14992 {
14993 ++it;
14994 }
14995 }
14996
14997 if (fNeedsSaving)
14998 {
14999 mData->mCurrentStateModified = TRUE;
15000 stsFlags |= SaveSTS_CurStateModified;
15001 }
15002 }
15003#endif /* VBOX_WITH_GUEST_PROPS */
15004
15005 rc = i_saveStateSettings(stsFlags);
15006
15007 if ( ( oldMachineState != MachineState_PoweredOff
15008 && oldMachineState != MachineState_Aborted
15009 && oldMachineState != MachineState_Teleported
15010 )
15011 && ( aMachineState == MachineState_PoweredOff
15012 || aMachineState == MachineState_Aborted
15013 || aMachineState == MachineState_Teleported
15014 )
15015 )
15016 {
15017 /* we've been shut down for any reason */
15018 /* no special action so far */
15019 }
15020
15021 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15022 LogFlowThisFuncLeave();
15023 return rc;
15024}
15025
15026/**
15027 * Sends the current machine state value to the VM process.
15028 *
15029 * @note Locks this object for reading, then calls a client process.
15030 */
15031HRESULT SessionMachine::i_updateMachineStateOnClient()
15032{
15033 AutoCaller autoCaller(this);
15034 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15035
15036 ComPtr<IInternalSessionControl> directControl;
15037 {
15038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15039 AssertReturn(!!mData, E_FAIL);
15040 if (mData->mSession.mLockType == LockType_VM)
15041 directControl = mData->mSession.mDirectControl;
15042
15043 /* directControl may be already set to NULL here in #OnSessionEnd()
15044 * called too early by the direct session process while there is still
15045 * some operation (like deleting the snapshot) in progress. The client
15046 * process in this case is waiting inside Session::close() for the
15047 * "end session" process object to complete, while #uninit() called by
15048 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15049 * operation to complete. For now, we accept this inconsistent behavior
15050 * and simply do nothing here. */
15051
15052 if (mData->mSession.mState == SessionState_Unlocking)
15053 return S_OK;
15054 }
15055
15056 /* ignore notifications sent after #OnSessionEnd() is called */
15057 if (!directControl)
15058 return S_OK;
15059
15060 return directControl->UpdateMachineState(mData->mMachineState);
15061}
15062
15063
15064/*static*/
15065HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15066{
15067 va_list args;
15068 va_start(args, pcszMsg);
15069 HRESULT rc = setErrorInternal(aResultCode,
15070 getStaticClassIID(),
15071 getStaticComponentName(),
15072 Utf8Str(pcszMsg, args),
15073 false /* aWarning */,
15074 true /* aLogIt */);
15075 va_end(args);
15076 return rc;
15077}
15078
15079
15080HRESULT Machine::updateState(MachineState_T aState)
15081{
15082 NOREF(aState);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15087{
15088 NOREF(aProgress);
15089 ReturnComNotImplemented();
15090}
15091
15092HRESULT Machine::endPowerUp(LONG aResult)
15093{
15094 NOREF(aResult);
15095 ReturnComNotImplemented();
15096}
15097
15098HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15099{
15100 NOREF(aProgress);
15101 ReturnComNotImplemented();
15102}
15103
15104HRESULT Machine::endPoweringDown(LONG aResult,
15105 const com::Utf8Str &aErrMsg)
15106{
15107 NOREF(aResult);
15108 NOREF(aErrMsg);
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15113 BOOL *aMatched,
15114 ULONG *aMaskedInterfaces)
15115{
15116 NOREF(aDevice);
15117 NOREF(aMatched);
15118 NOREF(aMaskedInterfaces);
15119 ReturnComNotImplemented();
15120
15121}
15122
15123HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15124{
15125 NOREF(aId); NOREF(aCaptureFilename);
15126 ReturnComNotImplemented();
15127}
15128
15129HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15130 BOOL aDone)
15131{
15132 NOREF(aId);
15133 NOREF(aDone);
15134 ReturnComNotImplemented();
15135}
15136
15137HRESULT Machine::autoCaptureUSBDevices()
15138{
15139 ReturnComNotImplemented();
15140}
15141
15142HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15143{
15144 NOREF(aDone);
15145 ReturnComNotImplemented();
15146}
15147
15148HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15149 ComPtr<IProgress> &aProgress)
15150{
15151 NOREF(aSession);
15152 NOREF(aProgress);
15153 ReturnComNotImplemented();
15154}
15155
15156HRESULT Machine::finishOnlineMergeMedium()
15157{
15158 ReturnComNotImplemented();
15159}
15160
15161HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15162 std::vector<com::Utf8Str> &aValues,
15163 std::vector<LONG64> &aTimestamps,
15164 std::vector<com::Utf8Str> &aFlags)
15165{
15166 NOREF(aNames);
15167 NOREF(aValues);
15168 NOREF(aTimestamps);
15169 NOREF(aFlags);
15170 ReturnComNotImplemented();
15171}
15172
15173HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15174 const com::Utf8Str &aValue,
15175 LONG64 aTimestamp,
15176 const com::Utf8Str &aFlags)
15177{
15178 NOREF(aName);
15179 NOREF(aValue);
15180 NOREF(aTimestamp);
15181 NOREF(aFlags);
15182 ReturnComNotImplemented();
15183}
15184
15185HRESULT Machine::lockMedia()
15186{
15187 ReturnComNotImplemented();
15188}
15189
15190HRESULT Machine::unlockMedia()
15191{
15192 ReturnComNotImplemented();
15193}
15194
15195HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15196 ComPtr<IMediumAttachment> &aNewAttachment)
15197{
15198 NOREF(aAttachment);
15199 NOREF(aNewAttachment);
15200 ReturnComNotImplemented();
15201}
15202
15203HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15204 ULONG aCpuUser,
15205 ULONG aCpuKernel,
15206 ULONG aCpuIdle,
15207 ULONG aMemTotal,
15208 ULONG aMemFree,
15209 ULONG aMemBalloon,
15210 ULONG aMemShared,
15211 ULONG aMemCache,
15212 ULONG aPagedTotal,
15213 ULONG aMemAllocTotal,
15214 ULONG aMemFreeTotal,
15215 ULONG aMemBalloonTotal,
15216 ULONG aMemSharedTotal,
15217 ULONG aVmNetRx,
15218 ULONG aVmNetTx)
15219{
15220 NOREF(aValidStats);
15221 NOREF(aCpuUser);
15222 NOREF(aCpuKernel);
15223 NOREF(aCpuIdle);
15224 NOREF(aMemTotal);
15225 NOREF(aMemFree);
15226 NOREF(aMemBalloon);
15227 NOREF(aMemShared);
15228 NOREF(aMemCache);
15229 NOREF(aPagedTotal);
15230 NOREF(aMemAllocTotal);
15231 NOREF(aMemFreeTotal);
15232 NOREF(aMemBalloonTotal);
15233 NOREF(aMemSharedTotal);
15234 NOREF(aVmNetRx);
15235 NOREF(aVmNetTx);
15236 ReturnComNotImplemented();
15237}
15238
15239HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15240 com::Utf8Str &aResult)
15241{
15242 NOREF(aAuthParams);
15243 NOREF(aResult);
15244 ReturnComNotImplemented();
15245}
15246
15247HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15248{
15249 NOREF(aFlags);
15250 ReturnComNotImplemented();
15251}
15252
15253/* This isn't handled entirely by the wrapper generator yet. */
15254#ifdef VBOX_WITH_XPCOM
15255NS_DECL_CLASSINFO(SessionMachine)
15256NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15257
15258NS_DECL_CLASSINFO(SnapshotMachine)
15259NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15260#endif
Note: See TracBrowser for help on using the repository browser.

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