VirtualBox

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

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

Main,VBoxManage: Added CPUPropertyType_HWVirt. Translates to --nested-hw-virt in VBoxManage/modifyvm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 525.2 KB
Line 
1/* $Id: MachineImpl.cpp 70712 2018-01-23 16:18:22Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#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#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mAPIC = true;
197 mX2APIC = false;
198 mIBPBOnVMExit = false;
199 mIBPBOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mParavirtProvider = ParavirtProvider_Default;
221 mEmulatedUSBCardReaderEnabled = FALSE;
222
223 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
224 mCPUAttached[i] = false;
225
226 mIOCacheEnabled = true;
227 mIOCacheSize = 5; /* 5MB */
228}
229
230Machine::HWData::~HWData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine() :
242#ifdef VBOX_WITH_RESOURCE_USAGE_API
243 mCollectorGuest(NULL),
244#endif
245 mPeer(NULL),
246 mParent(NULL),
247 mSerialPorts(),
248 mParallelPorts(),
249 uRegistryNeedsSaving(0)
250{}
251
252Machine::~Machine()
253{}
254
255HRESULT Machine::FinalConstruct()
256{
257 LogFlowThisFunc(("\n"));
258 return BaseFinalConstruct();
259}
260
261void Machine::FinalRelease()
262{
263 LogFlowThisFunc(("\n"));
264 uninit();
265 BaseFinalRelease();
266}
267
268/**
269 * Initializes a new machine instance; this init() variant creates a new, empty machine.
270 * This gets called from VirtualBox::CreateMachine().
271 *
272 * @param aParent Associated parent object
273 * @param strConfigFile Local file system path to the VM settings file (can
274 * be relative to the VirtualBox config directory).
275 * @param strName name for the machine
276 * @param llGroups list of groups for the machine
277 * @param aOsType OS Type of this machine or NULL.
278 * @param aId UUID for the new machine.
279 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
280 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
281 * scheme (includes the UUID).
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = i_isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->i_id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->i_applyDefaults(aOsType);
343
344 /* Let the OS type select 64-bit ness. */
345 mHWData->mLongMode = aOsType->i_is64Bit()
346 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
347
348 /* Let the OS type enable the X2APIC */
349 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
350 }
351
352 /* Apply network adapters defaults */
353 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
354 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
355
356 /* Apply serial port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
358 mSerialPorts[slot]->i_applyDefaults(aOsType);
359
360 /* Apply parallel port defaults */
361 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
362 mParallelPorts[slot]->i_applyDefaults();
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param strConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param aParent Associated parent object.
515 * @param strName Name for the new machine; this overrides what is specified in config and is used
516 * for the settings file as well.
517 * @param config Machine configuration loaded and parsed from XML.
518 *
519 * @return Success indicator. if not S_OK, the machine object is invalid
520 */
521HRESULT Machine::init(VirtualBox *aParent,
522 const Utf8Str &strName,
523 const settings::MachineConfigFile &config)
524{
525 LogFlowThisFuncEnter();
526
527 /* Enclose the state transition NotReady->InInit->Ready */
528 AutoInitSpan autoInitSpan(this);
529 AssertReturn(autoInitSpan.isOk(), E_FAIL);
530
531 Utf8Str strConfigFile;
532 aParent->i_getDefaultMachineFolder(strConfigFile);
533 strConfigFile.append(RTPATH_DELIMITER);
534 strConfigFile.append(strName);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(".vbox");
538
539 HRESULT rc = initImpl(aParent, strConfigFile);
540 if (FAILED(rc)) return rc;
541
542 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
543 if (FAILED(rc)) return rc;
544
545 rc = initDataAndChildObjects();
546
547 if (SUCCEEDED(rc))
548 {
549 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
550 mData->mAccessible = TRUE;
551
552 // create empty machine config for instance data
553 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
554
555 // generate fresh UUID, ignore machine config
556 unconst(mData->mUuid).create();
557
558 rc = i_loadMachineDataFromSettings(config,
559 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
560
561 // override VM name as well, it may be different
562 mUserData->s.strName = strName;
563
564 if (SUCCEEDED(rc))
565 {
566 /* At this point the changing of the current state modification
567 * flag is allowed. */
568 i_allowStateModification();
569
570 /* commit all changes made during the initialization */
571 i_commit();
572 }
573 }
574
575 /* Confirm a successful initialization when it's the case */
576 if (SUCCEEDED(rc))
577 {
578 if (mData->mAccessible)
579 autoInitSpan.setSucceeded();
580 else
581 {
582 /* Ignore all errors from unregistering, they would destroy
583- * the more interesting error information we already have,
584- * pinpointing the issue with the VM config. */
585 ErrorInfoKeeper eik;
586
587 autoInitSpan.setLimited();
588
589 // uninit media from this machine's media registry, or else
590 // reloading the settings will fail
591 mParent->i_unregisterMachineMedia(i_getId());
592 }
593 }
594
595 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
596 "rc=%08X\n",
597 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
598 mData->mRegistered, mData->mAccessible, rc));
599
600 LogFlowThisFuncLeave();
601
602 return rc;
603}
604
605/**
606 * Shared code between the various init() implementations.
607 * @param aParent The VirtualBox object.
608 * @param strConfigFile Settings file.
609 * @return
610 */
611HRESULT Machine::initImpl(VirtualBox *aParent,
612 const Utf8Str &strConfigFile)
613{
614 LogFlowThisFuncEnter();
615
616 AssertReturn(aParent, E_INVALIDARG);
617 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
618
619 HRESULT rc = S_OK;
620
621 /* share the parent weakly */
622 unconst(mParent) = aParent;
623
624 /* allocate the essential machine data structure (the rest will be
625 * allocated later by initDataAndChildObjects() */
626 mData.allocate();
627
628 /* memorize the config file name (as provided) */
629 mData->m_strConfigFile = strConfigFile;
630
631 /* get the full file name */
632 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
633 if (RT_FAILURE(vrc1))
634 return setError(VBOX_E_FILE_ERROR,
635 tr("Invalid machine settings file name '%s' (%Rrc)"),
636 strConfigFile.c_str(),
637 vrc1);
638
639 LogFlowThisFuncLeave();
640
641 return rc;
642}
643
644/**
645 * Tries to create a machine settings file in the path stored in the machine
646 * instance data. Used when a new machine is created to fail gracefully if
647 * the settings file could not be written (e.g. because machine dir is read-only).
648 * @return
649 */
650HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
651{
652 HRESULT rc = S_OK;
653
654 // when we create a new machine, we must be able to create the settings file
655 RTFILE f = NIL_RTFILE;
656 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
657 if ( RT_SUCCESS(vrc)
658 || vrc == VERR_SHARING_VIOLATION
659 )
660 {
661 if (RT_SUCCESS(vrc))
662 RTFileClose(f);
663 if (!fForceOverwrite)
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Machine settings file '%s' already exists"),
666 mData->m_strConfigFileFull.c_str());
667 else
668 {
669 /* try to delete the config file, as otherwise the creation
670 * of a new settings file will fail. */
671 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
672 if (RT_FAILURE(vrc2))
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Could not delete the existing settings file '%s' (%Rrc)"),
675 mData->m_strConfigFileFull.c_str(), vrc2);
676 }
677 }
678 else if ( vrc != VERR_FILE_NOT_FOUND
679 && vrc != VERR_PATH_NOT_FOUND
680 )
681 rc = setError(VBOX_E_FILE_ERROR,
682 tr("Invalid machine settings file name '%s' (%Rrc)"),
683 mData->m_strConfigFileFull.c_str(),
684 vrc);
685 return rc;
686}
687
688/**
689 * Initializes the registered machine by loading the settings file.
690 * This method is separated from #init() in order to make it possible to
691 * retry the operation after VirtualBox startup instead of refusing to
692 * startup the whole VirtualBox server in case if the settings file of some
693 * registered VM is invalid or inaccessible.
694 *
695 * @note Must be always called from this object's write lock
696 * (unless called from #init() that doesn't need any locking).
697 * @note Locks the mUSBController method for writing.
698 * @note Subclasses must not call this method.
699 */
700HRESULT Machine::i_registeredInit()
701{
702 AssertReturn(!i_isSessionMachine(), E_FAIL);
703 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
704 AssertReturn(mData->mUuid.isValid(), E_FAIL);
705 AssertReturn(!mData->mAccessible, E_FAIL);
706
707 HRESULT rc = initDataAndChildObjects();
708
709 if (SUCCEEDED(rc))
710 {
711 /* Temporarily reset the registered flag in order to let setters
712 * potentially called from loadSettings() succeed (isMutable() used in
713 * all setters will return FALSE for a Machine instance if mRegistered
714 * is TRUE). */
715 mData->mRegistered = FALSE;
716
717 try
718 {
719 // load and parse machine XML; this will throw on XML or logic errors
720 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
721
722 if (mData->mUuid != mData->pMachineConfigFile->uuid)
723 throw setError(E_FAIL,
724 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
725 mData->pMachineConfigFile->uuid.raw(),
726 mData->m_strConfigFileFull.c_str(),
727 mData->mUuid.toString().c_str(),
728 mParent->i_settingsFilePath().c_str());
729
730 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
731 NULL /* const Guid *puuidRegistry */);
732 if (FAILED(rc)) throw rc;
733 }
734 catch (HRESULT err)
735 {
736 /* we assume that error info is set by the thrower */
737 rc = err;
738 }
739 catch (...)
740 {
741 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
742 }
743
744 /* Restore the registered flag (even on failure) */
745 mData->mRegistered = TRUE;
746 }
747
748 if (SUCCEEDED(rc))
749 {
750 /* Set mAccessible to TRUE only if we successfully locked and loaded
751 * the settings file */
752 mData->mAccessible = TRUE;
753
754 /* commit all changes made during loading the settings file */
755 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
756 /// @todo r=klaus for some reason the settings loading logic backs up
757 // the settings, and therefore a commit is needed. Should probably be changed.
758 }
759 else
760 {
761 /* If the machine is registered, then, instead of returning a
762 * failure, we mark it as inaccessible and set the result to
763 * success to give it a try later */
764
765 /* fetch the current error info */
766 mData->mAccessError = com::ErrorInfo();
767 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it has a
835 * VirtualBox caller for the duration of the
836 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 Log1WarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from i_registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator
1052 it = mUserData->s.llGroups.begin();
1053 it != mUserData->s.llGroups.end();
1054 ++it, ++i)
1055 aGroups[i] = (*it);
1056
1057 return S_OK;
1058}
1059
1060HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1061{
1062 StringsList llGroups;
1063 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1064 if (FAILED(rc))
1065 return rc;
1066
1067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 rc = i_checkStateDependency(MutableOrSavedStateDep);
1070 if (FAILED(rc)) return rc;
1071
1072 i_setModified(IsModified_MachineData);
1073 mUserData.backup();
1074 mUserData->s.llGroups = llGroups;
1075
1076 return S_OK;
1077}
1078
1079HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1080{
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082
1083 aOSTypeId = mUserData->s.strOsType;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1089{
1090 /* look up the object by Id to check it is valid */
1091 ComObjPtr<GuestOSType> pGuestOSType;
1092 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1093 pGuestOSType);
1094 if (FAILED(rc)) return rc;
1095
1096 /* when setting, always use the "etalon" value for consistency -- lookup
1097 * by ID is case-insensitive and the input value may have different case */
1098 Utf8Str osTypeId = pGuestOSType->i_id();
1099
1100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1101
1102 rc = i_checkStateDependency(MutableStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 i_setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.strOsType = osTypeId;
1108
1109 return S_OK;
1110}
1111
1112HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1113{
1114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 *aFirmwareType = mHWData->mFirmwareType;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1122{
1123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 HRESULT rc = i_checkStateDependency(MutableStateDep);
1126 if (FAILED(rc)) return rc;
1127
1128 i_setModified(IsModified_MachineData);
1129 mHWData.backup();
1130 mHWData->mFirmwareType = aFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1136{
1137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1145{
1146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 HRESULT rc = i_checkStateDependency(MutableStateDep);
1149 if (FAILED(rc)) return rc;
1150
1151 i_setModified(IsModified_MachineData);
1152 mHWData.backup();
1153 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1154
1155 return S_OK;
1156}
1157
1158HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1159{
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aPointingHIDType = mHWData->mPointingHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1168{
1169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 HRESULT rc = i_checkStateDependency(MutableStateDep);
1172 if (FAILED(rc)) return rc;
1173
1174 i_setModified(IsModified_MachineData);
1175 mHWData.backup();
1176 mHWData->mPointingHIDType = aPointingHIDType;
1177
1178 return S_OK;
1179}
1180
1181HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1182{
1183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 *aChipsetType = mHWData->mChipsetType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1191{
1192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 HRESULT rc = i_checkStateDependency(MutableStateDep);
1195 if (FAILED(rc)) return rc;
1196
1197 if (aChipsetType != mHWData->mChipsetType)
1198 {
1199 i_setModified(IsModified_MachineData);
1200 mHWData.backup();
1201 mHWData->mChipsetType = aChipsetType;
1202
1203 // Resize network adapter array, to be finalized on commit/rollback.
1204 // We must not throw away entries yet, otherwise settings are lost
1205 // without a way to roll back.
1206 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1207 size_t oldCount = mNetworkAdapters.size();
1208 if (newCount > oldCount)
1209 {
1210 mNetworkAdapters.resize(newCount);
1211 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1212 {
1213 unconst(mNetworkAdapters[slot]).createObject();
1214 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1215 }
1216 }
1217 }
1218
1219 return S_OK;
1220}
1221
1222HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1223{
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 aParavirtDebug = mHWData->mParavirtDebug;
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 /** @todo Parse/validate options? */
1238 if (aParavirtDebug != mHWData->mParavirtDebug)
1239 {
1240 i_setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mParavirtDebug = aParavirtDebug;
1243 }
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1249{
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aParavirtProvider = mHWData->mParavirtProvider;
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1258{
1259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 HRESULT rc = i_checkStateDependency(MutableStateDep);
1262 if (FAILED(rc)) return rc;
1263
1264 if (aParavirtProvider != mHWData->mParavirtProvider)
1265 {
1266 i_setModified(IsModified_MachineData);
1267 mHWData.backup();
1268 mHWData->mParavirtProvider = aParavirtProvider;
1269 }
1270
1271 return S_OK;
1272}
1273
1274HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1275{
1276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1277
1278 *aParavirtProvider = mHWData->mParavirtProvider;
1279 switch (mHWData->mParavirtProvider)
1280 {
1281 case ParavirtProvider_None:
1282 case ParavirtProvider_HyperV:
1283 case ParavirtProvider_KVM:
1284 case ParavirtProvider_Minimal:
1285 break;
1286
1287 /* Resolve dynamic provider types to the effective types. */
1288 default:
1289 {
1290 ComObjPtr<GuestOSType> pGuestOSType;
1291 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1292 pGuestOSType);
1293 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1294
1295 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1296 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1297
1298 switch (mHWData->mParavirtProvider)
1299 {
1300 case ParavirtProvider_Legacy:
1301 {
1302 if (fOsXGuest)
1303 *aParavirtProvider = ParavirtProvider_Minimal;
1304 else
1305 *aParavirtProvider = ParavirtProvider_None;
1306 break;
1307 }
1308
1309 case ParavirtProvider_Default:
1310 {
1311 if (fOsXGuest)
1312 *aParavirtProvider = ParavirtProvider_Minimal;
1313 else if ( mUserData->s.strOsType == "Windows10"
1314 || mUserData->s.strOsType == "Windows10_64"
1315 || mUserData->s.strOsType == "Windows81"
1316 || mUserData->s.strOsType == "Windows81_64"
1317 || mUserData->s.strOsType == "Windows8"
1318 || mUserData->s.strOsType == "Windows8_64"
1319 || mUserData->s.strOsType == "Windows7"
1320 || mUserData->s.strOsType == "Windows7_64"
1321 || mUserData->s.strOsType == "WindowsVista"
1322 || mUserData->s.strOsType == "WindowsVista_64"
1323 || mUserData->s.strOsType == "Windows2012"
1324 || mUserData->s.strOsType == "Windows2012_64"
1325 || mUserData->s.strOsType == "Windows2008"
1326 || mUserData->s.strOsType == "Windows2008_64")
1327 {
1328 *aParavirtProvider = ParavirtProvider_HyperV;
1329 }
1330 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1331 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1332 || mUserData->s.strOsType == "Linux"
1333 || mUserData->s.strOsType == "Linux_64"
1334 || mUserData->s.strOsType == "ArchLinux"
1335 || mUserData->s.strOsType == "ArchLinux_64"
1336 || mUserData->s.strOsType == "Debian"
1337 || mUserData->s.strOsType == "Debian_64"
1338 || mUserData->s.strOsType == "Fedora"
1339 || mUserData->s.strOsType == "Fedora_64"
1340 || mUserData->s.strOsType == "Gentoo"
1341 || mUserData->s.strOsType == "Gentoo_64"
1342 || mUserData->s.strOsType == "Mandriva"
1343 || mUserData->s.strOsType == "Mandriva_64"
1344 || mUserData->s.strOsType == "OpenSUSE"
1345 || mUserData->s.strOsType == "OpenSUSE_64"
1346 || mUserData->s.strOsType == "Oracle"
1347 || mUserData->s.strOsType == "Oracle_64"
1348 || mUserData->s.strOsType == "RedHat"
1349 || mUserData->s.strOsType == "RedHat_64"
1350 || mUserData->s.strOsType == "Turbolinux"
1351 || mUserData->s.strOsType == "Turbolinux_64"
1352 || mUserData->s.strOsType == "Ubuntu"
1353 || mUserData->s.strOsType == "Ubuntu_64"
1354 || mUserData->s.strOsType == "Xandros"
1355 || mUserData->s.strOsType == "Xandros_64")
1356 {
1357 *aParavirtProvider = ParavirtProvider_KVM;
1358 }
1359 else
1360 *aParavirtProvider = ParavirtProvider_None;
1361 break;
1362 }
1363
1364 default: AssertFailedBreak(); /* Shut up MSC. */
1365 }
1366 break;
1367 }
1368 }
1369
1370 Assert( *aParavirtProvider == ParavirtProvider_None
1371 || *aParavirtProvider == ParavirtProvider_Minimal
1372 || *aParavirtProvider == ParavirtProvider_HyperV
1373 || *aParavirtProvider == ParavirtProvider_KVM);
1374 return S_OK;
1375}
1376
1377HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1378{
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 aHardwareVersion = mHWData->mHWVersion;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1387{
1388 /* check known version */
1389 Utf8Str hwVersion = aHardwareVersion;
1390 if ( hwVersion.compare("1") != 0
1391 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1392 return setError(E_INVALIDARG,
1393 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1394
1395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 HRESULT rc = i_checkStateDependency(MutableStateDep);
1398 if (FAILED(rc)) return rc;
1399
1400 i_setModified(IsModified_MachineData);
1401 mHWData.backup();
1402 mHWData->mHWVersion = aHardwareVersion;
1403
1404 return S_OK;
1405}
1406
1407HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1408{
1409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 if (!mHWData->mHardwareUUID.isZero())
1412 aHardwareUUID = mHWData->mHardwareUUID;
1413 else
1414 aHardwareUUID = mData->mUuid;
1415
1416 return S_OK;
1417}
1418
1419HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1420{
1421 if (!aHardwareUUID.isValid())
1422 return E_INVALIDARG;
1423
1424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1425
1426 HRESULT rc = i_checkStateDependency(MutableStateDep);
1427 if (FAILED(rc)) return rc;
1428
1429 i_setModified(IsModified_MachineData);
1430 mHWData.backup();
1431 if (aHardwareUUID == mData->mUuid)
1432 mHWData->mHardwareUUID.clear();
1433 else
1434 mHWData->mHardwareUUID = aHardwareUUID;
1435
1436 return S_OK;
1437}
1438
1439HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1440{
1441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1442
1443 *aMemorySize = mHWData->mMemorySize;
1444
1445 return S_OK;
1446}
1447
1448HRESULT Machine::setMemorySize(ULONG aMemorySize)
1449{
1450 /* check RAM limits */
1451 if ( aMemorySize < MM_RAM_MIN_IN_MB
1452 || aMemorySize > MM_RAM_MAX_IN_MB
1453 )
1454 return setError(E_INVALIDARG,
1455 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1456 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1457
1458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 HRESULT rc = i_checkStateDependency(MutableStateDep);
1461 if (FAILED(rc)) return rc;
1462
1463 i_setModified(IsModified_MachineData);
1464 mHWData.backup();
1465 mHWData->mMemorySize = aMemorySize;
1466
1467 return S_OK;
1468}
1469
1470HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1471{
1472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 *aCPUCount = mHWData->mCPUCount;
1475
1476 return S_OK;
1477}
1478
1479HRESULT Machine::setCPUCount(ULONG aCPUCount)
1480{
1481 /* check CPU limits */
1482 if ( aCPUCount < SchemaDefs::MinCPUCount
1483 || aCPUCount > SchemaDefs::MaxCPUCount
1484 )
1485 return setError(E_INVALIDARG,
1486 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1487 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1488
1489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1492 if (mHWData->mCPUHotPlugEnabled)
1493 {
1494 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1495 {
1496 if (mHWData->mCPUAttached[idx])
1497 return setError(E_INVALIDARG,
1498 tr("There is still a CPU attached to socket %lu."
1499 "Detach the CPU before removing the socket"),
1500 aCPUCount, idx+1);
1501 }
1502 }
1503
1504 HRESULT rc = i_checkStateDependency(MutableStateDep);
1505 if (FAILED(rc)) return rc;
1506
1507 i_setModified(IsModified_MachineData);
1508 mHWData.backup();
1509 mHWData->mCPUCount = aCPUCount;
1510
1511 return S_OK;
1512}
1513
1514HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1515{
1516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1519
1520 return S_OK;
1521}
1522
1523HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1524{
1525 HRESULT rc = S_OK;
1526
1527 /* check throttle limits */
1528 if ( aCPUExecutionCap < 1
1529 || aCPUExecutionCap > 100
1530 )
1531 return setError(E_INVALIDARG,
1532 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1533 aCPUExecutionCap, 1, 100);
1534
1535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 alock.release();
1538 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1539 alock.acquire();
1540 if (FAILED(rc)) return rc;
1541
1542 i_setModified(IsModified_MachineData);
1543 mHWData.backup();
1544 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1545
1546 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1547 if (Global::IsOnline(mData->mMachineState))
1548 i_saveSettings(NULL);
1549
1550 return S_OK;
1551}
1552
1553HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1554{
1555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1556
1557 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1558
1559 return S_OK;
1560}
1561
1562HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1563{
1564 HRESULT rc = S_OK;
1565
1566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 rc = i_checkStateDependency(MutableStateDep);
1569 if (FAILED(rc)) return rc;
1570
1571 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1572 {
1573 if (aCPUHotPlugEnabled)
1574 {
1575 i_setModified(IsModified_MachineData);
1576 mHWData.backup();
1577
1578 /* Add the amount of CPUs currently attached */
1579 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1580 mHWData->mCPUAttached[i] = true;
1581 }
1582 else
1583 {
1584 /*
1585 * We can disable hotplug only if the amount of maximum CPUs is equal
1586 * to the amount of attached CPUs
1587 */
1588 unsigned cCpusAttached = 0;
1589 unsigned iHighestId = 0;
1590
1591 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1592 {
1593 if (mHWData->mCPUAttached[i])
1594 {
1595 cCpusAttached++;
1596 iHighestId = i;
1597 }
1598 }
1599
1600 if ( (cCpusAttached != mHWData->mCPUCount)
1601 || (iHighestId >= mHWData->mCPUCount))
1602 return setError(E_INVALIDARG,
1603 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1604
1605 i_setModified(IsModified_MachineData);
1606 mHWData.backup();
1607 }
1608 }
1609
1610 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1611
1612 return rc;
1613}
1614
1615HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1616{
1617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
1619 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1620
1621 return S_OK;
1622}
1623
1624HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1625{
1626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1629 if (SUCCEEDED(hrc))
1630 {
1631 i_setModified(IsModified_MachineData);
1632 mHWData.backup();
1633 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1634 }
1635 return hrc;
1636}
1637
1638HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1639{
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641 aCPUProfile = mHWData->mCpuProfile;
1642 return S_OK;
1643}
1644
1645HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1646{
1647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1648 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1649 if (SUCCEEDED(hrc))
1650 {
1651 i_setModified(IsModified_MachineData);
1652 mHWData.backup();
1653 /* Empty equals 'host'. */
1654 if (aCPUProfile.isNotEmpty())
1655 mHWData->mCpuProfile = aCPUProfile;
1656 else
1657 mHWData->mCpuProfile = "host";
1658 }
1659 return hrc;
1660}
1661
1662HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1663{
1664#ifdef VBOX_WITH_USB_CARDREADER
1665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1666
1667 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1668
1669 return S_OK;
1670#else
1671 NOREF(aEmulatedUSBCardReaderEnabled);
1672 return E_NOTIMPL;
1673#endif
1674}
1675
1676HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1677{
1678#ifdef VBOX_WITH_USB_CARDREADER
1679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1682 if (FAILED(rc)) return rc;
1683
1684 i_setModified(IsModified_MachineData);
1685 mHWData.backup();
1686 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1687
1688 return S_OK;
1689#else
1690 NOREF(aEmulatedUSBCardReaderEnabled);
1691 return E_NOTIMPL;
1692#endif
1693}
1694
1695HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1696{
1697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1698
1699 *aHPETEnabled = mHWData->mHPETEnabled;
1700
1701 return S_OK;
1702}
1703
1704HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1705{
1706 HRESULT rc = S_OK;
1707
1708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 rc = i_checkStateDependency(MutableStateDep);
1711 if (FAILED(rc)) return rc;
1712
1713 i_setModified(IsModified_MachineData);
1714 mHWData.backup();
1715
1716 mHWData->mHPETEnabled = aHPETEnabled;
1717
1718 return rc;
1719}
1720
1721HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1722{
1723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1724
1725 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1726 return S_OK;
1727}
1728
1729HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1730{
1731 HRESULT rc = S_OK;
1732
1733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1734
1735 i_setModified(IsModified_MachineData);
1736 mHWData.backup();
1737 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1738
1739 alock.release();
1740 rc = i_onVideoCaptureChange();
1741 alock.acquire();
1742 if (FAILED(rc))
1743 {
1744 /*
1745 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1746 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1747 * determine if it should start or stop capturing. Therefore we need to manually
1748 * undo change.
1749 */
1750 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1751 return rc;
1752 }
1753
1754 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1755 if (Global::IsOnline(mData->mMachineState))
1756 i_saveSettings(NULL);
1757
1758 return rc;
1759}
1760
1761HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1762{
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1765 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1766 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1771{
1772 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1773 bool fChanged = false;
1774
1775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1776
1777 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1778 {
1779 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1780 {
1781 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1782 fChanged = true;
1783 }
1784 }
1785 if (fChanged)
1786 {
1787 alock.release();
1788 HRESULT rc = i_onVideoCaptureChange();
1789 alock.acquire();
1790 if (FAILED(rc)) return rc;
1791 i_setModified(IsModified_MachineData);
1792
1793 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1794 if (Global::IsOnline(mData->mMachineState))
1795 i_saveSettings(NULL);
1796 }
1797
1798 return S_OK;
1799}
1800
1801HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1802{
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804 if (mHWData->mVideoCaptureFile.isEmpty())
1805 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1806 else
1807 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1808 return S_OK;
1809}
1810
1811HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1812{
1813 Utf8Str strFile(aVideoCaptureFile);
1814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 if ( Global::IsOnline(mData->mMachineState)
1817 && mHWData->mVideoCaptureEnabled)
1818 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1819
1820 if (!RTPathStartsWithRoot(strFile.c_str()))
1821 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1822
1823 if (!strFile.isEmpty())
1824 {
1825 Utf8Str defaultFile;
1826 i_getDefaultVideoCaptureFile(defaultFile);
1827 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1828 strFile.setNull();
1829 }
1830
1831 i_setModified(IsModified_MachineData);
1832 mHWData.backup();
1833 mHWData->mVideoCaptureFile = strFile;
1834
1835 return S_OK;
1836}
1837
1838HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1839{
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1842 return S_OK;
1843}
1844
1845HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1846{
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 i_setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1856
1857 return S_OK;
1858}
1859
1860HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1864 return S_OK;
1865}
1866
1867HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1868{
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 if ( Global::IsOnline(mData->mMachineState)
1872 && mHWData->mVideoCaptureEnabled)
1873 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1874
1875 i_setModified(IsModified_MachineData);
1876 mHWData.backup();
1877 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1878
1879 return S_OK;
1880}
1881
1882HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1886 return S_OK;
1887}
1888
1889HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1890{
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 if ( Global::IsOnline(mData->mMachineState)
1894 && mHWData->mVideoCaptureEnabled)
1895 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1896
1897 i_setModified(IsModified_MachineData);
1898 mHWData.backup();
1899 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1900
1901 return S_OK;
1902}
1903
1904HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1905{
1906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1907 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1908 return S_OK;
1909}
1910
1911HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1912{
1913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1914
1915 if ( Global::IsOnline(mData->mMachineState)
1916 && mHWData->mVideoCaptureEnabled)
1917 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1918
1919 i_setModified(IsModified_MachineData);
1920 mHWData.backup();
1921 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1922
1923 return S_OK;
1924}
1925
1926HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1927{
1928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1929 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1930 return S_OK;
1931}
1932
1933HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1934{
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 if ( Global::IsOnline(mData->mMachineState)
1938 && mHWData->mVideoCaptureEnabled)
1939 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1940
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1944
1945 return S_OK;
1946}
1947
1948HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1949{
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1952 return S_OK;
1953}
1954
1955HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1956{
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 if ( Global::IsOnline(mData->mMachineState)
1960 && mHWData->mVideoCaptureEnabled)
1961 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1962
1963 i_setModified(IsModified_MachineData);
1964 mHWData.backup();
1965 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1966
1967 return S_OK;
1968}
1969
1970HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1971{
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1975 return S_OK;
1976}
1977
1978HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1979{
1980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1981
1982 if ( Global::IsOnline(mData->mMachineState)
1983 && mHWData->mVideoCaptureEnabled)
1984 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1985
1986 i_setModified(IsModified_MachineData);
1987 mHWData.backup();
1988 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1989
1990 return S_OK;
1991}
1992
1993HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1994{
1995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1996
1997 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1998
1999 return S_OK;
2000}
2001
2002HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2003{
2004 switch (aGraphicsControllerType)
2005 {
2006 case GraphicsControllerType_Null:
2007 case GraphicsControllerType_VBoxVGA:
2008#ifdef VBOX_WITH_VMSVGA
2009 case GraphicsControllerType_VMSVGA:
2010#endif
2011 break;
2012 default:
2013 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2014 }
2015
2016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2017
2018 HRESULT rc = i_checkStateDependency(MutableStateDep);
2019 if (FAILED(rc)) return rc;
2020
2021 i_setModified(IsModified_MachineData);
2022 mHWData.backup();
2023 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2024
2025 return S_OK;
2026}
2027
2028HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2029{
2030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 *aVRAMSize = mHWData->mVRAMSize;
2033
2034 return S_OK;
2035}
2036
2037HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2038{
2039 /* check VRAM limits */
2040 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2041 return setError(E_INVALIDARG,
2042 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2043 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2044
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = i_checkStateDependency(MutableStateDep);
2048 if (FAILED(rc)) return rc;
2049
2050 i_setModified(IsModified_MachineData);
2051 mHWData.backup();
2052 mHWData->mVRAMSize = aVRAMSize;
2053
2054 return S_OK;
2055}
2056
2057/** @todo this method should not be public */
2058HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2059{
2060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2061
2062 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2063
2064 return S_OK;
2065}
2066
2067/**
2068 * Set the memory balloon size.
2069 *
2070 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2071 * we have to make sure that we never call IGuest from here.
2072 */
2073HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2074{
2075 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2076#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2077 /* check limits */
2078 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2079 return setError(E_INVALIDARG,
2080 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2081 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2082
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2088
2089 return S_OK;
2090#else
2091 NOREF(aMemoryBalloonSize);
2092 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2093#endif
2094}
2095
2096HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2101 return S_OK;
2102}
2103
2104HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2105{
2106#ifdef VBOX_WITH_PAGE_SHARING
2107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2113 return S_OK;
2114#else
2115 NOREF(aPageFusionEnabled);
2116 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2117#endif
2118}
2119
2120HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2121{
2122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2123
2124 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2125
2126 return S_OK;
2127}
2128
2129HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2130{
2131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2132
2133 HRESULT rc = i_checkStateDependency(MutableStateDep);
2134 if (FAILED(rc)) return rc;
2135
2136 /** @todo check validity! */
2137
2138 i_setModified(IsModified_MachineData);
2139 mHWData.backup();
2140 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2141
2142 return S_OK;
2143}
2144
2145
2146HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2147{
2148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2149
2150 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2151
2152 return S_OK;
2153}
2154
2155HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2156{
2157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2158
2159 HRESULT rc = i_checkStateDependency(MutableStateDep);
2160 if (FAILED(rc)) return rc;
2161
2162 /** @todo check validity! */
2163 i_setModified(IsModified_MachineData);
2164 mHWData.backup();
2165 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2166
2167 return S_OK;
2168}
2169
2170HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2171{
2172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 *aMonitorCount = mHWData->mMonitorCount;
2175
2176 return S_OK;
2177}
2178
2179HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2180{
2181 /* make sure monitor count is a sensible number */
2182 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2183 return setError(E_INVALIDARG,
2184 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2185 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2186
2187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2188
2189 HRESULT rc = i_checkStateDependency(MutableStateDep);
2190 if (FAILED(rc)) return rc;
2191
2192 i_setModified(IsModified_MachineData);
2193 mHWData.backup();
2194 mHWData->mMonitorCount = aMonitorCount;
2195
2196 return S_OK;
2197}
2198
2199HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2200{
2201 /* mBIOSSettings is constant during life time, no need to lock */
2202 aBIOSSettings = mBIOSSettings;
2203
2204 return S_OK;
2205}
2206
2207HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2208{
2209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 switch (aProperty)
2212 {
2213 case CPUPropertyType_PAE:
2214 *aValue = mHWData->mPAEEnabled;
2215 break;
2216
2217 case CPUPropertyType_LongMode:
2218 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2219 *aValue = TRUE;
2220 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2221 *aValue = FALSE;
2222#if HC_ARCH_BITS == 64
2223 else
2224 *aValue = TRUE;
2225#else
2226 else
2227 {
2228 *aValue = FALSE;
2229
2230 ComObjPtr<GuestOSType> pGuestOSType;
2231 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2232 pGuestOSType);
2233 if (SUCCEEDED(hrc2))
2234 {
2235 if (pGuestOSType->i_is64Bit())
2236 {
2237 ComObjPtr<Host> pHost = mParent->i_host();
2238 alock.release();
2239
2240 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2241 if (FAILED(hrc2))
2242 *aValue = FALSE;
2243 }
2244 }
2245 }
2246#endif
2247 break;
2248
2249 case CPUPropertyType_TripleFaultReset:
2250 *aValue = mHWData->mTripleFaultReset;
2251 break;
2252
2253 case CPUPropertyType_APIC:
2254 *aValue = mHWData->mAPIC;
2255 break;
2256
2257 case CPUPropertyType_X2APIC:
2258 *aValue = mHWData->mX2APIC;
2259 break;
2260
2261 case CPUPropertyType_IBPBOnVMExit:
2262 *aValue = mHWData->mIBPBOnVMExit;
2263 break;
2264
2265 case CPUPropertyType_IBPBOnVMEntry:
2266 *aValue = mHWData->mIBPBOnVMEntry;
2267 break;
2268
2269 case CPUPropertyType_HWVirt:
2270 *aValue = mHWData->mNestedHWVirt;
2271 break;
2272
2273 default:
2274 return E_INVALIDARG;
2275 }
2276 return S_OK;
2277}
2278
2279HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2280{
2281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2282
2283 HRESULT rc = i_checkStateDependency(MutableStateDep);
2284 if (FAILED(rc)) return rc;
2285
2286 switch (aProperty)
2287 {
2288 case CPUPropertyType_PAE:
2289 i_setModified(IsModified_MachineData);
2290 mHWData.backup();
2291 mHWData->mPAEEnabled = !!aValue;
2292 break;
2293
2294 case CPUPropertyType_LongMode:
2295 i_setModified(IsModified_MachineData);
2296 mHWData.backup();
2297 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2298 break;
2299
2300 case CPUPropertyType_TripleFaultReset:
2301 i_setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mTripleFaultReset = !!aValue;
2304 break;
2305
2306 case CPUPropertyType_APIC:
2307 if (mHWData->mX2APIC)
2308 aValue = TRUE;
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mAPIC = !!aValue;
2312 break;
2313
2314 case CPUPropertyType_X2APIC:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mX2APIC = !!aValue;
2318 if (aValue)
2319 mHWData->mAPIC = !!aValue;
2320 break;
2321
2322 case CPUPropertyType_IBPBOnVMExit:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mIBPBOnVMExit = !!aValue;
2326 break;
2327
2328 case CPUPropertyType_IBPBOnVMEntry:
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mIBPBOnVMEntry = !!aValue;
2332 break;
2333
2334 case CPUPropertyType_HWVirt:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mNestedHWVirt = !!aValue;
2338 break;
2339
2340 default:
2341 return E_INVALIDARG;
2342 }
2343 return S_OK;
2344}
2345
2346HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2347 ULONG *aValEcx, ULONG *aValEdx)
2348{
2349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2350 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2351 {
2352 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2353 it != mHWData->mCpuIdLeafList.end();
2354 ++it)
2355 {
2356 if (aOrdinal == 0)
2357 {
2358 const settings::CpuIdLeaf &rLeaf= *it;
2359 *aIdx = rLeaf.idx;
2360 *aSubIdx = rLeaf.idxSub;
2361 *aValEax = rLeaf.uEax;
2362 *aValEbx = rLeaf.uEbx;
2363 *aValEcx = rLeaf.uEcx;
2364 *aValEdx = rLeaf.uEdx;
2365 return S_OK;
2366 }
2367 aOrdinal--;
2368 }
2369 }
2370 return E_INVALIDARG;
2371}
2372
2373HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2374{
2375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 /*
2378 * Search the list.
2379 */
2380 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2381 {
2382 const settings::CpuIdLeaf &rLeaf= *it;
2383 if ( rLeaf.idx == aIdx
2384 && ( aSubIdx == UINT32_MAX
2385 || rLeaf.idxSub == aSubIdx) )
2386 {
2387 *aValEax = rLeaf.uEax;
2388 *aValEbx = rLeaf.uEbx;
2389 *aValEcx = rLeaf.uEcx;
2390 *aValEdx = rLeaf.uEdx;
2391 return S_OK;
2392 }
2393 }
2394
2395 return E_INVALIDARG;
2396}
2397
2398
2399HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2400{
2401 /*
2402 * Validate input before taking locks and checking state.
2403 */
2404 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2405 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2406 if ( aIdx >= UINT32_C(0x20)
2407 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2408 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2409 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2410
2411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2412 HRESULT rc = i_checkStateDependency(MutableStateDep);
2413 if (FAILED(rc)) return rc;
2414
2415 /*
2416 * Impose a maximum number of leaves.
2417 */
2418 if (mHWData->mCpuIdLeafList.size() > 256)
2419 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2420
2421 /*
2422 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2423 */
2424 i_setModified(IsModified_MachineData);
2425 mHWData.backup();
2426
2427 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2428 {
2429 settings::CpuIdLeaf &rLeaf= *it;
2430 if ( rLeaf.idx == aIdx
2431 && ( aSubIdx == UINT32_MAX
2432 || rLeaf.idxSub == aSubIdx) )
2433 it = mHWData->mCpuIdLeafList.erase(it);
2434 else
2435 ++it;
2436 }
2437
2438 settings::CpuIdLeaf NewLeaf;
2439 NewLeaf.idx = aIdx;
2440 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2441 NewLeaf.uEax = aValEax;
2442 NewLeaf.uEbx = aValEbx;
2443 NewLeaf.uEcx = aValEcx;
2444 NewLeaf.uEdx = aValEdx;
2445 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2446 return S_OK;
2447}
2448
2449HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2450{
2451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 HRESULT rc = i_checkStateDependency(MutableStateDep);
2454 if (FAILED(rc)) return rc;
2455
2456 /*
2457 * Do the removal.
2458 */
2459 bool fModified = false;
2460 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2461 {
2462 settings::CpuIdLeaf &rLeaf= *it;
2463 if ( rLeaf.idx == aIdx
2464 && ( aSubIdx == UINT32_MAX
2465 || rLeaf.idxSub == aSubIdx) )
2466 {
2467 if (!fModified)
2468 {
2469 fModified = true;
2470 i_setModified(IsModified_MachineData);
2471 mHWData.backup();
2472 }
2473 it = mHWData->mCpuIdLeafList.erase(it);
2474 }
2475 else
2476 ++it;
2477 }
2478
2479 return S_OK;
2480}
2481
2482HRESULT Machine::removeAllCPUIDLeaves()
2483{
2484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 HRESULT rc = i_checkStateDependency(MutableStateDep);
2487 if (FAILED(rc)) return rc;
2488
2489 if (mHWData->mCpuIdLeafList.size() > 0)
2490 {
2491 i_setModified(IsModified_MachineData);
2492 mHWData.backup();
2493
2494 mHWData->mCpuIdLeafList.clear();
2495 }
2496
2497 return S_OK;
2498}
2499HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2500{
2501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 switch(aProperty)
2504 {
2505 case HWVirtExPropertyType_Enabled:
2506 *aValue = mHWData->mHWVirtExEnabled;
2507 break;
2508
2509 case HWVirtExPropertyType_VPID:
2510 *aValue = mHWData->mHWVirtExVPIDEnabled;
2511 break;
2512
2513 case HWVirtExPropertyType_NestedPaging:
2514 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2515 break;
2516
2517 case HWVirtExPropertyType_UnrestrictedExecution:
2518 *aValue = mHWData->mHWVirtExUXEnabled;
2519 break;
2520
2521 case HWVirtExPropertyType_LargePages:
2522 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2523#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2524 *aValue = FALSE;
2525#endif
2526 break;
2527
2528 case HWVirtExPropertyType_Force:
2529 *aValue = mHWData->mHWVirtExForceEnabled;
2530 break;
2531
2532 default:
2533 return E_INVALIDARG;
2534 }
2535 return S_OK;
2536}
2537
2538HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2539{
2540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 HRESULT rc = i_checkStateDependency(MutableStateDep);
2543 if (FAILED(rc)) return rc;
2544
2545 switch(aProperty)
2546 {
2547 case HWVirtExPropertyType_Enabled:
2548 i_setModified(IsModified_MachineData);
2549 mHWData.backup();
2550 mHWData->mHWVirtExEnabled = !!aValue;
2551 break;
2552
2553 case HWVirtExPropertyType_VPID:
2554 i_setModified(IsModified_MachineData);
2555 mHWData.backup();
2556 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2557 break;
2558
2559 case HWVirtExPropertyType_NestedPaging:
2560 i_setModified(IsModified_MachineData);
2561 mHWData.backup();
2562 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2563 break;
2564
2565 case HWVirtExPropertyType_UnrestrictedExecution:
2566 i_setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExUXEnabled = !!aValue;
2569 break;
2570
2571 case HWVirtExPropertyType_LargePages:
2572 i_setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2575 break;
2576
2577 case HWVirtExPropertyType_Force:
2578 i_setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExForceEnabled = !!aValue;
2581 break;
2582
2583 default:
2584 return E_INVALIDARG;
2585 }
2586
2587 return S_OK;
2588}
2589
2590HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2591{
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2595
2596 return S_OK;
2597}
2598
2599HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2600{
2601 /** @todo (r=dmik):
2602 * 1. Allow to change the name of the snapshot folder containing snapshots
2603 * 2. Rename the folder on disk instead of just changing the property
2604 * value (to be smart and not to leave garbage). Note that it cannot be
2605 * done here because the change may be rolled back. Thus, the right
2606 * place is #saveSettings().
2607 */
2608
2609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 HRESULT rc = i_checkStateDependency(MutableStateDep);
2612 if (FAILED(rc)) return rc;
2613
2614 if (!mData->mCurrentSnapshot.isNull())
2615 return setError(E_FAIL,
2616 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2617
2618 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2619
2620 if (strSnapshotFolder.isEmpty())
2621 strSnapshotFolder = "Snapshots";
2622 int vrc = i_calculateFullPath(strSnapshotFolder,
2623 strSnapshotFolder);
2624 if (RT_FAILURE(vrc))
2625 return setError(E_FAIL,
2626 tr("Invalid snapshot folder '%s' (%Rrc)"),
2627 strSnapshotFolder.c_str(), vrc);
2628
2629 i_setModified(IsModified_MachineData);
2630 mUserData.backup();
2631
2632 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aMediumAttachments.resize(mMediumAttachments->size());
2642 size_t i = 0;
2643 for (MediumAttachmentList::const_iterator
2644 it = mMediumAttachments->begin();
2645 it != mMediumAttachments->end();
2646 ++it, ++i)
2647 aMediumAttachments[i] = *it;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 Assert(!!mVRDEServer);
2657
2658 aVRDEServer = mVRDEServer;
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 aAudioAdapter = mAudioAdapter;
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2673{
2674#ifdef VBOX_WITH_VUSB
2675 clearError();
2676 MultiResult rc(S_OK);
2677
2678# ifdef VBOX_WITH_USB
2679 rc = mParent->i_host()->i_checkUSBProxyService();
2680 if (FAILED(rc)) return rc;
2681# endif
2682
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 aUSBControllers.resize(mUSBControllers->size());
2686 size_t i = 0;
2687 for (USBControllerList::const_iterator
2688 it = mUSBControllers->begin();
2689 it != mUSBControllers->end();
2690 ++it, ++i)
2691 aUSBControllers[i] = *it;
2692
2693 return S_OK;
2694#else
2695 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2696 * extended error info to indicate that USB is simply not available
2697 * (w/o treating it as a failure), for example, as in OSE */
2698 NOREF(aUSBControllers);
2699 ReturnComNotImplemented();
2700#endif /* VBOX_WITH_VUSB */
2701}
2702
2703HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2704{
2705#ifdef VBOX_WITH_VUSB
2706 clearError();
2707 MultiResult rc(S_OK);
2708
2709# ifdef VBOX_WITH_USB
2710 rc = mParent->i_host()->i_checkUSBProxyService();
2711 if (FAILED(rc)) return rc;
2712# endif
2713
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 aUSBDeviceFilters = mUSBDeviceFilters;
2717 return rc;
2718#else
2719 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2720 * extended error info to indicate that USB is simply not available
2721 * (w/o treating it as a failure), for example, as in OSE */
2722 NOREF(aUSBDeviceFilters);
2723 ReturnComNotImplemented();
2724#endif /* VBOX_WITH_VUSB */
2725}
2726
2727HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aSettingsFilePath = mData->m_strConfigFileFull;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2737{
2738 RT_NOREF(aSettingsFilePath);
2739 ReturnComNotImplemented();
2740}
2741
2742HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2743{
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2747 if (FAILED(rc)) return rc;
2748
2749 if (!mData->pMachineConfigFile->fileExists())
2750 // this is a new machine, and no config file exists yet:
2751 *aSettingsModified = TRUE;
2752 else
2753 *aSettingsModified = (mData->flModifications != 0);
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 *aSessionState = mData->mSession.mState;
2763
2764 return S_OK;
2765}
2766
2767HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2768{
2769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2770
2771 aSessionName = mData->mSession.mName;
2772
2773 return S_OK;
2774}
2775
2776HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2777{
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 *aSessionPID = mData->mSession.mPID;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::getState(MachineState_T *aState)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 *aState = mData->mMachineState;
2790 Assert(mData->mMachineState != MachineState_Null);
2791
2792 return S_OK;
2793}
2794
2795HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2796{
2797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 aStateFilePath = mSSData->strStateFilePath;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2814{
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 i_getLogFolder(aLogFolder);
2818
2819 return S_OK;
2820}
2821
2822HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2823{
2824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2825
2826 aCurrentSnapshot = mData->mCurrentSnapshot;
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834
2835 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2836 ? 0
2837 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2838
2839 return S_OK;
2840}
2841
2842HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2843{
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 /* Note: for machines with no snapshots, we always return FALSE
2847 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2848 * reasons :) */
2849
2850 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2851 ? FALSE
2852 : mData->mCurrentStateModified;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2858{
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 aSharedFolders.resize(mHWData->mSharedFolders.size());
2862 size_t i = 0;
2863 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2864 it = mHWData->mSharedFolders.begin();
2865 it != mHWData->mSharedFolders.end();
2866 ++it, ++i)
2867 aSharedFolders[i] = *it;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 *aClipboardMode = mHWData->mClipboardMode;
2877
2878 return S_OK;
2879}
2880
2881HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2882{
2883 HRESULT rc = S_OK;
2884
2885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 alock.release();
2888 rc = i_onClipboardModeChange(aClipboardMode);
2889 alock.acquire();
2890 if (FAILED(rc)) return rc;
2891
2892 i_setModified(IsModified_MachineData);
2893 mHWData.backup();
2894 mHWData->mClipboardMode = aClipboardMode;
2895
2896 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2897 if (Global::IsOnline(mData->mMachineState))
2898 i_saveSettings(NULL);
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2904{
2905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 *aDnDMode = mHWData->mDnDMode;
2908
2909 return S_OK;
2910}
2911
2912HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2913{
2914 HRESULT rc = S_OK;
2915
2916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 alock.release();
2919 rc = i_onDnDModeChange(aDnDMode);
2920
2921 alock.acquire();
2922 if (FAILED(rc)) return rc;
2923
2924 i_setModified(IsModified_MachineData);
2925 mHWData.backup();
2926 mHWData->mDnDMode = aDnDMode;
2927
2928 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2929 if (Global::IsOnline(mData->mMachineState))
2930 i_saveSettings(NULL);
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 aStorageControllers.resize(mStorageControllers->size());
2940 size_t i = 0;
2941 for (StorageControllerList::const_iterator
2942 it = mStorageControllers->begin();
2943 it != mStorageControllers->end();
2944 ++it, ++i)
2945 aStorageControllers[i] = *it;
2946
2947 return S_OK;
2948}
2949
2950HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2951{
2952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 *aEnabled = mUserData->s.fTeleporterEnabled;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2960{
2961 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 /* Only allow it to be set to true when PoweredOff or Aborted.
2964 (Clearing it is always permitted.) */
2965 if ( aTeleporterEnabled
2966 && mData->mRegistered
2967 && ( !i_isSessionMachine()
2968 || ( mData->mMachineState != MachineState_PoweredOff
2969 && mData->mMachineState != MachineState_Teleported
2970 && mData->mMachineState != MachineState_Aborted
2971 )
2972 )
2973 )
2974 return setError(VBOX_E_INVALID_VM_STATE,
2975 tr("The machine is not powered off (state is %s)"),
2976 Global::stringifyMachineState(mData->mMachineState));
2977
2978 i_setModified(IsModified_MachineData);
2979 mUserData.backup();
2980 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2981
2982 return S_OK;
2983}
2984
2985HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2986{
2987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2988
2989 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2990
2991 return S_OK;
2992}
2993
2994HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2995{
2996 if (aTeleporterPort >= _64K)
2997 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2998
2999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3002 if (FAILED(rc)) return rc;
3003
3004 i_setModified(IsModified_MachineData);
3005 mUserData.backup();
3006 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3007
3008 return S_OK;
3009}
3010
3011HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3012{
3013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3016
3017 return S_OK;
3018}
3019
3020HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3021{
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3025 if (FAILED(rc)) return rc;
3026
3027 i_setModified(IsModified_MachineData);
3028 mUserData.backup();
3029 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3030
3031 return S_OK;
3032}
3033
3034HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3035{
3036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3037 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3043{
3044 /*
3045 * Hash the password first.
3046 */
3047 com::Utf8Str aT = aTeleporterPassword;
3048
3049 if (!aT.isEmpty())
3050 {
3051 if (VBoxIsPasswordHashed(&aT))
3052 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3053 VBoxHashPassword(&aT);
3054 }
3055
3056 /*
3057 * Do the update.
3058 */
3059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3060 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3061 if (SUCCEEDED(hrc))
3062 {
3063 i_setModified(IsModified_MachineData);
3064 mUserData.backup();
3065 mUserData->s.strTeleporterPassword = aT;
3066 }
3067
3068 return hrc;
3069}
3070
3071HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3072{
3073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3074
3075 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3076 return S_OK;
3077}
3078
3079HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3080{
3081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3082
3083 /** @todo deal with running state change. */
3084 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3085 if (FAILED(rc)) return rc;
3086
3087 i_setModified(IsModified_MachineData);
3088 mUserData.backup();
3089 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3090 return S_OK;
3091}
3092
3093HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3094{
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3098 return S_OK;
3099}
3100
3101HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3102{
3103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 /** @todo deal with running state change. */
3106 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3107 if (FAILED(rc)) return rc;
3108
3109 i_setModified(IsModified_MachineData);
3110 mUserData.backup();
3111 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3112 return S_OK;
3113}
3114
3115HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3116{
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3124{
3125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 /** @todo deal with running state change. */
3128 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3129 if (FAILED(rc)) return rc;
3130
3131 i_setModified(IsModified_MachineData);
3132 mUserData.backup();
3133 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3134 return S_OK;
3135}
3136
3137HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3138{
3139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3142
3143 return S_OK;
3144}
3145
3146HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3147{
3148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 /** @todo deal with running state change. */
3151 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3152 if (FAILED(rc)) return rc;
3153
3154 i_setModified(IsModified_MachineData);
3155 mUserData.backup();
3156 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3157
3158 return S_OK;
3159}
3160
3161HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3162{
3163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3166 return S_OK;
3167}
3168
3169HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3170{
3171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 /** @todo deal with running state change. */
3174 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3175 if (FAILED(rc)) return rc;
3176
3177 i_setModified(IsModified_MachineData);
3178 mUserData.backup();
3179 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3180 return S_OK;
3181}
3182
3183HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3184{
3185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3186
3187 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3188
3189 return S_OK;
3190}
3191
3192HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3193{
3194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 /* Only allow it to be set to true when PoweredOff or Aborted.
3197 (Clearing it is always permitted.) */
3198 if ( aRTCUseUTC
3199 && mData->mRegistered
3200 && ( !i_isSessionMachine()
3201 || ( mData->mMachineState != MachineState_PoweredOff
3202 && mData->mMachineState != MachineState_Teleported
3203 && mData->mMachineState != MachineState_Aborted
3204 )
3205 )
3206 )
3207 return setError(VBOX_E_INVALID_VM_STATE,
3208 tr("The machine is not powered off (state is %s)"),
3209 Global::stringifyMachineState(mData->mMachineState));
3210
3211 i_setModified(IsModified_MachineData);
3212 mUserData.backup();
3213 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3214
3215 return S_OK;
3216}
3217
3218HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3219{
3220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3221
3222 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3223
3224 return S_OK;
3225}
3226
3227HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3228{
3229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3230
3231 HRESULT rc = i_checkStateDependency(MutableStateDep);
3232 if (FAILED(rc)) return rc;
3233
3234 i_setModified(IsModified_MachineData);
3235 mHWData.backup();
3236 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3237
3238 return S_OK;
3239}
3240
3241HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3242{
3243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3244
3245 *aIOCacheSize = mHWData->mIOCacheSize;
3246
3247 return S_OK;
3248}
3249
3250HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3251{
3252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3253
3254 HRESULT rc = i_checkStateDependency(MutableStateDep);
3255 if (FAILED(rc)) return rc;
3256
3257 i_setModified(IsModified_MachineData);
3258 mHWData.backup();
3259 mHWData->mIOCacheSize = aIOCacheSize;
3260
3261 return S_OK;
3262}
3263
3264
3265/**
3266 * @note Locks objects!
3267 */
3268HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3269 LockType_T aLockType)
3270{
3271 /* check the session state */
3272 SessionState_T state;
3273 HRESULT rc = aSession->COMGETTER(State)(&state);
3274 if (FAILED(rc)) return rc;
3275
3276 if (state != SessionState_Unlocked)
3277 return setError(VBOX_E_INVALID_OBJECT_STATE,
3278 tr("The given session is busy"));
3279
3280 // get the client's IInternalSessionControl interface
3281 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3282 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3283 E_INVALIDARG);
3284
3285 // session name (only used in some code paths)
3286 Utf8Str strSessionName;
3287
3288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3289
3290 if (!mData->mRegistered)
3291 return setError(E_UNEXPECTED,
3292 tr("The machine '%s' is not registered"),
3293 mUserData->s.strName.c_str());
3294
3295 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3296
3297 SessionState_T oldState = mData->mSession.mState;
3298 /* Hack: in case the session is closing and there is a progress object
3299 * which allows waiting for the session to be closed, take the opportunity
3300 * and do a limited wait (max. 1 second). This helps a lot when the system
3301 * is busy and thus session closing can take a little while. */
3302 if ( mData->mSession.mState == SessionState_Unlocking
3303 && mData->mSession.mProgress)
3304 {
3305 alock.release();
3306 mData->mSession.mProgress->WaitForCompletion(1000);
3307 alock.acquire();
3308 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3309 }
3310
3311 // try again now
3312 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3313 // (i.e. session machine exists)
3314 && (aLockType == LockType_Shared) // caller wants a shared link to the
3315 // existing session that holds the write lock:
3316 )
3317 {
3318 // OK, share the session... we are now dealing with three processes:
3319 // 1) VBoxSVC (where this code runs);
3320 // 2) process C: the caller's client process (who wants a shared session);
3321 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3322
3323 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3324 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3325 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3326 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3327 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3328
3329 /*
3330 * Release the lock before calling the client process. It's safe here
3331 * since the only thing to do after we get the lock again is to add
3332 * the remote control to the list (which doesn't directly influence
3333 * anything).
3334 */
3335 alock.release();
3336
3337 // get the console of the session holding the write lock (this is a remote call)
3338 ComPtr<IConsole> pConsoleW;
3339 if (mData->mSession.mLockType == LockType_VM)
3340 {
3341 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3342 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3343 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3344 if (FAILED(rc))
3345 // the failure may occur w/o any error info (from RPC), so provide one
3346 return setError(VBOX_E_VM_ERROR,
3347 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3348 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3349 }
3350
3351 // share the session machine and W's console with the caller's session
3352 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3353 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3354 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3355
3356 if (FAILED(rc))
3357 // the failure may occur w/o any error info (from RPC), so provide one
3358 return setError(VBOX_E_VM_ERROR,
3359 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3360 alock.acquire();
3361
3362 // need to revalidate the state after acquiring the lock again
3363 if (mData->mSession.mState != SessionState_Locked)
3364 {
3365 pSessionControl->Uninitialize();
3366 return setError(VBOX_E_INVALID_SESSION_STATE,
3367 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3368 mUserData->s.strName.c_str());
3369 }
3370
3371 // add the caller's session to the list
3372 mData->mSession.mRemoteControls.push_back(pSessionControl);
3373 }
3374 else if ( mData->mSession.mState == SessionState_Locked
3375 || mData->mSession.mState == SessionState_Unlocking
3376 )
3377 {
3378 // sharing not permitted, or machine still unlocking:
3379 return setError(VBOX_E_INVALID_OBJECT_STATE,
3380 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3381 mUserData->s.strName.c_str());
3382 }
3383 else
3384 {
3385 // machine is not locked: then write-lock the machine (create the session machine)
3386
3387 // must not be busy
3388 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3389
3390 // get the caller's session PID
3391 RTPROCESS pid = NIL_RTPROCESS;
3392 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3393 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3394 Assert(pid != NIL_RTPROCESS);
3395
3396 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3397
3398 if (fLaunchingVMProcess)
3399 {
3400 if (mData->mSession.mPID == NIL_RTPROCESS)
3401 {
3402 // two or more clients racing for a lock, the one which set the
3403 // session state to Spawning will win, the others will get an
3404 // error as we can't decide here if waiting a little would help
3405 // (only for shared locks this would avoid an error)
3406 return setError(VBOX_E_INVALID_OBJECT_STATE,
3407 tr("The machine '%s' already has a lock request pending"),
3408 mUserData->s.strName.c_str());
3409 }
3410
3411 // this machine is awaiting for a spawning session to be opened:
3412 // then the calling process must be the one that got started by
3413 // LaunchVMProcess()
3414
3415 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3416 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3417
3418#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3419 /* Hardened windows builds spawns three processes when a VM is
3420 launched, the 3rd one is the one that will end up here. */
3421 RTPROCESS ppid;
3422 int rc = RTProcQueryParent(pid, &ppid);
3423 if (RT_SUCCESS(rc))
3424 rc = RTProcQueryParent(ppid, &ppid);
3425 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3426 || rc == VERR_ACCESS_DENIED)
3427 {
3428 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3429 mData->mSession.mPID = pid;
3430 }
3431#endif
3432
3433 if (mData->mSession.mPID != pid)
3434 return setError(E_ACCESSDENIED,
3435 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3436 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3437 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3438 }
3439
3440 // create the mutable SessionMachine from the current machine
3441 ComObjPtr<SessionMachine> sessionMachine;
3442 sessionMachine.createObject();
3443 rc = sessionMachine->init(this);
3444 AssertComRC(rc);
3445
3446 /* NOTE: doing return from this function after this point but
3447 * before the end is forbidden since it may call SessionMachine::uninit()
3448 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3449 * lock while still holding the Machine lock in alock so that a deadlock
3450 * is possible due to the wrong lock order. */
3451
3452 if (SUCCEEDED(rc))
3453 {
3454 /*
3455 * Set the session state to Spawning to protect against subsequent
3456 * attempts to open a session and to unregister the machine after
3457 * we release the lock.
3458 */
3459 SessionState_T origState = mData->mSession.mState;
3460 mData->mSession.mState = SessionState_Spawning;
3461
3462#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3463 /* Get the client token ID to be passed to the client process */
3464 Utf8Str strTokenId;
3465 sessionMachine->i_getTokenId(strTokenId);
3466 Assert(!strTokenId.isEmpty());
3467#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3468 /* Get the client token to be passed to the client process */
3469 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3470 /* The token is now "owned" by pToken, fix refcount */
3471 if (!pToken.isNull())
3472 pToken->Release();
3473#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3474
3475 /*
3476 * Release the lock before calling the client process -- it will call
3477 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3478 * because the state is Spawning, so that LaunchVMProcess() and
3479 * LockMachine() calls will fail. This method, called before we
3480 * acquire the lock again, will fail because of the wrong PID.
3481 *
3482 * Note that mData->mSession.mRemoteControls accessed outside
3483 * the lock may not be modified when state is Spawning, so it's safe.
3484 */
3485 alock.release();
3486
3487 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3488#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3489 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3490#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3491 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3492 /* Now the token is owned by the client process. */
3493 pToken.setNull();
3494#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3495 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3496
3497 /* The failure may occur w/o any error info (from RPC), so provide one */
3498 if (FAILED(rc))
3499 setError(VBOX_E_VM_ERROR,
3500 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3501
3502 // get session name, either to remember or to compare against
3503 // the already known session name.
3504 {
3505 Bstr bstrSessionName;
3506 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3507 if (SUCCEEDED(rc2))
3508 strSessionName = bstrSessionName;
3509 }
3510
3511 if ( SUCCEEDED(rc)
3512 && fLaunchingVMProcess
3513 )
3514 {
3515 /* complete the remote session initialization */
3516
3517 /* get the console from the direct session */
3518 ComPtr<IConsole> console;
3519 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3520 ComAssertComRC(rc);
3521
3522 if (SUCCEEDED(rc) && !console)
3523 {
3524 ComAssert(!!console);
3525 rc = E_FAIL;
3526 }
3527
3528 /* assign machine & console to the remote session */
3529 if (SUCCEEDED(rc))
3530 {
3531 /*
3532 * after LaunchVMProcess(), the first and the only
3533 * entry in remoteControls is that remote session
3534 */
3535 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3536 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3537 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3538
3539 /* The failure may occur w/o any error info (from RPC), so provide one */
3540 if (FAILED(rc))
3541 setError(VBOX_E_VM_ERROR,
3542 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3543 }
3544
3545 if (FAILED(rc))
3546 pSessionControl->Uninitialize();
3547 }
3548
3549 /* acquire the lock again */
3550 alock.acquire();
3551
3552 /* Restore the session state */
3553 mData->mSession.mState = origState;
3554 }
3555
3556 // finalize spawning anyway (this is why we don't return on errors above)
3557 if (fLaunchingVMProcess)
3558 {
3559 Assert(mData->mSession.mName == strSessionName);
3560 /* Note that the progress object is finalized later */
3561 /** @todo Consider checking mData->mSession.mProgress for cancellation
3562 * around here. */
3563
3564 /* We don't reset mSession.mPID here because it is necessary for
3565 * SessionMachine::uninit() to reap the child process later. */
3566
3567 if (FAILED(rc))
3568 {
3569 /* Close the remote session, remove the remote control from the list
3570 * and reset session state to Closed (@note keep the code in sync
3571 * with the relevant part in checkForSpawnFailure()). */
3572
3573 Assert(mData->mSession.mRemoteControls.size() == 1);
3574 if (mData->mSession.mRemoteControls.size() == 1)
3575 {
3576 ErrorInfoKeeper eik;
3577 mData->mSession.mRemoteControls.front()->Uninitialize();
3578 }
3579
3580 mData->mSession.mRemoteControls.clear();
3581 mData->mSession.mState = SessionState_Unlocked;
3582 }
3583 }
3584 else
3585 {
3586 /* memorize PID of the directly opened session */
3587 if (SUCCEEDED(rc))
3588 mData->mSession.mPID = pid;
3589 }
3590
3591 if (SUCCEEDED(rc))
3592 {
3593 mData->mSession.mLockType = aLockType;
3594 /* memorize the direct session control and cache IUnknown for it */
3595 mData->mSession.mDirectControl = pSessionControl;
3596 mData->mSession.mState = SessionState_Locked;
3597 if (!fLaunchingVMProcess)
3598 mData->mSession.mName = strSessionName;
3599 /* associate the SessionMachine with this Machine */
3600 mData->mSession.mMachine = sessionMachine;
3601
3602 /* request an IUnknown pointer early from the remote party for later
3603 * identity checks (it will be internally cached within mDirectControl
3604 * at least on XPCOM) */
3605 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3606 NOREF(unk);
3607 }
3608
3609 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3610 * would break the lock order */
3611 alock.release();
3612
3613 /* uninitialize the created session machine on failure */
3614 if (FAILED(rc))
3615 sessionMachine->uninit();
3616 }
3617
3618 if (SUCCEEDED(rc))
3619 {
3620 /*
3621 * tell the client watcher thread to update the set of
3622 * machines that have open sessions
3623 */
3624 mParent->i_updateClientWatcher();
3625
3626 if (oldState != SessionState_Locked)
3627 /* fire an event */
3628 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3629 }
3630
3631 return rc;
3632}
3633
3634/**
3635 * @note Locks objects!
3636 */
3637HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3638 const com::Utf8Str &aName,
3639 const com::Utf8Str &aEnvironment,
3640 ComPtr<IProgress> &aProgress)
3641{
3642 Utf8Str strFrontend(aName);
3643 /* "emergencystop" doesn't need the session, so skip the checks/interface
3644 * retrieval. This code doesn't quite fit in here, but introducing a
3645 * special API method would be even more effort, and would require explicit
3646 * support by every API client. It's better to hide the feature a bit. */
3647 if (strFrontend != "emergencystop")
3648 CheckComArgNotNull(aSession);
3649
3650 HRESULT rc = S_OK;
3651 if (strFrontend.isEmpty())
3652 {
3653 Bstr bstrFrontend;
3654 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3655 if (FAILED(rc))
3656 return rc;
3657 strFrontend = bstrFrontend;
3658 if (strFrontend.isEmpty())
3659 {
3660 ComPtr<ISystemProperties> systemProperties;
3661 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3662 if (FAILED(rc))
3663 return rc;
3664 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3665 if (FAILED(rc))
3666 return rc;
3667 strFrontend = bstrFrontend;
3668 }
3669 /* paranoia - emergencystop is not a valid default */
3670 if (strFrontend == "emergencystop")
3671 strFrontend = Utf8Str::Empty;
3672 }
3673 /* default frontend: Qt GUI */
3674 if (strFrontend.isEmpty())
3675 strFrontend = "GUI/Qt";
3676
3677 if (strFrontend != "emergencystop")
3678 {
3679 /* check the session state */
3680 SessionState_T state;
3681 rc = aSession->COMGETTER(State)(&state);
3682 if (FAILED(rc))
3683 return rc;
3684
3685 if (state != SessionState_Unlocked)
3686 return setError(VBOX_E_INVALID_OBJECT_STATE,
3687 tr("The given session is busy"));
3688
3689 /* get the IInternalSessionControl interface */
3690 ComPtr<IInternalSessionControl> control(aSession);
3691 ComAssertMsgRet(!control.isNull(),
3692 ("No IInternalSessionControl interface"),
3693 E_INVALIDARG);
3694
3695 /* get the teleporter enable state for the progress object init. */
3696 BOOL fTeleporterEnabled;
3697 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3698 if (FAILED(rc))
3699 return rc;
3700
3701 /* create a progress object */
3702 ComObjPtr<ProgressProxy> progress;
3703 progress.createObject();
3704 rc = progress->init(mParent,
3705 static_cast<IMachine*>(this),
3706 Bstr(tr("Starting VM")).raw(),
3707 TRUE /* aCancelable */,
3708 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3709 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3710 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3711 2 /* uFirstOperationWeight */,
3712 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3713
3714 if (SUCCEEDED(rc))
3715 {
3716 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3717 if (SUCCEEDED(rc))
3718 {
3719 aProgress = progress;
3720
3721 /* signal the client watcher thread */
3722 mParent->i_updateClientWatcher();
3723
3724 /* fire an event */
3725 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3726 }
3727 }
3728 }
3729 else
3730 {
3731 /* no progress object - either instant success or failure */
3732 aProgress = NULL;
3733
3734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3735
3736 if (mData->mSession.mState != SessionState_Locked)
3737 return setError(VBOX_E_INVALID_OBJECT_STATE,
3738 tr("The machine '%s' is not locked by a session"),
3739 mUserData->s.strName.c_str());
3740
3741 /* must have a VM process associated - do not kill normal API clients
3742 * with an open session */
3743 if (!Global::IsOnline(mData->mMachineState))
3744 return setError(VBOX_E_INVALID_OBJECT_STATE,
3745 tr("The machine '%s' does not have a VM process"),
3746 mUserData->s.strName.c_str());
3747
3748 /* forcibly terminate the VM process */
3749 if (mData->mSession.mPID != NIL_RTPROCESS)
3750 RTProcTerminate(mData->mSession.mPID);
3751
3752 /* signal the client watcher thread, as most likely the client has
3753 * been terminated */
3754 mParent->i_updateClientWatcher();
3755 }
3756
3757 return rc;
3758}
3759
3760HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3761{
3762 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3763 return setError(E_INVALIDARG,
3764 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3765 aPosition, SchemaDefs::MaxBootPosition);
3766
3767 if (aDevice == DeviceType_USB)
3768 return setError(E_NOTIMPL,
3769 tr("Booting from USB device is currently not supported"));
3770
3771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3772
3773 HRESULT rc = i_checkStateDependency(MutableStateDep);
3774 if (FAILED(rc)) return rc;
3775
3776 i_setModified(IsModified_MachineData);
3777 mHWData.backup();
3778 mHWData->mBootOrder[aPosition - 1] = aDevice;
3779
3780 return S_OK;
3781}
3782
3783HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3784{
3785 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3786 return setError(E_INVALIDARG,
3787 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3788 aPosition, SchemaDefs::MaxBootPosition);
3789
3790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3791
3792 *aDevice = mHWData->mBootOrder[aPosition - 1];
3793
3794 return S_OK;
3795}
3796
3797HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3798 LONG aControllerPort,
3799 LONG aDevice,
3800 DeviceType_T aType,
3801 const ComPtr<IMedium> &aMedium)
3802{
3803 IMedium *aM = aMedium;
3804 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3805 aName.c_str(), aControllerPort, aDevice, aType, aM));
3806
3807 // request the host lock first, since might be calling Host methods for getting host drives;
3808 // next, protect the media tree all the while we're in here, as well as our member variables
3809 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3810 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3811
3812 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3813 if (FAILED(rc)) return rc;
3814
3815 /// @todo NEWMEDIA implicit machine registration
3816 if (!mData->mRegistered)
3817 return setError(VBOX_E_INVALID_OBJECT_STATE,
3818 tr("Cannot attach storage devices to an unregistered machine"));
3819
3820 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3821
3822 /* Check for an existing controller. */
3823 ComObjPtr<StorageController> ctl;
3824 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3825 if (FAILED(rc)) return rc;
3826
3827 StorageControllerType_T ctrlType;
3828 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3829 if (FAILED(rc))
3830 return setError(E_FAIL,
3831 tr("Could not get type of controller '%s'"),
3832 aName.c_str());
3833
3834 bool fSilent = false;
3835 Utf8Str strReconfig;
3836
3837 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3838 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3839 if ( mData->mMachineState == MachineState_Paused
3840 && strReconfig == "1")
3841 fSilent = true;
3842
3843 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3844 bool fHotplug = false;
3845 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3846 fHotplug = true;
3847
3848 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3849 return setError(VBOX_E_INVALID_VM_STATE,
3850 tr("Controller '%s' does not support hotplugging"),
3851 aName.c_str());
3852
3853 // check that the port and device are not out of range
3854 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3855 if (FAILED(rc)) return rc;
3856
3857 /* check if the device slot is already busy */
3858 MediumAttachment *pAttachTemp;
3859 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3860 aName,
3861 aControllerPort,
3862 aDevice)))
3863 {
3864 Medium *pMedium = pAttachTemp->i_getMedium();
3865 if (pMedium)
3866 {
3867 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3868 return setError(VBOX_E_OBJECT_IN_USE,
3869 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3870 pMedium->i_getLocationFull().c_str(),
3871 aControllerPort,
3872 aDevice,
3873 aName.c_str());
3874 }
3875 else
3876 return setError(VBOX_E_OBJECT_IN_USE,
3877 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3878 aControllerPort, aDevice, aName.c_str());
3879 }
3880
3881 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3882 if (aMedium && medium.isNull())
3883 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3884
3885 AutoCaller mediumCaller(medium);
3886 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3887
3888 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3889
3890 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3891 && !medium.isNull()
3892 )
3893 return setError(VBOX_E_OBJECT_IN_USE,
3894 tr("Medium '%s' is already attached to this virtual machine"),
3895 medium->i_getLocationFull().c_str());
3896
3897 if (!medium.isNull())
3898 {
3899 MediumType_T mtype = medium->i_getType();
3900 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3901 // For DVDs it's not written to the config file, so needs no global config
3902 // version bump. For floppies it's a new attribute "type", which is ignored
3903 // by older VirtualBox version, so needs no global config version bump either.
3904 // For hard disks this type is not accepted.
3905 if (mtype == MediumType_MultiAttach)
3906 {
3907 // This type is new with VirtualBox 4.0 and therefore requires settings
3908 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3909 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3910 // two reasons: The medium type is a property of the media registry tree, which
3911 // can reside in the global config file (for pre-4.0 media); we would therefore
3912 // possibly need to bump the global config version. We don't want to do that though
3913 // because that might make downgrading to pre-4.0 impossible.
3914 // As a result, we can only use these two new types if the medium is NOT in the
3915 // global registry:
3916 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3917 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3918 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3919 )
3920 return setError(VBOX_E_INVALID_OBJECT_STATE,
3921 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3922 "to machines that were created with VirtualBox 4.0 or later"),
3923 medium->i_getLocationFull().c_str());
3924 }
3925 }
3926
3927 bool fIndirect = false;
3928 if (!medium.isNull())
3929 fIndirect = medium->i_isReadOnly();
3930 bool associate = true;
3931
3932 do
3933 {
3934 if ( aType == DeviceType_HardDisk
3935 && mMediumAttachments.isBackedUp())
3936 {
3937 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3938
3939 /* check if the medium was attached to the VM before we started
3940 * changing attachments in which case the attachment just needs to
3941 * be restored */
3942 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3943 {
3944 AssertReturn(!fIndirect, E_FAIL);
3945
3946 /* see if it's the same bus/channel/device */
3947 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3948 {
3949 /* the simplest case: restore the whole attachment
3950 * and return, nothing else to do */
3951 mMediumAttachments->push_back(pAttachTemp);
3952
3953 /* Reattach the medium to the VM. */
3954 if (fHotplug || fSilent)
3955 {
3956 mediumLock.release();
3957 treeLock.release();
3958 alock.release();
3959
3960 MediumLockList *pMediumLockList(new MediumLockList());
3961
3962 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3963 medium /* pToLockWrite */,
3964 false /* fMediumLockWriteAll */,
3965 NULL,
3966 *pMediumLockList);
3967 alock.acquire();
3968 if (FAILED(rc))
3969 delete pMediumLockList;
3970 else
3971 {
3972 mData->mSession.mLockedMedia.Unlock();
3973 alock.release();
3974 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3975 mData->mSession.mLockedMedia.Lock();
3976 alock.acquire();
3977 }
3978 alock.release();
3979
3980 if (SUCCEEDED(rc))
3981 {
3982 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3983 /* Remove lock list in case of error. */
3984 if (FAILED(rc))
3985 {
3986 mData->mSession.mLockedMedia.Unlock();
3987 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3988 mData->mSession.mLockedMedia.Lock();
3989 }
3990 }
3991 }
3992
3993 return S_OK;
3994 }
3995
3996 /* bus/channel/device differ; we need a new attachment object,
3997 * but don't try to associate it again */
3998 associate = false;
3999 break;
4000 }
4001 }
4002
4003 /* go further only if the attachment is to be indirect */
4004 if (!fIndirect)
4005 break;
4006
4007 /* perform the so called smart attachment logic for indirect
4008 * attachments. Note that smart attachment is only applicable to base
4009 * hard disks. */
4010
4011 if (medium->i_getParent().isNull())
4012 {
4013 /* first, investigate the backup copy of the current hard disk
4014 * attachments to make it possible to re-attach existing diffs to
4015 * another device slot w/o losing their contents */
4016 if (mMediumAttachments.isBackedUp())
4017 {
4018 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4019
4020 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4021 uint32_t foundLevel = 0;
4022
4023 for (MediumAttachmentList::const_iterator
4024 it = oldAtts.begin();
4025 it != oldAtts.end();
4026 ++it)
4027 {
4028 uint32_t level = 0;
4029 MediumAttachment *pAttach = *it;
4030 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4031 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4032 if (pMedium.isNull())
4033 continue;
4034
4035 if (pMedium->i_getBase(&level) == medium)
4036 {
4037 /* skip the hard disk if its currently attached (we
4038 * cannot attach the same hard disk twice) */
4039 if (i_findAttachment(*mMediumAttachments.data(),
4040 pMedium))
4041 continue;
4042
4043 /* matched device, channel and bus (i.e. attached to the
4044 * same place) will win and immediately stop the search;
4045 * otherwise the attachment that has the youngest
4046 * descendant of medium will be used
4047 */
4048 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4049 {
4050 /* the simplest case: restore the whole attachment
4051 * and return, nothing else to do */
4052 mMediumAttachments->push_back(*it);
4053
4054 /* Reattach the medium to the VM. */
4055 if (fHotplug || fSilent)
4056 {
4057 mediumLock.release();
4058 treeLock.release();
4059 alock.release();
4060
4061 MediumLockList *pMediumLockList(new MediumLockList());
4062
4063 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4064 medium /* pToLockWrite */,
4065 false /* fMediumLockWriteAll */,
4066 NULL,
4067 *pMediumLockList);
4068 alock.acquire();
4069 if (FAILED(rc))
4070 delete pMediumLockList;
4071 else
4072 {
4073 mData->mSession.mLockedMedia.Unlock();
4074 alock.release();
4075 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4076 mData->mSession.mLockedMedia.Lock();
4077 alock.acquire();
4078 }
4079 alock.release();
4080
4081 if (SUCCEEDED(rc))
4082 {
4083 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4084 /* Remove lock list in case of error. */
4085 if (FAILED(rc))
4086 {
4087 mData->mSession.mLockedMedia.Unlock();
4088 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4089 mData->mSession.mLockedMedia.Lock();
4090 }
4091 }
4092 }
4093
4094 return S_OK;
4095 }
4096 else if ( foundIt == oldAtts.end()
4097 || level > foundLevel /* prefer younger */
4098 )
4099 {
4100 foundIt = it;
4101 foundLevel = level;
4102 }
4103 }
4104 }
4105
4106 if (foundIt != oldAtts.end())
4107 {
4108 /* use the previously attached hard disk */
4109 medium = (*foundIt)->i_getMedium();
4110 mediumCaller.attach(medium);
4111 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4112 mediumLock.attach(medium);
4113 /* not implicit, doesn't require association with this VM */
4114 fIndirect = false;
4115 associate = false;
4116 /* go right to the MediumAttachment creation */
4117 break;
4118 }
4119 }
4120
4121 /* must give up the medium lock and medium tree lock as below we
4122 * go over snapshots, which needs a lock with higher lock order. */
4123 mediumLock.release();
4124 treeLock.release();
4125
4126 /* then, search through snapshots for the best diff in the given
4127 * hard disk's chain to base the new diff on */
4128
4129 ComObjPtr<Medium> base;
4130 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4131 while (snap)
4132 {
4133 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4134
4135 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4136
4137 MediumAttachment *pAttachFound = NULL;
4138 uint32_t foundLevel = 0;
4139
4140 for (MediumAttachmentList::const_iterator
4141 it = snapAtts.begin();
4142 it != snapAtts.end();
4143 ++it)
4144 {
4145 MediumAttachment *pAttach = *it;
4146 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4147 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4148 if (pMedium.isNull())
4149 continue;
4150
4151 uint32_t level = 0;
4152 if (pMedium->i_getBase(&level) == medium)
4153 {
4154 /* matched device, channel and bus (i.e. attached to the
4155 * same place) will win and immediately stop the search;
4156 * otherwise the attachment that has the youngest
4157 * descendant of medium will be used
4158 */
4159 if ( pAttach->i_getDevice() == aDevice
4160 && pAttach->i_getPort() == aControllerPort
4161 && pAttach->i_getControllerName() == aName
4162 )
4163 {
4164 pAttachFound = pAttach;
4165 break;
4166 }
4167 else if ( !pAttachFound
4168 || level > foundLevel /* prefer younger */
4169 )
4170 {
4171 pAttachFound = pAttach;
4172 foundLevel = level;
4173 }
4174 }
4175 }
4176
4177 if (pAttachFound)
4178 {
4179 base = pAttachFound->i_getMedium();
4180 break;
4181 }
4182
4183 snap = snap->i_getParent();
4184 }
4185
4186 /* re-lock medium tree and the medium, as we need it below */
4187 treeLock.acquire();
4188 mediumLock.acquire();
4189
4190 /* found a suitable diff, use it as a base */
4191 if (!base.isNull())
4192 {
4193 medium = base;
4194 mediumCaller.attach(medium);
4195 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4196 mediumLock.attach(medium);
4197 }
4198 }
4199
4200 Utf8Str strFullSnapshotFolder;
4201 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4202
4203 ComObjPtr<Medium> diff;
4204 diff.createObject();
4205 // store this diff in the same registry as the parent
4206 Guid uuidRegistryParent;
4207 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4208 {
4209 // parent image has no registry: this can happen if we're attaching a new immutable
4210 // image that has not yet been attached (medium then points to the base and we're
4211 // creating the diff image for the immutable, and the parent is not yet registered);
4212 // put the parent in the machine registry then
4213 mediumLock.release();
4214 treeLock.release();
4215 alock.release();
4216 i_addMediumToRegistry(medium);
4217 alock.acquire();
4218 treeLock.acquire();
4219 mediumLock.acquire();
4220 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4221 }
4222 rc = diff->init(mParent,
4223 medium->i_getPreferredDiffFormat(),
4224 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4225 uuidRegistryParent,
4226 DeviceType_HardDisk);
4227 if (FAILED(rc)) return rc;
4228
4229 /* Apply the normal locking logic to the entire chain. */
4230 MediumLockList *pMediumLockList(new MediumLockList());
4231 mediumLock.release();
4232 treeLock.release();
4233 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4234 diff /* pToLockWrite */,
4235 false /* fMediumLockWriteAll */,
4236 medium,
4237 *pMediumLockList);
4238 treeLock.acquire();
4239 mediumLock.acquire();
4240 if (SUCCEEDED(rc))
4241 {
4242 mediumLock.release();
4243 treeLock.release();
4244 rc = pMediumLockList->Lock();
4245 treeLock.acquire();
4246 mediumLock.acquire();
4247 if (FAILED(rc))
4248 setError(rc,
4249 tr("Could not lock medium when creating diff '%s'"),
4250 diff->i_getLocationFull().c_str());
4251 else
4252 {
4253 /* will release the lock before the potentially lengthy
4254 * operation, so protect with the special state */
4255 MachineState_T oldState = mData->mMachineState;
4256 i_setMachineState(MachineState_SettingUp);
4257
4258 mediumLock.release();
4259 treeLock.release();
4260 alock.release();
4261
4262 rc = medium->i_createDiffStorage(diff,
4263 medium->i_getPreferredDiffVariant(),
4264 pMediumLockList,
4265 NULL /* aProgress */,
4266 true /* aWait */);
4267
4268 alock.acquire();
4269 treeLock.acquire();
4270 mediumLock.acquire();
4271
4272 i_setMachineState(oldState);
4273 }
4274 }
4275
4276 /* Unlock the media and free the associated memory. */
4277 delete pMediumLockList;
4278
4279 if (FAILED(rc)) return rc;
4280
4281 /* use the created diff for the actual attachment */
4282 medium = diff;
4283 mediumCaller.attach(medium);
4284 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4285 mediumLock.attach(medium);
4286 }
4287 while (0);
4288
4289 ComObjPtr<MediumAttachment> attachment;
4290 attachment.createObject();
4291 rc = attachment->init(this,
4292 medium,
4293 aName,
4294 aControllerPort,
4295 aDevice,
4296 aType,
4297 fIndirect,
4298 false /* fPassthrough */,
4299 false /* fTempEject */,
4300 false /* fNonRotational */,
4301 false /* fDiscard */,
4302 fHotplug /* fHotPluggable */,
4303 Utf8Str::Empty);
4304 if (FAILED(rc)) return rc;
4305
4306 if (associate && !medium.isNull())
4307 {
4308 // as the last step, associate the medium to the VM
4309 rc = medium->i_addBackReference(mData->mUuid);
4310 // here we can fail because of Deleting, or being in process of creating a Diff
4311 if (FAILED(rc)) return rc;
4312
4313 mediumLock.release();
4314 treeLock.release();
4315 alock.release();
4316 i_addMediumToRegistry(medium);
4317 alock.acquire();
4318 treeLock.acquire();
4319 mediumLock.acquire();
4320 }
4321
4322 /* success: finally remember the attachment */
4323 i_setModified(IsModified_Storage);
4324 mMediumAttachments.backup();
4325 mMediumAttachments->push_back(attachment);
4326
4327 mediumLock.release();
4328 treeLock.release();
4329 alock.release();
4330
4331 if (fHotplug || fSilent)
4332 {
4333 if (!medium.isNull())
4334 {
4335 MediumLockList *pMediumLockList(new MediumLockList());
4336
4337 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4338 medium /* pToLockWrite */,
4339 false /* fMediumLockWriteAll */,
4340 NULL,
4341 *pMediumLockList);
4342 alock.acquire();
4343 if (FAILED(rc))
4344 delete pMediumLockList;
4345 else
4346 {
4347 mData->mSession.mLockedMedia.Unlock();
4348 alock.release();
4349 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4350 mData->mSession.mLockedMedia.Lock();
4351 alock.acquire();
4352 }
4353 alock.release();
4354 }
4355
4356 if (SUCCEEDED(rc))
4357 {
4358 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4359 /* Remove lock list in case of error. */
4360 if (FAILED(rc))
4361 {
4362 mData->mSession.mLockedMedia.Unlock();
4363 mData->mSession.mLockedMedia.Remove(attachment);
4364 mData->mSession.mLockedMedia.Lock();
4365 }
4366 }
4367 }
4368
4369 /* Save modified registries, but skip this machine as it's the caller's
4370 * job to save its settings like all other settings changes. */
4371 mParent->i_unmarkRegistryModified(i_getId());
4372 mParent->i_saveModifiedRegistries();
4373
4374 return rc;
4375}
4376
4377HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4378 LONG aDevice)
4379{
4380 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4381 aName.c_str(), aControllerPort, aDevice));
4382
4383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4384
4385 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4386 if (FAILED(rc)) return rc;
4387
4388 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4389
4390 /* Check for an existing controller. */
4391 ComObjPtr<StorageController> ctl;
4392 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4393 if (FAILED(rc)) return rc;
4394
4395 StorageControllerType_T ctrlType;
4396 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4397 if (FAILED(rc))
4398 return setError(E_FAIL,
4399 tr("Could not get type of controller '%s'"),
4400 aName.c_str());
4401
4402 bool fSilent = false;
4403 Utf8Str strReconfig;
4404
4405 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4406 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4407 if ( mData->mMachineState == MachineState_Paused
4408 && strReconfig == "1")
4409 fSilent = true;
4410
4411 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4412 bool fHotplug = false;
4413 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4414 fHotplug = true;
4415
4416 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4417 return setError(VBOX_E_INVALID_VM_STATE,
4418 tr("Controller '%s' does not support hotplugging"),
4419 aName.c_str());
4420
4421 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4422 aName,
4423 aControllerPort,
4424 aDevice);
4425 if (!pAttach)
4426 return setError(VBOX_E_OBJECT_NOT_FOUND,
4427 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4428 aDevice, aControllerPort, aName.c_str());
4429
4430 if (fHotplug && !pAttach->i_getHotPluggable())
4431 return setError(VBOX_E_NOT_SUPPORTED,
4432 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4433 aDevice, aControllerPort, aName.c_str());
4434
4435 /*
4436 * The VM has to detach the device before we delete any implicit diffs.
4437 * If this fails we can roll back without loosing data.
4438 */
4439 if (fHotplug || fSilent)
4440 {
4441 alock.release();
4442 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4443 alock.acquire();
4444 }
4445 if (FAILED(rc)) return rc;
4446
4447 /* If we are here everything went well and we can delete the implicit now. */
4448 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4449
4450 alock.release();
4451
4452 /* Save modified registries, but skip this machine as it's the caller's
4453 * job to save its settings like all other settings changes. */
4454 mParent->i_unmarkRegistryModified(i_getId());
4455 mParent->i_saveModifiedRegistries();
4456
4457 return rc;
4458}
4459
4460HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4461 LONG aDevice, BOOL aPassthrough)
4462{
4463 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4464 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4465
4466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4467
4468 HRESULT rc = i_checkStateDependency(MutableStateDep);
4469 if (FAILED(rc)) return rc;
4470
4471 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4472
4473 if (Global::IsOnlineOrTransient(mData->mMachineState))
4474 return setError(VBOX_E_INVALID_VM_STATE,
4475 tr("Invalid machine state: %s"),
4476 Global::stringifyMachineState(mData->mMachineState));
4477
4478 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4479 aName,
4480 aControllerPort,
4481 aDevice);
4482 if (!pAttach)
4483 return setError(VBOX_E_OBJECT_NOT_FOUND,
4484 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4485 aDevice, aControllerPort, aName.c_str());
4486
4487
4488 i_setModified(IsModified_Storage);
4489 mMediumAttachments.backup();
4490
4491 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4492
4493 if (pAttach->i_getType() != DeviceType_DVD)
4494 return setError(E_INVALIDARG,
4495 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4496 aDevice, aControllerPort, aName.c_str());
4497 pAttach->i_updatePassthrough(!!aPassthrough);
4498
4499 return S_OK;
4500}
4501
4502HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4503 LONG aDevice, BOOL aTemporaryEject)
4504{
4505
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4507 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4508
4509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4510
4511 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4512 if (FAILED(rc)) return rc;
4513
4514 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4515 aName,
4516 aControllerPort,
4517 aDevice);
4518 if (!pAttach)
4519 return setError(VBOX_E_OBJECT_NOT_FOUND,
4520 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4521 aDevice, aControllerPort, aName.c_str());
4522
4523
4524 i_setModified(IsModified_Storage);
4525 mMediumAttachments.backup();
4526
4527 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4528
4529 if (pAttach->i_getType() != DeviceType_DVD)
4530 return setError(E_INVALIDARG,
4531 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4532 aDevice, aControllerPort, aName.c_str());
4533 pAttach->i_updateTempEject(!!aTemporaryEject);
4534
4535 return S_OK;
4536}
4537
4538HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4539 LONG aDevice, BOOL aNonRotational)
4540{
4541
4542 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4543 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4544
4545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4546
4547 HRESULT rc = i_checkStateDependency(MutableStateDep);
4548 if (FAILED(rc)) return rc;
4549
4550 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4551
4552 if (Global::IsOnlineOrTransient(mData->mMachineState))
4553 return setError(VBOX_E_INVALID_VM_STATE,
4554 tr("Invalid machine state: %s"),
4555 Global::stringifyMachineState(mData->mMachineState));
4556
4557 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4558 aName,
4559 aControllerPort,
4560 aDevice);
4561 if (!pAttach)
4562 return setError(VBOX_E_OBJECT_NOT_FOUND,
4563 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4564 aDevice, aControllerPort, aName.c_str());
4565
4566
4567 i_setModified(IsModified_Storage);
4568 mMediumAttachments.backup();
4569
4570 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4571
4572 if (pAttach->i_getType() != DeviceType_HardDisk)
4573 return setError(E_INVALIDARG,
4574 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"),
4575 aDevice, aControllerPort, aName.c_str());
4576 pAttach->i_updateNonRotational(!!aNonRotational);
4577
4578 return S_OK;
4579}
4580
4581HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4582 LONG aDevice, BOOL aDiscard)
4583{
4584
4585 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4586 aName.c_str(), aControllerPort, aDevice, aDiscard));
4587
4588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4589
4590 HRESULT rc = i_checkStateDependency(MutableStateDep);
4591 if (FAILED(rc)) return rc;
4592
4593 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4594
4595 if (Global::IsOnlineOrTransient(mData->mMachineState))
4596 return setError(VBOX_E_INVALID_VM_STATE,
4597 tr("Invalid machine state: %s"),
4598 Global::stringifyMachineState(mData->mMachineState));
4599
4600 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4601 aName,
4602 aControllerPort,
4603 aDevice);
4604 if (!pAttach)
4605 return setError(VBOX_E_OBJECT_NOT_FOUND,
4606 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4607 aDevice, aControllerPort, aName.c_str());
4608
4609
4610 i_setModified(IsModified_Storage);
4611 mMediumAttachments.backup();
4612
4613 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4614
4615 if (pAttach->i_getType() != DeviceType_HardDisk)
4616 return setError(E_INVALIDARG,
4617 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"),
4618 aDevice, aControllerPort, aName.c_str());
4619 pAttach->i_updateDiscard(!!aDiscard);
4620
4621 return S_OK;
4622}
4623
4624HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4625 LONG aDevice, BOOL aHotPluggable)
4626{
4627 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4628 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4629
4630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4631
4632 HRESULT rc = i_checkStateDependency(MutableStateDep);
4633 if (FAILED(rc)) return rc;
4634
4635 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4636
4637 if (Global::IsOnlineOrTransient(mData->mMachineState))
4638 return setError(VBOX_E_INVALID_VM_STATE,
4639 tr("Invalid machine state: %s"),
4640 Global::stringifyMachineState(mData->mMachineState));
4641
4642 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4643 aName,
4644 aControllerPort,
4645 aDevice);
4646 if (!pAttach)
4647 return setError(VBOX_E_OBJECT_NOT_FOUND,
4648 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4649 aDevice, aControllerPort, aName.c_str());
4650
4651 /* Check for an existing controller. */
4652 ComObjPtr<StorageController> ctl;
4653 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4654 if (FAILED(rc)) return rc;
4655
4656 StorageControllerType_T ctrlType;
4657 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4658 if (FAILED(rc))
4659 return setError(E_FAIL,
4660 tr("Could not get type of controller '%s'"),
4661 aName.c_str());
4662
4663 if (!i_isControllerHotplugCapable(ctrlType))
4664 return setError(VBOX_E_NOT_SUPPORTED,
4665 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4666 aName.c_str());
4667
4668 i_setModified(IsModified_Storage);
4669 mMediumAttachments.backup();
4670
4671 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4672
4673 if (pAttach->i_getType() == DeviceType_Floppy)
4674 return setError(E_INVALIDARG,
4675 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"),
4676 aDevice, aControllerPort, aName.c_str());
4677 pAttach->i_updateHotPluggable(!!aHotPluggable);
4678
4679 return S_OK;
4680}
4681
4682HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4683 LONG aDevice)
4684{
4685 int rc = S_OK;
4686 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4687 aName.c_str(), aControllerPort, aDevice));
4688
4689 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4690
4691 return rc;
4692}
4693
4694HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4695 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4696{
4697 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4698 aName.c_str(), aControllerPort, aDevice));
4699
4700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4701
4702 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4703 if (FAILED(rc)) return rc;
4704
4705 if (Global::IsOnlineOrTransient(mData->mMachineState))
4706 return setError(VBOX_E_INVALID_VM_STATE,
4707 tr("Invalid machine state: %s"),
4708 Global::stringifyMachineState(mData->mMachineState));
4709
4710 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4711 aName,
4712 aControllerPort,
4713 aDevice);
4714 if (!pAttach)
4715 return setError(VBOX_E_OBJECT_NOT_FOUND,
4716 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4717 aDevice, aControllerPort, aName.c_str());
4718
4719
4720 i_setModified(IsModified_Storage);
4721 mMediumAttachments.backup();
4722
4723 IBandwidthGroup *iB = aBandwidthGroup;
4724 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4725 if (aBandwidthGroup && group.isNull())
4726 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4727
4728 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4729
4730 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4731 if (strBandwidthGroupOld.isNotEmpty())
4732 {
4733 /* Get the bandwidth group object and release it - this must not fail. */
4734 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4735 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4736 Assert(SUCCEEDED(rc));
4737
4738 pBandwidthGroupOld->i_release();
4739 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4740 }
4741
4742 if (!group.isNull())
4743 {
4744 group->i_reference();
4745 pAttach->i_updateBandwidthGroup(group->i_getName());
4746 }
4747
4748 return S_OK;
4749}
4750
4751HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4752 LONG aControllerPort,
4753 LONG aDevice,
4754 DeviceType_T aType)
4755{
4756 HRESULT rc = S_OK;
4757
4758 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4759 aName.c_str(), aControllerPort, aDevice, aType));
4760
4761 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4762
4763 return rc;
4764}
4765
4766
4767HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4768 LONG aControllerPort,
4769 LONG aDevice,
4770 BOOL aForce)
4771{
4772 int rc = S_OK;
4773 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4774 aName.c_str(), aControllerPort, aForce));
4775
4776 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4777
4778 return rc;
4779}
4780
4781HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4782 LONG aControllerPort,
4783 LONG aDevice,
4784 const ComPtr<IMedium> &aMedium,
4785 BOOL aForce)
4786{
4787 int rc = S_OK;
4788 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4789 aName.c_str(), aControllerPort, aDevice, aForce));
4790
4791 // request the host lock first, since might be calling Host methods for getting host drives;
4792 // next, protect the media tree all the while we're in here, as well as our member variables
4793 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4794 this->lockHandle(),
4795 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4796
4797 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4798 aName,
4799 aControllerPort,
4800 aDevice);
4801 if (pAttach.isNull())
4802 return setError(VBOX_E_OBJECT_NOT_FOUND,
4803 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4804 aDevice, aControllerPort, aName.c_str());
4805
4806 /* Remember previously mounted medium. The medium before taking the
4807 * backup is not necessarily the same thing. */
4808 ComObjPtr<Medium> oldmedium;
4809 oldmedium = pAttach->i_getMedium();
4810
4811 IMedium *iM = aMedium;
4812 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4813 if (aMedium && pMedium.isNull())
4814 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4815
4816 AutoCaller mediumCaller(pMedium);
4817 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4818
4819 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4820 if (pMedium)
4821 {
4822 DeviceType_T mediumType = pAttach->i_getType();
4823 switch (mediumType)
4824 {
4825 case DeviceType_DVD:
4826 case DeviceType_Floppy:
4827 break;
4828
4829 default:
4830 return setError(VBOX_E_INVALID_OBJECT_STATE,
4831 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4832 aControllerPort,
4833 aDevice,
4834 aName.c_str());
4835 }
4836 }
4837
4838 i_setModified(IsModified_Storage);
4839 mMediumAttachments.backup();
4840
4841 {
4842 // The backup operation makes the pAttach reference point to the
4843 // old settings. Re-get the correct reference.
4844 pAttach = i_findAttachment(*mMediumAttachments.data(),
4845 aName,
4846 aControllerPort,
4847 aDevice);
4848 if (!oldmedium.isNull())
4849 oldmedium->i_removeBackReference(mData->mUuid);
4850 if (!pMedium.isNull())
4851 {
4852 pMedium->i_addBackReference(mData->mUuid);
4853
4854 mediumLock.release();
4855 multiLock.release();
4856 i_addMediumToRegistry(pMedium);
4857 multiLock.acquire();
4858 mediumLock.acquire();
4859 }
4860
4861 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4862 pAttach->i_updateMedium(pMedium);
4863 }
4864
4865 i_setModified(IsModified_Storage);
4866
4867 mediumLock.release();
4868 multiLock.release();
4869 rc = i_onMediumChange(pAttach, aForce);
4870 multiLock.acquire();
4871 mediumLock.acquire();
4872
4873 /* On error roll back this change only. */
4874 if (FAILED(rc))
4875 {
4876 if (!pMedium.isNull())
4877 pMedium->i_removeBackReference(mData->mUuid);
4878 pAttach = i_findAttachment(*mMediumAttachments.data(),
4879 aName,
4880 aControllerPort,
4881 aDevice);
4882 /* If the attachment is gone in the meantime, bail out. */
4883 if (pAttach.isNull())
4884 return rc;
4885 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4886 if (!oldmedium.isNull())
4887 oldmedium->i_addBackReference(mData->mUuid);
4888 pAttach->i_updateMedium(oldmedium);
4889 }
4890
4891 mediumLock.release();
4892 multiLock.release();
4893
4894 /* Save modified registries, but skip this machine as it's the caller's
4895 * job to save its settings like all other settings changes. */
4896 mParent->i_unmarkRegistryModified(i_getId());
4897 mParent->i_saveModifiedRegistries();
4898
4899 return rc;
4900}
4901HRESULT Machine::getMedium(const com::Utf8Str &aName,
4902 LONG aControllerPort,
4903 LONG aDevice,
4904 ComPtr<IMedium> &aMedium)
4905{
4906 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4907 aName.c_str(), aControllerPort, aDevice));
4908
4909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4910
4911 aMedium = NULL;
4912
4913 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4914 aName,
4915 aControllerPort,
4916 aDevice);
4917 if (pAttach.isNull())
4918 return setError(VBOX_E_OBJECT_NOT_FOUND,
4919 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4920 aDevice, aControllerPort, aName.c_str());
4921
4922 aMedium = pAttach->i_getMedium();
4923
4924 return S_OK;
4925}
4926
4927HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4928{
4929
4930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4931
4932 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4933
4934 return S_OK;
4935}
4936
4937HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4938{
4939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4940
4941 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4942
4943 return S_OK;
4944}
4945
4946HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4947{
4948 /* Do not assert if slot is out of range, just return the advertised
4949 status. testdriver/vbox.py triggers this in logVmInfo. */
4950 if (aSlot >= mNetworkAdapters.size())
4951 return setError(E_INVALIDARG,
4952 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4953 aSlot, mNetworkAdapters.size());
4954
4955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4956
4957 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4958
4959 return S_OK;
4960}
4961
4962HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4963{
4964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4967 size_t i = 0;
4968 for (settings::StringsMap::const_iterator
4969 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4970 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4971 ++it, ++i)
4972 aKeys[i] = it->first;
4973
4974 return S_OK;
4975}
4976
4977 /**
4978 * @note Locks this object for reading.
4979 */
4980HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4981 com::Utf8Str &aValue)
4982{
4983 /* start with nothing found */
4984 aValue = "";
4985
4986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4987
4988 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4989 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4990 // found:
4991 aValue = it->second; // source is a Utf8Str
4992
4993 /* return the result to caller (may be empty) */
4994 return S_OK;
4995}
4996
4997 /**
4998 * @note Locks mParent for writing + this object for writing.
4999 */
5000HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5001{
5002 Utf8Str strOldValue; // empty
5003
5004 // locking note: we only hold the read lock briefly to look up the old value,
5005 // then release it and call the onExtraCanChange callbacks. There is a small
5006 // chance of a race insofar as the callback might be called twice if two callers
5007 // change the same key at the same time, but that's a much better solution
5008 // than the deadlock we had here before. The actual changing of the extradata
5009 // is then performed under the write lock and race-free.
5010
5011 // look up the old value first; if nothing has changed then we need not do anything
5012 {
5013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5014
5015 // For snapshots don't even think about allowing changes, extradata
5016 // is global for a machine, so there is nothing snapshot specific.
5017 if (i_isSnapshotMachine())
5018 return setError(VBOX_E_INVALID_VM_STATE,
5019 tr("Cannot set extradata for a snapshot"));
5020
5021 // check if the right IMachine instance is used
5022 if (mData->mRegistered && !i_isSessionMachine())
5023 return setError(VBOX_E_INVALID_VM_STATE,
5024 tr("Cannot set extradata for an immutable machine"));
5025
5026 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5027 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5028 strOldValue = it->second;
5029 }
5030
5031 bool fChanged;
5032 if ((fChanged = (strOldValue != aValue)))
5033 {
5034 // ask for permission from all listeners outside the locks;
5035 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5036 // lock to copy the list of callbacks to invoke
5037 Bstr error;
5038 Bstr bstrValue(aValue);
5039
5040 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5041 {
5042 const char *sep = error.isEmpty() ? "" : ": ";
5043 CBSTR err = error.raw();
5044 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5045 return setError(E_ACCESSDENIED,
5046 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5047 aKey.c_str(),
5048 aValue.c_str(),
5049 sep,
5050 err);
5051 }
5052
5053 // data is changing and change not vetoed: then write it out under the lock
5054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5055
5056 if (aValue.isEmpty())
5057 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5058 else
5059 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5060 // creates a new key if needed
5061
5062 bool fNeedsGlobalSaveSettings = false;
5063 // This saving of settings is tricky: there is no "old state" for the
5064 // extradata items at all (unlike all other settings), so the old/new
5065 // settings comparison would give a wrong result!
5066 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5067
5068 if (fNeedsGlobalSaveSettings)
5069 {
5070 // save the global settings; for that we should hold only the VirtualBox lock
5071 alock.release();
5072 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5073 mParent->i_saveSettings();
5074 }
5075 }
5076
5077 // fire notification outside the lock
5078 if (fChanged)
5079 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5080
5081 return S_OK;
5082}
5083
5084HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5085{
5086 aProgress = NULL;
5087 NOREF(aSettingsFilePath);
5088 ReturnComNotImplemented();
5089}
5090
5091HRESULT Machine::saveSettings()
5092{
5093 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5094
5095 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5096 if (FAILED(rc)) return rc;
5097
5098 /* the settings file path may never be null */
5099 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5100
5101 /* save all VM data excluding snapshots */
5102 bool fNeedsGlobalSaveSettings = false;
5103 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5104 mlock.release();
5105
5106 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5107 {
5108 // save the global settings; for that we should hold only the VirtualBox lock
5109 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5110 rc = mParent->i_saveSettings();
5111 }
5112
5113 return rc;
5114}
5115
5116
5117HRESULT Machine::discardSettings()
5118{
5119 /*
5120 * We need to take the machine list lock here as well as the machine one
5121 * or we'll get into trouble should any media stuff require rolling back.
5122 *
5123 * Details:
5124 *
5125 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5126 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5127 * 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]
5128 * 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
5129 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5130 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5131 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5132 * 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
5133 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5134 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5135 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5136 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5137 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5138 * 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]
5139 * 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] (*)
5140 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5141 * 0:005> k
5142 * # Child-SP RetAddr Call Site
5143 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5144 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5145 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5146 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5147 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5148 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5149 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5150 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5151 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5152 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5153 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5154 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5155 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5156 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5157 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5158 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5159 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5160 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5161 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5162 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5163 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5164 *
5165 */
5166 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5170 if (FAILED(rc)) return rc;
5171
5172 /*
5173 * during this rollback, the session will be notified if data has
5174 * been actually changed
5175 */
5176 i_rollback(true /* aNotify */);
5177
5178 return S_OK;
5179}
5180
5181/** @note Locks objects! */
5182HRESULT Machine::unregister(AutoCaller &autoCaller,
5183 CleanupMode_T aCleanupMode,
5184 std::vector<ComPtr<IMedium> > &aMedia)
5185{
5186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5187
5188 Guid id(i_getId());
5189
5190 if (mData->mSession.mState != SessionState_Unlocked)
5191 return setError(VBOX_E_INVALID_OBJECT_STATE,
5192 tr("Cannot unregister the machine '%s' while it is locked"),
5193 mUserData->s.strName.c_str());
5194
5195 // wait for state dependents to drop to zero
5196 i_ensureNoStateDependencies();
5197
5198 if (!mData->mAccessible)
5199 {
5200 // inaccessible maschines can only be unregistered; uninitialize ourselves
5201 // here because currently there may be no unregistered that are inaccessible
5202 // (this state combination is not supported). Note releasing the caller and
5203 // leaving the lock before calling uninit()
5204 alock.release();
5205 autoCaller.release();
5206
5207 uninit();
5208
5209 mParent->i_unregisterMachine(this, id);
5210 // calls VirtualBox::i_saveSettings()
5211
5212 return S_OK;
5213 }
5214
5215 HRESULT rc = S_OK;
5216
5217 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5218 // discard saved state
5219 if (mData->mMachineState == MachineState_Saved)
5220 {
5221 // add the saved state file to the list of files the caller should delete
5222 Assert(!mSSData->strStateFilePath.isEmpty());
5223 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5224
5225 mSSData->strStateFilePath.setNull();
5226
5227 // unconditionally set the machine state to powered off, we now
5228 // know no session has locked the machine
5229 mData->mMachineState = MachineState_PoweredOff;
5230 }
5231
5232 size_t cSnapshots = 0;
5233 if (mData->mFirstSnapshot)
5234 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5235 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5236 // fail now before we start detaching media
5237 return setError(VBOX_E_INVALID_OBJECT_STATE,
5238 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5239 mUserData->s.strName.c_str(), cSnapshots);
5240
5241 // This list collects the medium objects from all medium attachments
5242 // which we will detach from the machine and its snapshots, in a specific
5243 // order which allows for closing all media without getting "media in use"
5244 // errors, simply by going through the list from the front to the back:
5245 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5246 // and must be closed before the parent media from the snapshots, or closing the parents
5247 // will fail because they still have children);
5248 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5249 // the root ("first") snapshot of the machine.
5250 MediaList llMedia;
5251
5252 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5253 && mMediumAttachments->size()
5254 )
5255 {
5256 // we have media attachments: detach them all and add the Medium objects to our list
5257 if (aCleanupMode != CleanupMode_UnregisterOnly)
5258 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5259 else
5260 return setError(VBOX_E_INVALID_OBJECT_STATE,
5261 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5262 mUserData->s.strName.c_str(), mMediumAttachments->size());
5263 }
5264
5265 if (cSnapshots)
5266 {
5267 // add the media from the medium attachments of the snapshots to llMedia
5268 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5269 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5270 // into the children first
5271
5272 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5273 MachineState_T oldState = mData->mMachineState;
5274 mData->mMachineState = MachineState_DeletingSnapshot;
5275
5276 // make a copy of the first snapshot so the refcount does not drop to 0
5277 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5278 // because of the AutoCaller voodoo)
5279 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5280
5281 // GO!
5282 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5283
5284 mData->mMachineState = oldState;
5285 }
5286
5287 if (FAILED(rc))
5288 {
5289 i_rollbackMedia();
5290 return rc;
5291 }
5292
5293 // commit all the media changes made above
5294 i_commitMedia();
5295
5296 mData->mRegistered = false;
5297
5298 // machine lock no longer needed
5299 alock.release();
5300
5301 // return media to caller
5302 aMedia.resize(llMedia.size());
5303 size_t i = 0;
5304 for (MediaList::const_iterator
5305 it = llMedia.begin();
5306 it != llMedia.end();
5307 ++it, ++i)
5308 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5309
5310 mParent->i_unregisterMachine(this, id);
5311 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5312
5313 return S_OK;
5314}
5315
5316/**
5317 * Task record for deleting a machine config.
5318 */
5319class Machine::DeleteConfigTask
5320 : public Machine::Task
5321{
5322public:
5323 DeleteConfigTask(Machine *m,
5324 Progress *p,
5325 const Utf8Str &t,
5326 const RTCList<ComPtr<IMedium> > &llMediums,
5327 const StringsList &llFilesToDelete)
5328 : Task(m, p, t),
5329 m_llMediums(llMediums),
5330 m_llFilesToDelete(llFilesToDelete)
5331 {}
5332
5333private:
5334 void handler()
5335 {
5336 try
5337 {
5338 m_pMachine->i_deleteConfigHandler(*this);
5339 }
5340 catch (...)
5341 {
5342 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5343 }
5344 }
5345
5346 RTCList<ComPtr<IMedium> > m_llMediums;
5347 StringsList m_llFilesToDelete;
5348
5349 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5350};
5351
5352/**
5353 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5354 * SessionMachine::taskHandler().
5355 *
5356 * @note Locks this object for writing.
5357 *
5358 * @param task
5359 * @return
5360 */
5361void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5362{
5363 LogFlowThisFuncEnter();
5364
5365 AutoCaller autoCaller(this);
5366 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5367 if (FAILED(autoCaller.rc()))
5368 {
5369 /* we might have been uninitialized because the session was accidentally
5370 * closed by the client, so don't assert */
5371 HRESULT rc = setError(E_FAIL,
5372 tr("The session has been accidentally closed"));
5373 task.m_pProgress->i_notifyComplete(rc);
5374 LogFlowThisFuncLeave();
5375 return;
5376 }
5377
5378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 HRESULT rc = S_OK;
5381
5382 try
5383 {
5384 ULONG uLogHistoryCount = 3;
5385 ComPtr<ISystemProperties> systemProperties;
5386 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5387 if (FAILED(rc)) throw rc;
5388
5389 if (!systemProperties.isNull())
5390 {
5391 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5392 if (FAILED(rc)) throw rc;
5393 }
5394
5395 MachineState_T oldState = mData->mMachineState;
5396 i_setMachineState(MachineState_SettingUp);
5397 alock.release();
5398 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5399 {
5400 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5401 {
5402 AutoCaller mac(pMedium);
5403 if (FAILED(mac.rc())) throw mac.rc();
5404 Utf8Str strLocation = pMedium->i_getLocationFull();
5405 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5406 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5407 if (FAILED(rc)) throw rc;
5408 }
5409 if (pMedium->i_isMediumFormatFile())
5410 {
5411 ComPtr<IProgress> pProgress2;
5412 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5413 if (FAILED(rc)) throw rc;
5414 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5415 if (FAILED(rc)) throw rc;
5416 /* Check the result of the asynchronous process. */
5417 LONG iRc;
5418 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5419 if (FAILED(rc)) throw rc;
5420 /* If the thread of the progress object has an error, then
5421 * retrieve the error info from there, or it'll be lost. */
5422 if (FAILED(iRc))
5423 throw setError(ProgressErrorInfo(pProgress2));
5424 }
5425
5426 /* Close the medium, deliberately without checking the return
5427 * code, and without leaving any trace in the error info, as
5428 * a failure here is a very minor issue, which shouldn't happen
5429 * as above we even managed to delete the medium. */
5430 {
5431 ErrorInfoKeeper eik;
5432 pMedium->Close();
5433 }
5434 }
5435 i_setMachineState(oldState);
5436 alock.acquire();
5437
5438 // delete the files pushed on the task list by Machine::Delete()
5439 // (this includes saved states of the machine and snapshots and
5440 // medium storage files from the IMedium list passed in, and the
5441 // machine XML file)
5442 for (StringsList::const_iterator
5443 it = task.m_llFilesToDelete.begin();
5444 it != task.m_llFilesToDelete.end();
5445 ++it)
5446 {
5447 const Utf8Str &strFile = *it;
5448 LogFunc(("Deleting file %s\n", strFile.c_str()));
5449 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5450 if (FAILED(rc)) throw rc;
5451
5452 int vrc = RTFileDelete(strFile.c_str());
5453 if (RT_FAILURE(vrc))
5454 throw setError(VBOX_E_IPRT_ERROR,
5455 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5456 }
5457
5458 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5459 if (FAILED(rc)) throw rc;
5460
5461 /* delete the settings only when the file actually exists */
5462 if (mData->pMachineConfigFile->fileExists())
5463 {
5464 /* Delete any backup or uncommitted XML files. Ignore failures.
5465 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5466 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5467 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5468 RTFileDelete(otherXml.c_str());
5469 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5470 RTFileDelete(otherXml.c_str());
5471
5472 /* delete the Logs folder, nothing important should be left
5473 * there (we don't check for errors because the user might have
5474 * some private files there that we don't want to delete) */
5475 Utf8Str logFolder;
5476 getLogFolder(logFolder);
5477 Assert(logFolder.length());
5478 if (RTDirExists(logFolder.c_str()))
5479 {
5480 /* Delete all VBox.log[.N] files from the Logs folder
5481 * (this must be in sync with the rotation logic in
5482 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5483 * files that may have been created by the GUI. */
5484 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5485 logFolder.c_str(), RTPATH_DELIMITER);
5486 RTFileDelete(log.c_str());
5487 log = Utf8StrFmt("%s%cVBox.png",
5488 logFolder.c_str(), RTPATH_DELIMITER);
5489 RTFileDelete(log.c_str());
5490 for (int i = uLogHistoryCount; i > 0; i--)
5491 {
5492 log = Utf8StrFmt("%s%cVBox.log.%d",
5493 logFolder.c_str(), RTPATH_DELIMITER, i);
5494 RTFileDelete(log.c_str());
5495 log = Utf8StrFmt("%s%cVBox.png.%d",
5496 logFolder.c_str(), RTPATH_DELIMITER, i);
5497 RTFileDelete(log.c_str());
5498 }
5499#if defined(RT_OS_WINDOWS)
5500 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5501 RTFileDelete(log.c_str());
5502 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5503 RTFileDelete(log.c_str());
5504#endif
5505
5506 RTDirRemove(logFolder.c_str());
5507 }
5508
5509 /* delete the Snapshots folder, nothing important should be left
5510 * there (we don't check for errors because the user might have
5511 * some private files there that we don't want to delete) */
5512 Utf8Str strFullSnapshotFolder;
5513 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5514 Assert(!strFullSnapshotFolder.isEmpty());
5515 if (RTDirExists(strFullSnapshotFolder.c_str()))
5516 RTDirRemove(strFullSnapshotFolder.c_str());
5517
5518 // delete the directory that contains the settings file, but only
5519 // if it matches the VM name
5520 Utf8Str settingsDir;
5521 if (i_isInOwnDir(&settingsDir))
5522 RTDirRemove(settingsDir.c_str());
5523 }
5524
5525 alock.release();
5526
5527 mParent->i_saveModifiedRegistries();
5528 }
5529 catch (HRESULT aRC) { rc = aRC; }
5530
5531 task.m_pProgress->i_notifyComplete(rc);
5532
5533 LogFlowThisFuncLeave();
5534}
5535
5536HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5537{
5538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5539
5540 HRESULT rc = i_checkStateDependency(MutableStateDep);
5541 if (FAILED(rc)) return rc;
5542
5543 if (mData->mRegistered)
5544 return setError(VBOX_E_INVALID_VM_STATE,
5545 tr("Cannot delete settings of a registered machine"));
5546
5547 // collect files to delete
5548 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5549 if (mData->pMachineConfigFile->fileExists())
5550 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5551
5552 RTCList<ComPtr<IMedium> > llMediums;
5553 for (size_t i = 0; i < aMedia.size(); ++i)
5554 {
5555 IMedium *pIMedium(aMedia[i]);
5556 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5557 if (pMedium.isNull())
5558 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5559 SafeArray<BSTR> ids;
5560 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5561 if (FAILED(rc)) return rc;
5562 /* At this point the medium should not have any back references
5563 * anymore. If it has it is attached to another VM and *must* not
5564 * deleted. */
5565 if (ids.size() < 1)
5566 llMediums.append(pMedium);
5567 }
5568
5569 ComObjPtr<Progress> pProgress;
5570 pProgress.createObject();
5571 rc = pProgress->init(i_getVirtualBox(),
5572 static_cast<IMachine*>(this) /* aInitiator */,
5573 tr("Deleting files"),
5574 true /* fCancellable */,
5575 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5576 tr("Collecting file inventory"));
5577 if (FAILED(rc))
5578 return rc;
5579
5580 /* create and start the task on a separate thread (note that it will not
5581 * start working until we release alock) */
5582 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5583 rc = pTask->createThread();
5584 if (FAILED(rc))
5585 return rc;
5586
5587 pProgress.queryInterfaceTo(aProgress.asOutParam());
5588
5589 LogFlowFuncLeave();
5590
5591 return S_OK;
5592}
5593
5594HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5595{
5596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5597
5598 ComObjPtr<Snapshot> pSnapshot;
5599 HRESULT rc;
5600
5601 if (aNameOrId.isEmpty())
5602 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5603 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5604 else
5605 {
5606 Guid uuid(aNameOrId);
5607 if (uuid.isValid())
5608 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5609 else
5610 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5611 }
5612 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5613
5614 return rc;
5615}
5616
5617HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5618{
5619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5620
5621 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5622 if (FAILED(rc)) return rc;
5623
5624 ComObjPtr<SharedFolder> sharedFolder;
5625 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5626 if (SUCCEEDED(rc))
5627 return setError(VBOX_E_OBJECT_IN_USE,
5628 tr("Shared folder named '%s' already exists"),
5629 aName.c_str());
5630
5631 sharedFolder.createObject();
5632 rc = sharedFolder->init(i_getMachine(),
5633 aName,
5634 aHostPath,
5635 !!aWritable,
5636 !!aAutomount,
5637 true /* fFailOnError */);
5638 if (FAILED(rc)) return rc;
5639
5640 i_setModified(IsModified_SharedFolders);
5641 mHWData.backup();
5642 mHWData->mSharedFolders.push_back(sharedFolder);
5643
5644 /* inform the direct session if any */
5645 alock.release();
5646 i_onSharedFolderChange();
5647
5648 return S_OK;
5649}
5650
5651HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5652{
5653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5654
5655 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5656 if (FAILED(rc)) return rc;
5657
5658 ComObjPtr<SharedFolder> sharedFolder;
5659 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5660 if (FAILED(rc)) return rc;
5661
5662 i_setModified(IsModified_SharedFolders);
5663 mHWData.backup();
5664 mHWData->mSharedFolders.remove(sharedFolder);
5665
5666 /* inform the direct session if any */
5667 alock.release();
5668 i_onSharedFolderChange();
5669
5670 return S_OK;
5671}
5672
5673HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5674{
5675 /* start with No */
5676 *aCanShow = FALSE;
5677
5678 ComPtr<IInternalSessionControl> directControl;
5679 {
5680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5681
5682 if (mData->mSession.mState != SessionState_Locked)
5683 return setError(VBOX_E_INVALID_VM_STATE,
5684 tr("Machine is not locked for session (session state: %s)"),
5685 Global::stringifySessionState(mData->mSession.mState));
5686
5687 if (mData->mSession.mLockType == LockType_VM)
5688 directControl = mData->mSession.mDirectControl;
5689 }
5690
5691 /* ignore calls made after #OnSessionEnd() is called */
5692 if (!directControl)
5693 return S_OK;
5694
5695 LONG64 dummy;
5696 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5697}
5698
5699HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5700{
5701 ComPtr<IInternalSessionControl> directControl;
5702 {
5703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5704
5705 if (mData->mSession.mState != SessionState_Locked)
5706 return setError(E_FAIL,
5707 tr("Machine is not locked for session (session state: %s)"),
5708 Global::stringifySessionState(mData->mSession.mState));
5709
5710 if (mData->mSession.mLockType == LockType_VM)
5711 directControl = mData->mSession.mDirectControl;
5712 }
5713
5714 /* ignore calls made after #OnSessionEnd() is called */
5715 if (!directControl)
5716 return S_OK;
5717
5718 BOOL dummy;
5719 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5720}
5721
5722#ifdef VBOX_WITH_GUEST_PROPS
5723/**
5724 * Look up a guest property in VBoxSVC's internal structures.
5725 */
5726HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5727 com::Utf8Str &aValue,
5728 LONG64 *aTimestamp,
5729 com::Utf8Str &aFlags) const
5730{
5731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5732
5733 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5734 if (it != mHWData->mGuestProperties.end())
5735 {
5736 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5737 aValue = it->second.strValue;
5738 *aTimestamp = it->second.mTimestamp;
5739 GuestPropWriteFlags(it->second.mFlags, szFlags);
5740 aFlags = Utf8Str(szFlags);
5741 }
5742
5743 return S_OK;
5744}
5745
5746/**
5747 * Query the VM that a guest property belongs to for the property.
5748 * @returns E_ACCESSDENIED if the VM process is not available or not
5749 * currently handling queries and the lookup should then be done in
5750 * VBoxSVC.
5751 */
5752HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5753 com::Utf8Str &aValue,
5754 LONG64 *aTimestamp,
5755 com::Utf8Str &aFlags) const
5756{
5757 HRESULT rc = S_OK;
5758 BSTR bValue = NULL;
5759 BSTR bFlags = NULL;
5760
5761 ComPtr<IInternalSessionControl> directControl;
5762 {
5763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5764 if (mData->mSession.mLockType == LockType_VM)
5765 directControl = mData->mSession.mDirectControl;
5766 }
5767
5768 /* ignore calls made after #OnSessionEnd() is called */
5769 if (!directControl)
5770 rc = E_ACCESSDENIED;
5771 else
5772 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5773 0 /* accessMode */,
5774 &bValue, aTimestamp, &bFlags);
5775
5776 aValue = bValue;
5777 aFlags = bFlags;
5778
5779 return rc;
5780}
5781#endif // VBOX_WITH_GUEST_PROPS
5782
5783HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5784 com::Utf8Str &aValue,
5785 LONG64 *aTimestamp,
5786 com::Utf8Str &aFlags)
5787{
5788#ifndef VBOX_WITH_GUEST_PROPS
5789 ReturnComNotImplemented();
5790#else // VBOX_WITH_GUEST_PROPS
5791
5792 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5793
5794 if (rc == E_ACCESSDENIED)
5795 /* The VM is not running or the service is not (yet) accessible */
5796 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5797 return rc;
5798#endif // VBOX_WITH_GUEST_PROPS
5799}
5800
5801HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5802{
5803 LONG64 dummyTimestamp;
5804 com::Utf8Str dummyFlags;
5805 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5806 return rc;
5807
5808}
5809HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5810{
5811 com::Utf8Str dummyFlags;
5812 com::Utf8Str dummyValue;
5813 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5814 return rc;
5815}
5816
5817#ifdef VBOX_WITH_GUEST_PROPS
5818/**
5819 * Set a guest property in VBoxSVC's internal structures.
5820 */
5821HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5822 const com::Utf8Str &aFlags, bool fDelete)
5823{
5824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5825 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5826 if (FAILED(rc)) return rc;
5827
5828 try
5829 {
5830 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5831 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5832 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5833
5834 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5835 if (it == mHWData->mGuestProperties.end())
5836 {
5837 if (!fDelete)
5838 {
5839 i_setModified(IsModified_MachineData);
5840 mHWData.backupEx();
5841
5842 RTTIMESPEC time;
5843 HWData::GuestProperty prop;
5844 prop.strValue = Bstr(aValue).raw();
5845 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5846 prop.mFlags = fFlags;
5847 mHWData->mGuestProperties[aName] = prop;
5848 }
5849 }
5850 else
5851 {
5852 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5853 {
5854 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5855 }
5856 else
5857 {
5858 i_setModified(IsModified_MachineData);
5859 mHWData.backupEx();
5860
5861 /* The backupEx() operation invalidates our iterator,
5862 * so get a new one. */
5863 it = mHWData->mGuestProperties.find(aName);
5864 Assert(it != mHWData->mGuestProperties.end());
5865
5866 if (!fDelete)
5867 {
5868 RTTIMESPEC time;
5869 it->second.strValue = aValue;
5870 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5871 it->second.mFlags = fFlags;
5872 }
5873 else
5874 mHWData->mGuestProperties.erase(it);
5875 }
5876 }
5877
5878 if (SUCCEEDED(rc))
5879 {
5880 alock.release();
5881
5882 mParent->i_onGuestPropertyChange(mData->mUuid,
5883 Bstr(aName).raw(),
5884 Bstr(aValue).raw(),
5885 Bstr(aFlags).raw());
5886 }
5887 }
5888 catch (std::bad_alloc &)
5889 {
5890 rc = E_OUTOFMEMORY;
5891 }
5892
5893 return rc;
5894}
5895
5896/**
5897 * Set a property on the VM that that property belongs to.
5898 * @returns E_ACCESSDENIED if the VM process is not available or not
5899 * currently handling queries and the setting should then be done in
5900 * VBoxSVC.
5901 */
5902HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5903 const com::Utf8Str &aFlags, bool fDelete)
5904{
5905 HRESULT rc;
5906
5907 try
5908 {
5909 ComPtr<IInternalSessionControl> directControl;
5910 {
5911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5912 if (mData->mSession.mLockType == LockType_VM)
5913 directControl = mData->mSession.mDirectControl;
5914 }
5915
5916 BSTR dummy = NULL; /* will not be changed (setter) */
5917 LONG64 dummy64;
5918 if (!directControl)
5919 rc = E_ACCESSDENIED;
5920 else
5921 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5922 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5923 fDelete? 2: 1 /* accessMode */,
5924 &dummy, &dummy64, &dummy);
5925 }
5926 catch (std::bad_alloc &)
5927 {
5928 rc = E_OUTOFMEMORY;
5929 }
5930
5931 return rc;
5932}
5933#endif // VBOX_WITH_GUEST_PROPS
5934
5935HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5936 const com::Utf8Str &aFlags)
5937{
5938#ifndef VBOX_WITH_GUEST_PROPS
5939 ReturnComNotImplemented();
5940#else // VBOX_WITH_GUEST_PROPS
5941 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5942 if (rc == E_ACCESSDENIED)
5943 /* The VM is not running or the service is not (yet) accessible */
5944 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5945 return rc;
5946#endif // VBOX_WITH_GUEST_PROPS
5947}
5948
5949HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5950{
5951 return setGuestProperty(aProperty, aValue, "");
5952}
5953
5954HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5955{
5956#ifndef VBOX_WITH_GUEST_PROPS
5957 ReturnComNotImplemented();
5958#else // VBOX_WITH_GUEST_PROPS
5959 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5960 if (rc == E_ACCESSDENIED)
5961 /* The VM is not running or the service is not (yet) accessible */
5962 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5963 return rc;
5964#endif // VBOX_WITH_GUEST_PROPS
5965}
5966
5967#ifdef VBOX_WITH_GUEST_PROPS
5968/**
5969 * Enumerate the guest properties in VBoxSVC's internal structures.
5970 */
5971HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5972 std::vector<com::Utf8Str> &aNames,
5973 std::vector<com::Utf8Str> &aValues,
5974 std::vector<LONG64> &aTimestamps,
5975 std::vector<com::Utf8Str> &aFlags)
5976{
5977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5978 Utf8Str strPatterns(aPatterns);
5979
5980 /*
5981 * Look for matching patterns and build up a list.
5982 */
5983 HWData::GuestPropertyMap propMap;
5984 for (HWData::GuestPropertyMap::const_iterator
5985 it = mHWData->mGuestProperties.begin();
5986 it != mHWData->mGuestProperties.end();
5987 ++it)
5988 {
5989 if ( strPatterns.isEmpty()
5990 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5991 RTSTR_MAX,
5992 it->first.c_str(),
5993 RTSTR_MAX,
5994 NULL)
5995 )
5996 propMap.insert(*it);
5997 }
5998
5999 alock.release();
6000
6001 /*
6002 * And build up the arrays for returning the property information.
6003 */
6004 size_t cEntries = propMap.size();
6005
6006 aNames.resize(cEntries);
6007 aValues.resize(cEntries);
6008 aTimestamps.resize(cEntries);
6009 aFlags.resize(cEntries);
6010
6011 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6012 size_t i = 0;
6013 for (HWData::GuestPropertyMap::const_iterator
6014 it = propMap.begin();
6015 it != propMap.end();
6016 ++it, ++i)
6017 {
6018 aNames[i] = it->first;
6019 aValues[i] = it->second.strValue;
6020 aTimestamps[i] = it->second.mTimestamp;
6021 GuestPropWriteFlags(it->second.mFlags, szFlags);
6022 aFlags[i] = Utf8Str(szFlags);
6023 }
6024
6025 return S_OK;
6026}
6027
6028/**
6029 * Enumerate the properties managed by a VM.
6030 * @returns E_ACCESSDENIED if the VM process is not available or not
6031 * currently handling queries and the setting should then be done in
6032 * VBoxSVC.
6033 */
6034HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6035 std::vector<com::Utf8Str> &aNames,
6036 std::vector<com::Utf8Str> &aValues,
6037 std::vector<LONG64> &aTimestamps,
6038 std::vector<com::Utf8Str> &aFlags)
6039{
6040 HRESULT rc;
6041 ComPtr<IInternalSessionControl> directControl;
6042 {
6043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6044 if (mData->mSession.mLockType == LockType_VM)
6045 directControl = mData->mSession.mDirectControl;
6046 }
6047
6048 com::SafeArray<BSTR> bNames;
6049 com::SafeArray<BSTR> bValues;
6050 com::SafeArray<LONG64> bTimestamps;
6051 com::SafeArray<BSTR> bFlags;
6052
6053 if (!directControl)
6054 rc = E_ACCESSDENIED;
6055 else
6056 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6057 ComSafeArrayAsOutParam(bNames),
6058 ComSafeArrayAsOutParam(bValues),
6059 ComSafeArrayAsOutParam(bTimestamps),
6060 ComSafeArrayAsOutParam(bFlags));
6061 size_t i;
6062 aNames.resize(bNames.size());
6063 for (i = 0; i < bNames.size(); ++i)
6064 aNames[i] = Utf8Str(bNames[i]);
6065 aValues.resize(bValues.size());
6066 for (i = 0; i < bValues.size(); ++i)
6067 aValues[i] = Utf8Str(bValues[i]);
6068 aTimestamps.resize(bTimestamps.size());
6069 for (i = 0; i < bTimestamps.size(); ++i)
6070 aTimestamps[i] = bTimestamps[i];
6071 aFlags.resize(bFlags.size());
6072 for (i = 0; i < bFlags.size(); ++i)
6073 aFlags[i] = Utf8Str(bFlags[i]);
6074
6075 return rc;
6076}
6077#endif // VBOX_WITH_GUEST_PROPS
6078HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6079 std::vector<com::Utf8Str> &aNames,
6080 std::vector<com::Utf8Str> &aValues,
6081 std::vector<LONG64> &aTimestamps,
6082 std::vector<com::Utf8Str> &aFlags)
6083{
6084#ifndef VBOX_WITH_GUEST_PROPS
6085 ReturnComNotImplemented();
6086#else // VBOX_WITH_GUEST_PROPS
6087
6088 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6089
6090 if (rc == E_ACCESSDENIED)
6091 /* The VM is not running or the service is not (yet) accessible */
6092 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6093 return rc;
6094#endif // VBOX_WITH_GUEST_PROPS
6095}
6096
6097HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6098 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6099{
6100 MediumAttachmentList atts;
6101
6102 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6103 if (FAILED(rc)) return rc;
6104
6105 aMediumAttachments.resize(atts.size());
6106 size_t i = 0;
6107 for (MediumAttachmentList::const_iterator
6108 it = atts.begin();
6109 it != atts.end();
6110 ++it, ++i)
6111 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6112
6113 return S_OK;
6114}
6115
6116HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6117 LONG aControllerPort,
6118 LONG aDevice,
6119 ComPtr<IMediumAttachment> &aAttachment)
6120{
6121 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6122 aName.c_str(), aControllerPort, aDevice));
6123
6124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6125
6126 aAttachment = NULL;
6127
6128 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6129 aName,
6130 aControllerPort,
6131 aDevice);
6132 if (pAttach.isNull())
6133 return setError(VBOX_E_OBJECT_NOT_FOUND,
6134 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6135 aDevice, aControllerPort, aName.c_str());
6136
6137 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6138
6139 return S_OK;
6140}
6141
6142
6143HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6144 StorageBus_T aConnectionType,
6145 ComPtr<IStorageController> &aController)
6146{
6147 if ( (aConnectionType <= StorageBus_Null)
6148 || (aConnectionType > StorageBus_PCIe))
6149 return setError(E_INVALIDARG,
6150 tr("Invalid connection type: %d"),
6151 aConnectionType);
6152
6153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6154
6155 HRESULT rc = i_checkStateDependency(MutableStateDep);
6156 if (FAILED(rc)) return rc;
6157
6158 /* try to find one with the name first. */
6159 ComObjPtr<StorageController> ctrl;
6160
6161 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6162 if (SUCCEEDED(rc))
6163 return setError(VBOX_E_OBJECT_IN_USE,
6164 tr("Storage controller named '%s' already exists"),
6165 aName.c_str());
6166
6167 ctrl.createObject();
6168
6169 /* get a new instance number for the storage controller */
6170 ULONG ulInstance = 0;
6171 bool fBootable = true;
6172 for (StorageControllerList::const_iterator
6173 it = mStorageControllers->begin();
6174 it != mStorageControllers->end();
6175 ++it)
6176 {
6177 if ((*it)->i_getStorageBus() == aConnectionType)
6178 {
6179 ULONG ulCurInst = (*it)->i_getInstance();
6180
6181 if (ulCurInst >= ulInstance)
6182 ulInstance = ulCurInst + 1;
6183
6184 /* Only one controller of each type can be marked as bootable. */
6185 if ((*it)->i_getBootable())
6186 fBootable = false;
6187 }
6188 }
6189
6190 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6191 if (FAILED(rc)) return rc;
6192
6193 i_setModified(IsModified_Storage);
6194 mStorageControllers.backup();
6195 mStorageControllers->push_back(ctrl);
6196
6197 ctrl.queryInterfaceTo(aController.asOutParam());
6198
6199 /* inform the direct session if any */
6200 alock.release();
6201 i_onStorageControllerChange();
6202
6203 return S_OK;
6204}
6205
6206HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6207 ComPtr<IStorageController> &aStorageController)
6208{
6209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6210
6211 ComObjPtr<StorageController> ctrl;
6212
6213 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6214 if (SUCCEEDED(rc))
6215 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6216
6217 return rc;
6218}
6219
6220HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6221 ULONG aInstance,
6222 ComPtr<IStorageController> &aStorageController)
6223{
6224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 for (StorageControllerList::const_iterator
6227 it = mStorageControllers->begin();
6228 it != mStorageControllers->end();
6229 ++it)
6230 {
6231 if ( (*it)->i_getStorageBus() == aConnectionType
6232 && (*it)->i_getInstance() == aInstance)
6233 {
6234 (*it).queryInterfaceTo(aStorageController.asOutParam());
6235 return S_OK;
6236 }
6237 }
6238
6239 return setError(VBOX_E_OBJECT_NOT_FOUND,
6240 tr("Could not find a storage controller with instance number '%lu'"),
6241 aInstance);
6242}
6243
6244HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6245{
6246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 HRESULT rc = i_checkStateDependency(MutableStateDep);
6249 if (FAILED(rc)) return rc;
6250
6251 ComObjPtr<StorageController> ctrl;
6252
6253 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6254 if (SUCCEEDED(rc))
6255 {
6256 /* Ensure that only one controller of each type is marked as bootable. */
6257 if (aBootable == TRUE)
6258 {
6259 for (StorageControllerList::const_iterator
6260 it = mStorageControllers->begin();
6261 it != mStorageControllers->end();
6262 ++it)
6263 {
6264 ComObjPtr<StorageController> aCtrl = (*it);
6265
6266 if ( (aCtrl->i_getName() != aName)
6267 && aCtrl->i_getBootable() == TRUE
6268 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6269 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6270 {
6271 aCtrl->i_setBootable(FALSE);
6272 break;
6273 }
6274 }
6275 }
6276
6277 if (SUCCEEDED(rc))
6278 {
6279 ctrl->i_setBootable(aBootable);
6280 i_setModified(IsModified_Storage);
6281 }
6282 }
6283
6284 if (SUCCEEDED(rc))
6285 {
6286 /* inform the direct session if any */
6287 alock.release();
6288 i_onStorageControllerChange();
6289 }
6290
6291 return rc;
6292}
6293
6294HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6295{
6296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6297
6298 HRESULT rc = i_checkStateDependency(MutableStateDep);
6299 if (FAILED(rc)) return rc;
6300
6301 ComObjPtr<StorageController> ctrl;
6302 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6303 if (FAILED(rc)) return rc;
6304
6305 {
6306 /* find all attached devices to the appropriate storage controller and detach them all */
6307 // make a temporary list because detachDevice invalidates iterators into
6308 // mMediumAttachments
6309 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6310
6311 for (MediumAttachmentList::const_iterator
6312 it = llAttachments2.begin();
6313 it != llAttachments2.end();
6314 ++it)
6315 {
6316 MediumAttachment *pAttachTemp = *it;
6317
6318 AutoCaller localAutoCaller(pAttachTemp);
6319 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6320
6321 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6322
6323 if (pAttachTemp->i_getControllerName() == aName)
6324 {
6325 rc = i_detachDevice(pAttachTemp, alock, NULL);
6326 if (FAILED(rc)) return rc;
6327 }
6328 }
6329 }
6330
6331 /* We can remove it now. */
6332 i_setModified(IsModified_Storage);
6333 mStorageControllers.backup();
6334
6335 ctrl->i_unshare();
6336
6337 mStorageControllers->remove(ctrl);
6338
6339 /* inform the direct session if any */
6340 alock.release();
6341 i_onStorageControllerChange();
6342
6343 return S_OK;
6344}
6345
6346HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6347 ComPtr<IUSBController> &aController)
6348{
6349 if ( (aType <= USBControllerType_Null)
6350 || (aType >= USBControllerType_Last))
6351 return setError(E_INVALIDARG,
6352 tr("Invalid USB controller type: %d"),
6353 aType);
6354
6355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6356
6357 HRESULT rc = i_checkStateDependency(MutableStateDep);
6358 if (FAILED(rc)) return rc;
6359
6360 /* try to find one with the same type first. */
6361 ComObjPtr<USBController> ctrl;
6362
6363 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6364 if (SUCCEEDED(rc))
6365 return setError(VBOX_E_OBJECT_IN_USE,
6366 tr("USB controller named '%s' already exists"),
6367 aName.c_str());
6368
6369 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6370 ULONG maxInstances;
6371 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6372 if (FAILED(rc))
6373 return rc;
6374
6375 ULONG cInstances = i_getUSBControllerCountByType(aType);
6376 if (cInstances >= maxInstances)
6377 return setError(E_INVALIDARG,
6378 tr("Too many USB controllers of this type"));
6379
6380 ctrl.createObject();
6381
6382 rc = ctrl->init(this, aName, aType);
6383 if (FAILED(rc)) return rc;
6384
6385 i_setModified(IsModified_USB);
6386 mUSBControllers.backup();
6387 mUSBControllers->push_back(ctrl);
6388
6389 ctrl.queryInterfaceTo(aController.asOutParam());
6390
6391 /* inform the direct session if any */
6392 alock.release();
6393 i_onUSBControllerChange();
6394
6395 return S_OK;
6396}
6397
6398HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6399{
6400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6401
6402 ComObjPtr<USBController> ctrl;
6403
6404 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6405 if (SUCCEEDED(rc))
6406 ctrl.queryInterfaceTo(aController.asOutParam());
6407
6408 return rc;
6409}
6410
6411HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6412 ULONG *aControllers)
6413{
6414 if ( (aType <= USBControllerType_Null)
6415 || (aType >= USBControllerType_Last))
6416 return setError(E_INVALIDARG,
6417 tr("Invalid USB controller type: %d"),
6418 aType);
6419
6420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6421
6422 ComObjPtr<USBController> ctrl;
6423
6424 *aControllers = i_getUSBControllerCountByType(aType);
6425
6426 return S_OK;
6427}
6428
6429HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6430{
6431
6432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6433
6434 HRESULT rc = i_checkStateDependency(MutableStateDep);
6435 if (FAILED(rc)) return rc;
6436
6437 ComObjPtr<USBController> ctrl;
6438 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6439 if (FAILED(rc)) return rc;
6440
6441 i_setModified(IsModified_USB);
6442 mUSBControllers.backup();
6443
6444 ctrl->i_unshare();
6445
6446 mUSBControllers->remove(ctrl);
6447
6448 /* inform the direct session if any */
6449 alock.release();
6450 i_onUSBControllerChange();
6451
6452 return S_OK;
6453}
6454
6455HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6456 ULONG *aOriginX,
6457 ULONG *aOriginY,
6458 ULONG *aWidth,
6459 ULONG *aHeight,
6460 BOOL *aEnabled)
6461{
6462 uint32_t u32OriginX= 0;
6463 uint32_t u32OriginY= 0;
6464 uint32_t u32Width = 0;
6465 uint32_t u32Height = 0;
6466 uint16_t u16Flags = 0;
6467
6468 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6469 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6470 if (RT_FAILURE(vrc))
6471 {
6472#ifdef RT_OS_WINDOWS
6473 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6474 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6475 * So just assign fEnable to TRUE again.
6476 * The right fix would be to change GUI API wrappers to make sure that parameters
6477 * are changed only if API succeeds.
6478 */
6479 *aEnabled = TRUE;
6480#endif
6481 return setError(VBOX_E_IPRT_ERROR,
6482 tr("Saved guest size is not available (%Rrc)"),
6483 vrc);
6484 }
6485
6486 *aOriginX = u32OriginX;
6487 *aOriginY = u32OriginY;
6488 *aWidth = u32Width;
6489 *aHeight = u32Height;
6490 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6491
6492 return S_OK;
6493}
6494
6495HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6496 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6497{
6498 if (aScreenId != 0)
6499 return E_NOTIMPL;
6500
6501 if ( aBitmapFormat != BitmapFormat_BGR0
6502 && aBitmapFormat != BitmapFormat_BGRA
6503 && aBitmapFormat != BitmapFormat_RGBA
6504 && aBitmapFormat != BitmapFormat_PNG)
6505 return setError(E_NOTIMPL,
6506 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6507
6508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 uint8_t *pu8Data = NULL;
6511 uint32_t cbData = 0;
6512 uint32_t u32Width = 0;
6513 uint32_t u32Height = 0;
6514
6515 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6516
6517 if (RT_FAILURE(vrc))
6518 return setError(VBOX_E_IPRT_ERROR,
6519 tr("Saved thumbnail data is not available (%Rrc)"),
6520 vrc);
6521
6522 HRESULT hr = S_OK;
6523
6524 *aWidth = u32Width;
6525 *aHeight = u32Height;
6526
6527 if (cbData > 0)
6528 {
6529 /* Convert pixels to the format expected by the API caller. */
6530 if (aBitmapFormat == BitmapFormat_BGR0)
6531 {
6532 /* [0] B, [1] G, [2] R, [3] 0. */
6533 aData.resize(cbData);
6534 memcpy(&aData.front(), pu8Data, cbData);
6535 }
6536 else if (aBitmapFormat == BitmapFormat_BGRA)
6537 {
6538 /* [0] B, [1] G, [2] R, [3] A. */
6539 aData.resize(cbData);
6540 for (uint32_t i = 0; i < cbData; i += 4)
6541 {
6542 aData[i] = pu8Data[i];
6543 aData[i + 1] = pu8Data[i + 1];
6544 aData[i + 2] = pu8Data[i + 2];
6545 aData[i + 3] = 0xff;
6546 }
6547 }
6548 else if (aBitmapFormat == BitmapFormat_RGBA)
6549 {
6550 /* [0] R, [1] G, [2] B, [3] A. */
6551 aData.resize(cbData);
6552 for (uint32_t i = 0; i < cbData; i += 4)
6553 {
6554 aData[i] = pu8Data[i + 2];
6555 aData[i + 1] = pu8Data[i + 1];
6556 aData[i + 2] = pu8Data[i];
6557 aData[i + 3] = 0xff;
6558 }
6559 }
6560 else if (aBitmapFormat == BitmapFormat_PNG)
6561 {
6562 uint8_t *pu8PNG = NULL;
6563 uint32_t cbPNG = 0;
6564 uint32_t cxPNG = 0;
6565 uint32_t cyPNG = 0;
6566
6567 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6568
6569 if (RT_SUCCESS(vrc))
6570 {
6571 aData.resize(cbPNG);
6572 if (cbPNG)
6573 memcpy(&aData.front(), pu8PNG, cbPNG);
6574 }
6575 else
6576 hr = setError(VBOX_E_IPRT_ERROR,
6577 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6578 vrc);
6579
6580 RTMemFree(pu8PNG);
6581 }
6582 }
6583
6584 freeSavedDisplayScreenshot(pu8Data);
6585
6586 return hr;
6587}
6588
6589HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6590 ULONG *aWidth,
6591 ULONG *aHeight,
6592 std::vector<BitmapFormat_T> &aBitmapFormats)
6593{
6594 if (aScreenId != 0)
6595 return E_NOTIMPL;
6596
6597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6598
6599 uint8_t *pu8Data = NULL;
6600 uint32_t cbData = 0;
6601 uint32_t u32Width = 0;
6602 uint32_t u32Height = 0;
6603
6604 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6605
6606 if (RT_FAILURE(vrc))
6607 return setError(VBOX_E_IPRT_ERROR,
6608 tr("Saved screenshot data is not available (%Rrc)"),
6609 vrc);
6610
6611 *aWidth = u32Width;
6612 *aHeight = u32Height;
6613 aBitmapFormats.resize(1);
6614 aBitmapFormats[0] = BitmapFormat_PNG;
6615
6616 freeSavedDisplayScreenshot(pu8Data);
6617
6618 return S_OK;
6619}
6620
6621HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6622 BitmapFormat_T aBitmapFormat,
6623 ULONG *aWidth,
6624 ULONG *aHeight,
6625 std::vector<BYTE> &aData)
6626{
6627 if (aScreenId != 0)
6628 return E_NOTIMPL;
6629
6630 if (aBitmapFormat != BitmapFormat_PNG)
6631 return E_NOTIMPL;
6632
6633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 uint8_t *pu8Data = NULL;
6636 uint32_t cbData = 0;
6637 uint32_t u32Width = 0;
6638 uint32_t u32Height = 0;
6639
6640 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6641
6642 if (RT_FAILURE(vrc))
6643 return setError(VBOX_E_IPRT_ERROR,
6644 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6645 vrc);
6646
6647 *aWidth = u32Width;
6648 *aHeight = u32Height;
6649
6650 aData.resize(cbData);
6651 if (cbData)
6652 memcpy(&aData.front(), pu8Data, cbData);
6653
6654 freeSavedDisplayScreenshot(pu8Data);
6655
6656 return S_OK;
6657}
6658
6659HRESULT Machine::hotPlugCPU(ULONG aCpu)
6660{
6661 HRESULT rc = S_OK;
6662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6663
6664 if (!mHWData->mCPUHotPlugEnabled)
6665 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6666
6667 if (aCpu >= mHWData->mCPUCount)
6668 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6669
6670 if (mHWData->mCPUAttached[aCpu])
6671 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6672
6673 alock.release();
6674 rc = i_onCPUChange(aCpu, false);
6675 alock.acquire();
6676 if (FAILED(rc)) return rc;
6677
6678 i_setModified(IsModified_MachineData);
6679 mHWData.backup();
6680 mHWData->mCPUAttached[aCpu] = true;
6681
6682 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6683 if (Global::IsOnline(mData->mMachineState))
6684 i_saveSettings(NULL);
6685
6686 return S_OK;
6687}
6688
6689HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6690{
6691 HRESULT rc = S_OK;
6692
6693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6694
6695 if (!mHWData->mCPUHotPlugEnabled)
6696 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6697
6698 if (aCpu >= SchemaDefs::MaxCPUCount)
6699 return setError(E_INVALIDARG,
6700 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6701 SchemaDefs::MaxCPUCount);
6702
6703 if (!mHWData->mCPUAttached[aCpu])
6704 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6705
6706 /* CPU 0 can't be detached */
6707 if (aCpu == 0)
6708 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6709
6710 alock.release();
6711 rc = i_onCPUChange(aCpu, true);
6712 alock.acquire();
6713 if (FAILED(rc)) return rc;
6714
6715 i_setModified(IsModified_MachineData);
6716 mHWData.backup();
6717 mHWData->mCPUAttached[aCpu] = false;
6718
6719 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6720 if (Global::IsOnline(mData->mMachineState))
6721 i_saveSettings(NULL);
6722
6723 return S_OK;
6724}
6725
6726HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6727{
6728 *aAttached = false;
6729
6730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6731
6732 /* If hotplug is enabled the CPU is always enabled. */
6733 if (!mHWData->mCPUHotPlugEnabled)
6734 {
6735 if (aCpu < mHWData->mCPUCount)
6736 *aAttached = true;
6737 }
6738 else
6739 {
6740 if (aCpu < SchemaDefs::MaxCPUCount)
6741 *aAttached = mHWData->mCPUAttached[aCpu];
6742 }
6743
6744 return S_OK;
6745}
6746
6747HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6748{
6749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6750
6751 Utf8Str log = i_getLogFilename(aIdx);
6752 if (!RTFileExists(log.c_str()))
6753 log.setNull();
6754 aFilename = log;
6755
6756 return S_OK;
6757}
6758
6759HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6760{
6761 if (aSize < 0)
6762 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6763
6764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6765
6766 HRESULT rc = S_OK;
6767 Utf8Str log = i_getLogFilename(aIdx);
6768
6769 /* do not unnecessarily hold the lock while doing something which does
6770 * not need the lock and potentially takes a long time. */
6771 alock.release();
6772
6773 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6774 * keeps the SOAP reply size under 1M for the webservice (we're using
6775 * base64 encoded strings for binary data for years now, avoiding the
6776 * expansion of each byte array element to approx. 25 bytes of XML. */
6777 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6778 aData.resize(cbData);
6779
6780 RTFILE LogFile;
6781 int vrc = RTFileOpen(&LogFile, log.c_str(),
6782 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6783 if (RT_SUCCESS(vrc))
6784 {
6785 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6786 if (RT_SUCCESS(vrc))
6787 aData.resize(cbData);
6788 else
6789 rc = setError(VBOX_E_IPRT_ERROR,
6790 tr("Could not read log file '%s' (%Rrc)"),
6791 log.c_str(), vrc);
6792 RTFileClose(LogFile);
6793 }
6794 else
6795 rc = setError(VBOX_E_IPRT_ERROR,
6796 tr("Could not open log file '%s' (%Rrc)"),
6797 log.c_str(), vrc);
6798
6799 if (FAILED(rc))
6800 aData.resize(0);
6801
6802 return rc;
6803}
6804
6805
6806/**
6807 * Currently this method doesn't attach device to the running VM,
6808 * just makes sure it's plugged on next VM start.
6809 */
6810HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6811{
6812 // lock scope
6813 {
6814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6815
6816 HRESULT rc = i_checkStateDependency(MutableStateDep);
6817 if (FAILED(rc)) return rc;
6818
6819 ChipsetType_T aChipset = ChipsetType_PIIX3;
6820 COMGETTER(ChipsetType)(&aChipset);
6821
6822 if (aChipset != ChipsetType_ICH9)
6823 {
6824 return setError(E_INVALIDARG,
6825 tr("Host PCI attachment only supported with ICH9 chipset"));
6826 }
6827
6828 // check if device with this host PCI address already attached
6829 for (HWData::PCIDeviceAssignmentList::const_iterator
6830 it = mHWData->mPCIDeviceAssignments.begin();
6831 it != mHWData->mPCIDeviceAssignments.end();
6832 ++it)
6833 {
6834 LONG iHostAddress = -1;
6835 ComPtr<PCIDeviceAttachment> pAttach;
6836 pAttach = *it;
6837 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6838 if (iHostAddress == aHostAddress)
6839 return setError(E_INVALIDARG,
6840 tr("Device with host PCI address already attached to this VM"));
6841 }
6842
6843 ComObjPtr<PCIDeviceAttachment> pda;
6844 char name[32];
6845
6846 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6847 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6848 pda.createObject();
6849 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6850 i_setModified(IsModified_MachineData);
6851 mHWData.backup();
6852 mHWData->mPCIDeviceAssignments.push_back(pda);
6853 }
6854
6855 return S_OK;
6856}
6857
6858/**
6859 * Currently this method doesn't detach device from the running VM,
6860 * just makes sure it's not plugged on next VM start.
6861 */
6862HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6863{
6864 ComObjPtr<PCIDeviceAttachment> pAttach;
6865 bool fRemoved = false;
6866 HRESULT rc;
6867
6868 // lock scope
6869 {
6870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6871
6872 rc = i_checkStateDependency(MutableStateDep);
6873 if (FAILED(rc)) return rc;
6874
6875 for (HWData::PCIDeviceAssignmentList::const_iterator
6876 it = mHWData->mPCIDeviceAssignments.begin();
6877 it != mHWData->mPCIDeviceAssignments.end();
6878 ++it)
6879 {
6880 LONG iHostAddress = -1;
6881 pAttach = *it;
6882 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6883 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6884 {
6885 i_setModified(IsModified_MachineData);
6886 mHWData.backup();
6887 mHWData->mPCIDeviceAssignments.remove(pAttach);
6888 fRemoved = true;
6889 break;
6890 }
6891 }
6892 }
6893
6894
6895 /* Fire event outside of the lock */
6896 if (fRemoved)
6897 {
6898 Assert(!pAttach.isNull());
6899 ComPtr<IEventSource> es;
6900 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6901 Assert(SUCCEEDED(rc));
6902 Bstr mid;
6903 rc = this->COMGETTER(Id)(mid.asOutParam());
6904 Assert(SUCCEEDED(rc));
6905 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6906 }
6907
6908 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6909 tr("No host PCI device %08x attached"),
6910 aHostAddress
6911 );
6912}
6913
6914HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6915{
6916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6917
6918 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6919 size_t i = 0;
6920 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6921 it = mHWData->mPCIDeviceAssignments.begin();
6922 it != mHWData->mPCIDeviceAssignments.end();
6923 ++it, ++i)
6924 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6925
6926 return S_OK;
6927}
6928
6929HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6930{
6931 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6932
6933 return S_OK;
6934}
6935
6936HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6937{
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6941
6942 return S_OK;
6943}
6944
6945HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6946{
6947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6948 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6949 if (SUCCEEDED(hrc))
6950 {
6951 hrc = mHWData.backupEx();
6952 if (SUCCEEDED(hrc))
6953 {
6954 i_setModified(IsModified_MachineData);
6955 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6956 }
6957 }
6958 return hrc;
6959}
6960
6961HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6962{
6963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6964 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6965 return S_OK;
6966}
6967
6968HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6969{
6970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6972 if (SUCCEEDED(hrc))
6973 {
6974 hrc = mHWData.backupEx();
6975 if (SUCCEEDED(hrc))
6976 {
6977 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6978 if (SUCCEEDED(hrc))
6979 i_setModified(IsModified_MachineData);
6980 }
6981 }
6982 return hrc;
6983}
6984
6985HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6986{
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6990
6991 return S_OK;
6992}
6993
6994HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6995{
6996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6998 if (SUCCEEDED(hrc))
6999 {
7000 hrc = mHWData.backupEx();
7001 if (SUCCEEDED(hrc))
7002 {
7003 i_setModified(IsModified_MachineData);
7004 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7005 }
7006 }
7007 return hrc;
7008}
7009
7010HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7011{
7012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7013
7014 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7015
7016 return S_OK;
7017}
7018
7019HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7020{
7021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7022
7023 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7024 if ( SUCCEEDED(hrc)
7025 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7026 {
7027 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7028 int vrc;
7029
7030 if (aAutostartEnabled)
7031 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7032 else
7033 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7034
7035 if (RT_SUCCESS(vrc))
7036 {
7037 hrc = mHWData.backupEx();
7038 if (SUCCEEDED(hrc))
7039 {
7040 i_setModified(IsModified_MachineData);
7041 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7042 }
7043 }
7044 else if (vrc == VERR_NOT_SUPPORTED)
7045 hrc = setError(VBOX_E_NOT_SUPPORTED,
7046 tr("The VM autostart feature is not supported on this platform"));
7047 else if (vrc == VERR_PATH_NOT_FOUND)
7048 hrc = setError(E_FAIL,
7049 tr("The path to the autostart database is not set"));
7050 else
7051 hrc = setError(E_UNEXPECTED,
7052 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7053 aAutostartEnabled ? "Adding" : "Removing",
7054 mUserData->s.strName.c_str(), vrc);
7055 }
7056 return hrc;
7057}
7058
7059HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7060{
7061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7062
7063 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7064
7065 return S_OK;
7066}
7067
7068HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7069{
7070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7071 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7072 if (SUCCEEDED(hrc))
7073 {
7074 hrc = mHWData.backupEx();
7075 if (SUCCEEDED(hrc))
7076 {
7077 i_setModified(IsModified_MachineData);
7078 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7079 }
7080 }
7081 return hrc;
7082}
7083
7084HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7085{
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087
7088 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7089
7090 return S_OK;
7091}
7092
7093HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7094{
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7097 if ( SUCCEEDED(hrc)
7098 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7099 {
7100 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7101 int vrc;
7102
7103 if (aAutostopType != AutostopType_Disabled)
7104 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7105 else
7106 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7107
7108 if (RT_SUCCESS(vrc))
7109 {
7110 hrc = mHWData.backupEx();
7111 if (SUCCEEDED(hrc))
7112 {
7113 i_setModified(IsModified_MachineData);
7114 mHWData->mAutostart.enmAutostopType = aAutostopType;
7115 }
7116 }
7117 else if (vrc == VERR_NOT_SUPPORTED)
7118 hrc = setError(VBOX_E_NOT_SUPPORTED,
7119 tr("The VM autostop feature is not supported on this platform"));
7120 else if (vrc == VERR_PATH_NOT_FOUND)
7121 hrc = setError(E_FAIL,
7122 tr("The path to the autostart database is not set"));
7123 else
7124 hrc = setError(E_UNEXPECTED,
7125 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7126 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7127 mUserData->s.strName.c_str(), vrc);
7128 }
7129 return hrc;
7130}
7131
7132HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7133{
7134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7135
7136 aDefaultFrontend = mHWData->mDefaultFrontend;
7137
7138 return S_OK;
7139}
7140
7141HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7142{
7143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7144 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7145 if (SUCCEEDED(hrc))
7146 {
7147 hrc = mHWData.backupEx();
7148 if (SUCCEEDED(hrc))
7149 {
7150 i_setModified(IsModified_MachineData);
7151 mHWData->mDefaultFrontend = aDefaultFrontend;
7152 }
7153 }
7154 return hrc;
7155}
7156
7157HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7158{
7159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7160 size_t cbIcon = mUserData->s.ovIcon.size();
7161 aIcon.resize(cbIcon);
7162 if (cbIcon)
7163 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7164 return S_OK;
7165}
7166
7167HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7168{
7169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7170 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7171 if (SUCCEEDED(hrc))
7172 {
7173 i_setModified(IsModified_MachineData);
7174 mUserData.backup();
7175 size_t cbIcon = aIcon.size();
7176 mUserData->s.ovIcon.resize(cbIcon);
7177 if (cbIcon)
7178 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7179 }
7180 return hrc;
7181}
7182
7183HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7184{
7185#ifdef VBOX_WITH_USB
7186 *aUSBProxyAvailable = true;
7187#else
7188 *aUSBProxyAvailable = false;
7189#endif
7190 return S_OK;
7191}
7192
7193HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7194{
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 aVMProcessPriority = mUserData->s.strVMPriority;
7198
7199 return S_OK;
7200}
7201
7202HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7203{
7204 RT_NOREF(aVMProcessPriority);
7205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7206 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7207 if (SUCCEEDED(hrc))
7208 {
7209 /** @todo r=klaus: currently this is marked as not implemented, as
7210 * the code for setting the priority of the process is not there
7211 * (neither when starting the VM nor at runtime). */
7212 ReturnComNotImplemented();
7213#if 0
7214 hrc = mUserData.backupEx();
7215 if (SUCCEEDED(hrc))
7216 {
7217 i_setModified(IsModified_MachineData);
7218 mUserData->s.strVMPriority = aVMProcessPriority;
7219 }
7220#endif
7221 }
7222 return hrc;
7223}
7224
7225HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7226 ComPtr<IProgress> &aProgress)
7227{
7228 ComObjPtr<Progress> pP;
7229 Progress *ppP = pP;
7230 IProgress *iP = static_cast<IProgress *>(ppP);
7231 IProgress **pProgress = &iP;
7232
7233 IMachine *pTarget = aTarget;
7234
7235 /* Convert the options. */
7236 RTCList<CloneOptions_T> optList;
7237 if (aOptions.size())
7238 for (size_t i = 0; i < aOptions.size(); ++i)
7239 optList.append(aOptions[i]);
7240
7241 if (optList.contains(CloneOptions_Link))
7242 {
7243 if (!i_isSnapshotMachine())
7244 return setError(E_INVALIDARG,
7245 tr("Linked clone can only be created from a snapshot"));
7246 if (aMode != CloneMode_MachineState)
7247 return setError(E_INVALIDARG,
7248 tr("Linked clone can only be created for a single machine state"));
7249 }
7250 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7251
7252 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7253
7254 HRESULT rc = pWorker->start(pProgress);
7255
7256 pP = static_cast<Progress *>(*pProgress);
7257 pP.queryInterfaceTo(aProgress.asOutParam());
7258
7259 return rc;
7260
7261}
7262
7263HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7264 const com::Utf8Str &aType,
7265 ComPtr<IProgress> &aProgress)
7266{
7267 LogFlowThisFuncEnter();
7268
7269 ComObjPtr<Progress> progress;
7270
7271 progress.createObject();
7272
7273 HRESULT rc = S_OK;
7274 Utf8Str targetPath = aTargetPath;
7275 Utf8Str type = aType;
7276
7277 /* Initialize our worker task */
7278 MachineMoveVM* task = NULL;
7279 try
7280 {
7281 task = new MachineMoveVM(this, targetPath, type, progress);
7282 }
7283 catch(...)
7284 {
7285 delete task;
7286 return rc;
7287 }
7288
7289 /*
7290 * task pointer will be owned by the ThreadTask class.
7291 * There is no need to call operator "delete" in the end.
7292 */
7293 rc = task->init();
7294 if (SUCCEEDED(rc))
7295 {
7296 rc = task->createThread();
7297 if (FAILED(rc))
7298 {
7299 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7300 }
7301
7302 /* Return progress to the caller */
7303 progress.queryInterfaceTo(aProgress.asOutParam());
7304 }
7305
7306 LogFlowThisFuncLeave();
7307 return rc;
7308
7309}
7310
7311HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7312{
7313 NOREF(aProgress);
7314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7315
7316 // This check should always fail.
7317 HRESULT rc = i_checkStateDependency(MutableStateDep);
7318 if (FAILED(rc)) return rc;
7319
7320 AssertFailedReturn(E_NOTIMPL);
7321}
7322
7323HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7324{
7325 NOREF(aSavedStateFile);
7326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7327
7328 // This check should always fail.
7329 HRESULT rc = i_checkStateDependency(MutableStateDep);
7330 if (FAILED(rc)) return rc;
7331
7332 AssertFailedReturn(E_NOTIMPL);
7333}
7334
7335HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7336{
7337 NOREF(aFRemoveFile);
7338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7339
7340 // This check should always fail.
7341 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7342 if (FAILED(rc)) return rc;
7343
7344 AssertFailedReturn(E_NOTIMPL);
7345}
7346
7347// public methods for internal purposes
7348/////////////////////////////////////////////////////////////////////////////
7349
7350/**
7351 * Adds the given IsModified_* flag to the dirty flags of the machine.
7352 * This must be called either during i_loadSettings or under the machine write lock.
7353 * @param fl Flag
7354 * @param fAllowStateModification If state modifications are allowed.
7355 */
7356void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7357{
7358 mData->flModifications |= fl;
7359 if (fAllowStateModification && i_isStateModificationAllowed())
7360 mData->mCurrentStateModified = true;
7361}
7362
7363/**
7364 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7365 * care of the write locking.
7366 *
7367 * @param fModification The flag to add.
7368 * @param fAllowStateModification If state modifications are allowed.
7369 */
7370void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7371{
7372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7373 i_setModified(fModification, fAllowStateModification);
7374}
7375
7376/**
7377 * Saves the registry entry of this machine to the given configuration node.
7378 *
7379 * @param data Machine registry data.
7380 *
7381 * @note locks this object for reading.
7382 */
7383HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7384{
7385 AutoLimitedCaller autoCaller(this);
7386 AssertComRCReturnRC(autoCaller.rc());
7387
7388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7389
7390 data.uuid = mData->mUuid;
7391 data.strSettingsFile = mData->m_strConfigFile;
7392
7393 return S_OK;
7394}
7395
7396/**
7397 * Calculates the absolute path of the given path taking the directory of the
7398 * machine settings file as the current directory.
7399 *
7400 * @param strPath Path to calculate the absolute path for.
7401 * @param aResult Where to put the result (used only on success, can be the
7402 * same Utf8Str instance as passed in @a aPath).
7403 * @return IPRT result.
7404 *
7405 * @note Locks this object for reading.
7406 */
7407int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7408{
7409 AutoCaller autoCaller(this);
7410 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7411
7412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7413
7414 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7415
7416 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7417
7418 strSettingsDir.stripFilename();
7419 char folder[RTPATH_MAX];
7420 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7421 if (RT_SUCCESS(vrc))
7422 aResult = folder;
7423
7424 return vrc;
7425}
7426
7427/**
7428 * Copies strSource to strTarget, making it relative to the machine folder
7429 * if it is a subdirectory thereof, or simply copying it otherwise.
7430 *
7431 * @param strSource Path to evaluate and copy.
7432 * @param strTarget Buffer to receive target path.
7433 *
7434 * @note Locks this object for reading.
7435 */
7436void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7437 Utf8Str &strTarget)
7438{
7439 AutoCaller autoCaller(this);
7440 AssertComRCReturn(autoCaller.rc(), (void)0);
7441
7442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7443
7444 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7445 // use strTarget as a temporary buffer to hold the machine settings dir
7446 strTarget = mData->m_strConfigFileFull;
7447 strTarget.stripFilename();
7448 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7449 {
7450 // is relative: then append what's left
7451 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7452 // for empty paths (only possible for subdirs) use "." to avoid
7453 // triggering default settings for not present config attributes.
7454 if (strTarget.isEmpty())
7455 strTarget = ".";
7456 }
7457 else
7458 // is not relative: then overwrite
7459 strTarget = strSource;
7460}
7461
7462/**
7463 * Returns the full path to the machine's log folder in the
7464 * \a aLogFolder argument.
7465 */
7466void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7467{
7468 AutoCaller autoCaller(this);
7469 AssertComRCReturnVoid(autoCaller.rc());
7470
7471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7472
7473 char szTmp[RTPATH_MAX];
7474 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7475 if (RT_SUCCESS(vrc))
7476 {
7477 if (szTmp[0] && !mUserData.isNull())
7478 {
7479 char szTmp2[RTPATH_MAX];
7480 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7481 if (RT_SUCCESS(vrc))
7482 aLogFolder = Utf8StrFmt("%s%c%s",
7483 szTmp2,
7484 RTPATH_DELIMITER,
7485 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7486 }
7487 else
7488 vrc = VERR_PATH_IS_RELATIVE;
7489 }
7490
7491 if (RT_FAILURE(vrc))
7492 {
7493 // fallback if VBOX_USER_LOGHOME is not set or invalid
7494 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7495 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7496 aLogFolder.append(RTPATH_DELIMITER);
7497 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7498 }
7499}
7500
7501/**
7502 * Returns the full path to the machine's log file for an given index.
7503 */
7504Utf8Str Machine::i_getLogFilename(ULONG idx)
7505{
7506 Utf8Str logFolder;
7507 getLogFolder(logFolder);
7508 Assert(logFolder.length());
7509
7510 Utf8Str log;
7511 if (idx == 0)
7512 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7513#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7514 else if (idx == 1)
7515 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7516 else
7517 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7518#else
7519 else
7520 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7521#endif
7522 return log;
7523}
7524
7525/**
7526 * Returns the full path to the machine's hardened log file.
7527 */
7528Utf8Str Machine::i_getHardeningLogFilename(void)
7529{
7530 Utf8Str strFilename;
7531 getLogFolder(strFilename);
7532 Assert(strFilename.length());
7533 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7534 return strFilename;
7535}
7536
7537
7538/**
7539 * Composes a unique saved state filename based on the current system time. The filename is
7540 * granular to the second so this will work so long as no more than one snapshot is taken on
7541 * a machine per second.
7542 *
7543 * Before version 4.1, we used this formula for saved state files:
7544 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7545 * which no longer works because saved state files can now be shared between the saved state of the
7546 * "saved" machine and an online snapshot, and the following would cause problems:
7547 * 1) save machine
7548 * 2) create online snapshot from that machine state --> reusing saved state file
7549 * 3) save machine again --> filename would be reused, breaking the online snapshot
7550 *
7551 * So instead we now use a timestamp.
7552 *
7553 * @param strStateFilePath
7554 */
7555
7556void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7557{
7558 AutoCaller autoCaller(this);
7559 AssertComRCReturnVoid(autoCaller.rc());
7560
7561 {
7562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7563 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7564 }
7565
7566 RTTIMESPEC ts;
7567 RTTimeNow(&ts);
7568 RTTIME time;
7569 RTTimeExplode(&time, &ts);
7570
7571 strStateFilePath += RTPATH_DELIMITER;
7572 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7573 time.i32Year, time.u8Month, time.u8MonthDay,
7574 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7575}
7576
7577/**
7578 * Returns the full path to the default video capture file.
7579 */
7580void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7581{
7582 AutoCaller autoCaller(this);
7583 AssertComRCReturnVoid(autoCaller.rc());
7584
7585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7586
7587 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7588 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7589 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7590}
7591
7592/**
7593 * Returns whether at least one USB controller is present for the VM.
7594 */
7595bool Machine::i_isUSBControllerPresent()
7596{
7597 AutoCaller autoCaller(this);
7598 AssertComRCReturn(autoCaller.rc(), false);
7599
7600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7601
7602 return (mUSBControllers->size() > 0);
7603}
7604
7605/**
7606 * @note Locks this object for writing, calls the client process
7607 * (inside the lock).
7608 */
7609HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7610 const Utf8Str &strFrontend,
7611 const Utf8Str &strEnvironment,
7612 ProgressProxy *aProgress)
7613{
7614 LogFlowThisFuncEnter();
7615
7616 AssertReturn(aControl, E_FAIL);
7617 AssertReturn(aProgress, E_FAIL);
7618 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7619
7620 AutoCaller autoCaller(this);
7621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7622
7623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7624
7625 if (!mData->mRegistered)
7626 return setError(E_UNEXPECTED,
7627 tr("The machine '%s' is not registered"),
7628 mUserData->s.strName.c_str());
7629
7630 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7631
7632 /* The process started when launching a VM with separate UI/VM processes is always
7633 * the UI process, i.e. needs special handling as it won't claim the session. */
7634 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7635
7636 if (fSeparate)
7637 {
7638 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7639 return setError(VBOX_E_INVALID_OBJECT_STATE,
7640 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7641 mUserData->s.strName.c_str());
7642 }
7643 else
7644 {
7645 if ( mData->mSession.mState == SessionState_Locked
7646 || mData->mSession.mState == SessionState_Spawning
7647 || mData->mSession.mState == SessionState_Unlocking)
7648 return setError(VBOX_E_INVALID_OBJECT_STATE,
7649 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7650 mUserData->s.strName.c_str());
7651
7652 /* may not be busy */
7653 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7654 }
7655
7656 /* get the path to the executable */
7657 char szPath[RTPATH_MAX];
7658 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7659 size_t cchBufLeft = strlen(szPath);
7660 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7661 szPath[cchBufLeft] = 0;
7662 char *pszNamePart = szPath + cchBufLeft;
7663 cchBufLeft = sizeof(szPath) - cchBufLeft;
7664
7665 int vrc = VINF_SUCCESS;
7666 RTPROCESS pid = NIL_RTPROCESS;
7667
7668 RTENV env = RTENV_DEFAULT;
7669
7670 if (!strEnvironment.isEmpty())
7671 {
7672 char *newEnvStr = NULL;
7673
7674 do
7675 {
7676 /* clone the current environment */
7677 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7678 AssertRCBreakStmt(vrc2, vrc = vrc2);
7679
7680 newEnvStr = RTStrDup(strEnvironment.c_str());
7681 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7682
7683 /* put new variables to the environment
7684 * (ignore empty variable names here since RTEnv API
7685 * intentionally doesn't do that) */
7686 char *var = newEnvStr;
7687 for (char *p = newEnvStr; *p; ++p)
7688 {
7689 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7690 {
7691 *p = '\0';
7692 if (*var)
7693 {
7694 char *val = strchr(var, '=');
7695 if (val)
7696 {
7697 *val++ = '\0';
7698 vrc2 = RTEnvSetEx(env, var, val);
7699 }
7700 else
7701 vrc2 = RTEnvUnsetEx(env, var);
7702 if (RT_FAILURE(vrc2))
7703 break;
7704 }
7705 var = p + 1;
7706 }
7707 }
7708 if (RT_SUCCESS(vrc2) && *var)
7709 vrc2 = RTEnvPutEx(env, var);
7710
7711 AssertRCBreakStmt(vrc2, vrc = vrc2);
7712 }
7713 while (0);
7714
7715 if (newEnvStr != NULL)
7716 RTStrFree(newEnvStr);
7717 }
7718
7719 /* Hardening logging */
7720#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7721 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7722 {
7723 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7724 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7725 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7726 {
7727 Utf8Str strStartupLogDir = strHardeningLogFile;
7728 strStartupLogDir.stripFilename();
7729 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7730 file without stripping the file. */
7731 }
7732 strSupHardeningLogArg.append(strHardeningLogFile);
7733
7734 /* Remove legacy log filename to avoid confusion. */
7735 Utf8Str strOldStartupLogFile;
7736 getLogFolder(strOldStartupLogFile);
7737 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7738 RTFileDelete(strOldStartupLogFile.c_str());
7739 }
7740 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7741#else
7742 const char *pszSupHardeningLogArg = NULL;
7743#endif
7744
7745 Utf8Str strCanonicalName;
7746
7747#ifdef VBOX_WITH_QTGUI
7748 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7749 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7750 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7751 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7752 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7753 {
7754 strCanonicalName = "GUI/Qt";
7755# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7756 /* Modify the base path so that we don't need to use ".." below. */
7757 RTPathStripTrailingSlash(szPath);
7758 RTPathStripFilename(szPath);
7759 cchBufLeft = strlen(szPath);
7760 pszNamePart = szPath + cchBufLeft;
7761 cchBufLeft = sizeof(szPath) - cchBufLeft;
7762
7763# define OSX_APP_NAME "VirtualBoxVM"
7764# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7765
7766 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7767 if ( strAppOverride.contains(".")
7768 || strAppOverride.contains("/")
7769 || strAppOverride.contains("\\")
7770 || strAppOverride.contains(":"))
7771 strAppOverride.setNull();
7772 Utf8Str strAppPath;
7773 if (!strAppOverride.isEmpty())
7774 {
7775 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7776 Utf8Str strFullPath(szPath);
7777 strFullPath.append(strAppPath);
7778 /* there is a race, but people using this deserve the failure */
7779 if (!RTFileExists(strFullPath.c_str()))
7780 strAppOverride.setNull();
7781 }
7782 if (strAppOverride.isEmpty())
7783 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7784 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7785 strcpy(pszNamePart, strAppPath.c_str());
7786# else
7787 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7788 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7789 strcpy(pszNamePart, s_szVirtualBox_exe);
7790# endif
7791
7792 Utf8Str idStr = mData->mUuid.toString();
7793 const char *apszArgs[] =
7794 {
7795 szPath,
7796 "--comment", mUserData->s.strName.c_str(),
7797 "--startvm", idStr.c_str(),
7798 "--no-startvm-errormsgbox",
7799 NULL, /* For "--separate". */
7800 NULL, /* For "--sup-startup-log". */
7801 NULL
7802 };
7803 unsigned iArg = 6;
7804 if (fSeparate)
7805 apszArgs[iArg++] = "--separate";
7806 apszArgs[iArg++] = pszSupHardeningLogArg;
7807
7808 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7809 }
7810#else /* !VBOX_WITH_QTGUI */
7811 if (0)
7812 ;
7813#endif /* VBOX_WITH_QTGUI */
7814
7815 else
7816
7817#ifdef VBOX_WITH_VBOXSDL
7818 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7819 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7820 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7821 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7822 {
7823 strCanonicalName = "GUI/SDL";
7824 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7825 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7826 strcpy(pszNamePart, s_szVBoxSDL_exe);
7827
7828 Utf8Str idStr = mData->mUuid.toString();
7829 const char *apszArgs[] =
7830 {
7831 szPath,
7832 "--comment", mUserData->s.strName.c_str(),
7833 "--startvm", idStr.c_str(),
7834 NULL, /* For "--separate". */
7835 NULL, /* For "--sup-startup-log". */
7836 NULL
7837 };
7838 unsigned iArg = 5;
7839 if (fSeparate)
7840 apszArgs[iArg++] = "--separate";
7841 apszArgs[iArg++] = pszSupHardeningLogArg;
7842
7843 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7844 }
7845#else /* !VBOX_WITH_VBOXSDL */
7846 if (0)
7847 ;
7848#endif /* !VBOX_WITH_VBOXSDL */
7849
7850 else
7851
7852#ifdef VBOX_WITH_HEADLESS
7853 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7854 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7855 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7856 )
7857 {
7858 strCanonicalName = "headless";
7859 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7860 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7861 * and a VM works even if the server has not been installed.
7862 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7863 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7864 * differently in 4.0 and 3.x.
7865 */
7866 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7867 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7868 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7869
7870 Utf8Str idStr = mData->mUuid.toString();
7871 const char *apszArgs[] =
7872 {
7873 szPath,
7874 "--comment", mUserData->s.strName.c_str(),
7875 "--startvm", idStr.c_str(),
7876 "--vrde", "config",
7877 NULL, /* For "--capture". */
7878 NULL, /* For "--sup-startup-log". */
7879 NULL
7880 };
7881 unsigned iArg = 7;
7882 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7883 apszArgs[iArg++] = "--capture";
7884 apszArgs[iArg++] = pszSupHardeningLogArg;
7885
7886# ifdef RT_OS_WINDOWS
7887 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7888# else
7889 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7890# endif
7891 }
7892#else /* !VBOX_WITH_HEADLESS */
7893 if (0)
7894 ;
7895#endif /* !VBOX_WITH_HEADLESS */
7896 else
7897 {
7898 RTEnvDestroy(env);
7899 return setError(E_INVALIDARG,
7900 tr("Invalid frontend name: '%s'"),
7901 strFrontend.c_str());
7902 }
7903
7904 RTEnvDestroy(env);
7905
7906 if (RT_FAILURE(vrc))
7907 return setError(VBOX_E_IPRT_ERROR,
7908 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7909 mUserData->s.strName.c_str(), vrc);
7910
7911 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7912
7913 if (!fSeparate)
7914 {
7915 /*
7916 * Note that we don't release the lock here before calling the client,
7917 * because it doesn't need to call us back if called with a NULL argument.
7918 * Releasing the lock here is dangerous because we didn't prepare the
7919 * launch data yet, but the client we've just started may happen to be
7920 * too fast and call LockMachine() that will fail (because of PID, etc.),
7921 * so that the Machine will never get out of the Spawning session state.
7922 */
7923
7924 /* inform the session that it will be a remote one */
7925 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7926#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7927 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7928#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7929 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7930#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7931 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7932
7933 if (FAILED(rc))
7934 {
7935 /* restore the session state */
7936 mData->mSession.mState = SessionState_Unlocked;
7937 alock.release();
7938 mParent->i_addProcessToReap(pid);
7939 /* The failure may occur w/o any error info (from RPC), so provide one */
7940 return setError(VBOX_E_VM_ERROR,
7941 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7942 }
7943
7944 /* attach launch data to the machine */
7945 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7946 mData->mSession.mRemoteControls.push_back(aControl);
7947 mData->mSession.mProgress = aProgress;
7948 mData->mSession.mPID = pid;
7949 mData->mSession.mState = SessionState_Spawning;
7950 Assert(strCanonicalName.isNotEmpty());
7951 mData->mSession.mName = strCanonicalName;
7952 }
7953 else
7954 {
7955 /* For separate UI process we declare the launch as completed instantly, as the
7956 * actual headless VM start may or may not come. No point in remembering anything
7957 * yet, as what matters for us is when the headless VM gets started. */
7958 aProgress->i_notifyComplete(S_OK);
7959 }
7960
7961 alock.release();
7962 mParent->i_addProcessToReap(pid);
7963
7964 LogFlowThisFuncLeave();
7965 return S_OK;
7966}
7967
7968/**
7969 * Returns @c true if the given session machine instance has an open direct
7970 * session (and optionally also for direct sessions which are closing) and
7971 * returns the session control machine instance if so.
7972 *
7973 * Note that when the method returns @c false, the arguments remain unchanged.
7974 *
7975 * @param aMachine Session machine object.
7976 * @param aControl Direct session control object (optional).
7977 * @param aRequireVM If true then only allow VM sessions.
7978 * @param aAllowClosing If true then additionally a session which is currently
7979 * being closed will also be allowed.
7980 *
7981 * @note locks this object for reading.
7982 */
7983bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7984 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7985 bool aRequireVM /*= false*/,
7986 bool aAllowClosing /*= false*/)
7987{
7988 AutoLimitedCaller autoCaller(this);
7989 AssertComRCReturn(autoCaller.rc(), false);
7990
7991 /* just return false for inaccessible machines */
7992 if (getObjectState().getState() != ObjectState::Ready)
7993 return false;
7994
7995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7996
7997 if ( ( mData->mSession.mState == SessionState_Locked
7998 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7999 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8000 )
8001 {
8002 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8003
8004 aMachine = mData->mSession.mMachine;
8005
8006 if (aControl != NULL)
8007 *aControl = mData->mSession.mDirectControl;
8008
8009 return true;
8010 }
8011
8012 return false;
8013}
8014
8015/**
8016 * Returns @c true if the given machine has an spawning direct session.
8017 *
8018 * @note locks this object for reading.
8019 */
8020bool Machine::i_isSessionSpawning()
8021{
8022 AutoLimitedCaller autoCaller(this);
8023 AssertComRCReturn(autoCaller.rc(), false);
8024
8025 /* just return false for inaccessible machines */
8026 if (getObjectState().getState() != ObjectState::Ready)
8027 return false;
8028
8029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8030
8031 if (mData->mSession.mState == SessionState_Spawning)
8032 return true;
8033
8034 return false;
8035}
8036
8037/**
8038 * Called from the client watcher thread to check for unexpected client process
8039 * death during Session_Spawning state (e.g. before it successfully opened a
8040 * direct session).
8041 *
8042 * On Win32 and on OS/2, this method is called only when we've got the
8043 * direct client's process termination notification, so it always returns @c
8044 * true.
8045 *
8046 * On other platforms, this method returns @c true if the client process is
8047 * terminated and @c false if it's still alive.
8048 *
8049 * @note Locks this object for writing.
8050 */
8051bool Machine::i_checkForSpawnFailure()
8052{
8053 AutoCaller autoCaller(this);
8054 if (!autoCaller.isOk())
8055 {
8056 /* nothing to do */
8057 LogFlowThisFunc(("Already uninitialized!\n"));
8058 return true;
8059 }
8060
8061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8062
8063 if (mData->mSession.mState != SessionState_Spawning)
8064 {
8065 /* nothing to do */
8066 LogFlowThisFunc(("Not spawning any more!\n"));
8067 return true;
8068 }
8069
8070 HRESULT rc = S_OK;
8071
8072 /* PID not yet initialized, skip check. */
8073 if (mData->mSession.mPID == NIL_RTPROCESS)
8074 return false;
8075
8076 RTPROCSTATUS status;
8077 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8078
8079 if (vrc != VERR_PROCESS_RUNNING)
8080 {
8081 Utf8Str strExtraInfo;
8082
8083#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8084 /* If the startup logfile exists and is of non-zero length, tell the
8085 user to look there for more details to encourage them to attach it
8086 when reporting startup issues. */
8087 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8088 uint64_t cbStartupLogFile = 0;
8089 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8090 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8091 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8092#endif
8093
8094 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8095 rc = setError(E_FAIL,
8096 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8097 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8098 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8099 rc = setError(E_FAIL,
8100 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8101 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8102 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8103 rc = setError(E_FAIL,
8104 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8105 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8106 else
8107 rc = setError(E_FAIL,
8108 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8109 i_getName().c_str(), vrc, strExtraInfo.c_str());
8110 }
8111
8112 if (FAILED(rc))
8113 {
8114 /* Close the remote session, remove the remote control from the list
8115 * and reset session state to Closed (@note keep the code in sync with
8116 * the relevant part in LockMachine()). */
8117
8118 Assert(mData->mSession.mRemoteControls.size() == 1);
8119 if (mData->mSession.mRemoteControls.size() == 1)
8120 {
8121 ErrorInfoKeeper eik;
8122 mData->mSession.mRemoteControls.front()->Uninitialize();
8123 }
8124
8125 mData->mSession.mRemoteControls.clear();
8126 mData->mSession.mState = SessionState_Unlocked;
8127
8128 /* finalize the progress after setting the state */
8129 if (!mData->mSession.mProgress.isNull())
8130 {
8131 mData->mSession.mProgress->notifyComplete(rc);
8132 mData->mSession.mProgress.setNull();
8133 }
8134
8135 mData->mSession.mPID = NIL_RTPROCESS;
8136
8137 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8138 return true;
8139 }
8140
8141 return false;
8142}
8143
8144/**
8145 * Checks whether the machine can be registered. If so, commits and saves
8146 * all settings.
8147 *
8148 * @note Must be called from mParent's write lock. Locks this object and
8149 * children for writing.
8150 */
8151HRESULT Machine::i_prepareRegister()
8152{
8153 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8154
8155 AutoLimitedCaller autoCaller(this);
8156 AssertComRCReturnRC(autoCaller.rc());
8157
8158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8159
8160 /* wait for state dependents to drop to zero */
8161 i_ensureNoStateDependencies();
8162
8163 if (!mData->mAccessible)
8164 return setError(VBOX_E_INVALID_OBJECT_STATE,
8165 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8166 mUserData->s.strName.c_str(),
8167 mData->mUuid.toString().c_str());
8168
8169 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8170
8171 if (mData->mRegistered)
8172 return setError(VBOX_E_INVALID_OBJECT_STATE,
8173 tr("The machine '%s' with UUID {%s} is already registered"),
8174 mUserData->s.strName.c_str(),
8175 mData->mUuid.toString().c_str());
8176
8177 HRESULT rc = S_OK;
8178
8179 // Ensure the settings are saved. If we are going to be registered and
8180 // no config file exists yet, create it by calling i_saveSettings() too.
8181 if ( (mData->flModifications)
8182 || (!mData->pMachineConfigFile->fileExists())
8183 )
8184 {
8185 rc = i_saveSettings(NULL);
8186 // no need to check whether VirtualBox.xml needs saving too since
8187 // we can't have a machine XML file rename pending
8188 if (FAILED(rc)) return rc;
8189 }
8190
8191 /* more config checking goes here */
8192
8193 if (SUCCEEDED(rc))
8194 {
8195 /* we may have had implicit modifications we want to fix on success */
8196 i_commit();
8197
8198 mData->mRegistered = true;
8199 }
8200 else
8201 {
8202 /* we may have had implicit modifications we want to cancel on failure*/
8203 i_rollback(false /* aNotify */);
8204 }
8205
8206 return rc;
8207}
8208
8209/**
8210 * Increases the number of objects dependent on the machine state or on the
8211 * registered state. Guarantees that these two states will not change at least
8212 * until #i_releaseStateDependency() is called.
8213 *
8214 * Depending on the @a aDepType value, additional state checks may be made.
8215 * These checks will set extended error info on failure. See
8216 * #i_checkStateDependency() for more info.
8217 *
8218 * If this method returns a failure, the dependency is not added and the caller
8219 * is not allowed to rely on any particular machine state or registration state
8220 * value and may return the failed result code to the upper level.
8221 *
8222 * @param aDepType Dependency type to add.
8223 * @param aState Current machine state (NULL if not interested).
8224 * @param aRegistered Current registered state (NULL if not interested).
8225 *
8226 * @note Locks this object for writing.
8227 */
8228HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8229 MachineState_T *aState /* = NULL */,
8230 BOOL *aRegistered /* = NULL */)
8231{
8232 AutoCaller autoCaller(this);
8233 AssertComRCReturnRC(autoCaller.rc());
8234
8235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8236
8237 HRESULT rc = i_checkStateDependency(aDepType);
8238 if (FAILED(rc)) return rc;
8239
8240 {
8241 if (mData->mMachineStateChangePending != 0)
8242 {
8243 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8244 * drop to zero so don't add more. It may make sense to wait a bit
8245 * and retry before reporting an error (since the pending state
8246 * transition should be really quick) but let's just assert for
8247 * now to see if it ever happens on practice. */
8248
8249 AssertFailed();
8250
8251 return setError(E_ACCESSDENIED,
8252 tr("Machine state change is in progress. Please retry the operation later."));
8253 }
8254
8255 ++mData->mMachineStateDeps;
8256 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8257 }
8258
8259 if (aState)
8260 *aState = mData->mMachineState;
8261 if (aRegistered)
8262 *aRegistered = mData->mRegistered;
8263
8264 return S_OK;
8265}
8266
8267/**
8268 * Decreases the number of objects dependent on the machine state.
8269 * Must always complete the #i_addStateDependency() call after the state
8270 * dependency is no more necessary.
8271 */
8272void Machine::i_releaseStateDependency()
8273{
8274 AutoCaller autoCaller(this);
8275 AssertComRCReturnVoid(autoCaller.rc());
8276
8277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8278
8279 /* releaseStateDependency() w/o addStateDependency()? */
8280 AssertReturnVoid(mData->mMachineStateDeps != 0);
8281 -- mData->mMachineStateDeps;
8282
8283 if (mData->mMachineStateDeps == 0)
8284 {
8285 /* inform i_ensureNoStateDependencies() that there are no more deps */
8286 if (mData->mMachineStateChangePending != 0)
8287 {
8288 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8289 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8290 }
8291 }
8292}
8293
8294Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8295{
8296 /* start with nothing found */
8297 Utf8Str strResult("");
8298
8299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8300
8301 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8302 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8303 // found:
8304 strResult = it->second; // source is a Utf8Str
8305
8306 return strResult;
8307}
8308
8309// protected methods
8310/////////////////////////////////////////////////////////////////////////////
8311
8312/**
8313 * Performs machine state checks based on the @a aDepType value. If a check
8314 * fails, this method will set extended error info, otherwise it will return
8315 * S_OK. It is supposed, that on failure, the caller will immediately return
8316 * the return value of this method to the upper level.
8317 *
8318 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8319 *
8320 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8321 * current state of this machine object allows to change settings of the
8322 * machine (i.e. the machine is not registered, or registered but not running
8323 * and not saved). It is useful to call this method from Machine setters
8324 * before performing any change.
8325 *
8326 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8327 * as for MutableStateDep except that if the machine is saved, S_OK is also
8328 * returned. This is useful in setters which allow changing machine
8329 * properties when it is in the saved state.
8330 *
8331 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8332 * if the current state of this machine object allows to change runtime
8333 * changeable settings of the machine (i.e. the machine is not registered, or
8334 * registered but either running or not running and not saved). It is useful
8335 * to call this method from Machine setters before performing any changes to
8336 * runtime changeable settings.
8337 *
8338 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8339 * the same as for MutableOrRunningStateDep except that if the machine is
8340 * saved, S_OK is also returned. This is useful in setters which allow
8341 * changing runtime and saved state changeable machine properties.
8342 *
8343 * @param aDepType Dependency type to check.
8344 *
8345 * @note Non Machine based classes should use #i_addStateDependency() and
8346 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8347 * template.
8348 *
8349 * @note This method must be called from under this object's read or write
8350 * lock.
8351 */
8352HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8353{
8354 switch (aDepType)
8355 {
8356 case AnyStateDep:
8357 {
8358 break;
8359 }
8360 case MutableStateDep:
8361 {
8362 if ( mData->mRegistered
8363 && ( !i_isSessionMachine()
8364 || ( mData->mMachineState != MachineState_Aborted
8365 && mData->mMachineState != MachineState_Teleported
8366 && mData->mMachineState != MachineState_PoweredOff
8367 )
8368 )
8369 )
8370 return setError(VBOX_E_INVALID_VM_STATE,
8371 tr("The machine is not mutable (state is %s)"),
8372 Global::stringifyMachineState(mData->mMachineState));
8373 break;
8374 }
8375 case MutableOrSavedStateDep:
8376 {
8377 if ( mData->mRegistered
8378 && ( !i_isSessionMachine()
8379 || ( mData->mMachineState != MachineState_Aborted
8380 && mData->mMachineState != MachineState_Teleported
8381 && mData->mMachineState != MachineState_Saved
8382 && mData->mMachineState != MachineState_PoweredOff
8383 )
8384 )
8385 )
8386 return setError(VBOX_E_INVALID_VM_STATE,
8387 tr("The machine is not mutable or saved (state is %s)"),
8388 Global::stringifyMachineState(mData->mMachineState));
8389 break;
8390 }
8391 case MutableOrRunningStateDep:
8392 {
8393 if ( mData->mRegistered
8394 && ( !i_isSessionMachine()
8395 || ( mData->mMachineState != MachineState_Aborted
8396 && mData->mMachineState != MachineState_Teleported
8397 && mData->mMachineState != MachineState_PoweredOff
8398 && !Global::IsOnline(mData->mMachineState)
8399 )
8400 )
8401 )
8402 return setError(VBOX_E_INVALID_VM_STATE,
8403 tr("The machine is not mutable or running (state is %s)"),
8404 Global::stringifyMachineState(mData->mMachineState));
8405 break;
8406 }
8407 case MutableOrSavedOrRunningStateDep:
8408 {
8409 if ( mData->mRegistered
8410 && ( !i_isSessionMachine()
8411 || ( mData->mMachineState != MachineState_Aborted
8412 && mData->mMachineState != MachineState_Teleported
8413 && mData->mMachineState != MachineState_Saved
8414 && mData->mMachineState != MachineState_PoweredOff
8415 && !Global::IsOnline(mData->mMachineState)
8416 )
8417 )
8418 )
8419 return setError(VBOX_E_INVALID_VM_STATE,
8420 tr("The machine is not mutable, saved or running (state is %s)"),
8421 Global::stringifyMachineState(mData->mMachineState));
8422 break;
8423 }
8424 }
8425
8426 return S_OK;
8427}
8428
8429/**
8430 * Helper to initialize all associated child objects and allocate data
8431 * structures.
8432 *
8433 * This method must be called as a part of the object's initialization procedure
8434 * (usually done in the #init() method).
8435 *
8436 * @note Must be called only from #init() or from #i_registeredInit().
8437 */
8438HRESULT Machine::initDataAndChildObjects()
8439{
8440 AutoCaller autoCaller(this);
8441 AssertComRCReturnRC(autoCaller.rc());
8442 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8443 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8444
8445 AssertReturn(!mData->mAccessible, E_FAIL);
8446
8447 /* allocate data structures */
8448 mSSData.allocate();
8449 mUserData.allocate();
8450 mHWData.allocate();
8451 mMediumAttachments.allocate();
8452 mStorageControllers.allocate();
8453 mUSBControllers.allocate();
8454
8455 /* initialize mOSTypeId */
8456 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8457
8458/** @todo r=bird: init() methods never fails, right? Why don't we make them
8459 * return void then! */
8460
8461 /* create associated BIOS settings object */
8462 unconst(mBIOSSettings).createObject();
8463 mBIOSSettings->init(this);
8464
8465 /* create an associated VRDE object (default is disabled) */
8466 unconst(mVRDEServer).createObject();
8467 mVRDEServer->init(this);
8468
8469 /* create associated serial port objects */
8470 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8471 {
8472 unconst(mSerialPorts[slot]).createObject();
8473 mSerialPorts[slot]->init(this, slot);
8474 }
8475
8476 /* create associated parallel port objects */
8477 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8478 {
8479 unconst(mParallelPorts[slot]).createObject();
8480 mParallelPorts[slot]->init(this, slot);
8481 }
8482
8483 /* create the audio adapter object (always present, default is disabled) */
8484 unconst(mAudioAdapter).createObject();
8485 mAudioAdapter->init(this);
8486
8487 /* create the USB device filters object (always present) */
8488 unconst(mUSBDeviceFilters).createObject();
8489 mUSBDeviceFilters->init(this);
8490
8491 /* create associated network adapter objects */
8492 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8493 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8494 {
8495 unconst(mNetworkAdapters[slot]).createObject();
8496 mNetworkAdapters[slot]->init(this, slot);
8497 }
8498
8499 /* create the bandwidth control */
8500 unconst(mBandwidthControl).createObject();
8501 mBandwidthControl->init(this);
8502
8503 return S_OK;
8504}
8505
8506/**
8507 * Helper to uninitialize all associated child objects and to free all data
8508 * structures.
8509 *
8510 * This method must be called as a part of the object's uninitialization
8511 * procedure (usually done in the #uninit() method).
8512 *
8513 * @note Must be called only from #uninit() or from #i_registeredInit().
8514 */
8515void Machine::uninitDataAndChildObjects()
8516{
8517 AutoCaller autoCaller(this);
8518 AssertComRCReturnVoid(autoCaller.rc());
8519 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8520 || getObjectState().getState() == ObjectState::Limited);
8521
8522 /* tell all our other child objects we've been uninitialized */
8523 if (mBandwidthControl)
8524 {
8525 mBandwidthControl->uninit();
8526 unconst(mBandwidthControl).setNull();
8527 }
8528
8529 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8530 {
8531 if (mNetworkAdapters[slot])
8532 {
8533 mNetworkAdapters[slot]->uninit();
8534 unconst(mNetworkAdapters[slot]).setNull();
8535 }
8536 }
8537
8538 if (mUSBDeviceFilters)
8539 {
8540 mUSBDeviceFilters->uninit();
8541 unconst(mUSBDeviceFilters).setNull();
8542 }
8543
8544 if (mAudioAdapter)
8545 {
8546 mAudioAdapter->uninit();
8547 unconst(mAudioAdapter).setNull();
8548 }
8549
8550 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8551 {
8552 if (mParallelPorts[slot])
8553 {
8554 mParallelPorts[slot]->uninit();
8555 unconst(mParallelPorts[slot]).setNull();
8556 }
8557 }
8558
8559 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8560 {
8561 if (mSerialPorts[slot])
8562 {
8563 mSerialPorts[slot]->uninit();
8564 unconst(mSerialPorts[slot]).setNull();
8565 }
8566 }
8567
8568 if (mVRDEServer)
8569 {
8570 mVRDEServer->uninit();
8571 unconst(mVRDEServer).setNull();
8572 }
8573
8574 if (mBIOSSettings)
8575 {
8576 mBIOSSettings->uninit();
8577 unconst(mBIOSSettings).setNull();
8578 }
8579
8580 /* Deassociate media (only when a real Machine or a SnapshotMachine
8581 * instance is uninitialized; SessionMachine instances refer to real
8582 * Machine media). This is necessary for a clean re-initialization of
8583 * the VM after successfully re-checking the accessibility state. Note
8584 * that in case of normal Machine or SnapshotMachine uninitialization (as
8585 * a result of unregistering or deleting the snapshot), outdated media
8586 * attachments will already be uninitialized and deleted, so this
8587 * code will not affect them. */
8588 if ( !mMediumAttachments.isNull()
8589 && !i_isSessionMachine()
8590 )
8591 {
8592 for (MediumAttachmentList::const_iterator
8593 it = mMediumAttachments->begin();
8594 it != mMediumAttachments->end();
8595 ++it)
8596 {
8597 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8598 if (pMedium.isNull())
8599 continue;
8600 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8601 AssertComRC(rc);
8602 }
8603 }
8604
8605 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8606 {
8607 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8608 if (mData->mFirstSnapshot)
8609 {
8610 // snapshots tree is protected by machine write lock; strictly
8611 // this isn't necessary here since we're deleting the entire
8612 // machine, but otherwise we assert in Snapshot::uninit()
8613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8614 mData->mFirstSnapshot->uninit();
8615 mData->mFirstSnapshot.setNull();
8616 }
8617
8618 mData->mCurrentSnapshot.setNull();
8619 }
8620
8621 /* free data structures (the essential mData structure is not freed here
8622 * since it may be still in use) */
8623 mMediumAttachments.free();
8624 mStorageControllers.free();
8625 mUSBControllers.free();
8626 mHWData.free();
8627 mUserData.free();
8628 mSSData.free();
8629}
8630
8631/**
8632 * Returns a pointer to the Machine object for this machine that acts like a
8633 * parent for complex machine data objects such as shared folders, etc.
8634 *
8635 * For primary Machine objects and for SnapshotMachine objects, returns this
8636 * object's pointer itself. For SessionMachine objects, returns the peer
8637 * (primary) machine pointer.
8638 */
8639Machine *Machine::i_getMachine()
8640{
8641 if (i_isSessionMachine())
8642 return (Machine*)mPeer;
8643 return this;
8644}
8645
8646/**
8647 * Makes sure that there are no machine state dependents. If necessary, waits
8648 * for the number of dependents to drop to zero.
8649 *
8650 * Make sure this method is called from under this object's write lock to
8651 * guarantee that no new dependents may be added when this method returns
8652 * control to the caller.
8653 *
8654 * @note Locks this object for writing. The lock will be released while waiting
8655 * (if necessary).
8656 *
8657 * @warning To be used only in methods that change the machine state!
8658 */
8659void Machine::i_ensureNoStateDependencies()
8660{
8661 AssertReturnVoid(isWriteLockOnCurrentThread());
8662
8663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8664
8665 /* Wait for all state dependents if necessary */
8666 if (mData->mMachineStateDeps != 0)
8667 {
8668 /* lazy semaphore creation */
8669 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8670 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8671
8672 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8673 mData->mMachineStateDeps));
8674
8675 ++mData->mMachineStateChangePending;
8676
8677 /* reset the semaphore before waiting, the last dependent will signal
8678 * it */
8679 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8680
8681 alock.release();
8682
8683 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8684
8685 alock.acquire();
8686
8687 -- mData->mMachineStateChangePending;
8688 }
8689}
8690
8691/**
8692 * Changes the machine state and informs callbacks.
8693 *
8694 * This method is not intended to fail so it either returns S_OK or asserts (and
8695 * returns a failure).
8696 *
8697 * @note Locks this object for writing.
8698 */
8699HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8700{
8701 LogFlowThisFuncEnter();
8702 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8703 Assert(aMachineState != MachineState_Null);
8704
8705 AutoCaller autoCaller(this);
8706 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8707
8708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8709
8710 /* wait for state dependents to drop to zero */
8711 i_ensureNoStateDependencies();
8712
8713 MachineState_T const enmOldState = mData->mMachineState;
8714 if (enmOldState != aMachineState)
8715 {
8716 mData->mMachineState = aMachineState;
8717 RTTimeNow(&mData->mLastStateChange);
8718
8719#ifdef VBOX_WITH_DTRACE_R3_MAIN
8720 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8721#endif
8722 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8723 }
8724
8725 LogFlowThisFuncLeave();
8726 return S_OK;
8727}
8728
8729/**
8730 * Searches for a shared folder with the given logical name
8731 * in the collection of shared folders.
8732 *
8733 * @param aName logical name of the shared folder
8734 * @param aSharedFolder where to return the found object
8735 * @param aSetError whether to set the error info if the folder is
8736 * not found
8737 * @return
8738 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8739 *
8740 * @note
8741 * must be called from under the object's lock!
8742 */
8743HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8744 ComObjPtr<SharedFolder> &aSharedFolder,
8745 bool aSetError /* = false */)
8746{
8747 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8748 for (HWData::SharedFolderList::const_iterator
8749 it = mHWData->mSharedFolders.begin();
8750 it != mHWData->mSharedFolders.end();
8751 ++it)
8752 {
8753 SharedFolder *pSF = *it;
8754 AutoCaller autoCaller(pSF);
8755 if (pSF->i_getName() == aName)
8756 {
8757 aSharedFolder = pSF;
8758 rc = S_OK;
8759 break;
8760 }
8761 }
8762
8763 if (aSetError && FAILED(rc))
8764 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8765
8766 return rc;
8767}
8768
8769/**
8770 * Initializes all machine instance data from the given settings structures
8771 * from XML. The exception is the machine UUID which needs special handling
8772 * depending on the caller's use case, so the caller needs to set that herself.
8773 *
8774 * This gets called in several contexts during machine initialization:
8775 *
8776 * -- When machine XML exists on disk already and needs to be loaded into memory,
8777 * for example, from #i_registeredInit() to load all registered machines on
8778 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8779 * attached to the machine should be part of some media registry already.
8780 *
8781 * -- During OVF import, when a machine config has been constructed from an
8782 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8783 * ensure that the media listed as attachments in the config (which have
8784 * been imported from the OVF) receive the correct registry ID.
8785 *
8786 * -- During VM cloning.
8787 *
8788 * @param config Machine settings from XML.
8789 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8790 * for each attached medium in the config.
8791 * @return
8792 */
8793HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8794 const Guid *puuidRegistry)
8795{
8796 // copy name, description, OS type, teleporter, UTC etc.
8797 mUserData->s = config.machineUserData;
8798
8799 // look up the object by Id to check it is valid
8800 ComObjPtr<GuestOSType> pGuestOSType;
8801 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8802 pGuestOSType);
8803 if (FAILED(rc)) return rc;
8804 mUserData->s.strOsType = pGuestOSType->i_id();
8805
8806 // stateFile (optional)
8807 if (config.strStateFile.isEmpty())
8808 mSSData->strStateFilePath.setNull();
8809 else
8810 {
8811 Utf8Str stateFilePathFull(config.strStateFile);
8812 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8813 if (RT_FAILURE(vrc))
8814 return setError(E_FAIL,
8815 tr("Invalid saved state file path '%s' (%Rrc)"),
8816 config.strStateFile.c_str(),
8817 vrc);
8818 mSSData->strStateFilePath = stateFilePathFull;
8819 }
8820
8821 // snapshot folder needs special processing so set it again
8822 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8823 if (FAILED(rc)) return rc;
8824
8825 /* Copy the extra data items (config may or may not be the same as
8826 * mData->pMachineConfigFile) if necessary. When loading the XML files
8827 * from disk they are the same, but not for OVF import. */
8828 if (mData->pMachineConfigFile != &config)
8829 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8830
8831 /* currentStateModified (optional, default is true) */
8832 mData->mCurrentStateModified = config.fCurrentStateModified;
8833
8834 mData->mLastStateChange = config.timeLastStateChange;
8835
8836 /*
8837 * note: all mUserData members must be assigned prior this point because
8838 * we need to commit changes in order to let mUserData be shared by all
8839 * snapshot machine instances.
8840 */
8841 mUserData.commitCopy();
8842
8843 // machine registry, if present (must be loaded before snapshots)
8844 if (config.canHaveOwnMediaRegistry())
8845 {
8846 // determine machine folder
8847 Utf8Str strMachineFolder = i_getSettingsFileFull();
8848 strMachineFolder.stripFilename();
8849 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8850 config.mediaRegistry,
8851 strMachineFolder);
8852 if (FAILED(rc)) return rc;
8853 }
8854
8855 /* Snapshot node (optional) */
8856 size_t cRootSnapshots;
8857 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8858 {
8859 // there must be only one root snapshot
8860 Assert(cRootSnapshots == 1);
8861
8862 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8863
8864 rc = i_loadSnapshot(snap,
8865 config.uuidCurrentSnapshot,
8866 NULL); // no parent == first snapshot
8867 if (FAILED(rc)) return rc;
8868 }
8869
8870 // hardware data
8871 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8872 if (FAILED(rc)) return rc;
8873
8874 /*
8875 * NOTE: the assignment below must be the last thing to do,
8876 * otherwise it will be not possible to change the settings
8877 * somewhere in the code above because all setters will be
8878 * blocked by i_checkStateDependency(MutableStateDep).
8879 */
8880
8881 /* set the machine state to Aborted or Saved when appropriate */
8882 if (config.fAborted)
8883 {
8884 mSSData->strStateFilePath.setNull();
8885
8886 /* no need to use i_setMachineState() during init() */
8887 mData->mMachineState = MachineState_Aborted;
8888 }
8889 else if (!mSSData->strStateFilePath.isEmpty())
8890 {
8891 /* no need to use i_setMachineState() during init() */
8892 mData->mMachineState = MachineState_Saved;
8893 }
8894
8895 // after loading settings, we are no longer different from the XML on disk
8896 mData->flModifications = 0;
8897
8898 return S_OK;
8899}
8900
8901/**
8902 * Recursively loads all snapshots starting from the given.
8903 *
8904 * @param data snapshot settings.
8905 * @param aCurSnapshotId Current snapshot ID from the settings file.
8906 * @param aParentSnapshot Parent snapshot.
8907 */
8908HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8909 const Guid &aCurSnapshotId,
8910 Snapshot *aParentSnapshot)
8911{
8912 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8913 AssertReturn(!i_isSessionMachine(), E_FAIL);
8914
8915 HRESULT rc = S_OK;
8916
8917 Utf8Str strStateFile;
8918 if (!data.strStateFile.isEmpty())
8919 {
8920 /* optional */
8921 strStateFile = data.strStateFile;
8922 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8923 if (RT_FAILURE(vrc))
8924 return setError(E_FAIL,
8925 tr("Invalid saved state file path '%s' (%Rrc)"),
8926 strStateFile.c_str(),
8927 vrc);
8928 }
8929
8930 /* create a snapshot machine object */
8931 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8932 pSnapshotMachine.createObject();
8933 rc = pSnapshotMachine->initFromSettings(this,
8934 data.hardware,
8935 &data.debugging,
8936 &data.autostart,
8937 data.uuid.ref(),
8938 strStateFile);
8939 if (FAILED(rc)) return rc;
8940
8941 /* create a snapshot object */
8942 ComObjPtr<Snapshot> pSnapshot;
8943 pSnapshot.createObject();
8944 /* initialize the snapshot */
8945 rc = pSnapshot->init(mParent, // VirtualBox object
8946 data.uuid,
8947 data.strName,
8948 data.strDescription,
8949 data.timestamp,
8950 pSnapshotMachine,
8951 aParentSnapshot);
8952 if (FAILED(rc)) return rc;
8953
8954 /* memorize the first snapshot if necessary */
8955 if (!mData->mFirstSnapshot)
8956 mData->mFirstSnapshot = pSnapshot;
8957
8958 /* memorize the current snapshot when appropriate */
8959 if ( !mData->mCurrentSnapshot
8960 && pSnapshot->i_getId() == aCurSnapshotId
8961 )
8962 mData->mCurrentSnapshot = pSnapshot;
8963
8964 // now create the children
8965 for (settings::SnapshotsList::const_iterator
8966 it = data.llChildSnapshots.begin();
8967 it != data.llChildSnapshots.end();
8968 ++it)
8969 {
8970 const settings::Snapshot &childData = *it;
8971 // recurse
8972 rc = i_loadSnapshot(childData,
8973 aCurSnapshotId,
8974 pSnapshot); // parent = the one we created above
8975 if (FAILED(rc)) return rc;
8976 }
8977
8978 return rc;
8979}
8980
8981/**
8982 * Loads settings into mHWData.
8983 *
8984 * @param puuidRegistry Registry ID.
8985 * @param puuidSnapshot Snapshot ID
8986 * @param data Reference to the hardware settings.
8987 * @param pDbg Pointer to the debugging settings.
8988 * @param pAutostart Pointer to the autostart settings.
8989 */
8990HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8991 const Guid *puuidSnapshot,
8992 const settings::Hardware &data,
8993 const settings::Debugging *pDbg,
8994 const settings::Autostart *pAutostart)
8995{
8996 AssertReturn(!i_isSessionMachine(), E_FAIL);
8997
8998 HRESULT rc = S_OK;
8999
9000 try
9001 {
9002 ComObjPtr<GuestOSType> pGuestOSType;
9003 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9004 pGuestOSType);
9005 if (FAILED(rc))
9006 return rc;
9007
9008 /* The hardware version attribute (optional). */
9009 mHWData->mHWVersion = data.strVersion;
9010 mHWData->mHardwareUUID = data.uuid;
9011
9012 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9013 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9014 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9015 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9016 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9017 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9018 mHWData->mPAEEnabled = data.fPAE;
9019 mHWData->mLongMode = data.enmLongMode;
9020 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9021 mHWData->mAPIC = data.fAPIC;
9022 mHWData->mX2APIC = data.fX2APIC;
9023 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9024 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9025 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9026 mHWData->mCPUCount = data.cCPUs;
9027 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9028 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9029 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9030 mHWData->mCpuProfile = data.strCpuProfile;
9031
9032 // cpu
9033 if (mHWData->mCPUHotPlugEnabled)
9034 {
9035 for (settings::CpuList::const_iterator
9036 it = data.llCpus.begin();
9037 it != data.llCpus.end();
9038 ++it)
9039 {
9040 const settings::Cpu &cpu = *it;
9041
9042 mHWData->mCPUAttached[cpu.ulId] = true;
9043 }
9044 }
9045
9046 // cpuid leafs
9047 for (settings::CpuIdLeafsList::const_iterator
9048 it = data.llCpuIdLeafs.begin();
9049 it != data.llCpuIdLeafs.end();
9050 ++it)
9051 {
9052 const settings::CpuIdLeaf &rLeaf= *it;
9053 if ( rLeaf.idx < UINT32_C(0x20)
9054 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9055 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9056 mHWData->mCpuIdLeafList.push_back(rLeaf);
9057 /* else: just ignore */
9058 }
9059
9060 mHWData->mMemorySize = data.ulMemorySizeMB;
9061 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9062
9063 // boot order
9064 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9065 {
9066 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9067 if (it == data.mapBootOrder.end())
9068 mHWData->mBootOrder[i] = DeviceType_Null;
9069 else
9070 mHWData->mBootOrder[i] = it->second;
9071 }
9072
9073 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9074 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9075 mHWData->mMonitorCount = data.cMonitors;
9076 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9077 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9078 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9079 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9080 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9081 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9082 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9083 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9084 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9085 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9086 if (!data.strVideoCaptureFile.isEmpty())
9087 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9088 else
9089 mHWData->mVideoCaptureFile.setNull();
9090 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9091 mHWData->mFirmwareType = data.firmwareType;
9092 mHWData->mPointingHIDType = data.pointingHIDType;
9093 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9094 mHWData->mChipsetType = data.chipsetType;
9095 mHWData->mParavirtProvider = data.paravirtProvider;
9096 mHWData->mParavirtDebug = data.strParavirtDebug;
9097 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9098 mHWData->mHPETEnabled = data.fHPETEnabled;
9099
9100 /* VRDEServer */
9101 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9102 if (FAILED(rc)) return rc;
9103
9104 /* BIOS */
9105 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9106 if (FAILED(rc)) return rc;
9107
9108 // Bandwidth control (must come before network adapters)
9109 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9110 if (FAILED(rc)) return rc;
9111
9112 /* Shared folders */
9113 for (settings::USBControllerList::const_iterator
9114 it = data.usbSettings.llUSBControllers.begin();
9115 it != data.usbSettings.llUSBControllers.end();
9116 ++it)
9117 {
9118 const settings::USBController &settingsCtrl = *it;
9119 ComObjPtr<USBController> newCtrl;
9120
9121 newCtrl.createObject();
9122 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9123 mUSBControllers->push_back(newCtrl);
9124 }
9125
9126 /* USB device filters */
9127 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9128 if (FAILED(rc)) return rc;
9129
9130 // network adapters (establish array size first and apply defaults, to
9131 // ensure reading the same settings as we saved, since the list skips
9132 // adapters having defaults)
9133 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9134 size_t oldCount = mNetworkAdapters.size();
9135 if (newCount > oldCount)
9136 {
9137 mNetworkAdapters.resize(newCount);
9138 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9139 {
9140 unconst(mNetworkAdapters[slot]).createObject();
9141 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9142 }
9143 }
9144 else if (newCount < oldCount)
9145 mNetworkAdapters.resize(newCount);
9146 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9147 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9148 for (settings::NetworkAdaptersList::const_iterator
9149 it = data.llNetworkAdapters.begin();
9150 it != data.llNetworkAdapters.end();
9151 ++it)
9152 {
9153 const settings::NetworkAdapter &nic = *it;
9154
9155 /* slot uniqueness is guaranteed by XML Schema */
9156 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9157 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9158 if (FAILED(rc)) return rc;
9159 }
9160
9161 // serial ports (establish defaults first, to ensure reading the same
9162 // settings as we saved, since the list skips ports having defaults)
9163 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9164 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9165 for (settings::SerialPortsList::const_iterator
9166 it = data.llSerialPorts.begin();
9167 it != data.llSerialPorts.end();
9168 ++it)
9169 {
9170 const settings::SerialPort &s = *it;
9171
9172 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9173 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9174 if (FAILED(rc)) return rc;
9175 }
9176
9177 // parallel ports (establish defaults first, to ensure reading the same
9178 // settings as we saved, since the list skips ports having defaults)
9179 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9180 mParallelPorts[i]->i_applyDefaults();
9181 for (settings::ParallelPortsList::const_iterator
9182 it = data.llParallelPorts.begin();
9183 it != data.llParallelPorts.end();
9184 ++it)
9185 {
9186 const settings::ParallelPort &p = *it;
9187
9188 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9189 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9190 if (FAILED(rc)) return rc;
9191 }
9192
9193 /* AudioAdapter */
9194 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9195 if (FAILED(rc)) return rc;
9196
9197 /* storage controllers */
9198 rc = i_loadStorageControllers(data.storage,
9199 puuidRegistry,
9200 puuidSnapshot);
9201 if (FAILED(rc)) return rc;
9202
9203 /* Shared folders */
9204 for (settings::SharedFoldersList::const_iterator
9205 it = data.llSharedFolders.begin();
9206 it != data.llSharedFolders.end();
9207 ++it)
9208 {
9209 const settings::SharedFolder &sf = *it;
9210
9211 ComObjPtr<SharedFolder> sharedFolder;
9212 /* Check for double entries. Not allowed! */
9213 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9214 if (SUCCEEDED(rc))
9215 return setError(VBOX_E_OBJECT_IN_USE,
9216 tr("Shared folder named '%s' already exists"),
9217 sf.strName.c_str());
9218
9219 /* Create the new shared folder. Don't break on error. This will be
9220 * reported when the machine starts. */
9221 sharedFolder.createObject();
9222 rc = sharedFolder->init(i_getMachine(),
9223 sf.strName,
9224 sf.strHostPath,
9225 RT_BOOL(sf.fWritable),
9226 RT_BOOL(sf.fAutoMount),
9227 false /* fFailOnError */);
9228 if (FAILED(rc)) return rc;
9229 mHWData->mSharedFolders.push_back(sharedFolder);
9230 }
9231
9232 // Clipboard
9233 mHWData->mClipboardMode = data.clipboardMode;
9234
9235 // drag'n'drop
9236 mHWData->mDnDMode = data.dndMode;
9237
9238 // guest settings
9239 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9240
9241 // IO settings
9242 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9243 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9244
9245 // Host PCI devices
9246 for (settings::HostPCIDeviceAttachmentList::const_iterator
9247 it = data.pciAttachments.begin();
9248 it != data.pciAttachments.end();
9249 ++it)
9250 {
9251 const settings::HostPCIDeviceAttachment &hpda = *it;
9252 ComObjPtr<PCIDeviceAttachment> pda;
9253
9254 pda.createObject();
9255 pda->i_loadSettings(this, hpda);
9256 mHWData->mPCIDeviceAssignments.push_back(pda);
9257 }
9258
9259 /*
9260 * (The following isn't really real hardware, but it lives in HWData
9261 * for reasons of convenience.)
9262 */
9263
9264#ifdef VBOX_WITH_GUEST_PROPS
9265 /* Guest properties (optional) */
9266
9267 /* Only load transient guest properties for configs which have saved
9268 * state, because there shouldn't be any for powered off VMs. The same
9269 * logic applies for snapshots, as offline snapshots shouldn't have
9270 * any such properties. They confuse the code in various places.
9271 * Note: can't rely on the machine state, as it isn't set yet. */
9272 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9273 /* apologies for the hacky unconst() usage, but this needs hacking
9274 * actually inconsistent settings into consistency, otherwise there
9275 * will be some corner cases where the inconsistency survives
9276 * surprisingly long without getting fixed, especially for snapshots
9277 * as there are no config changes. */
9278 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9279 for (settings::GuestPropertiesList::iterator
9280 it = llGuestProperties.begin();
9281 it != llGuestProperties.end();
9282 /*nothing*/)
9283 {
9284 const settings::GuestProperty &prop = *it;
9285 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9286 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9287 if ( fSkipTransientGuestProperties
9288 && ( fFlags & GUEST_PROP_F_TRANSIENT
9289 || fFlags & GUEST_PROP_F_TRANSRESET))
9290 {
9291 it = llGuestProperties.erase(it);
9292 continue;
9293 }
9294 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9295 mHWData->mGuestProperties[prop.strName] = property;
9296 ++it;
9297 }
9298#endif /* VBOX_WITH_GUEST_PROPS defined */
9299
9300 rc = i_loadDebugging(pDbg);
9301 if (FAILED(rc))
9302 return rc;
9303
9304 mHWData->mAutostart = *pAutostart;
9305
9306 /* default frontend */
9307 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9308 }
9309 catch (std::bad_alloc &)
9310 {
9311 return E_OUTOFMEMORY;
9312 }
9313
9314 AssertComRC(rc);
9315 return rc;
9316}
9317
9318/**
9319 * Called from i_loadHardware() to load the debugging settings of the
9320 * machine.
9321 *
9322 * @param pDbg Pointer to the settings.
9323 */
9324HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9325{
9326 mHWData->mDebugging = *pDbg;
9327 /* no more processing currently required, this will probably change. */
9328 return S_OK;
9329}
9330
9331/**
9332 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9333 *
9334 * @param data storage settings.
9335 * @param puuidRegistry media registry ID to set media to or NULL;
9336 * see Machine::i_loadMachineDataFromSettings()
9337 * @param puuidSnapshot snapshot ID
9338 * @return
9339 */
9340HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9341 const Guid *puuidRegistry,
9342 const Guid *puuidSnapshot)
9343{
9344 AssertReturn(!i_isSessionMachine(), E_FAIL);
9345
9346 HRESULT rc = S_OK;
9347
9348 for (settings::StorageControllersList::const_iterator
9349 it = data.llStorageControllers.begin();
9350 it != data.llStorageControllers.end();
9351 ++it)
9352 {
9353 const settings::StorageController &ctlData = *it;
9354
9355 ComObjPtr<StorageController> pCtl;
9356 /* Try to find one with the name first. */
9357 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9358 if (SUCCEEDED(rc))
9359 return setError(VBOX_E_OBJECT_IN_USE,
9360 tr("Storage controller named '%s' already exists"),
9361 ctlData.strName.c_str());
9362
9363 pCtl.createObject();
9364 rc = pCtl->init(this,
9365 ctlData.strName,
9366 ctlData.storageBus,
9367 ctlData.ulInstance,
9368 ctlData.fBootable);
9369 if (FAILED(rc)) return rc;
9370
9371 mStorageControllers->push_back(pCtl);
9372
9373 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9374 if (FAILED(rc)) return rc;
9375
9376 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9377 if (FAILED(rc)) return rc;
9378
9379 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9380 if (FAILED(rc)) return rc;
9381
9382 /* Load the attached devices now. */
9383 rc = i_loadStorageDevices(pCtl,
9384 ctlData,
9385 puuidRegistry,
9386 puuidSnapshot);
9387 if (FAILED(rc)) return rc;
9388 }
9389
9390 return S_OK;
9391}
9392
9393/**
9394 * Called from i_loadStorageControllers for a controller's devices.
9395 *
9396 * @param aStorageController
9397 * @param data
9398 * @param puuidRegistry media registry ID to set media to or NULL; see
9399 * Machine::i_loadMachineDataFromSettings()
9400 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9401 * @return
9402 */
9403HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9404 const settings::StorageController &data,
9405 const Guid *puuidRegistry,
9406 const Guid *puuidSnapshot)
9407{
9408 HRESULT rc = S_OK;
9409
9410 /* paranoia: detect duplicate attachments */
9411 for (settings::AttachedDevicesList::const_iterator
9412 it = data.llAttachedDevices.begin();
9413 it != data.llAttachedDevices.end();
9414 ++it)
9415 {
9416 const settings::AttachedDevice &ad = *it;
9417
9418 for (settings::AttachedDevicesList::const_iterator it2 = it;
9419 it2 != data.llAttachedDevices.end();
9420 ++it2)
9421 {
9422 if (it == it2)
9423 continue;
9424
9425 const settings::AttachedDevice &ad2 = *it2;
9426
9427 if ( ad.lPort == ad2.lPort
9428 && ad.lDevice == ad2.lDevice)
9429 {
9430 return setError(E_FAIL,
9431 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9432 aStorageController->i_getName().c_str(),
9433 ad.lPort,
9434 ad.lDevice,
9435 mUserData->s.strName.c_str());
9436 }
9437 }
9438 }
9439
9440 for (settings::AttachedDevicesList::const_iterator
9441 it = data.llAttachedDevices.begin();
9442 it != data.llAttachedDevices.end();
9443 ++it)
9444 {
9445 const settings::AttachedDevice &dev = *it;
9446 ComObjPtr<Medium> medium;
9447
9448 switch (dev.deviceType)
9449 {
9450 case DeviceType_Floppy:
9451 case DeviceType_DVD:
9452 if (dev.strHostDriveSrc.isNotEmpty())
9453 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9454 false /* fRefresh */, medium);
9455 else
9456 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9457 dev.uuid,
9458 false /* fRefresh */,
9459 false /* aSetError */,
9460 medium);
9461 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9462 // This is not an error. The host drive or UUID might have vanished, so just go
9463 // ahead without this removeable medium attachment
9464 rc = S_OK;
9465 break;
9466
9467 case DeviceType_HardDisk:
9468 {
9469 /* find a hard disk by UUID */
9470 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9471 if (FAILED(rc))
9472 {
9473 if (i_isSnapshotMachine())
9474 {
9475 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9476 // so the user knows that the bad disk is in a snapshot somewhere
9477 com::ErrorInfo info;
9478 return setError(E_FAIL,
9479 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9480 puuidSnapshot->raw(),
9481 info.getText().raw());
9482 }
9483 else
9484 return rc;
9485 }
9486
9487 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9488
9489 if (medium->i_getType() == MediumType_Immutable)
9490 {
9491 if (i_isSnapshotMachine())
9492 return setError(E_FAIL,
9493 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9494 "of the virtual machine '%s' ('%s')"),
9495 medium->i_getLocationFull().c_str(),
9496 dev.uuid.raw(),
9497 puuidSnapshot->raw(),
9498 mUserData->s.strName.c_str(),
9499 mData->m_strConfigFileFull.c_str());
9500
9501 return setError(E_FAIL,
9502 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9503 medium->i_getLocationFull().c_str(),
9504 dev.uuid.raw(),
9505 mUserData->s.strName.c_str(),
9506 mData->m_strConfigFileFull.c_str());
9507 }
9508
9509 if (medium->i_getType() == MediumType_MultiAttach)
9510 {
9511 if (i_isSnapshotMachine())
9512 return setError(E_FAIL,
9513 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9514 "of the virtual machine '%s' ('%s')"),
9515 medium->i_getLocationFull().c_str(),
9516 dev.uuid.raw(),
9517 puuidSnapshot->raw(),
9518 mUserData->s.strName.c_str(),
9519 mData->m_strConfigFileFull.c_str());
9520
9521 return setError(E_FAIL,
9522 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9523 medium->i_getLocationFull().c_str(),
9524 dev.uuid.raw(),
9525 mUserData->s.strName.c_str(),
9526 mData->m_strConfigFileFull.c_str());
9527 }
9528
9529 if ( !i_isSnapshotMachine()
9530 && medium->i_getChildren().size() != 0
9531 )
9532 return setError(E_FAIL,
9533 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9534 "because it has %d differencing child hard disks"),
9535 medium->i_getLocationFull().c_str(),
9536 dev.uuid.raw(),
9537 mUserData->s.strName.c_str(),
9538 mData->m_strConfigFileFull.c_str(),
9539 medium->i_getChildren().size());
9540
9541 if (i_findAttachment(*mMediumAttachments.data(),
9542 medium))
9543 return setError(E_FAIL,
9544 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9545 medium->i_getLocationFull().c_str(),
9546 dev.uuid.raw(),
9547 mUserData->s.strName.c_str(),
9548 mData->m_strConfigFileFull.c_str());
9549
9550 break;
9551 }
9552
9553 default:
9554 return setError(E_FAIL,
9555 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9556 medium->i_getLocationFull().c_str(),
9557 mUserData->s.strName.c_str(),
9558 mData->m_strConfigFileFull.c_str());
9559 }
9560
9561 if (FAILED(rc))
9562 break;
9563
9564 /* Bandwidth groups are loaded at this point. */
9565 ComObjPtr<BandwidthGroup> pBwGroup;
9566
9567 if (!dev.strBwGroup.isEmpty())
9568 {
9569 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9570 if (FAILED(rc))
9571 return setError(E_FAIL,
9572 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9573 medium->i_getLocationFull().c_str(),
9574 dev.strBwGroup.c_str(),
9575 mUserData->s.strName.c_str(),
9576 mData->m_strConfigFileFull.c_str());
9577 pBwGroup->i_reference();
9578 }
9579
9580 const Utf8Str controllerName = aStorageController->i_getName();
9581 ComObjPtr<MediumAttachment> pAttachment;
9582 pAttachment.createObject();
9583 rc = pAttachment->init(this,
9584 medium,
9585 controllerName,
9586 dev.lPort,
9587 dev.lDevice,
9588 dev.deviceType,
9589 false,
9590 dev.fPassThrough,
9591 dev.fTempEject,
9592 dev.fNonRotational,
9593 dev.fDiscard,
9594 dev.fHotPluggable,
9595 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9596 if (FAILED(rc)) break;
9597
9598 /* associate the medium with this machine and snapshot */
9599 if (!medium.isNull())
9600 {
9601 AutoCaller medCaller(medium);
9602 if (FAILED(medCaller.rc())) return medCaller.rc();
9603 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9604
9605 if (i_isSnapshotMachine())
9606 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9607 else
9608 rc = medium->i_addBackReference(mData->mUuid);
9609 /* If the medium->addBackReference fails it sets an appropriate
9610 * error message, so no need to do any guesswork here. */
9611
9612 if (puuidRegistry)
9613 // caller wants registry ID to be set on all attached media (OVF import case)
9614 medium->i_addRegistry(*puuidRegistry);
9615 }
9616
9617 if (FAILED(rc))
9618 break;
9619
9620 /* back up mMediumAttachments to let registeredInit() properly rollback
9621 * on failure (= limited accessibility) */
9622 i_setModified(IsModified_Storage);
9623 mMediumAttachments.backup();
9624 mMediumAttachments->push_back(pAttachment);
9625 }
9626
9627 return rc;
9628}
9629
9630/**
9631 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9632 *
9633 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9634 * @param aSnapshot where to return the found snapshot
9635 * @param aSetError true to set extended error info on failure
9636 */
9637HRESULT Machine::i_findSnapshotById(const Guid &aId,
9638 ComObjPtr<Snapshot> &aSnapshot,
9639 bool aSetError /* = false */)
9640{
9641 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9642
9643 if (!mData->mFirstSnapshot)
9644 {
9645 if (aSetError)
9646 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9647 return E_FAIL;
9648 }
9649
9650 if (aId.isZero())
9651 aSnapshot = mData->mFirstSnapshot;
9652 else
9653 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9654
9655 if (!aSnapshot)
9656 {
9657 if (aSetError)
9658 return setError(E_FAIL,
9659 tr("Could not find a snapshot with UUID {%s}"),
9660 aId.toString().c_str());
9661 return E_FAIL;
9662 }
9663
9664 return S_OK;
9665}
9666
9667/**
9668 * Returns the snapshot with the given name or fails of no such snapshot.
9669 *
9670 * @param strName snapshot name to find
9671 * @param aSnapshot where to return the found snapshot
9672 * @param aSetError true to set extended error info on failure
9673 */
9674HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9675 ComObjPtr<Snapshot> &aSnapshot,
9676 bool aSetError /* = false */)
9677{
9678 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9679
9680 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9681
9682 if (!mData->mFirstSnapshot)
9683 {
9684 if (aSetError)
9685 return setError(VBOX_E_OBJECT_NOT_FOUND,
9686 tr("This machine does not have any snapshots"));
9687 return VBOX_E_OBJECT_NOT_FOUND;
9688 }
9689
9690 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9691
9692 if (!aSnapshot)
9693 {
9694 if (aSetError)
9695 return setError(VBOX_E_OBJECT_NOT_FOUND,
9696 tr("Could not find a snapshot named '%s'"), strName.c_str());
9697 return VBOX_E_OBJECT_NOT_FOUND;
9698 }
9699
9700 return S_OK;
9701}
9702
9703/**
9704 * Returns a storage controller object with the given name.
9705 *
9706 * @param aName storage controller name to find
9707 * @param aStorageController where to return the found storage controller
9708 * @param aSetError true to set extended error info on failure
9709 */
9710HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9711 ComObjPtr<StorageController> &aStorageController,
9712 bool aSetError /* = false */)
9713{
9714 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9715
9716 for (StorageControllerList::const_iterator
9717 it = mStorageControllers->begin();
9718 it != mStorageControllers->end();
9719 ++it)
9720 {
9721 if ((*it)->i_getName() == aName)
9722 {
9723 aStorageController = (*it);
9724 return S_OK;
9725 }
9726 }
9727
9728 if (aSetError)
9729 return setError(VBOX_E_OBJECT_NOT_FOUND,
9730 tr("Could not find a storage controller named '%s'"),
9731 aName.c_str());
9732 return VBOX_E_OBJECT_NOT_FOUND;
9733}
9734
9735/**
9736 * Returns a USB controller object with the given name.
9737 *
9738 * @param aName USB controller name to find
9739 * @param aUSBController where to return the found USB controller
9740 * @param aSetError true to set extended error info on failure
9741 */
9742HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9743 ComObjPtr<USBController> &aUSBController,
9744 bool aSetError /* = false */)
9745{
9746 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9747
9748 for (USBControllerList::const_iterator
9749 it = mUSBControllers->begin();
9750 it != mUSBControllers->end();
9751 ++it)
9752 {
9753 if ((*it)->i_getName() == aName)
9754 {
9755 aUSBController = (*it);
9756 return S_OK;
9757 }
9758 }
9759
9760 if (aSetError)
9761 return setError(VBOX_E_OBJECT_NOT_FOUND,
9762 tr("Could not find a storage controller named '%s'"),
9763 aName.c_str());
9764 return VBOX_E_OBJECT_NOT_FOUND;
9765}
9766
9767/**
9768 * Returns the number of USB controller instance of the given type.
9769 *
9770 * @param enmType USB controller type.
9771 */
9772ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9773{
9774 ULONG cCtrls = 0;
9775
9776 for (USBControllerList::const_iterator
9777 it = mUSBControllers->begin();
9778 it != mUSBControllers->end();
9779 ++it)
9780 {
9781 if ((*it)->i_getControllerType() == enmType)
9782 cCtrls++;
9783 }
9784
9785 return cCtrls;
9786}
9787
9788HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9789 MediumAttachmentList &atts)
9790{
9791 AutoCaller autoCaller(this);
9792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9793
9794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9795
9796 for (MediumAttachmentList::const_iterator
9797 it = mMediumAttachments->begin();
9798 it != mMediumAttachments->end();
9799 ++it)
9800 {
9801 const ComObjPtr<MediumAttachment> &pAtt = *it;
9802 // should never happen, but deal with NULL pointers in the list.
9803 AssertContinue(!pAtt.isNull());
9804
9805 // getControllerName() needs caller+read lock
9806 AutoCaller autoAttCaller(pAtt);
9807 if (FAILED(autoAttCaller.rc()))
9808 {
9809 atts.clear();
9810 return autoAttCaller.rc();
9811 }
9812 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9813
9814 if (pAtt->i_getControllerName() == aName)
9815 atts.push_back(pAtt);
9816 }
9817
9818 return S_OK;
9819}
9820
9821
9822/**
9823 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9824 * file if the machine name was changed and about creating a new settings file
9825 * if this is a new machine.
9826 *
9827 * @note Must be never called directly but only from #saveSettings().
9828 */
9829HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9830{
9831 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9832
9833 HRESULT rc = S_OK;
9834
9835 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9836
9837 /// @todo need to handle primary group change, too
9838
9839 /* attempt to rename the settings file if machine name is changed */
9840 if ( mUserData->s.fNameSync
9841 && mUserData.isBackedUp()
9842 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9843 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9844 )
9845 {
9846 bool dirRenamed = false;
9847 bool fileRenamed = false;
9848
9849 Utf8Str configFile, newConfigFile;
9850 Utf8Str configFilePrev, newConfigFilePrev;
9851 Utf8Str configDir, newConfigDir;
9852
9853 do
9854 {
9855 int vrc = VINF_SUCCESS;
9856
9857 Utf8Str name = mUserData.backedUpData()->s.strName;
9858 Utf8Str newName = mUserData->s.strName;
9859 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9860 if (group == "/")
9861 group.setNull();
9862 Utf8Str newGroup = mUserData->s.llGroups.front();
9863 if (newGroup == "/")
9864 newGroup.setNull();
9865
9866 configFile = mData->m_strConfigFileFull;
9867
9868 /* first, rename the directory if it matches the group and machine name */
9869 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9870 group.c_str(), RTPATH_DELIMITER, name.c_str());
9871 /** @todo hack, make somehow use of ComposeMachineFilename */
9872 if (mUserData->s.fDirectoryIncludesUUID)
9873 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9874 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9875 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9876 /** @todo hack, make somehow use of ComposeMachineFilename */
9877 if (mUserData->s.fDirectoryIncludesUUID)
9878 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9879 configDir = configFile;
9880 configDir.stripFilename();
9881 newConfigDir = configDir;
9882 if ( configDir.length() >= groupPlusName.length()
9883 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9884 groupPlusName.c_str()))
9885 {
9886 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9887 Utf8Str newConfigBaseDir(newConfigDir);
9888 newConfigDir.append(newGroupPlusName);
9889 /* consistency: use \ if appropriate on the platform */
9890 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9891 /* new dir and old dir cannot be equal here because of 'if'
9892 * above and because name != newName */
9893 Assert(configDir != newConfigDir);
9894 if (!fSettingsFileIsNew)
9895 {
9896 /* perform real rename only if the machine is not new */
9897 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9898 if ( vrc == VERR_FILE_NOT_FOUND
9899 || vrc == VERR_PATH_NOT_FOUND)
9900 {
9901 /* create the parent directory, then retry renaming */
9902 Utf8Str parent(newConfigDir);
9903 parent.stripFilename();
9904 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9905 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9906 }
9907 if (RT_FAILURE(vrc))
9908 {
9909 rc = setError(E_FAIL,
9910 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9911 configDir.c_str(),
9912 newConfigDir.c_str(),
9913 vrc);
9914 break;
9915 }
9916 /* delete subdirectories which are no longer needed */
9917 Utf8Str dir(configDir);
9918 dir.stripFilename();
9919 while (dir != newConfigBaseDir && dir != ".")
9920 {
9921 vrc = RTDirRemove(dir.c_str());
9922 if (RT_FAILURE(vrc))
9923 break;
9924 dir.stripFilename();
9925 }
9926 dirRenamed = true;
9927 }
9928 }
9929
9930 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9931 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9932
9933 /* then try to rename the settings file itself */
9934 if (newConfigFile != configFile)
9935 {
9936 /* get the path to old settings file in renamed directory */
9937 configFile = Utf8StrFmt("%s%c%s",
9938 newConfigDir.c_str(),
9939 RTPATH_DELIMITER,
9940 RTPathFilename(configFile.c_str()));
9941 if (!fSettingsFileIsNew)
9942 {
9943 /* perform real rename only if the machine is not new */
9944 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9945 if (RT_FAILURE(vrc))
9946 {
9947 rc = setError(E_FAIL,
9948 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9949 configFile.c_str(),
9950 newConfigFile.c_str(),
9951 vrc);
9952 break;
9953 }
9954 fileRenamed = true;
9955 configFilePrev = configFile;
9956 configFilePrev += "-prev";
9957 newConfigFilePrev = newConfigFile;
9958 newConfigFilePrev += "-prev";
9959 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9960 }
9961 }
9962
9963 // update m_strConfigFileFull amd mConfigFile
9964 mData->m_strConfigFileFull = newConfigFile;
9965 // compute the relative path too
9966 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9967
9968 // store the old and new so that VirtualBox::i_saveSettings() can update
9969 // the media registry
9970 if ( mData->mRegistered
9971 && (configDir != newConfigDir || configFile != newConfigFile))
9972 {
9973 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9974
9975 if (pfNeedsGlobalSaveSettings)
9976 *pfNeedsGlobalSaveSettings = true;
9977 }
9978
9979 // in the saved state file path, replace the old directory with the new directory
9980 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9981 {
9982 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9983 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9984 }
9985
9986 // and do the same thing for the saved state file paths of all the online snapshots
9987 if (mData->mFirstSnapshot)
9988 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9989 newConfigDir.c_str());
9990 }
9991 while (0);
9992
9993 if (FAILED(rc))
9994 {
9995 /* silently try to rename everything back */
9996 if (fileRenamed)
9997 {
9998 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9999 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10000 }
10001 if (dirRenamed)
10002 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10003 }
10004
10005 if (FAILED(rc)) return rc;
10006 }
10007
10008 if (fSettingsFileIsNew)
10009 {
10010 /* create a virgin config file */
10011 int vrc = VINF_SUCCESS;
10012
10013 /* ensure the settings directory exists */
10014 Utf8Str path(mData->m_strConfigFileFull);
10015 path.stripFilename();
10016 if (!RTDirExists(path.c_str()))
10017 {
10018 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10019 if (RT_FAILURE(vrc))
10020 {
10021 return setError(E_FAIL,
10022 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10023 path.c_str(),
10024 vrc);
10025 }
10026 }
10027
10028 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10029 path = Utf8Str(mData->m_strConfigFileFull);
10030 RTFILE f = NIL_RTFILE;
10031 vrc = RTFileOpen(&f, path.c_str(),
10032 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10033 if (RT_FAILURE(vrc))
10034 return setError(E_FAIL,
10035 tr("Could not create the settings file '%s' (%Rrc)"),
10036 path.c_str(),
10037 vrc);
10038 RTFileClose(f);
10039 }
10040
10041 return rc;
10042}
10043
10044/**
10045 * Saves and commits machine data, user data and hardware data.
10046 *
10047 * Note that on failure, the data remains uncommitted.
10048 *
10049 * @a aFlags may combine the following flags:
10050 *
10051 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10052 * Used when saving settings after an operation that makes them 100%
10053 * correspond to the settings from the current snapshot.
10054 * - SaveS_Force: settings will be saved without doing a deep compare of the
10055 * settings structures. This is used when this is called because snapshots
10056 * have changed to avoid the overhead of the deep compare.
10057 *
10058 * @note Must be called from under this object's write lock. Locks children for
10059 * writing.
10060 *
10061 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10062 * initialized to false and that will be set to true by this function if
10063 * the caller must invoke VirtualBox::i_saveSettings() because the global
10064 * settings have changed. This will happen if a machine rename has been
10065 * saved and the global machine and media registries will therefore need
10066 * updating.
10067 * @param aFlags Flags.
10068 */
10069HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10070 int aFlags /*= 0*/)
10071{
10072 LogFlowThisFuncEnter();
10073
10074 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10075
10076 /* make sure child objects are unable to modify the settings while we are
10077 * saving them */
10078 i_ensureNoStateDependencies();
10079
10080 AssertReturn(!i_isSnapshotMachine(),
10081 E_FAIL);
10082
10083 HRESULT rc = S_OK;
10084 bool fNeedsWrite = false;
10085
10086 /* First, prepare to save settings. It will care about renaming the
10087 * settings directory and file if the machine name was changed and about
10088 * creating a new settings file if this is a new machine. */
10089 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10090 if (FAILED(rc)) return rc;
10091
10092 // keep a pointer to the current settings structures
10093 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10094 settings::MachineConfigFile *pNewConfig = NULL;
10095
10096 try
10097 {
10098 // make a fresh one to have everyone write stuff into
10099 pNewConfig = new settings::MachineConfigFile(NULL);
10100 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10101
10102 // now go and copy all the settings data from COM to the settings structures
10103 // (this calls i_saveSettings() on all the COM objects in the machine)
10104 i_copyMachineDataToSettings(*pNewConfig);
10105
10106 if (aFlags & SaveS_ResetCurStateModified)
10107 {
10108 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10109 mData->mCurrentStateModified = FALSE;
10110 fNeedsWrite = true; // always, no need to compare
10111 }
10112 else if (aFlags & SaveS_Force)
10113 {
10114 fNeedsWrite = true; // always, no need to compare
10115 }
10116 else
10117 {
10118 if (!mData->mCurrentStateModified)
10119 {
10120 // do a deep compare of the settings that we just saved with the settings
10121 // previously stored in the config file; this invokes MachineConfigFile::operator==
10122 // which does a deep compare of all the settings, which is expensive but less expensive
10123 // than writing out XML in vain
10124 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10125
10126 // could still be modified if any settings changed
10127 mData->mCurrentStateModified = fAnySettingsChanged;
10128
10129 fNeedsWrite = fAnySettingsChanged;
10130 }
10131 else
10132 fNeedsWrite = true;
10133 }
10134
10135 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10136
10137 if (fNeedsWrite)
10138 // now spit it all out!
10139 pNewConfig->write(mData->m_strConfigFileFull);
10140
10141 mData->pMachineConfigFile = pNewConfig;
10142 delete pOldConfig;
10143 i_commit();
10144
10145 // after saving settings, we are no longer different from the XML on disk
10146 mData->flModifications = 0;
10147 }
10148 catch (HRESULT err)
10149 {
10150 // we assume that error info is set by the thrower
10151 rc = err;
10152
10153 // restore old config
10154 delete pNewConfig;
10155 mData->pMachineConfigFile = pOldConfig;
10156 }
10157 catch (...)
10158 {
10159 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10160 }
10161
10162 if (fNeedsWrite)
10163 {
10164 /* Fire the data change event, even on failure (since we've already
10165 * committed all data). This is done only for SessionMachines because
10166 * mutable Machine instances are always not registered (i.e. private
10167 * to the client process that creates them) and thus don't need to
10168 * inform callbacks. */
10169 if (i_isSessionMachine())
10170 mParent->i_onMachineDataChange(mData->mUuid);
10171 }
10172
10173 LogFlowThisFunc(("rc=%08X\n", rc));
10174 LogFlowThisFuncLeave();
10175 return rc;
10176}
10177
10178/**
10179 * Implementation for saving the machine settings into the given
10180 * settings::MachineConfigFile instance. This copies machine extradata
10181 * from the previous machine config file in the instance data, if any.
10182 *
10183 * This gets called from two locations:
10184 *
10185 * -- Machine::i_saveSettings(), during the regular XML writing;
10186 *
10187 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10188 * exported to OVF and we write the VirtualBox proprietary XML
10189 * into a <vbox:Machine> tag.
10190 *
10191 * This routine fills all the fields in there, including snapshots, *except*
10192 * for the following:
10193 *
10194 * -- fCurrentStateModified. There is some special logic associated with that.
10195 *
10196 * The caller can then call MachineConfigFile::write() or do something else
10197 * with it.
10198 *
10199 * Caller must hold the machine lock!
10200 *
10201 * This throws XML errors and HRESULT, so the caller must have a catch block!
10202 */
10203void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10204{
10205 // deep copy extradata, being extra careful with self assignment (the STL
10206 // map assignment on Mac OS X clang based Xcode isn't checking)
10207 if (&config != mData->pMachineConfigFile)
10208 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10209
10210 config.uuid = mData->mUuid;
10211
10212 // copy name, description, OS type, teleport, UTC etc.
10213 config.machineUserData = mUserData->s;
10214
10215 if ( mData->mMachineState == MachineState_Saved
10216 || mData->mMachineState == MachineState_Restoring
10217 // when doing certain snapshot operations we may or may not have
10218 // a saved state in the current state, so keep everything as is
10219 || ( ( mData->mMachineState == MachineState_Snapshotting
10220 || mData->mMachineState == MachineState_DeletingSnapshot
10221 || mData->mMachineState == MachineState_RestoringSnapshot)
10222 && (!mSSData->strStateFilePath.isEmpty())
10223 )
10224 )
10225 {
10226 Assert(!mSSData->strStateFilePath.isEmpty());
10227 /* try to make the file name relative to the settings file dir */
10228 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10229 }
10230 else
10231 {
10232 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10233 config.strStateFile.setNull();
10234 }
10235
10236 if (mData->mCurrentSnapshot)
10237 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10238 else
10239 config.uuidCurrentSnapshot.clear();
10240
10241 config.timeLastStateChange = mData->mLastStateChange;
10242 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10243 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10244
10245 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10246 if (FAILED(rc)) throw rc;
10247
10248 // save machine's media registry if this is VirtualBox 4.0 or later
10249 if (config.canHaveOwnMediaRegistry())
10250 {
10251 // determine machine folder
10252 Utf8Str strMachineFolder = i_getSettingsFileFull();
10253 strMachineFolder.stripFilename();
10254 mParent->i_saveMediaRegistry(config.mediaRegistry,
10255 i_getId(), // only media with registry ID == machine UUID
10256 strMachineFolder);
10257 // this throws HRESULT
10258 }
10259
10260 // save snapshots
10261 rc = i_saveAllSnapshots(config);
10262 if (FAILED(rc)) throw rc;
10263}
10264
10265/**
10266 * Saves all snapshots of the machine into the given machine config file. Called
10267 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10268 * @param config
10269 * @return
10270 */
10271HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10272{
10273 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10274
10275 HRESULT rc = S_OK;
10276
10277 try
10278 {
10279 config.llFirstSnapshot.clear();
10280
10281 if (mData->mFirstSnapshot)
10282 {
10283 // the settings use a list for "the first snapshot"
10284 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10285
10286 // get reference to the snapshot on the list and work on that
10287 // element straight in the list to avoid excessive copying later
10288 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10289 if (FAILED(rc)) throw rc;
10290 }
10291
10292// if (mType == IsSessionMachine)
10293// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10294
10295 }
10296 catch (HRESULT err)
10297 {
10298 /* we assume that error info is set by the thrower */
10299 rc = err;
10300 }
10301 catch (...)
10302 {
10303 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10304 }
10305
10306 return rc;
10307}
10308
10309/**
10310 * Saves the VM hardware configuration. It is assumed that the
10311 * given node is empty.
10312 *
10313 * @param data Reference to the settings object for the hardware config.
10314 * @param pDbg Pointer to the settings object for the debugging config
10315 * which happens to live in mHWData.
10316 * @param pAutostart Pointer to the settings object for the autostart config
10317 * which happens to live in mHWData.
10318 */
10319HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10320 settings::Autostart *pAutostart)
10321{
10322 HRESULT rc = S_OK;
10323
10324 try
10325 {
10326 /* The hardware version attribute (optional).
10327 Automatically upgrade from 1 to current default hardware version
10328 when there is no saved state. (ugly!) */
10329 if ( mHWData->mHWVersion == "1"
10330 && mSSData->strStateFilePath.isEmpty()
10331 )
10332 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10333
10334 data.strVersion = mHWData->mHWVersion;
10335 data.uuid = mHWData->mHardwareUUID;
10336
10337 // CPU
10338 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10339 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10340 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10341 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10342 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10343 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10344 data.fPAE = !!mHWData->mPAEEnabled;
10345 data.enmLongMode = mHWData->mLongMode;
10346 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10347 data.fAPIC = !!mHWData->mAPIC;
10348 data.fX2APIC = !!mHWData->mX2APIC;
10349 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10350 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10351 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10352 data.cCPUs = mHWData->mCPUCount;
10353 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10354 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10355 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10356 data.strCpuProfile = mHWData->mCpuProfile;
10357
10358 data.llCpus.clear();
10359 if (data.fCpuHotPlug)
10360 {
10361 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10362 {
10363 if (mHWData->mCPUAttached[idx])
10364 {
10365 settings::Cpu cpu;
10366 cpu.ulId = idx;
10367 data.llCpus.push_back(cpu);
10368 }
10369 }
10370 }
10371
10372 /* Standard and Extended CPUID leafs. */
10373 data.llCpuIdLeafs.clear();
10374 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10375
10376 // memory
10377 data.ulMemorySizeMB = mHWData->mMemorySize;
10378 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10379
10380 // firmware
10381 data.firmwareType = mHWData->mFirmwareType;
10382
10383 // HID
10384 data.pointingHIDType = mHWData->mPointingHIDType;
10385 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10386
10387 // chipset
10388 data.chipsetType = mHWData->mChipsetType;
10389
10390 // paravirt
10391 data.paravirtProvider = mHWData->mParavirtProvider;
10392 data.strParavirtDebug = mHWData->mParavirtDebug;
10393
10394 // emulated USB card reader
10395 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10396
10397 // HPET
10398 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10399
10400 // boot order
10401 data.mapBootOrder.clear();
10402 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10403 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10404
10405 // display
10406 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10407 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10408 data.cMonitors = mHWData->mMonitorCount;
10409 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10410 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10411 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10412 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10413 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10414 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10415 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10416 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10417 {
10418 if (mHWData->maVideoCaptureScreens[i])
10419 ASMBitSet(&data.u64VideoCaptureScreens, i);
10420 else
10421 ASMBitClear(&data.u64VideoCaptureScreens, i);
10422 }
10423 /* store relative video capture file if possible */
10424 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10425 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10426
10427 /* VRDEServer settings (optional) */
10428 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10429 if (FAILED(rc)) throw rc;
10430
10431 /* BIOS (required) */
10432 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10433 if (FAILED(rc)) throw rc;
10434
10435 /* USB Controller (required) */
10436 data.usbSettings.llUSBControllers.clear();
10437 for (USBControllerList::const_iterator
10438 it = mUSBControllers->begin();
10439 it != mUSBControllers->end();
10440 ++it)
10441 {
10442 ComObjPtr<USBController> ctrl = *it;
10443 settings::USBController settingsCtrl;
10444
10445 settingsCtrl.strName = ctrl->i_getName();
10446 settingsCtrl.enmType = ctrl->i_getControllerType();
10447
10448 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10449 }
10450
10451 /* USB device filters (required) */
10452 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10453 if (FAILED(rc)) throw rc;
10454
10455 /* Network adapters (required) */
10456 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10457 data.llNetworkAdapters.clear();
10458 /* Write out only the nominal number of network adapters for this
10459 * chipset type. Since Machine::commit() hasn't been called there
10460 * may be extra NIC settings in the vector. */
10461 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10462 {
10463 settings::NetworkAdapter nic;
10464 nic.ulSlot = (uint32_t)slot;
10465 /* paranoia check... must not be NULL, but must not crash either. */
10466 if (mNetworkAdapters[slot])
10467 {
10468 if (mNetworkAdapters[slot]->i_hasDefaults())
10469 continue;
10470
10471 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10472 if (FAILED(rc)) throw rc;
10473
10474 data.llNetworkAdapters.push_back(nic);
10475 }
10476 }
10477
10478 /* Serial ports */
10479 data.llSerialPorts.clear();
10480 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10481 {
10482 if (mSerialPorts[slot]->i_hasDefaults())
10483 continue;
10484
10485 settings::SerialPort s;
10486 s.ulSlot = slot;
10487 rc = mSerialPorts[slot]->i_saveSettings(s);
10488 if (FAILED(rc)) return rc;
10489
10490 data.llSerialPorts.push_back(s);
10491 }
10492
10493 /* Parallel ports */
10494 data.llParallelPorts.clear();
10495 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10496 {
10497 if (mParallelPorts[slot]->i_hasDefaults())
10498 continue;
10499
10500 settings::ParallelPort p;
10501 p.ulSlot = slot;
10502 rc = mParallelPorts[slot]->i_saveSettings(p);
10503 if (FAILED(rc)) return rc;
10504
10505 data.llParallelPorts.push_back(p);
10506 }
10507
10508 /* Audio adapter */
10509 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10510 if (FAILED(rc)) return rc;
10511
10512 rc = i_saveStorageControllers(data.storage);
10513 if (FAILED(rc)) return rc;
10514
10515 /* Shared folders */
10516 data.llSharedFolders.clear();
10517 for (HWData::SharedFolderList::const_iterator
10518 it = mHWData->mSharedFolders.begin();
10519 it != mHWData->mSharedFolders.end();
10520 ++it)
10521 {
10522 SharedFolder *pSF = *it;
10523 AutoCaller sfCaller(pSF);
10524 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10525 settings::SharedFolder sf;
10526 sf.strName = pSF->i_getName();
10527 sf.strHostPath = pSF->i_getHostPath();
10528 sf.fWritable = !!pSF->i_isWritable();
10529 sf.fAutoMount = !!pSF->i_isAutoMounted();
10530
10531 data.llSharedFolders.push_back(sf);
10532 }
10533
10534 // clipboard
10535 data.clipboardMode = mHWData->mClipboardMode;
10536
10537 // drag'n'drop
10538 data.dndMode = mHWData->mDnDMode;
10539
10540 /* Guest */
10541 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10542
10543 // IO settings
10544 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10545 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10546
10547 /* BandwidthControl (required) */
10548 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10549 if (FAILED(rc)) throw rc;
10550
10551 /* Host PCI devices */
10552 data.pciAttachments.clear();
10553 for (HWData::PCIDeviceAssignmentList::const_iterator
10554 it = mHWData->mPCIDeviceAssignments.begin();
10555 it != mHWData->mPCIDeviceAssignments.end();
10556 ++it)
10557 {
10558 ComObjPtr<PCIDeviceAttachment> pda = *it;
10559 settings::HostPCIDeviceAttachment hpda;
10560
10561 rc = pda->i_saveSettings(hpda);
10562 if (FAILED(rc)) throw rc;
10563
10564 data.pciAttachments.push_back(hpda);
10565 }
10566
10567 // guest properties
10568 data.llGuestProperties.clear();
10569#ifdef VBOX_WITH_GUEST_PROPS
10570 for (HWData::GuestPropertyMap::const_iterator
10571 it = mHWData->mGuestProperties.begin();
10572 it != mHWData->mGuestProperties.end();
10573 ++it)
10574 {
10575 HWData::GuestProperty property = it->second;
10576
10577 /* Remove transient guest properties at shutdown unless we
10578 * are saving state. Note that restoring snapshot intentionally
10579 * keeps them, they will be removed if appropriate once the final
10580 * machine state is set (as crashes etc. need to work). */
10581 if ( ( mData->mMachineState == MachineState_PoweredOff
10582 || mData->mMachineState == MachineState_Aborted
10583 || mData->mMachineState == MachineState_Teleported)
10584 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10585 continue;
10586 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10587 prop.strName = it->first;
10588 prop.strValue = property.strValue;
10589 prop.timestamp = property.mTimestamp;
10590 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10591 GuestPropWriteFlags(property.mFlags, szFlags);
10592 prop.strFlags = szFlags;
10593
10594 data.llGuestProperties.push_back(prop);
10595 }
10596
10597 /* I presume this doesn't require a backup(). */
10598 mData->mGuestPropertiesModified = FALSE;
10599#endif /* VBOX_WITH_GUEST_PROPS defined */
10600
10601 *pDbg = mHWData->mDebugging;
10602 *pAutostart = mHWData->mAutostart;
10603
10604 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10605 }
10606 catch (std::bad_alloc &)
10607 {
10608 return E_OUTOFMEMORY;
10609 }
10610
10611 AssertComRC(rc);
10612 return rc;
10613}
10614
10615/**
10616 * Saves the storage controller configuration.
10617 *
10618 * @param data storage settings.
10619 */
10620HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10621{
10622 data.llStorageControllers.clear();
10623
10624 for (StorageControllerList::const_iterator
10625 it = mStorageControllers->begin();
10626 it != mStorageControllers->end();
10627 ++it)
10628 {
10629 HRESULT rc;
10630 ComObjPtr<StorageController> pCtl = *it;
10631
10632 settings::StorageController ctl;
10633 ctl.strName = pCtl->i_getName();
10634 ctl.controllerType = pCtl->i_getControllerType();
10635 ctl.storageBus = pCtl->i_getStorageBus();
10636 ctl.ulInstance = pCtl->i_getInstance();
10637 ctl.fBootable = pCtl->i_getBootable();
10638
10639 /* Save the port count. */
10640 ULONG portCount;
10641 rc = pCtl->COMGETTER(PortCount)(&portCount);
10642 ComAssertComRCRet(rc, rc);
10643 ctl.ulPortCount = portCount;
10644
10645 /* Save fUseHostIOCache */
10646 BOOL fUseHostIOCache;
10647 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10648 ComAssertComRCRet(rc, rc);
10649 ctl.fUseHostIOCache = !!fUseHostIOCache;
10650
10651 /* save the devices now. */
10652 rc = i_saveStorageDevices(pCtl, ctl);
10653 ComAssertComRCRet(rc, rc);
10654
10655 data.llStorageControllers.push_back(ctl);
10656 }
10657
10658 return S_OK;
10659}
10660
10661/**
10662 * Saves the hard disk configuration.
10663 */
10664HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10665 settings::StorageController &data)
10666{
10667 MediumAttachmentList atts;
10668
10669 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10670 if (FAILED(rc)) return rc;
10671
10672 data.llAttachedDevices.clear();
10673 for (MediumAttachmentList::const_iterator
10674 it = atts.begin();
10675 it != atts.end();
10676 ++it)
10677 {
10678 settings::AttachedDevice dev;
10679 IMediumAttachment *iA = *it;
10680 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10681 Medium *pMedium = pAttach->i_getMedium();
10682
10683 dev.deviceType = pAttach->i_getType();
10684 dev.lPort = pAttach->i_getPort();
10685 dev.lDevice = pAttach->i_getDevice();
10686 dev.fPassThrough = pAttach->i_getPassthrough();
10687 dev.fHotPluggable = pAttach->i_getHotPluggable();
10688 if (pMedium)
10689 {
10690 if (pMedium->i_isHostDrive())
10691 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10692 else
10693 dev.uuid = pMedium->i_getId();
10694 dev.fTempEject = pAttach->i_getTempEject();
10695 dev.fNonRotational = pAttach->i_getNonRotational();
10696 dev.fDiscard = pAttach->i_getDiscard();
10697 }
10698
10699 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10700
10701 data.llAttachedDevices.push_back(dev);
10702 }
10703
10704 return S_OK;
10705}
10706
10707/**
10708 * Saves machine state settings as defined by aFlags
10709 * (SaveSTS_* values).
10710 *
10711 * @param aFlags Combination of SaveSTS_* flags.
10712 *
10713 * @note Locks objects for writing.
10714 */
10715HRESULT Machine::i_saveStateSettings(int aFlags)
10716{
10717 if (aFlags == 0)
10718 return S_OK;
10719
10720 AutoCaller autoCaller(this);
10721 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10722
10723 /* This object's write lock is also necessary to serialize file access
10724 * (prevent concurrent reads and writes) */
10725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10726
10727 HRESULT rc = S_OK;
10728
10729 Assert(mData->pMachineConfigFile);
10730
10731 try
10732 {
10733 if (aFlags & SaveSTS_CurStateModified)
10734 mData->pMachineConfigFile->fCurrentStateModified = true;
10735
10736 if (aFlags & SaveSTS_StateFilePath)
10737 {
10738 if (!mSSData->strStateFilePath.isEmpty())
10739 /* try to make the file name relative to the settings file dir */
10740 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10741 else
10742 mData->pMachineConfigFile->strStateFile.setNull();
10743 }
10744
10745 if (aFlags & SaveSTS_StateTimeStamp)
10746 {
10747 Assert( mData->mMachineState != MachineState_Aborted
10748 || mSSData->strStateFilePath.isEmpty());
10749
10750 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10751
10752 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10753/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10754 }
10755
10756 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10757 }
10758 catch (...)
10759 {
10760 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10761 }
10762
10763 return rc;
10764}
10765
10766/**
10767 * Ensures that the given medium is added to a media registry. If this machine
10768 * was created with 4.0 or later, then the machine registry is used. Otherwise
10769 * the global VirtualBox media registry is used.
10770 *
10771 * Caller must NOT hold machine lock, media tree or any medium locks!
10772 *
10773 * @param pMedium
10774 */
10775void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10776{
10777 /* Paranoia checks: do not hold machine or media tree locks. */
10778 AssertReturnVoid(!isWriteLockOnCurrentThread());
10779 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10780
10781 ComObjPtr<Medium> pBase;
10782 {
10783 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10784 pBase = pMedium->i_getBase();
10785 }
10786
10787 /* Paranoia checks: do not hold medium locks. */
10788 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10789 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10790
10791 // decide which medium registry to use now that the medium is attached:
10792 Guid uuid;
10793 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10794 // machine XML is VirtualBox 4.0 or higher:
10795 uuid = i_getId(); // machine UUID
10796 else
10797 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10798
10799 if (pMedium->i_addRegistry(uuid))
10800 mParent->i_markRegistryModified(uuid);
10801
10802 /* For more complex hard disk structures it can happen that the base
10803 * medium isn't yet associated with any medium registry. Do that now. */
10804 if (pMedium != pBase)
10805 {
10806 /* Tree lock needed by Medium::addRegistry when recursing. */
10807 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10808 if (pBase->i_addRegistryRecursive(uuid))
10809 {
10810 treeLock.release();
10811 mParent->i_markRegistryModified(uuid);
10812 }
10813 }
10814}
10815
10816/**
10817 * Creates differencing hard disks for all normal hard disks attached to this
10818 * machine and a new set of attachments to refer to created disks.
10819 *
10820 * Used when taking a snapshot or when deleting the current state. Gets called
10821 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10822 *
10823 * This method assumes that mMediumAttachments contains the original hard disk
10824 * attachments it needs to create diffs for. On success, these attachments will
10825 * be replaced with the created diffs.
10826 *
10827 * Attachments with non-normal hard disks are left as is.
10828 *
10829 * If @a aOnline is @c false then the original hard disks that require implicit
10830 * diffs will be locked for reading. Otherwise it is assumed that they are
10831 * already locked for writing (when the VM was started). Note that in the latter
10832 * case it is responsibility of the caller to lock the newly created diffs for
10833 * writing if this method succeeds.
10834 *
10835 * @param aProgress Progress object to run (must contain at least as
10836 * many operations left as the number of hard disks
10837 * attached).
10838 * @param aWeight Weight of this operation.
10839 * @param aOnline Whether the VM was online prior to this operation.
10840 *
10841 * @note The progress object is not marked as completed, neither on success nor
10842 * on failure. This is a responsibility of the caller.
10843 *
10844 * @note Locks this object and the media tree for writing.
10845 */
10846HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10847 ULONG aWeight,
10848 bool aOnline)
10849{
10850 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10851
10852 AutoCaller autoCaller(this);
10853 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10854
10855 AutoMultiWriteLock2 alock(this->lockHandle(),
10856 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10857
10858 /* must be in a protective state because we release the lock below */
10859 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10860 || mData->mMachineState == MachineState_OnlineSnapshotting
10861 || mData->mMachineState == MachineState_LiveSnapshotting
10862 || mData->mMachineState == MachineState_RestoringSnapshot
10863 || mData->mMachineState == MachineState_DeletingSnapshot
10864 , E_FAIL);
10865
10866 HRESULT rc = S_OK;
10867
10868 // use appropriate locked media map (online or offline)
10869 MediumLockListMap lockedMediaOffline;
10870 MediumLockListMap *lockedMediaMap;
10871 if (aOnline)
10872 lockedMediaMap = &mData->mSession.mLockedMedia;
10873 else
10874 lockedMediaMap = &lockedMediaOffline;
10875
10876 try
10877 {
10878 if (!aOnline)
10879 {
10880 /* lock all attached hard disks early to detect "in use"
10881 * situations before creating actual diffs */
10882 for (MediumAttachmentList::const_iterator
10883 it = mMediumAttachments->begin();
10884 it != mMediumAttachments->end();
10885 ++it)
10886 {
10887 MediumAttachment *pAtt = *it;
10888 if (pAtt->i_getType() == DeviceType_HardDisk)
10889 {
10890 Medium *pMedium = pAtt->i_getMedium();
10891 Assert(pMedium);
10892
10893 MediumLockList *pMediumLockList(new MediumLockList());
10894 alock.release();
10895 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10896 NULL /* pToLockWrite */,
10897 false /* fMediumLockWriteAll */,
10898 NULL,
10899 *pMediumLockList);
10900 alock.acquire();
10901 if (FAILED(rc))
10902 {
10903 delete pMediumLockList;
10904 throw rc;
10905 }
10906 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10907 if (FAILED(rc))
10908 {
10909 throw setError(rc,
10910 tr("Collecting locking information for all attached media failed"));
10911 }
10912 }
10913 }
10914
10915 /* Now lock all media. If this fails, nothing is locked. */
10916 alock.release();
10917 rc = lockedMediaMap->Lock();
10918 alock.acquire();
10919 if (FAILED(rc))
10920 {
10921 throw setError(rc,
10922 tr("Locking of attached media failed"));
10923 }
10924 }
10925
10926 /* remember the current list (note that we don't use backup() since
10927 * mMediumAttachments may be already backed up) */
10928 MediumAttachmentList atts = *mMediumAttachments.data();
10929
10930 /* start from scratch */
10931 mMediumAttachments->clear();
10932
10933 /* go through remembered attachments and create diffs for normal hard
10934 * disks and attach them */
10935 for (MediumAttachmentList::const_iterator
10936 it = atts.begin();
10937 it != atts.end();
10938 ++it)
10939 {
10940 MediumAttachment *pAtt = *it;
10941
10942 DeviceType_T devType = pAtt->i_getType();
10943 Medium *pMedium = pAtt->i_getMedium();
10944
10945 if ( devType != DeviceType_HardDisk
10946 || pMedium == NULL
10947 || pMedium->i_getType() != MediumType_Normal)
10948 {
10949 /* copy the attachment as is */
10950
10951 /** @todo the progress object created in SessionMachine::TakeSnaphot
10952 * only expects operations for hard disks. Later other
10953 * device types need to show up in the progress as well. */
10954 if (devType == DeviceType_HardDisk)
10955 {
10956 if (pMedium == NULL)
10957 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10958 aWeight); // weight
10959 else
10960 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10961 pMedium->i_getBase()->i_getName().c_str()).raw(),
10962 aWeight); // weight
10963 }
10964
10965 mMediumAttachments->push_back(pAtt);
10966 continue;
10967 }
10968
10969 /* need a diff */
10970 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10971 pMedium->i_getBase()->i_getName().c_str()).raw(),
10972 aWeight); // weight
10973
10974 Utf8Str strFullSnapshotFolder;
10975 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10976
10977 ComObjPtr<Medium> diff;
10978 diff.createObject();
10979 // store the diff in the same registry as the parent
10980 // (this cannot fail here because we can't create implicit diffs for
10981 // unregistered images)
10982 Guid uuidRegistryParent;
10983 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10984 Assert(fInRegistry); NOREF(fInRegistry);
10985 rc = diff->init(mParent,
10986 pMedium->i_getPreferredDiffFormat(),
10987 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10988 uuidRegistryParent,
10989 DeviceType_HardDisk);
10990 if (FAILED(rc)) throw rc;
10991
10992 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10993 * the push_back? Looks like we're going to release medium with the
10994 * wrong kind of lock (general issue with if we fail anywhere at all)
10995 * and an orphaned VDI in the snapshots folder. */
10996
10997 /* update the appropriate lock list */
10998 MediumLockList *pMediumLockList;
10999 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11000 AssertComRCThrowRC(rc);
11001 if (aOnline)
11002 {
11003 alock.release();
11004 /* The currently attached medium will be read-only, change
11005 * the lock type to read. */
11006 rc = pMediumLockList->Update(pMedium, false);
11007 alock.acquire();
11008 AssertComRCThrowRC(rc);
11009 }
11010
11011 /* release the locks before the potentially lengthy operation */
11012 alock.release();
11013 rc = pMedium->i_createDiffStorage(diff,
11014 pMedium->i_getPreferredDiffVariant(),
11015 pMediumLockList,
11016 NULL /* aProgress */,
11017 true /* aWait */);
11018 alock.acquire();
11019 if (FAILED(rc)) throw rc;
11020
11021 /* actual lock list update is done in Machine::i_commitMedia */
11022
11023 rc = diff->i_addBackReference(mData->mUuid);
11024 AssertComRCThrowRC(rc);
11025
11026 /* add a new attachment */
11027 ComObjPtr<MediumAttachment> attachment;
11028 attachment.createObject();
11029 rc = attachment->init(this,
11030 diff,
11031 pAtt->i_getControllerName(),
11032 pAtt->i_getPort(),
11033 pAtt->i_getDevice(),
11034 DeviceType_HardDisk,
11035 true /* aImplicit */,
11036 false /* aPassthrough */,
11037 false /* aTempEject */,
11038 pAtt->i_getNonRotational(),
11039 pAtt->i_getDiscard(),
11040 pAtt->i_getHotPluggable(),
11041 pAtt->i_getBandwidthGroup());
11042 if (FAILED(rc)) throw rc;
11043
11044 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11045 AssertComRCThrowRC(rc);
11046 mMediumAttachments->push_back(attachment);
11047 }
11048 }
11049 catch (HRESULT aRC) { rc = aRC; }
11050
11051 /* unlock all hard disks we locked when there is no VM */
11052 if (!aOnline)
11053 {
11054 ErrorInfoKeeper eik;
11055
11056 HRESULT rc1 = lockedMediaMap->Clear();
11057 AssertComRC(rc1);
11058 }
11059
11060 return rc;
11061}
11062
11063/**
11064 * Deletes implicit differencing hard disks created either by
11065 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11066 * mMediumAttachments.
11067 *
11068 * Note that to delete hard disks created by #attachDevice() this method is
11069 * called from #i_rollbackMedia() when the changes are rolled back.
11070 *
11071 * @note Locks this object and the media tree for writing.
11072 */
11073HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11074{
11075 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11076
11077 AutoCaller autoCaller(this);
11078 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11079
11080 AutoMultiWriteLock2 alock(this->lockHandle(),
11081 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11082
11083 /* We absolutely must have backed up state. */
11084 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11085
11086 /* Check if there are any implicitly created diff images. */
11087 bool fImplicitDiffs = false;
11088 for (MediumAttachmentList::const_iterator
11089 it = mMediumAttachments->begin();
11090 it != mMediumAttachments->end();
11091 ++it)
11092 {
11093 const ComObjPtr<MediumAttachment> &pAtt = *it;
11094 if (pAtt->i_isImplicit())
11095 {
11096 fImplicitDiffs = true;
11097 break;
11098 }
11099 }
11100 /* If there is nothing to do, leave early. This saves lots of image locking
11101 * effort. It also avoids a MachineStateChanged event without real reason.
11102 * This is important e.g. when loading a VM config, because there should be
11103 * no events. Otherwise API clients can become thoroughly confused for
11104 * inaccessible VMs (the code for loading VM configs uses this method for
11105 * cleanup if the config makes no sense), as they take such events as an
11106 * indication that the VM is alive, and they would force the VM config to
11107 * be reread, leading to an endless loop. */
11108 if (!fImplicitDiffs)
11109 return S_OK;
11110
11111 HRESULT rc = S_OK;
11112 MachineState_T oldState = mData->mMachineState;
11113
11114 /* will release the lock before the potentially lengthy operation,
11115 * so protect with the special state (unless already protected) */
11116 if ( oldState != MachineState_Snapshotting
11117 && oldState != MachineState_OnlineSnapshotting
11118 && oldState != MachineState_LiveSnapshotting
11119 && oldState != MachineState_RestoringSnapshot
11120 && oldState != MachineState_DeletingSnapshot
11121 && oldState != MachineState_DeletingSnapshotOnline
11122 && oldState != MachineState_DeletingSnapshotPaused
11123 )
11124 i_setMachineState(MachineState_SettingUp);
11125
11126 // use appropriate locked media map (online or offline)
11127 MediumLockListMap lockedMediaOffline;
11128 MediumLockListMap *lockedMediaMap;
11129 if (aOnline)
11130 lockedMediaMap = &mData->mSession.mLockedMedia;
11131 else
11132 lockedMediaMap = &lockedMediaOffline;
11133
11134 try
11135 {
11136 if (!aOnline)
11137 {
11138 /* lock all attached hard disks early to detect "in use"
11139 * situations before deleting actual diffs */
11140 for (MediumAttachmentList::const_iterator
11141 it = mMediumAttachments->begin();
11142 it != mMediumAttachments->end();
11143 ++it)
11144 {
11145 MediumAttachment *pAtt = *it;
11146 if (pAtt->i_getType() == DeviceType_HardDisk)
11147 {
11148 Medium *pMedium = pAtt->i_getMedium();
11149 Assert(pMedium);
11150
11151 MediumLockList *pMediumLockList(new MediumLockList());
11152 alock.release();
11153 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11154 NULL /* pToLockWrite */,
11155 false /* fMediumLockWriteAll */,
11156 NULL,
11157 *pMediumLockList);
11158 alock.acquire();
11159
11160 if (FAILED(rc))
11161 {
11162 delete pMediumLockList;
11163 throw rc;
11164 }
11165
11166 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11167 if (FAILED(rc))
11168 throw rc;
11169 }
11170 }
11171
11172 if (FAILED(rc))
11173 throw rc;
11174 } // end of offline
11175
11176 /* Lock lists are now up to date and include implicitly created media */
11177
11178 /* Go through remembered attachments and delete all implicitly created
11179 * diffs and fix up the attachment information */
11180 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11181 MediumAttachmentList implicitAtts;
11182 for (MediumAttachmentList::const_iterator
11183 it = mMediumAttachments->begin();
11184 it != mMediumAttachments->end();
11185 ++it)
11186 {
11187 ComObjPtr<MediumAttachment> pAtt = *it;
11188 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11189 if (pMedium.isNull())
11190 continue;
11191
11192 // Implicit attachments go on the list for deletion and back references are removed.
11193 if (pAtt->i_isImplicit())
11194 {
11195 /* Deassociate and mark for deletion */
11196 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11197 rc = pMedium->i_removeBackReference(mData->mUuid);
11198 if (FAILED(rc))
11199 throw rc;
11200 implicitAtts.push_back(pAtt);
11201 continue;
11202 }
11203
11204 /* Was this medium attached before? */
11205 if (!i_findAttachment(oldAtts, pMedium))
11206 {
11207 /* no: de-associate */
11208 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11209 rc = pMedium->i_removeBackReference(mData->mUuid);
11210 if (FAILED(rc))
11211 throw rc;
11212 continue;
11213 }
11214 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11215 }
11216
11217 /* If there are implicit attachments to delete, throw away the lock
11218 * map contents (which will unlock all media) since the medium
11219 * attachments will be rolled back. Below we need to completely
11220 * recreate the lock map anyway since it is infinitely complex to
11221 * do this incrementally (would need reconstructing each attachment
11222 * change, which would be extremely hairy). */
11223 if (implicitAtts.size() != 0)
11224 {
11225 ErrorInfoKeeper eik;
11226
11227 HRESULT rc1 = lockedMediaMap->Clear();
11228 AssertComRC(rc1);
11229 }
11230
11231 /* rollback hard disk changes */
11232 mMediumAttachments.rollback();
11233
11234 MultiResult mrc(S_OK);
11235
11236 // Delete unused implicit diffs.
11237 if (implicitAtts.size() != 0)
11238 {
11239 alock.release();
11240
11241 for (MediumAttachmentList::const_iterator
11242 it = implicitAtts.begin();
11243 it != implicitAtts.end();
11244 ++it)
11245 {
11246 // Remove medium associated with this attachment.
11247 ComObjPtr<MediumAttachment> pAtt = *it;
11248 Assert(pAtt);
11249 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11250 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11251 Assert(pMedium);
11252
11253 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11254 // continue on delete failure, just collect error messages
11255 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11256 pMedium->i_getLocationFull().c_str() ));
11257 mrc = rc;
11258 }
11259 // Clear the list of deleted implicit attachments now, while not
11260 // holding the lock, as it will ultimately trigger Medium::uninit()
11261 // calls which assume that the media tree lock isn't held.
11262 implicitAtts.clear();
11263
11264 alock.acquire();
11265
11266 /* if there is a VM recreate media lock map as mentioned above,
11267 * otherwise it is a waste of time and we leave things unlocked */
11268 if (aOnline)
11269 {
11270 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11271 /* must never be NULL, but better safe than sorry */
11272 if (!pMachine.isNull())
11273 {
11274 alock.release();
11275 rc = mData->mSession.mMachine->i_lockMedia();
11276 alock.acquire();
11277 if (FAILED(rc))
11278 throw rc;
11279 }
11280 }
11281 }
11282 }
11283 catch (HRESULT aRC) {rc = aRC;}
11284
11285 if (mData->mMachineState == MachineState_SettingUp)
11286 i_setMachineState(oldState);
11287
11288 /* unlock all hard disks we locked when there is no VM */
11289 if (!aOnline)
11290 {
11291 ErrorInfoKeeper eik;
11292
11293 HRESULT rc1 = lockedMediaMap->Clear();
11294 AssertComRC(rc1);
11295 }
11296
11297 return rc;
11298}
11299
11300
11301/**
11302 * Looks through the given list of media attachments for one with the given parameters
11303 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11304 * can be searched as well if needed.
11305 *
11306 * @param ll
11307 * @param aControllerName
11308 * @param aControllerPort
11309 * @param aDevice
11310 * @return
11311 */
11312MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11313 const Utf8Str &aControllerName,
11314 LONG aControllerPort,
11315 LONG aDevice)
11316{
11317 for (MediumAttachmentList::const_iterator
11318 it = ll.begin();
11319 it != ll.end();
11320 ++it)
11321 {
11322 MediumAttachment *pAttach = *it;
11323 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11324 return pAttach;
11325 }
11326
11327 return NULL;
11328}
11329
11330/**
11331 * Looks through the given list of media attachments for one with the given parameters
11332 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11333 * can be searched as well if needed.
11334 *
11335 * @param ll
11336 * @param pMedium
11337 * @return
11338 */
11339MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11340 ComObjPtr<Medium> pMedium)
11341{
11342 for (MediumAttachmentList::const_iterator
11343 it = ll.begin();
11344 it != ll.end();
11345 ++it)
11346 {
11347 MediumAttachment *pAttach = *it;
11348 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11349 if (pMediumThis == pMedium)
11350 return pAttach;
11351 }
11352
11353 return NULL;
11354}
11355
11356/**
11357 * Looks through the given list of media attachments for one with the given parameters
11358 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11359 * can be searched as well if needed.
11360 *
11361 * @param ll
11362 * @param id
11363 * @return
11364 */
11365MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11366 Guid &id)
11367{
11368 for (MediumAttachmentList::const_iterator
11369 it = ll.begin();
11370 it != ll.end();
11371 ++it)
11372 {
11373 MediumAttachment *pAttach = *it;
11374 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11375 if (pMediumThis->i_getId() == id)
11376 return pAttach;
11377 }
11378
11379 return NULL;
11380}
11381
11382/**
11383 * Main implementation for Machine::DetachDevice. This also gets called
11384 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11385 *
11386 * @param pAttach Medium attachment to detach.
11387 * @param writeLock Machine write lock which the caller must have locked once.
11388 * This may be released temporarily in here.
11389 * @param pSnapshot If NULL, then the detachment is for the current machine.
11390 * Otherwise this is for a SnapshotMachine, and this must be
11391 * its snapshot.
11392 * @return
11393 */
11394HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11395 AutoWriteLock &writeLock,
11396 Snapshot *pSnapshot)
11397{
11398 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11399 DeviceType_T mediumType = pAttach->i_getType();
11400
11401 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11402
11403 if (pAttach->i_isImplicit())
11404 {
11405 /* attempt to implicitly delete the implicitly created diff */
11406
11407 /// @todo move the implicit flag from MediumAttachment to Medium
11408 /// and forbid any hard disk operation when it is implicit. Or maybe
11409 /// a special media state for it to make it even more simple.
11410
11411 Assert(mMediumAttachments.isBackedUp());
11412
11413 /* will release the lock before the potentially lengthy operation, so
11414 * protect with the special state */
11415 MachineState_T oldState = mData->mMachineState;
11416 i_setMachineState(MachineState_SettingUp);
11417
11418 writeLock.release();
11419
11420 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11421 true /*aWait*/);
11422
11423 writeLock.acquire();
11424
11425 i_setMachineState(oldState);
11426
11427 if (FAILED(rc)) return rc;
11428 }
11429
11430 i_setModified(IsModified_Storage);
11431 mMediumAttachments.backup();
11432 mMediumAttachments->remove(pAttach);
11433
11434 if (!oldmedium.isNull())
11435 {
11436 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11437 if (pSnapshot)
11438 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11439 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11440 else if (mediumType != DeviceType_HardDisk)
11441 oldmedium->i_removeBackReference(mData->mUuid);
11442 }
11443
11444 return S_OK;
11445}
11446
11447/**
11448 * Goes thru all media of the given list and
11449 *
11450 * 1) calls i_detachDevice() on each of them for this machine and
11451 * 2) adds all Medium objects found in the process to the given list,
11452 * depending on cleanupMode.
11453 *
11454 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11455 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11456 * media to the list.
11457 *
11458 * This gets called from Machine::Unregister, both for the actual Machine and
11459 * the SnapshotMachine objects that might be found in the snapshots.
11460 *
11461 * Requires caller and locking. The machine lock must be passed in because it
11462 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11463 *
11464 * @param writeLock Machine lock from top-level caller; this gets passed to
11465 * i_detachDevice.
11466 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11467 * object if called for a SnapshotMachine.
11468 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11469 * added to llMedia; if Full, then all media get added;
11470 * otherwise no media get added.
11471 * @param llMedia Caller's list to receive Medium objects which got detached so
11472 * caller can close() them, depending on cleanupMode.
11473 * @return
11474 */
11475HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11476 Snapshot *pSnapshot,
11477 CleanupMode_T cleanupMode,
11478 MediaList &llMedia)
11479{
11480 Assert(isWriteLockOnCurrentThread());
11481
11482 HRESULT rc;
11483
11484 // make a temporary list because i_detachDevice invalidates iterators into
11485 // mMediumAttachments
11486 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11487
11488 for (MediumAttachmentList::iterator
11489 it = llAttachments2.begin();
11490 it != llAttachments2.end();
11491 ++it)
11492 {
11493 ComObjPtr<MediumAttachment> &pAttach = *it;
11494 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11495
11496 if (!pMedium.isNull())
11497 {
11498 AutoCaller mac(pMedium);
11499 if (FAILED(mac.rc())) return mac.rc();
11500 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11501 DeviceType_T devType = pMedium->i_getDeviceType();
11502 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11503 && devType == DeviceType_HardDisk)
11504 || (cleanupMode == CleanupMode_Full)
11505 )
11506 {
11507 llMedia.push_back(pMedium);
11508 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11509 /* Not allowed to keep this lock as below we need the parent
11510 * medium lock, and the lock order is parent to child. */
11511 lock.release();
11512 /*
11513 * Search for medias which are not attached to any machine, but
11514 * in the chain to an attached disk. Mediums are only consided
11515 * if they are:
11516 * - have only one child
11517 * - no references to any machines
11518 * - are of normal medium type
11519 */
11520 while (!pParent.isNull())
11521 {
11522 AutoCaller mac1(pParent);
11523 if (FAILED(mac1.rc())) return mac1.rc();
11524 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11525 if (pParent->i_getChildren().size() == 1)
11526 {
11527 if ( pParent->i_getMachineBackRefCount() == 0
11528 && pParent->i_getType() == MediumType_Normal
11529 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11530 llMedia.push_back(pParent);
11531 }
11532 else
11533 break;
11534 pParent = pParent->i_getParent();
11535 }
11536 }
11537 }
11538
11539 // real machine: then we need to use the proper method
11540 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11541
11542 if (FAILED(rc))
11543 return rc;
11544 }
11545
11546 return S_OK;
11547}
11548
11549/**
11550 * Perform deferred hard disk detachments.
11551 *
11552 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11553 * changed (not backed up).
11554 *
11555 * If @a aOnline is @c true then this method will also unlock the old hard
11556 * disks for which the new implicit diffs were created and will lock these new
11557 * diffs for writing.
11558 *
11559 * @param aOnline Whether the VM was online prior to this operation.
11560 *
11561 * @note Locks this object for writing!
11562 */
11563void Machine::i_commitMedia(bool aOnline /*= false*/)
11564{
11565 AutoCaller autoCaller(this);
11566 AssertComRCReturnVoid(autoCaller.rc());
11567
11568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11569
11570 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11571
11572 HRESULT rc = S_OK;
11573
11574 /* no attach/detach operations -- nothing to do */
11575 if (!mMediumAttachments.isBackedUp())
11576 return;
11577
11578 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11579 bool fMediaNeedsLocking = false;
11580
11581 /* enumerate new attachments */
11582 for (MediumAttachmentList::const_iterator
11583 it = mMediumAttachments->begin();
11584 it != mMediumAttachments->end();
11585 ++it)
11586 {
11587 MediumAttachment *pAttach = *it;
11588
11589 pAttach->i_commit();
11590
11591 Medium *pMedium = pAttach->i_getMedium();
11592 bool fImplicit = pAttach->i_isImplicit();
11593
11594 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11595 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11596 fImplicit));
11597
11598 /** @todo convert all this Machine-based voodoo to MediumAttachment
11599 * based commit logic. */
11600 if (fImplicit)
11601 {
11602 /* convert implicit attachment to normal */
11603 pAttach->i_setImplicit(false);
11604
11605 if ( aOnline
11606 && pMedium
11607 && pAttach->i_getType() == DeviceType_HardDisk
11608 )
11609 {
11610 /* update the appropriate lock list */
11611 MediumLockList *pMediumLockList;
11612 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11613 AssertComRC(rc);
11614 if (pMediumLockList)
11615 {
11616 /* unlock if there's a need to change the locking */
11617 if (!fMediaNeedsLocking)
11618 {
11619 rc = mData->mSession.mLockedMedia.Unlock();
11620 AssertComRC(rc);
11621 fMediaNeedsLocking = true;
11622 }
11623 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11624 AssertComRC(rc);
11625 rc = pMediumLockList->Append(pMedium, true);
11626 AssertComRC(rc);
11627 }
11628 }
11629
11630 continue;
11631 }
11632
11633 if (pMedium)
11634 {
11635 /* was this medium attached before? */
11636 for (MediumAttachmentList::iterator
11637 oldIt = oldAtts.begin();
11638 oldIt != oldAtts.end();
11639 ++oldIt)
11640 {
11641 MediumAttachment *pOldAttach = *oldIt;
11642 if (pOldAttach->i_getMedium() == pMedium)
11643 {
11644 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11645
11646 /* yes: remove from old to avoid de-association */
11647 oldAtts.erase(oldIt);
11648 break;
11649 }
11650 }
11651 }
11652 }
11653
11654 /* enumerate remaining old attachments and de-associate from the
11655 * current machine state */
11656 for (MediumAttachmentList::const_iterator
11657 it = oldAtts.begin();
11658 it != oldAtts.end();
11659 ++it)
11660 {
11661 MediumAttachment *pAttach = *it;
11662 Medium *pMedium = pAttach->i_getMedium();
11663
11664 /* Detach only hard disks, since DVD/floppy media is detached
11665 * instantly in MountMedium. */
11666 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11667 {
11668 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11669
11670 /* now de-associate from the current machine state */
11671 rc = pMedium->i_removeBackReference(mData->mUuid);
11672 AssertComRC(rc);
11673
11674 if (aOnline)
11675 {
11676 /* unlock since medium is not used anymore */
11677 MediumLockList *pMediumLockList;
11678 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11679 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11680 {
11681 /* this happens for online snapshots, there the attachment
11682 * is changing, but only to a diff image created under
11683 * the old one, so there is no separate lock list */
11684 Assert(!pMediumLockList);
11685 }
11686 else
11687 {
11688 AssertComRC(rc);
11689 if (pMediumLockList)
11690 {
11691 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11692 AssertComRC(rc);
11693 }
11694 }
11695 }
11696 }
11697 }
11698
11699 /* take media locks again so that the locking state is consistent */
11700 if (fMediaNeedsLocking)
11701 {
11702 Assert(aOnline);
11703 rc = mData->mSession.mLockedMedia.Lock();
11704 AssertComRC(rc);
11705 }
11706
11707 /* commit the hard disk changes */
11708 mMediumAttachments.commit();
11709
11710 if (i_isSessionMachine())
11711 {
11712 /*
11713 * Update the parent machine to point to the new owner.
11714 * This is necessary because the stored parent will point to the
11715 * session machine otherwise and cause crashes or errors later
11716 * when the session machine gets invalid.
11717 */
11718 /** @todo Change the MediumAttachment class to behave like any other
11719 * class in this regard by creating peer MediumAttachment
11720 * objects for session machines and share the data with the peer
11721 * machine.
11722 */
11723 for (MediumAttachmentList::const_iterator
11724 it = mMediumAttachments->begin();
11725 it != mMediumAttachments->end();
11726 ++it)
11727 (*it)->i_updateParentMachine(mPeer);
11728
11729 /* attach new data to the primary machine and reshare it */
11730 mPeer->mMediumAttachments.attach(mMediumAttachments);
11731 }
11732
11733 return;
11734}
11735
11736/**
11737 * Perform deferred deletion of implicitly created diffs.
11738 *
11739 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11740 * changed (not backed up).
11741 *
11742 * @note Locks this object for writing!
11743 */
11744void Machine::i_rollbackMedia()
11745{
11746 AutoCaller autoCaller(this);
11747 AssertComRCReturnVoid(autoCaller.rc());
11748
11749 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11750 LogFlowThisFunc(("Entering rollbackMedia\n"));
11751
11752 HRESULT rc = S_OK;
11753
11754 /* no attach/detach operations -- nothing to do */
11755 if (!mMediumAttachments.isBackedUp())
11756 return;
11757
11758 /* enumerate new attachments */
11759 for (MediumAttachmentList::const_iterator
11760 it = mMediumAttachments->begin();
11761 it != mMediumAttachments->end();
11762 ++it)
11763 {
11764 MediumAttachment *pAttach = *it;
11765 /* Fix up the backrefs for DVD/floppy media. */
11766 if (pAttach->i_getType() != DeviceType_HardDisk)
11767 {
11768 Medium *pMedium = pAttach->i_getMedium();
11769 if (pMedium)
11770 {
11771 rc = pMedium->i_removeBackReference(mData->mUuid);
11772 AssertComRC(rc);
11773 }
11774 }
11775
11776 (*it)->i_rollback();
11777
11778 pAttach = *it;
11779 /* Fix up the backrefs for DVD/floppy media. */
11780 if (pAttach->i_getType() != DeviceType_HardDisk)
11781 {
11782 Medium *pMedium = pAttach->i_getMedium();
11783 if (pMedium)
11784 {
11785 rc = pMedium->i_addBackReference(mData->mUuid);
11786 AssertComRC(rc);
11787 }
11788 }
11789 }
11790
11791 /** @todo convert all this Machine-based voodoo to MediumAttachment
11792 * based rollback logic. */
11793 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11794
11795 return;
11796}
11797
11798/**
11799 * Returns true if the settings file is located in the directory named exactly
11800 * as the machine; this means, among other things, that the machine directory
11801 * should be auto-renamed.
11802 *
11803 * @param aSettingsDir if not NULL, the full machine settings file directory
11804 * name will be assigned there.
11805 *
11806 * @note Doesn't lock anything.
11807 * @note Not thread safe (must be called from this object's lock).
11808 */
11809bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11810{
11811 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11812 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11813 if (aSettingsDir)
11814 *aSettingsDir = strMachineDirName;
11815 strMachineDirName.stripPath(); // vmname
11816 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11817 strConfigFileOnly.stripPath() // vmname.vbox
11818 .stripSuffix(); // vmname
11819 /** @todo hack, make somehow use of ComposeMachineFilename */
11820 if (mUserData->s.fDirectoryIncludesUUID)
11821 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11822
11823 AssertReturn(!strMachineDirName.isEmpty(), false);
11824 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11825
11826 return strMachineDirName == strConfigFileOnly;
11827}
11828
11829/**
11830 * Discards all changes to machine settings.
11831 *
11832 * @param aNotify Whether to notify the direct session about changes or not.
11833 *
11834 * @note Locks objects for writing!
11835 */
11836void Machine::i_rollback(bool aNotify)
11837{
11838 AutoCaller autoCaller(this);
11839 AssertComRCReturn(autoCaller.rc(), (void)0);
11840
11841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11842
11843 if (!mStorageControllers.isNull())
11844 {
11845 if (mStorageControllers.isBackedUp())
11846 {
11847 /* unitialize all new devices (absent in the backed up list). */
11848 StorageControllerList *backedList = mStorageControllers.backedUpData();
11849 for (StorageControllerList::const_iterator
11850 it = mStorageControllers->begin();
11851 it != mStorageControllers->end();
11852 ++it)
11853 {
11854 if ( std::find(backedList->begin(), backedList->end(), *it)
11855 == backedList->end()
11856 )
11857 {
11858 (*it)->uninit();
11859 }
11860 }
11861
11862 /* restore the list */
11863 mStorageControllers.rollback();
11864 }
11865
11866 /* rollback any changes to devices after restoring the list */
11867 if (mData->flModifications & IsModified_Storage)
11868 {
11869 for (StorageControllerList::const_iterator
11870 it = mStorageControllers->begin();
11871 it != mStorageControllers->end();
11872 ++it)
11873 {
11874 (*it)->i_rollback();
11875 }
11876 }
11877 }
11878
11879 if (!mUSBControllers.isNull())
11880 {
11881 if (mUSBControllers.isBackedUp())
11882 {
11883 /* unitialize all new devices (absent in the backed up list). */
11884 USBControllerList *backedList = mUSBControllers.backedUpData();
11885 for (USBControllerList::const_iterator
11886 it = mUSBControllers->begin();
11887 it != mUSBControllers->end();
11888 ++it)
11889 {
11890 if ( std::find(backedList->begin(), backedList->end(), *it)
11891 == backedList->end()
11892 )
11893 {
11894 (*it)->uninit();
11895 }
11896 }
11897
11898 /* restore the list */
11899 mUSBControllers.rollback();
11900 }
11901
11902 /* rollback any changes to devices after restoring the list */
11903 if (mData->flModifications & IsModified_USB)
11904 {
11905 for (USBControllerList::const_iterator
11906 it = mUSBControllers->begin();
11907 it != mUSBControllers->end();
11908 ++it)
11909 {
11910 (*it)->i_rollback();
11911 }
11912 }
11913 }
11914
11915 mUserData.rollback();
11916
11917 mHWData.rollback();
11918
11919 if (mData->flModifications & IsModified_Storage)
11920 i_rollbackMedia();
11921
11922 if (mBIOSSettings)
11923 mBIOSSettings->i_rollback();
11924
11925 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11926 mVRDEServer->i_rollback();
11927
11928 if (mAudioAdapter)
11929 mAudioAdapter->i_rollback();
11930
11931 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11932 mUSBDeviceFilters->i_rollback();
11933
11934 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11935 mBandwidthControl->i_rollback();
11936
11937 if (!mHWData.isNull())
11938 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11939 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11940 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11941 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11942
11943 if (mData->flModifications & IsModified_NetworkAdapters)
11944 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11945 if ( mNetworkAdapters[slot]
11946 && mNetworkAdapters[slot]->i_isModified())
11947 {
11948 mNetworkAdapters[slot]->i_rollback();
11949 networkAdapters[slot] = mNetworkAdapters[slot];
11950 }
11951
11952 if (mData->flModifications & IsModified_SerialPorts)
11953 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11954 if ( mSerialPorts[slot]
11955 && mSerialPorts[slot]->i_isModified())
11956 {
11957 mSerialPorts[slot]->i_rollback();
11958 serialPorts[slot] = mSerialPorts[slot];
11959 }
11960
11961 if (mData->flModifications & IsModified_ParallelPorts)
11962 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11963 if ( mParallelPorts[slot]
11964 && mParallelPorts[slot]->i_isModified())
11965 {
11966 mParallelPorts[slot]->i_rollback();
11967 parallelPorts[slot] = mParallelPorts[slot];
11968 }
11969
11970 if (aNotify)
11971 {
11972 /* inform the direct session about changes */
11973
11974 ComObjPtr<Machine> that = this;
11975 uint32_t flModifications = mData->flModifications;
11976 alock.release();
11977
11978 if (flModifications & IsModified_SharedFolders)
11979 that->i_onSharedFolderChange();
11980
11981 if (flModifications & IsModified_VRDEServer)
11982 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11983 if (flModifications & IsModified_USB)
11984 that->i_onUSBControllerChange();
11985
11986 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11987 if (networkAdapters[slot])
11988 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11989 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11990 if (serialPorts[slot])
11991 that->i_onSerialPortChange(serialPorts[slot]);
11992 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11993 if (parallelPorts[slot])
11994 that->i_onParallelPortChange(parallelPorts[slot]);
11995
11996 if (flModifications & IsModified_Storage)
11997 that->i_onStorageControllerChange();
11998
11999#if 0
12000 if (flModifications & IsModified_BandwidthControl)
12001 that->onBandwidthControlChange();
12002#endif
12003 }
12004}
12005
12006/**
12007 * Commits all the changes to machine settings.
12008 *
12009 * Note that this operation is supposed to never fail.
12010 *
12011 * @note Locks this object and children for writing.
12012 */
12013void Machine::i_commit()
12014{
12015 AutoCaller autoCaller(this);
12016 AssertComRCReturnVoid(autoCaller.rc());
12017
12018 AutoCaller peerCaller(mPeer);
12019 AssertComRCReturnVoid(peerCaller.rc());
12020
12021 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12022
12023 /*
12024 * use safe commit to ensure Snapshot machines (that share mUserData)
12025 * will still refer to a valid memory location
12026 */
12027 mUserData.commitCopy();
12028
12029 mHWData.commit();
12030
12031 if (mMediumAttachments.isBackedUp())
12032 i_commitMedia(Global::IsOnline(mData->mMachineState));
12033
12034 mBIOSSettings->i_commit();
12035 mVRDEServer->i_commit();
12036 mAudioAdapter->i_commit();
12037 mUSBDeviceFilters->i_commit();
12038 mBandwidthControl->i_commit();
12039
12040 /* Since mNetworkAdapters is a list which might have been changed (resized)
12041 * without using the Backupable<> template we need to handle the copying
12042 * of the list entries manually, including the creation of peers for the
12043 * new objects. */
12044 bool commitNetworkAdapters = false;
12045 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12046 if (mPeer)
12047 {
12048 /* commit everything, even the ones which will go away */
12049 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12050 mNetworkAdapters[slot]->i_commit();
12051 /* copy over the new entries, creating a peer and uninit the original */
12052 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12053 for (size_t slot = 0; slot < newSize; slot++)
12054 {
12055 /* look if this adapter has a peer device */
12056 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12057 if (!peer)
12058 {
12059 /* no peer means the adapter is a newly created one;
12060 * create a peer owning data this data share it with */
12061 peer.createObject();
12062 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12063 }
12064 mPeer->mNetworkAdapters[slot] = peer;
12065 }
12066 /* uninit any no longer needed network adapters */
12067 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12068 mNetworkAdapters[slot]->uninit();
12069 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12070 {
12071 if (mPeer->mNetworkAdapters[slot])
12072 mPeer->mNetworkAdapters[slot]->uninit();
12073 }
12074 /* Keep the original network adapter count until this point, so that
12075 * discarding a chipset type change will not lose settings. */
12076 mNetworkAdapters.resize(newSize);
12077 mPeer->mNetworkAdapters.resize(newSize);
12078 }
12079 else
12080 {
12081 /* we have no peer (our parent is the newly created machine);
12082 * just commit changes to the network adapters */
12083 commitNetworkAdapters = true;
12084 }
12085 if (commitNetworkAdapters)
12086 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12087 mNetworkAdapters[slot]->i_commit();
12088
12089 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12090 mSerialPorts[slot]->i_commit();
12091 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12092 mParallelPorts[slot]->i_commit();
12093
12094 bool commitStorageControllers = false;
12095
12096 if (mStorageControllers.isBackedUp())
12097 {
12098 mStorageControllers.commit();
12099
12100 if (mPeer)
12101 {
12102 /* Commit all changes to new controllers (this will reshare data with
12103 * peers for those who have peers) */
12104 StorageControllerList *newList = new StorageControllerList();
12105 for (StorageControllerList::const_iterator
12106 it = mStorageControllers->begin();
12107 it != mStorageControllers->end();
12108 ++it)
12109 {
12110 (*it)->i_commit();
12111
12112 /* look if this controller has a peer device */
12113 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12114 if (!peer)
12115 {
12116 /* no peer means the device is a newly created one;
12117 * create a peer owning data this device share it with */
12118 peer.createObject();
12119 peer->init(mPeer, *it, true /* aReshare */);
12120 }
12121 else
12122 {
12123 /* remove peer from the old list */
12124 mPeer->mStorageControllers->remove(peer);
12125 }
12126 /* and add it to the new list */
12127 newList->push_back(peer);
12128 }
12129
12130 /* uninit old peer's controllers that are left */
12131 for (StorageControllerList::const_iterator
12132 it = mPeer->mStorageControllers->begin();
12133 it != mPeer->mStorageControllers->end();
12134 ++it)
12135 {
12136 (*it)->uninit();
12137 }
12138
12139 /* attach new list of controllers to our peer */
12140 mPeer->mStorageControllers.attach(newList);
12141 }
12142 else
12143 {
12144 /* we have no peer (our parent is the newly created machine);
12145 * just commit changes to devices */
12146 commitStorageControllers = true;
12147 }
12148 }
12149 else
12150 {
12151 /* the list of controllers itself is not changed,
12152 * just commit changes to controllers themselves */
12153 commitStorageControllers = true;
12154 }
12155
12156 if (commitStorageControllers)
12157 {
12158 for (StorageControllerList::const_iterator
12159 it = mStorageControllers->begin();
12160 it != mStorageControllers->end();
12161 ++it)
12162 {
12163 (*it)->i_commit();
12164 }
12165 }
12166
12167 bool commitUSBControllers = false;
12168
12169 if (mUSBControllers.isBackedUp())
12170 {
12171 mUSBControllers.commit();
12172
12173 if (mPeer)
12174 {
12175 /* Commit all changes to new controllers (this will reshare data with
12176 * peers for those who have peers) */
12177 USBControllerList *newList = new USBControllerList();
12178 for (USBControllerList::const_iterator
12179 it = mUSBControllers->begin();
12180 it != mUSBControllers->end();
12181 ++it)
12182 {
12183 (*it)->i_commit();
12184
12185 /* look if this controller has a peer device */
12186 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12187 if (!peer)
12188 {
12189 /* no peer means the device is a newly created one;
12190 * create a peer owning data this device share it with */
12191 peer.createObject();
12192 peer->init(mPeer, *it, true /* aReshare */);
12193 }
12194 else
12195 {
12196 /* remove peer from the old list */
12197 mPeer->mUSBControllers->remove(peer);
12198 }
12199 /* and add it to the new list */
12200 newList->push_back(peer);
12201 }
12202
12203 /* uninit old peer's controllers that are left */
12204 for (USBControllerList::const_iterator
12205 it = mPeer->mUSBControllers->begin();
12206 it != mPeer->mUSBControllers->end();
12207 ++it)
12208 {
12209 (*it)->uninit();
12210 }
12211
12212 /* attach new list of controllers to our peer */
12213 mPeer->mUSBControllers.attach(newList);
12214 }
12215 else
12216 {
12217 /* we have no peer (our parent is the newly created machine);
12218 * just commit changes to devices */
12219 commitUSBControllers = true;
12220 }
12221 }
12222 else
12223 {
12224 /* the list of controllers itself is not changed,
12225 * just commit changes to controllers themselves */
12226 commitUSBControllers = true;
12227 }
12228
12229 if (commitUSBControllers)
12230 {
12231 for (USBControllerList::const_iterator
12232 it = mUSBControllers->begin();
12233 it != mUSBControllers->end();
12234 ++it)
12235 {
12236 (*it)->i_commit();
12237 }
12238 }
12239
12240 if (i_isSessionMachine())
12241 {
12242 /* attach new data to the primary machine and reshare it */
12243 mPeer->mUserData.attach(mUserData);
12244 mPeer->mHWData.attach(mHWData);
12245 /* mmMediumAttachments is reshared by fixupMedia */
12246 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12247 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12248 }
12249}
12250
12251/**
12252 * Copies all the hardware data from the given machine.
12253 *
12254 * Currently, only called when the VM is being restored from a snapshot. In
12255 * particular, this implies that the VM is not running during this method's
12256 * call.
12257 *
12258 * @note This method must be called from under this object's lock.
12259 *
12260 * @note This method doesn't call #i_commit(), so all data remains backed up and
12261 * unsaved.
12262 */
12263void Machine::i_copyFrom(Machine *aThat)
12264{
12265 AssertReturnVoid(!i_isSnapshotMachine());
12266 AssertReturnVoid(aThat->i_isSnapshotMachine());
12267
12268 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12269
12270 mHWData.assignCopy(aThat->mHWData);
12271
12272 // create copies of all shared folders (mHWData after attaching a copy
12273 // contains just references to original objects)
12274 for (HWData::SharedFolderList::iterator
12275 it = mHWData->mSharedFolders.begin();
12276 it != mHWData->mSharedFolders.end();
12277 ++it)
12278 {
12279 ComObjPtr<SharedFolder> folder;
12280 folder.createObject();
12281 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12282 AssertComRC(rc);
12283 *it = folder;
12284 }
12285
12286 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12287 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12288 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12289 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12290 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12291
12292 /* create private copies of all controllers */
12293 mStorageControllers.backup();
12294 mStorageControllers->clear();
12295 for (StorageControllerList::const_iterator
12296 it = aThat->mStorageControllers->begin();
12297 it != aThat->mStorageControllers->end();
12298 ++it)
12299 {
12300 ComObjPtr<StorageController> ctrl;
12301 ctrl.createObject();
12302 ctrl->initCopy(this, *it);
12303 mStorageControllers->push_back(ctrl);
12304 }
12305
12306 /* create private copies of all USB controllers */
12307 mUSBControllers.backup();
12308 mUSBControllers->clear();
12309 for (USBControllerList::const_iterator
12310 it = aThat->mUSBControllers->begin();
12311 it != aThat->mUSBControllers->end();
12312 ++it)
12313 {
12314 ComObjPtr<USBController> ctrl;
12315 ctrl.createObject();
12316 ctrl->initCopy(this, *it);
12317 mUSBControllers->push_back(ctrl);
12318 }
12319
12320 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12321 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12322 {
12323 if (mNetworkAdapters[slot].isNotNull())
12324 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12325 else
12326 {
12327 unconst(mNetworkAdapters[slot]).createObject();
12328 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12329 }
12330 }
12331 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12332 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12333 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12334 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12335}
12336
12337/**
12338 * Returns whether the given storage controller is hotplug capable.
12339 *
12340 * @returns true if the controller supports hotplugging
12341 * false otherwise.
12342 * @param enmCtrlType The controller type to check for.
12343 */
12344bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12345{
12346 ComPtr<ISystemProperties> systemProperties;
12347 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12348 if (FAILED(rc))
12349 return false;
12350
12351 BOOL aHotplugCapable = FALSE;
12352 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12353
12354 return RT_BOOL(aHotplugCapable);
12355}
12356
12357#ifdef VBOX_WITH_RESOURCE_USAGE_API
12358
12359void Machine::i_getDiskList(MediaList &list)
12360{
12361 for (MediumAttachmentList::const_iterator
12362 it = mMediumAttachments->begin();
12363 it != mMediumAttachments->end();
12364 ++it)
12365 {
12366 MediumAttachment *pAttach = *it;
12367 /* just in case */
12368 AssertContinue(pAttach);
12369
12370 AutoCaller localAutoCallerA(pAttach);
12371 if (FAILED(localAutoCallerA.rc())) continue;
12372
12373 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12374
12375 if (pAttach->i_getType() == DeviceType_HardDisk)
12376 list.push_back(pAttach->i_getMedium());
12377 }
12378}
12379
12380void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12381{
12382 AssertReturnVoid(isWriteLockOnCurrentThread());
12383 AssertPtrReturnVoid(aCollector);
12384
12385 pm::CollectorHAL *hal = aCollector->getHAL();
12386 /* Create sub metrics */
12387 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12388 "Percentage of processor time spent in user mode by the VM process.");
12389 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12390 "Percentage of processor time spent in kernel mode by the VM process.");
12391 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12392 "Size of resident portion of VM process in memory.");
12393 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12394 "Actual size of all VM disks combined.");
12395 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12396 "Network receive rate.");
12397 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12398 "Network transmit rate.");
12399 /* Create and register base metrics */
12400 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12401 cpuLoadUser, cpuLoadKernel);
12402 aCollector->registerBaseMetric(cpuLoad);
12403 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12404 ramUsageUsed);
12405 aCollector->registerBaseMetric(ramUsage);
12406 MediaList disks;
12407 i_getDiskList(disks);
12408 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12409 diskUsageUsed);
12410 aCollector->registerBaseMetric(diskUsage);
12411
12412 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12413 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12414 new pm::AggregateAvg()));
12415 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12416 new pm::AggregateMin()));
12417 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12418 new pm::AggregateMax()));
12419 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12420 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12421 new pm::AggregateAvg()));
12422 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12423 new pm::AggregateMin()));
12424 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12425 new pm::AggregateMax()));
12426
12427 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12428 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12429 new pm::AggregateAvg()));
12430 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12431 new pm::AggregateMin()));
12432 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12433 new pm::AggregateMax()));
12434
12435 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12436 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12437 new pm::AggregateAvg()));
12438 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12439 new pm::AggregateMin()));
12440 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12441 new pm::AggregateMax()));
12442
12443
12444 /* Guest metrics collector */
12445 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12446 aCollector->registerGuest(mCollectorGuest);
12447 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12448
12449 /* Create sub metrics */
12450 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12451 "Percentage of processor time spent in user mode as seen by the guest.");
12452 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12453 "Percentage of processor time spent in kernel mode as seen by the guest.");
12454 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12455 "Percentage of processor time spent idling as seen by the guest.");
12456
12457 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12458 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12459 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12460 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12461 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12462 pm::SubMetric *guestMemCache = new pm::SubMetric(
12463 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12464
12465 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12466 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12467
12468 /* Create and register base metrics */
12469 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12470 machineNetRx, machineNetTx);
12471 aCollector->registerBaseMetric(machineNetRate);
12472
12473 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12474 guestLoadUser, guestLoadKernel, guestLoadIdle);
12475 aCollector->registerBaseMetric(guestCpuLoad);
12476
12477 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12478 guestMemTotal, guestMemFree,
12479 guestMemBalloon, guestMemShared,
12480 guestMemCache, guestPagedTotal);
12481 aCollector->registerBaseMetric(guestCpuMem);
12482
12483 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12484 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12485 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12486 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12487
12488 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12489 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12490 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12491 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12492
12493 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12494 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12495 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12496 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12497
12498 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12499 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12500 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12501 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12502
12503 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12504 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12505 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12506 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12507
12508 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12509 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12510 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12511 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12512
12513 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12514 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12515 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12516 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12517
12518 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12519 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12520 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12521 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12522
12523 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12524 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12525 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12526 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12527
12528 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12529 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12530 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12531 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12532
12533 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12534 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12535 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12536 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12537}
12538
12539void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12540{
12541 AssertReturnVoid(isWriteLockOnCurrentThread());
12542
12543 if (aCollector)
12544 {
12545 aCollector->unregisterMetricsFor(aMachine);
12546 aCollector->unregisterBaseMetricsFor(aMachine);
12547 }
12548}
12549
12550#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12551
12552
12553////////////////////////////////////////////////////////////////////////////////
12554
12555DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12556
12557HRESULT SessionMachine::FinalConstruct()
12558{
12559 LogFlowThisFunc(("\n"));
12560
12561 mClientToken = NULL;
12562
12563 return BaseFinalConstruct();
12564}
12565
12566void SessionMachine::FinalRelease()
12567{
12568 LogFlowThisFunc(("\n"));
12569
12570 Assert(!mClientToken);
12571 /* paranoia, should not hang around any more */
12572 if (mClientToken)
12573 {
12574 delete mClientToken;
12575 mClientToken = NULL;
12576 }
12577
12578 uninit(Uninit::Unexpected);
12579
12580 BaseFinalRelease();
12581}
12582
12583/**
12584 * @note Must be called only by Machine::LockMachine() from its own write lock.
12585 */
12586HRESULT SessionMachine::init(Machine *aMachine)
12587{
12588 LogFlowThisFuncEnter();
12589 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12590
12591 AssertReturn(aMachine, E_INVALIDARG);
12592
12593 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12594
12595 /* Enclose the state transition NotReady->InInit->Ready */
12596 AutoInitSpan autoInitSpan(this);
12597 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12598
12599 HRESULT rc = S_OK;
12600
12601 RT_ZERO(mAuthLibCtx);
12602
12603 /* create the machine client token */
12604 try
12605 {
12606 mClientToken = new ClientToken(aMachine, this);
12607 if (!mClientToken->isReady())
12608 {
12609 delete mClientToken;
12610 mClientToken = NULL;
12611 rc = E_FAIL;
12612 }
12613 }
12614 catch (std::bad_alloc &)
12615 {
12616 rc = E_OUTOFMEMORY;
12617 }
12618 if (FAILED(rc))
12619 return rc;
12620
12621 /* memorize the peer Machine */
12622 unconst(mPeer) = aMachine;
12623 /* share the parent pointer */
12624 unconst(mParent) = aMachine->mParent;
12625
12626 /* take the pointers to data to share */
12627 mData.share(aMachine->mData);
12628 mSSData.share(aMachine->mSSData);
12629
12630 mUserData.share(aMachine->mUserData);
12631 mHWData.share(aMachine->mHWData);
12632 mMediumAttachments.share(aMachine->mMediumAttachments);
12633
12634 mStorageControllers.allocate();
12635 for (StorageControllerList::const_iterator
12636 it = aMachine->mStorageControllers->begin();
12637 it != aMachine->mStorageControllers->end();
12638 ++it)
12639 {
12640 ComObjPtr<StorageController> ctl;
12641 ctl.createObject();
12642 ctl->init(this, *it);
12643 mStorageControllers->push_back(ctl);
12644 }
12645
12646 mUSBControllers.allocate();
12647 for (USBControllerList::const_iterator
12648 it = aMachine->mUSBControllers->begin();
12649 it != aMachine->mUSBControllers->end();
12650 ++it)
12651 {
12652 ComObjPtr<USBController> ctl;
12653 ctl.createObject();
12654 ctl->init(this, *it);
12655 mUSBControllers->push_back(ctl);
12656 }
12657
12658 unconst(mBIOSSettings).createObject();
12659 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12660 /* create another VRDEServer object that will be mutable */
12661 unconst(mVRDEServer).createObject();
12662 mVRDEServer->init(this, aMachine->mVRDEServer);
12663 /* create another audio adapter object that will be mutable */
12664 unconst(mAudioAdapter).createObject();
12665 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12666 /* create a list of serial ports that will be mutable */
12667 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12668 {
12669 unconst(mSerialPorts[slot]).createObject();
12670 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12671 }
12672 /* create a list of parallel ports that will be mutable */
12673 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12674 {
12675 unconst(mParallelPorts[slot]).createObject();
12676 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12677 }
12678
12679 /* create another USB device filters object that will be mutable */
12680 unconst(mUSBDeviceFilters).createObject();
12681 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12682
12683 /* create a list of network adapters that will be mutable */
12684 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12685 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12686 {
12687 unconst(mNetworkAdapters[slot]).createObject();
12688 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12689 }
12690
12691 /* create another bandwidth control object that will be mutable */
12692 unconst(mBandwidthControl).createObject();
12693 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12694
12695 /* default is to delete saved state on Saved -> PoweredOff transition */
12696 mRemoveSavedState = true;
12697
12698 /* Confirm a successful initialization when it's the case */
12699 autoInitSpan.setSucceeded();
12700
12701 miNATNetworksStarted = 0;
12702
12703 LogFlowThisFuncLeave();
12704 return rc;
12705}
12706
12707/**
12708 * Uninitializes this session object. If the reason is other than
12709 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12710 * or the client watcher code.
12711 *
12712 * @param aReason uninitialization reason
12713 *
12714 * @note Locks mParent + this object for writing.
12715 */
12716void SessionMachine::uninit(Uninit::Reason aReason)
12717{
12718 LogFlowThisFuncEnter();
12719 LogFlowThisFunc(("reason=%d\n", aReason));
12720
12721 /*
12722 * Strongly reference ourselves to prevent this object deletion after
12723 * mData->mSession.mMachine.setNull() below (which can release the last
12724 * reference and call the destructor). Important: this must be done before
12725 * accessing any members (and before AutoUninitSpan that does it as well).
12726 * This self reference will be released as the very last step on return.
12727 */
12728 ComObjPtr<SessionMachine> selfRef;
12729 if (aReason != Uninit::Unexpected)
12730 selfRef = this;
12731
12732 /* Enclose the state transition Ready->InUninit->NotReady */
12733 AutoUninitSpan autoUninitSpan(this);
12734 if (autoUninitSpan.uninitDone())
12735 {
12736 LogFlowThisFunc(("Already uninitialized\n"));
12737 LogFlowThisFuncLeave();
12738 return;
12739 }
12740
12741 if (autoUninitSpan.initFailed())
12742 {
12743 /* We've been called by init() because it's failed. It's not really
12744 * necessary (nor it's safe) to perform the regular uninit sequence
12745 * below, the following is enough.
12746 */
12747 LogFlowThisFunc(("Initialization failed.\n"));
12748 /* destroy the machine client token */
12749 if (mClientToken)
12750 {
12751 delete mClientToken;
12752 mClientToken = NULL;
12753 }
12754 uninitDataAndChildObjects();
12755 mData.free();
12756 unconst(mParent) = NULL;
12757 unconst(mPeer) = NULL;
12758 LogFlowThisFuncLeave();
12759 return;
12760 }
12761
12762 MachineState_T lastState;
12763 {
12764 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12765 lastState = mData->mMachineState;
12766 }
12767 NOREF(lastState);
12768
12769#ifdef VBOX_WITH_USB
12770 // release all captured USB devices, but do this before requesting the locks below
12771 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12772 {
12773 /* Console::captureUSBDevices() is called in the VM process only after
12774 * setting the machine state to Starting or Restoring.
12775 * Console::detachAllUSBDevices() will be called upon successful
12776 * termination. So, we need to release USB devices only if there was
12777 * an abnormal termination of a running VM.
12778 *
12779 * This is identical to SessionMachine::DetachAllUSBDevices except
12780 * for the aAbnormal argument. */
12781 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12782 AssertComRC(rc);
12783 NOREF(rc);
12784
12785 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12786 if (service)
12787 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12788 }
12789#endif /* VBOX_WITH_USB */
12790
12791 // we need to lock this object in uninit() because the lock is shared
12792 // with mPeer (as well as data we modify below). mParent lock is needed
12793 // by several calls to it.
12794 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12795
12796#ifdef VBOX_WITH_RESOURCE_USAGE_API
12797 /*
12798 * It is safe to call Machine::i_unregisterMetrics() here because
12799 * PerformanceCollector::samplerCallback no longer accesses guest methods
12800 * holding the lock.
12801 */
12802 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12803 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12804 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12805 if (mCollectorGuest)
12806 {
12807 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12808 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12809 mCollectorGuest = NULL;
12810 }
12811#endif
12812
12813 if (aReason == Uninit::Abnormal)
12814 {
12815 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12816
12817 /* reset the state to Aborted */
12818 if (mData->mMachineState != MachineState_Aborted)
12819 i_setMachineState(MachineState_Aborted);
12820 }
12821
12822 // any machine settings modified?
12823 if (mData->flModifications)
12824 {
12825 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12826 i_rollback(false /* aNotify */);
12827 }
12828
12829 mData->mSession.mPID = NIL_RTPROCESS;
12830
12831 if (aReason == Uninit::Unexpected)
12832 {
12833 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12834 * client watcher thread to update the set of machines that have open
12835 * sessions. */
12836 mParent->i_updateClientWatcher();
12837 }
12838
12839 /* uninitialize all remote controls */
12840 if (mData->mSession.mRemoteControls.size())
12841 {
12842 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12843 mData->mSession.mRemoteControls.size()));
12844
12845 /* Always restart a the beginning, since the iterator is invalidated
12846 * by using erase(). */
12847 for (Data::Session::RemoteControlList::iterator
12848 it = mData->mSession.mRemoteControls.begin();
12849 it != mData->mSession.mRemoteControls.end();
12850 it = mData->mSession.mRemoteControls.begin())
12851 {
12852 ComPtr<IInternalSessionControl> pControl = *it;
12853 mData->mSession.mRemoteControls.erase(it);
12854 multilock.release();
12855 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12856 HRESULT rc = pControl->Uninitialize();
12857 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12858 if (FAILED(rc))
12859 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12860 multilock.acquire();
12861 }
12862 mData->mSession.mRemoteControls.clear();
12863 }
12864
12865 /* Remove all references to the NAT network service. The service will stop
12866 * if all references (also from other VMs) are removed. */
12867 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12868 {
12869 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12870 {
12871 BOOL enabled;
12872 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12873 if ( FAILED(hrc)
12874 || !enabled)
12875 continue;
12876
12877 NetworkAttachmentType_T type;
12878 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12879 if ( SUCCEEDED(hrc)
12880 && type == NetworkAttachmentType_NATNetwork)
12881 {
12882 Bstr name;
12883 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12884 if (SUCCEEDED(hrc))
12885 {
12886 multilock.release();
12887 Utf8Str strName(name);
12888 LogRel(("VM '%s' stops using NAT network '%s'\n",
12889 mUserData->s.strName.c_str(), strName.c_str()));
12890 mParent->i_natNetworkRefDec(strName);
12891 multilock.acquire();
12892 }
12893 }
12894 }
12895 }
12896
12897 /*
12898 * An expected uninitialization can come only from #i_checkForDeath().
12899 * Otherwise it means that something's gone really wrong (for example,
12900 * the Session implementation has released the VirtualBox reference
12901 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12902 * etc). However, it's also possible, that the client releases the IPC
12903 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12904 * but the VirtualBox release event comes first to the server process.
12905 * This case is practically possible, so we should not assert on an
12906 * unexpected uninit, just log a warning.
12907 */
12908
12909 if (aReason == Uninit::Unexpected)
12910 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12911
12912 if (aReason != Uninit::Normal)
12913 {
12914 mData->mSession.mDirectControl.setNull();
12915 }
12916 else
12917 {
12918 /* this must be null here (see #OnSessionEnd()) */
12919 Assert(mData->mSession.mDirectControl.isNull());
12920 Assert(mData->mSession.mState == SessionState_Unlocking);
12921 Assert(!mData->mSession.mProgress.isNull());
12922 }
12923 if (mData->mSession.mProgress)
12924 {
12925 if (aReason == Uninit::Normal)
12926 mData->mSession.mProgress->i_notifyComplete(S_OK);
12927 else
12928 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12929 COM_IIDOF(ISession),
12930 getComponentName(),
12931 tr("The VM session was aborted"));
12932 mData->mSession.mProgress.setNull();
12933 }
12934
12935 if (mConsoleTaskData.mProgress)
12936 {
12937 Assert(aReason == Uninit::Abnormal);
12938 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12939 COM_IIDOF(ISession),
12940 getComponentName(),
12941 tr("The VM session was aborted"));
12942 mConsoleTaskData.mProgress.setNull();
12943 }
12944
12945 /* remove the association between the peer machine and this session machine */
12946 Assert( (SessionMachine*)mData->mSession.mMachine == this
12947 || aReason == Uninit::Unexpected);
12948
12949 /* reset the rest of session data */
12950 mData->mSession.mLockType = LockType_Null;
12951 mData->mSession.mMachine.setNull();
12952 mData->mSession.mState = SessionState_Unlocked;
12953 mData->mSession.mName.setNull();
12954
12955 /* destroy the machine client token before leaving the exclusive lock */
12956 if (mClientToken)
12957 {
12958 delete mClientToken;
12959 mClientToken = NULL;
12960 }
12961
12962 /* fire an event */
12963 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12964
12965 uninitDataAndChildObjects();
12966
12967 /* free the essential data structure last */
12968 mData.free();
12969
12970 /* release the exclusive lock before setting the below two to NULL */
12971 multilock.release();
12972
12973 unconst(mParent) = NULL;
12974 unconst(mPeer) = NULL;
12975
12976 AuthLibUnload(&mAuthLibCtx);
12977
12978 LogFlowThisFuncLeave();
12979}
12980
12981// util::Lockable interface
12982////////////////////////////////////////////////////////////////////////////////
12983
12984/**
12985 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12986 * with the primary Machine instance (mPeer).
12987 */
12988RWLockHandle *SessionMachine::lockHandle() const
12989{
12990 AssertReturn(mPeer != NULL, NULL);
12991 return mPeer->lockHandle();
12992}
12993
12994// IInternalMachineControl methods
12995////////////////////////////////////////////////////////////////////////////////
12996
12997/**
12998 * Passes collected guest statistics to performance collector object
12999 */
13000HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13001 ULONG aCpuKernel, ULONG aCpuIdle,
13002 ULONG aMemTotal, ULONG aMemFree,
13003 ULONG aMemBalloon, ULONG aMemShared,
13004 ULONG aMemCache, ULONG aPageTotal,
13005 ULONG aAllocVMM, ULONG aFreeVMM,
13006 ULONG aBalloonedVMM, ULONG aSharedVMM,
13007 ULONG aVmNetRx, ULONG aVmNetTx)
13008{
13009#ifdef VBOX_WITH_RESOURCE_USAGE_API
13010 if (mCollectorGuest)
13011 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13012 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13013 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13014 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13015
13016 return S_OK;
13017#else
13018 NOREF(aValidStats);
13019 NOREF(aCpuUser);
13020 NOREF(aCpuKernel);
13021 NOREF(aCpuIdle);
13022 NOREF(aMemTotal);
13023 NOREF(aMemFree);
13024 NOREF(aMemBalloon);
13025 NOREF(aMemShared);
13026 NOREF(aMemCache);
13027 NOREF(aPageTotal);
13028 NOREF(aAllocVMM);
13029 NOREF(aFreeVMM);
13030 NOREF(aBalloonedVMM);
13031 NOREF(aSharedVMM);
13032 NOREF(aVmNetRx);
13033 NOREF(aVmNetTx);
13034 return E_NOTIMPL;
13035#endif
13036}
13037
13038////////////////////////////////////////////////////////////////////////////////
13039//
13040// SessionMachine task records
13041//
13042////////////////////////////////////////////////////////////////////////////////
13043
13044/**
13045 * Task record for saving the machine state.
13046 */
13047class SessionMachine::SaveStateTask
13048 : public Machine::Task
13049{
13050public:
13051 SaveStateTask(SessionMachine *m,
13052 Progress *p,
13053 const Utf8Str &t,
13054 Reason_T enmReason,
13055 const Utf8Str &strStateFilePath)
13056 : Task(m, p, t),
13057 m_enmReason(enmReason),
13058 m_strStateFilePath(strStateFilePath)
13059 {}
13060
13061private:
13062 void handler()
13063 {
13064 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13065 }
13066
13067 Reason_T m_enmReason;
13068 Utf8Str m_strStateFilePath;
13069
13070 friend class SessionMachine;
13071};
13072
13073/**
13074 * Task thread implementation for SessionMachine::SaveState(), called from
13075 * SessionMachine::taskHandler().
13076 *
13077 * @note Locks this object for writing.
13078 *
13079 * @param task
13080 * @return
13081 */
13082void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13083{
13084 LogFlowThisFuncEnter();
13085
13086 AutoCaller autoCaller(this);
13087 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13088 if (FAILED(autoCaller.rc()))
13089 {
13090 /* we might have been uninitialized because the session was accidentally
13091 * closed by the client, so don't assert */
13092 HRESULT rc = setError(E_FAIL,
13093 tr("The session has been accidentally closed"));
13094 task.m_pProgress->i_notifyComplete(rc);
13095 LogFlowThisFuncLeave();
13096 return;
13097 }
13098
13099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13100
13101 HRESULT rc = S_OK;
13102
13103 try
13104 {
13105 ComPtr<IInternalSessionControl> directControl;
13106 if (mData->mSession.mLockType == LockType_VM)
13107 directControl = mData->mSession.mDirectControl;
13108 if (directControl.isNull())
13109 throw setError(VBOX_E_INVALID_VM_STATE,
13110 tr("Trying to save state without a running VM"));
13111 alock.release();
13112 BOOL fSuspendedBySave;
13113 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13114 Assert(!fSuspendedBySave);
13115 alock.acquire();
13116
13117 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13118 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13119 throw E_FAIL);
13120
13121 if (SUCCEEDED(rc))
13122 {
13123 mSSData->strStateFilePath = task.m_strStateFilePath;
13124
13125 /* save all VM settings */
13126 rc = i_saveSettings(NULL);
13127 // no need to check whether VirtualBox.xml needs saving also since
13128 // we can't have a name change pending at this point
13129 }
13130 else
13131 {
13132 // On failure, set the state to the state we had at the beginning.
13133 i_setMachineState(task.m_machineStateBackup);
13134 i_updateMachineStateOnClient();
13135
13136 // Delete the saved state file (might have been already created).
13137 // No need to check whether this is shared with a snapshot here
13138 // because we certainly created a fresh saved state file here.
13139 RTFileDelete(task.m_strStateFilePath.c_str());
13140 }
13141 }
13142 catch (HRESULT aRC) { rc = aRC; }
13143
13144 task.m_pProgress->i_notifyComplete(rc);
13145
13146 LogFlowThisFuncLeave();
13147}
13148
13149/**
13150 * @note Locks this object for writing.
13151 */
13152HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13153{
13154 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13155}
13156
13157HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13158{
13159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13160
13161 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13162 if (FAILED(rc)) return rc;
13163
13164 if ( mData->mMachineState != MachineState_Running
13165 && mData->mMachineState != MachineState_Paused
13166 )
13167 return setError(VBOX_E_INVALID_VM_STATE,
13168 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13169 Global::stringifyMachineState(mData->mMachineState));
13170
13171 ComObjPtr<Progress> pProgress;
13172 pProgress.createObject();
13173 rc = pProgress->init(i_getVirtualBox(),
13174 static_cast<IMachine *>(this) /* aInitiator */,
13175 tr("Saving the execution state of the virtual machine"),
13176 FALSE /* aCancelable */);
13177 if (FAILED(rc))
13178 return rc;
13179
13180 Utf8Str strStateFilePath;
13181 i_composeSavedStateFilename(strStateFilePath);
13182
13183 /* create and start the task on a separate thread (note that it will not
13184 * start working until we release alock) */
13185 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13186 rc = pTask->createThread();
13187 if (FAILED(rc))
13188 return rc;
13189
13190 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13191 i_setMachineState(MachineState_Saving);
13192 i_updateMachineStateOnClient();
13193
13194 pProgress.queryInterfaceTo(aProgress.asOutParam());
13195
13196 return S_OK;
13197}
13198
13199/**
13200 * @note Locks this object for writing.
13201 */
13202HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13203{
13204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13205
13206 HRESULT rc = i_checkStateDependency(MutableStateDep);
13207 if (FAILED(rc)) return rc;
13208
13209 if ( mData->mMachineState != MachineState_PoweredOff
13210 && mData->mMachineState != MachineState_Teleported
13211 && mData->mMachineState != MachineState_Aborted
13212 )
13213 return setError(VBOX_E_INVALID_VM_STATE,
13214 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13215 Global::stringifyMachineState(mData->mMachineState));
13216
13217 com::Utf8Str stateFilePathFull;
13218 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13219 if (RT_FAILURE(vrc))
13220 return setError(VBOX_E_FILE_ERROR,
13221 tr("Invalid saved state file path '%s' (%Rrc)"),
13222 aSavedStateFile.c_str(),
13223 vrc);
13224
13225 mSSData->strStateFilePath = stateFilePathFull;
13226
13227 /* The below i_setMachineState() will detect the state transition and will
13228 * update the settings file */
13229
13230 return i_setMachineState(MachineState_Saved);
13231}
13232
13233/**
13234 * @note Locks this object for writing.
13235 */
13236HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13237{
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13241 if (FAILED(rc)) return rc;
13242
13243 if (mData->mMachineState != MachineState_Saved)
13244 return setError(VBOX_E_INVALID_VM_STATE,
13245 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13246 Global::stringifyMachineState(mData->mMachineState));
13247
13248 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13249
13250 /*
13251 * Saved -> PoweredOff transition will be detected in the SessionMachine
13252 * and properly handled.
13253 */
13254 rc = i_setMachineState(MachineState_PoweredOff);
13255 return rc;
13256}
13257
13258
13259/**
13260 * @note Locks the same as #i_setMachineState() does.
13261 */
13262HRESULT SessionMachine::updateState(MachineState_T aState)
13263{
13264 return i_setMachineState(aState);
13265}
13266
13267/**
13268 * @note Locks this object for writing.
13269 */
13270HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13271{
13272 IProgress *pProgress(aProgress);
13273
13274 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13275
13276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13277
13278 if (mData->mSession.mState != SessionState_Locked)
13279 return VBOX_E_INVALID_OBJECT_STATE;
13280
13281 if (!mData->mSession.mProgress.isNull())
13282 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13283
13284 /* If we didn't reference the NAT network service yet, add a reference to
13285 * force a start */
13286 if (miNATNetworksStarted < 1)
13287 {
13288 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13289 {
13290 BOOL enabled;
13291 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13292 if ( FAILED(hrc)
13293 || !enabled)
13294 continue;
13295
13296 NetworkAttachmentType_T type;
13297 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13298 if ( SUCCEEDED(hrc)
13299 && type == NetworkAttachmentType_NATNetwork)
13300 {
13301 Bstr name;
13302 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13303 if (SUCCEEDED(hrc))
13304 {
13305 Utf8Str strName(name);
13306 LogRel(("VM '%s' starts using NAT network '%s'\n",
13307 mUserData->s.strName.c_str(), strName.c_str()));
13308 mPeer->lockHandle()->unlockWrite();
13309 mParent->i_natNetworkRefInc(strName);
13310#ifdef RT_LOCK_STRICT
13311 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13312#else
13313 mPeer->lockHandle()->lockWrite();
13314#endif
13315 }
13316 }
13317 }
13318 miNATNetworksStarted++;
13319 }
13320
13321 LogFlowThisFunc(("returns S_OK.\n"));
13322 return S_OK;
13323}
13324
13325/**
13326 * @note Locks this object for writing.
13327 */
13328HRESULT SessionMachine::endPowerUp(LONG aResult)
13329{
13330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13331
13332 if (mData->mSession.mState != SessionState_Locked)
13333 return VBOX_E_INVALID_OBJECT_STATE;
13334
13335 /* Finalize the LaunchVMProcess progress object. */
13336 if (mData->mSession.mProgress)
13337 {
13338 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13339 mData->mSession.mProgress.setNull();
13340 }
13341
13342 if (SUCCEEDED((HRESULT)aResult))
13343 {
13344#ifdef VBOX_WITH_RESOURCE_USAGE_API
13345 /* The VM has been powered up successfully, so it makes sense
13346 * now to offer the performance metrics for a running machine
13347 * object. Doing it earlier wouldn't be safe. */
13348 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13349 mData->mSession.mPID);
13350#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13351 }
13352
13353 return S_OK;
13354}
13355
13356/**
13357 * @note Locks this object for writing.
13358 */
13359HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13360{
13361 LogFlowThisFuncEnter();
13362
13363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13364
13365 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13366 E_FAIL);
13367
13368 /* create a progress object to track operation completion */
13369 ComObjPtr<Progress> pProgress;
13370 pProgress.createObject();
13371 pProgress->init(i_getVirtualBox(),
13372 static_cast<IMachine *>(this) /* aInitiator */,
13373 tr("Stopping the virtual machine"),
13374 FALSE /* aCancelable */);
13375
13376 /* fill in the console task data */
13377 mConsoleTaskData.mLastState = mData->mMachineState;
13378 mConsoleTaskData.mProgress = pProgress;
13379
13380 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13381 i_setMachineState(MachineState_Stopping);
13382
13383 pProgress.queryInterfaceTo(aProgress.asOutParam());
13384
13385 return S_OK;
13386}
13387
13388/**
13389 * @note Locks this object for writing.
13390 */
13391HRESULT SessionMachine::endPoweringDown(LONG aResult,
13392 const com::Utf8Str &aErrMsg)
13393{
13394 LogFlowThisFuncEnter();
13395
13396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13397
13398 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13399 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13400 && mConsoleTaskData.mLastState != MachineState_Null,
13401 E_FAIL);
13402
13403 /*
13404 * On failure, set the state to the state we had when BeginPoweringDown()
13405 * was called (this is expected by Console::PowerDown() and the associated
13406 * task). On success the VM process already changed the state to
13407 * MachineState_PoweredOff, so no need to do anything.
13408 */
13409 if (FAILED(aResult))
13410 i_setMachineState(mConsoleTaskData.mLastState);
13411
13412 /* notify the progress object about operation completion */
13413 Assert(mConsoleTaskData.mProgress);
13414 if (SUCCEEDED(aResult))
13415 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13416 else
13417 {
13418 if (aErrMsg.length())
13419 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13420 COM_IIDOF(ISession),
13421 getComponentName(),
13422 aErrMsg.c_str());
13423 else
13424 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13425 }
13426
13427 /* clear out the temporary saved state data */
13428 mConsoleTaskData.mLastState = MachineState_Null;
13429 mConsoleTaskData.mProgress.setNull();
13430
13431 LogFlowThisFuncLeave();
13432 return S_OK;
13433}
13434
13435
13436/**
13437 * Goes through the USB filters of the given machine to see if the given
13438 * device matches any filter or not.
13439 *
13440 * @note Locks the same as USBController::hasMatchingFilter() does.
13441 */
13442HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13443 BOOL *aMatched,
13444 ULONG *aMaskedInterfaces)
13445{
13446 LogFlowThisFunc(("\n"));
13447
13448#ifdef VBOX_WITH_USB
13449 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13450#else
13451 NOREF(aDevice);
13452 NOREF(aMaskedInterfaces);
13453 *aMatched = FALSE;
13454#endif
13455
13456 return S_OK;
13457}
13458
13459/**
13460 * @note Locks the same as Host::captureUSBDevice() does.
13461 */
13462HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466#ifdef VBOX_WITH_USB
13467 /* if captureDeviceForVM() fails, it must have set extended error info */
13468 clearError();
13469 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13470 if (FAILED(rc)) return rc;
13471
13472 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13473 AssertReturn(service, E_FAIL);
13474 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13475#else
13476 NOREF(aId);
13477 return E_NOTIMPL;
13478#endif
13479}
13480
13481/**
13482 * @note Locks the same as Host::detachUSBDevice() does.
13483 */
13484HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13485 BOOL aDone)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489#ifdef VBOX_WITH_USB
13490 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13491 AssertReturn(service, E_FAIL);
13492 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13493#else
13494 NOREF(aId);
13495 NOREF(aDone);
13496 return E_NOTIMPL;
13497#endif
13498}
13499
13500/**
13501 * Inserts all machine filters to the USB proxy service and then calls
13502 * Host::autoCaptureUSBDevices().
13503 *
13504 * Called by Console from the VM process upon VM startup.
13505 *
13506 * @note Locks what called methods lock.
13507 */
13508HRESULT SessionMachine::autoCaptureUSBDevices()
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512#ifdef VBOX_WITH_USB
13513 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13514 AssertComRC(rc);
13515 NOREF(rc);
13516
13517 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13518 AssertReturn(service, E_FAIL);
13519 return service->autoCaptureDevicesForVM(this);
13520#else
13521 return S_OK;
13522#endif
13523}
13524
13525/**
13526 * Removes all machine filters from the USB proxy service and then calls
13527 * Host::detachAllUSBDevices().
13528 *
13529 * Called by Console from the VM process upon normal VM termination or by
13530 * SessionMachine::uninit() upon abnormal VM termination (from under the
13531 * Machine/SessionMachine lock).
13532 *
13533 * @note Locks what called methods lock.
13534 */
13535HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13536{
13537 LogFlowThisFunc(("\n"));
13538
13539#ifdef VBOX_WITH_USB
13540 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13541 AssertComRC(rc);
13542 NOREF(rc);
13543
13544 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13545 AssertReturn(service, E_FAIL);
13546 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13547#else
13548 NOREF(aDone);
13549 return S_OK;
13550#endif
13551}
13552
13553/**
13554 * @note Locks this object for writing.
13555 */
13556HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13557 ComPtr<IProgress> &aProgress)
13558{
13559 LogFlowThisFuncEnter();
13560
13561 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13562 /*
13563 * We don't assert below because it might happen that a non-direct session
13564 * informs us it is closed right after we've been uninitialized -- it's ok.
13565 */
13566
13567 /* get IInternalSessionControl interface */
13568 ComPtr<IInternalSessionControl> control(aSession);
13569
13570 ComAssertRet(!control.isNull(), E_INVALIDARG);
13571
13572 /* Creating a Progress object requires the VirtualBox lock, and
13573 * thus locking it here is required by the lock order rules. */
13574 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13575
13576 if (control == mData->mSession.mDirectControl)
13577 {
13578 /* The direct session is being normally closed by the client process
13579 * ----------------------------------------------------------------- */
13580
13581 /* go to the closing state (essential for all open*Session() calls and
13582 * for #i_checkForDeath()) */
13583 Assert(mData->mSession.mState == SessionState_Locked);
13584 mData->mSession.mState = SessionState_Unlocking;
13585
13586 /* set direct control to NULL to release the remote instance */
13587 mData->mSession.mDirectControl.setNull();
13588 LogFlowThisFunc(("Direct control is set to NULL\n"));
13589
13590 if (mData->mSession.mProgress)
13591 {
13592 /* finalize the progress, someone might wait if a frontend
13593 * closes the session before powering on the VM. */
13594 mData->mSession.mProgress->notifyComplete(E_FAIL,
13595 COM_IIDOF(ISession),
13596 getComponentName(),
13597 tr("The VM session was closed before any attempt to power it on"));
13598 mData->mSession.mProgress.setNull();
13599 }
13600
13601 /* Create the progress object the client will use to wait until
13602 * #i_checkForDeath() is called to uninitialize this session object after
13603 * it releases the IPC semaphore.
13604 * Note! Because we're "reusing" mProgress here, this must be a proxy
13605 * object just like for LaunchVMProcess. */
13606 Assert(mData->mSession.mProgress.isNull());
13607 ComObjPtr<ProgressProxy> progress;
13608 progress.createObject();
13609 ComPtr<IUnknown> pPeer(mPeer);
13610 progress->init(mParent, pPeer,
13611 Bstr(tr("Closing session")).raw(),
13612 FALSE /* aCancelable */);
13613 progress.queryInterfaceTo(aProgress.asOutParam());
13614 mData->mSession.mProgress = progress;
13615 }
13616 else
13617 {
13618 /* the remote session is being normally closed */
13619 bool found = false;
13620 for (Data::Session::RemoteControlList::iterator
13621 it = mData->mSession.mRemoteControls.begin();
13622 it != mData->mSession.mRemoteControls.end();
13623 ++it)
13624 {
13625 if (control == *it)
13626 {
13627 found = true;
13628 // This MUST be erase(it), not remove(*it) as the latter
13629 // triggers a very nasty use after free due to the place where
13630 // the value "lives".
13631 mData->mSession.mRemoteControls.erase(it);
13632 break;
13633 }
13634 }
13635 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13636 E_INVALIDARG);
13637 }
13638
13639 /* signal the client watcher thread, because the client is going away */
13640 mParent->i_updateClientWatcher();
13641
13642 LogFlowThisFuncLeave();
13643 return S_OK;
13644}
13645
13646HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13647 std::vector<com::Utf8Str> &aValues,
13648 std::vector<LONG64> &aTimestamps,
13649 std::vector<com::Utf8Str> &aFlags)
13650{
13651 LogFlowThisFunc(("\n"));
13652
13653#ifdef VBOX_WITH_GUEST_PROPS
13654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13655
13656 size_t cEntries = mHWData->mGuestProperties.size();
13657 aNames.resize(cEntries);
13658 aValues.resize(cEntries);
13659 aTimestamps.resize(cEntries);
13660 aFlags.resize(cEntries);
13661
13662 size_t i = 0;
13663 for (HWData::GuestPropertyMap::const_iterator
13664 it = mHWData->mGuestProperties.begin();
13665 it != mHWData->mGuestProperties.end();
13666 ++it, ++i)
13667 {
13668 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13669 aNames[i] = it->first;
13670 aValues[i] = it->second.strValue;
13671 aTimestamps[i] = it->second.mTimestamp;
13672
13673 /* If it is NULL, keep it NULL. */
13674 if (it->second.mFlags)
13675 {
13676 GuestPropWriteFlags(it->second.mFlags, szFlags);
13677 aFlags[i] = szFlags;
13678 }
13679 else
13680 aFlags[i] = "";
13681 }
13682 return S_OK;
13683#else
13684 ReturnComNotImplemented();
13685#endif
13686}
13687
13688HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13689 const com::Utf8Str &aValue,
13690 LONG64 aTimestamp,
13691 const com::Utf8Str &aFlags)
13692{
13693 LogFlowThisFunc(("\n"));
13694
13695#ifdef VBOX_WITH_GUEST_PROPS
13696 try
13697 {
13698 /*
13699 * Convert input up front.
13700 */
13701 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13702 if (aFlags.length())
13703 {
13704 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13705 AssertRCReturn(vrc, E_INVALIDARG);
13706 }
13707
13708 /*
13709 * Now grab the object lock, validate the state and do the update.
13710 */
13711
13712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13713
13714 if (!Global::IsOnline(mData->mMachineState))
13715 {
13716 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13717 VBOX_E_INVALID_VM_STATE);
13718 }
13719
13720 i_setModified(IsModified_MachineData);
13721 mHWData.backup();
13722
13723 bool fDelete = !aValue.length();
13724 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13725 if (it != mHWData->mGuestProperties.end())
13726 {
13727 if (!fDelete)
13728 {
13729 it->second.strValue = aValue;
13730 it->second.mTimestamp = aTimestamp;
13731 it->second.mFlags = fFlags;
13732 }
13733 else
13734 mHWData->mGuestProperties.erase(it);
13735
13736 mData->mGuestPropertiesModified = TRUE;
13737 }
13738 else if (!fDelete)
13739 {
13740 HWData::GuestProperty prop;
13741 prop.strValue = aValue;
13742 prop.mTimestamp = aTimestamp;
13743 prop.mFlags = fFlags;
13744
13745 mHWData->mGuestProperties[aName] = prop;
13746 mData->mGuestPropertiesModified = TRUE;
13747 }
13748
13749 alock.release();
13750
13751 mParent->i_onGuestPropertyChange(mData->mUuid,
13752 Bstr(aName).raw(),
13753 Bstr(aValue).raw(),
13754 Bstr(aFlags).raw());
13755 }
13756 catch (...)
13757 {
13758 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13759 }
13760 return S_OK;
13761#else
13762 ReturnComNotImplemented();
13763#endif
13764}
13765
13766
13767HRESULT SessionMachine::lockMedia()
13768{
13769 AutoMultiWriteLock2 alock(this->lockHandle(),
13770 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13771
13772 AssertReturn( mData->mMachineState == MachineState_Starting
13773 || mData->mMachineState == MachineState_Restoring
13774 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13775
13776 clearError();
13777 alock.release();
13778 return i_lockMedia();
13779}
13780
13781HRESULT SessionMachine::unlockMedia()
13782{
13783 HRESULT hrc = i_unlockMedia();
13784 return hrc;
13785}
13786
13787HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13788 ComPtr<IMediumAttachment> &aNewAttachment)
13789{
13790 // request the host lock first, since might be calling Host methods for getting host drives;
13791 // next, protect the media tree all the while we're in here, as well as our member variables
13792 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13793 this->lockHandle(),
13794 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13795
13796 IMediumAttachment *iAttach = aAttachment;
13797 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13798
13799 Utf8Str ctrlName;
13800 LONG lPort;
13801 LONG lDevice;
13802 bool fTempEject;
13803 {
13804 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13805
13806 /* Need to query the details first, as the IMediumAttachment reference
13807 * might be to the original settings, which we are going to change. */
13808 ctrlName = pAttach->i_getControllerName();
13809 lPort = pAttach->i_getPort();
13810 lDevice = pAttach->i_getDevice();
13811 fTempEject = pAttach->i_getTempEject();
13812 }
13813
13814 if (!fTempEject)
13815 {
13816 /* Remember previously mounted medium. The medium before taking the
13817 * backup is not necessarily the same thing. */
13818 ComObjPtr<Medium> oldmedium;
13819 oldmedium = pAttach->i_getMedium();
13820
13821 i_setModified(IsModified_Storage);
13822 mMediumAttachments.backup();
13823
13824 // The backup operation makes the pAttach reference point to the
13825 // old settings. Re-get the correct reference.
13826 pAttach = i_findAttachment(*mMediumAttachments.data(),
13827 ctrlName,
13828 lPort,
13829 lDevice);
13830
13831 {
13832 AutoCaller autoAttachCaller(this);
13833 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13834
13835 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13836 if (!oldmedium.isNull())
13837 oldmedium->i_removeBackReference(mData->mUuid);
13838
13839 pAttach->i_updateMedium(NULL);
13840 pAttach->i_updateEjected();
13841 }
13842
13843 i_setModified(IsModified_Storage);
13844 }
13845 else
13846 {
13847 {
13848 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13849 pAttach->i_updateEjected();
13850 }
13851 }
13852
13853 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13854
13855 return S_OK;
13856}
13857
13858HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13859 com::Utf8Str &aResult)
13860{
13861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13862
13863 HRESULT hr = S_OK;
13864
13865 if (!mAuthLibCtx.hAuthLibrary)
13866 {
13867 /* Load the external authentication library. */
13868 Bstr authLibrary;
13869 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13870
13871 Utf8Str filename = authLibrary;
13872
13873 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13874 if (RT_FAILURE(rc))
13875 {
13876 hr = setError(E_FAIL,
13877 tr("Could not load the external authentication library '%s' (%Rrc)"),
13878 filename.c_str(), rc);
13879 }
13880 }
13881
13882 /* The auth library might need the machine lock. */
13883 alock.release();
13884
13885 if (FAILED(hr))
13886 return hr;
13887
13888 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13889 {
13890 enum VRDEAuthParams
13891 {
13892 parmUuid = 1,
13893 parmGuestJudgement,
13894 parmUser,
13895 parmPassword,
13896 parmDomain,
13897 parmClientId
13898 };
13899
13900 AuthResult result = AuthResultAccessDenied;
13901
13902 Guid uuid(aAuthParams[parmUuid]);
13903 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13904 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13905
13906 result = AuthLibAuthenticate(&mAuthLibCtx,
13907 uuid.raw(), guestJudgement,
13908 aAuthParams[parmUser].c_str(),
13909 aAuthParams[parmPassword].c_str(),
13910 aAuthParams[parmDomain].c_str(),
13911 u32ClientId);
13912
13913 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13914 size_t cbPassword = aAuthParams[parmPassword].length();
13915 if (cbPassword)
13916 {
13917 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13918 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13919 }
13920
13921 if (result == AuthResultAccessGranted)
13922 aResult = "granted";
13923 else
13924 aResult = "denied";
13925
13926 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13927 aAuthParams[parmUser].c_str(), aResult.c_str()));
13928 }
13929 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13930 {
13931 enum VRDEAuthDisconnectParams
13932 {
13933 parmUuid = 1,
13934 parmClientId
13935 };
13936
13937 Guid uuid(aAuthParams[parmUuid]);
13938 uint32_t u32ClientId = 0;
13939 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13940 }
13941 else
13942 {
13943 hr = E_INVALIDARG;
13944 }
13945
13946 return hr;
13947}
13948
13949// public methods only for internal purposes
13950/////////////////////////////////////////////////////////////////////////////
13951
13952#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13953/**
13954 * Called from the client watcher thread to check for expected or unexpected
13955 * death of the client process that has a direct session to this machine.
13956 *
13957 * On Win32 and on OS/2, this method is called only when we've got the
13958 * mutex (i.e. the client has either died or terminated normally) so it always
13959 * returns @c true (the client is terminated, the session machine is
13960 * uninitialized).
13961 *
13962 * On other platforms, the method returns @c true if the client process has
13963 * terminated normally or abnormally and the session machine was uninitialized,
13964 * and @c false if the client process is still alive.
13965 *
13966 * @note Locks this object for writing.
13967 */
13968bool SessionMachine::i_checkForDeath()
13969{
13970 Uninit::Reason reason;
13971 bool terminated = false;
13972
13973 /* Enclose autoCaller with a block because calling uninit() from under it
13974 * will deadlock. */
13975 {
13976 AutoCaller autoCaller(this);
13977 if (!autoCaller.isOk())
13978 {
13979 /* return true if not ready, to cause the client watcher to exclude
13980 * the corresponding session from watching */
13981 LogFlowThisFunc(("Already uninitialized!\n"));
13982 return true;
13983 }
13984
13985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13986
13987 /* Determine the reason of death: if the session state is Closing here,
13988 * everything is fine. Otherwise it means that the client did not call
13989 * OnSessionEnd() before it released the IPC semaphore. This may happen
13990 * either because the client process has abnormally terminated, or
13991 * because it simply forgot to call ISession::Close() before exiting. We
13992 * threat the latter also as an abnormal termination (see
13993 * Session::uninit() for details). */
13994 reason = mData->mSession.mState == SessionState_Unlocking ?
13995 Uninit::Normal :
13996 Uninit::Abnormal;
13997
13998 if (mClientToken)
13999 terminated = mClientToken->release();
14000 } /* AutoCaller block */
14001
14002 if (terminated)
14003 uninit(reason);
14004
14005 return terminated;
14006}
14007
14008void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 strTokenId.setNull();
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturnVoid(autoCaller.rc());
14016
14017 Assert(mClientToken);
14018 if (mClientToken)
14019 mClientToken->getId(strTokenId);
14020}
14021#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14022IToken *SessionMachine::i_getToken()
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), NULL);
14028
14029 Assert(mClientToken);
14030 if (mClientToken)
14031 return mClientToken->getToken();
14032 else
14033 return NULL;
14034}
14035#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14036
14037Machine::ClientToken *SessionMachine::i_getClientToken()
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), NULL);
14043
14044 return mClientToken;
14045}
14046
14047
14048/**
14049 * @note Locks this object for reading.
14050 */
14051HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14052{
14053 LogFlowThisFunc(("\n"));
14054
14055 AutoCaller autoCaller(this);
14056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14057
14058 ComPtr<IInternalSessionControl> directControl;
14059 {
14060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14061 if (mData->mSession.mLockType == LockType_VM)
14062 directControl = mData->mSession.mDirectControl;
14063 }
14064
14065 /* ignore notifications sent after #OnSessionEnd() is called */
14066 if (!directControl)
14067 return S_OK;
14068
14069 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14070}
14071
14072/**
14073 * @note Locks this object for reading.
14074 */
14075HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14076 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14077 IN_BSTR aGuestIp, LONG aGuestPort)
14078{
14079 LogFlowThisFunc(("\n"));
14080
14081 AutoCaller autoCaller(this);
14082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14083
14084 ComPtr<IInternalSessionControl> directControl;
14085 {
14086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14087 if (mData->mSession.mLockType == LockType_VM)
14088 directControl = mData->mSession.mDirectControl;
14089 }
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094 /*
14095 * instead acting like callback we ask IVirtualBox deliver corresponding event
14096 */
14097
14098 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14099 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14100 return S_OK;
14101}
14102
14103/**
14104 * @note Locks this object for reading.
14105 */
14106HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14107{
14108 LogFlowThisFunc(("\n"));
14109
14110 AutoCaller autoCaller(this);
14111 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14112
14113 ComPtr<IInternalSessionControl> directControl;
14114 {
14115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14116 if (mData->mSession.mLockType == LockType_VM)
14117 directControl = mData->mSession.mDirectControl;
14118 }
14119
14120 /* ignore notifications sent after #OnSessionEnd() is called */
14121 if (!directControl)
14122 return S_OK;
14123
14124 return directControl->OnAudioAdapterChange(audioAdapter);
14125}
14126
14127/**
14128 * @note Locks this object for reading.
14129 */
14130HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14131{
14132 LogFlowThisFunc(("\n"));
14133
14134 AutoCaller autoCaller(this);
14135 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14136
14137 ComPtr<IInternalSessionControl> directControl;
14138 {
14139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14140 if (mData->mSession.mLockType == LockType_VM)
14141 directControl = mData->mSession.mDirectControl;
14142 }
14143
14144 /* ignore notifications sent after #OnSessionEnd() is called */
14145 if (!directControl)
14146 return S_OK;
14147
14148 return directControl->OnSerialPortChange(serialPort);
14149}
14150
14151/**
14152 * @note Locks this object for reading.
14153 */
14154HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14155{
14156 LogFlowThisFunc(("\n"));
14157
14158 AutoCaller autoCaller(this);
14159 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14160
14161 ComPtr<IInternalSessionControl> directControl;
14162 {
14163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14164 if (mData->mSession.mLockType == LockType_VM)
14165 directControl = mData->mSession.mDirectControl;
14166 }
14167
14168 /* ignore notifications sent after #OnSessionEnd() is called */
14169 if (!directControl)
14170 return S_OK;
14171
14172 return directControl->OnParallelPortChange(parallelPort);
14173}
14174
14175/**
14176 * @note Locks this object for reading.
14177 */
14178HRESULT SessionMachine::i_onStorageControllerChange()
14179{
14180 LogFlowThisFunc(("\n"));
14181
14182 AutoCaller autoCaller(this);
14183 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14184
14185 ComPtr<IInternalSessionControl> directControl;
14186 {
14187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14188 if (mData->mSession.mLockType == LockType_VM)
14189 directControl = mData->mSession.mDirectControl;
14190 }
14191
14192 /* ignore notifications sent after #OnSessionEnd() is called */
14193 if (!directControl)
14194 return S_OK;
14195
14196 return directControl->OnStorageControllerChange();
14197}
14198
14199/**
14200 * @note Locks this object for reading.
14201 */
14202HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14203{
14204 LogFlowThisFunc(("\n"));
14205
14206 AutoCaller autoCaller(this);
14207 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14208
14209 ComPtr<IInternalSessionControl> directControl;
14210 {
14211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14212 if (mData->mSession.mLockType == LockType_VM)
14213 directControl = mData->mSession.mDirectControl;
14214 }
14215
14216 /* ignore notifications sent after #OnSessionEnd() is called */
14217 if (!directControl)
14218 return S_OK;
14219
14220 return directControl->OnMediumChange(aAttachment, aForce);
14221}
14222
14223/**
14224 * @note Locks this object for reading.
14225 */
14226HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14227{
14228 LogFlowThisFunc(("\n"));
14229
14230 AutoCaller autoCaller(this);
14231 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14232
14233 ComPtr<IInternalSessionControl> directControl;
14234 {
14235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14236 if (mData->mSession.mLockType == LockType_VM)
14237 directControl = mData->mSession.mDirectControl;
14238 }
14239
14240 /* ignore notifications sent after #OnSessionEnd() is called */
14241 if (!directControl)
14242 return S_OK;
14243
14244 return directControl->OnCPUChange(aCPU, aRemove);
14245}
14246
14247HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14248{
14249 LogFlowThisFunc(("\n"));
14250
14251 AutoCaller autoCaller(this);
14252 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14253
14254 ComPtr<IInternalSessionControl> directControl;
14255 {
14256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14257 if (mData->mSession.mLockType == LockType_VM)
14258 directControl = mData->mSession.mDirectControl;
14259 }
14260
14261 /* ignore notifications sent after #OnSessionEnd() is called */
14262 if (!directControl)
14263 return S_OK;
14264
14265 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14266}
14267
14268/**
14269 * @note Locks this object for reading.
14270 */
14271HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14272{
14273 LogFlowThisFunc(("\n"));
14274
14275 AutoCaller autoCaller(this);
14276 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14277
14278 ComPtr<IInternalSessionControl> directControl;
14279 {
14280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14281 if (mData->mSession.mLockType == LockType_VM)
14282 directControl = mData->mSession.mDirectControl;
14283 }
14284
14285 /* ignore notifications sent after #OnSessionEnd() is called */
14286 if (!directControl)
14287 return S_OK;
14288
14289 return directControl->OnVRDEServerChange(aRestart);
14290}
14291
14292/**
14293 * @note Locks this object for reading.
14294 */
14295HRESULT SessionMachine::i_onVideoCaptureChange()
14296{
14297 LogFlowThisFunc(("\n"));
14298
14299 AutoCaller autoCaller(this);
14300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14301
14302 ComPtr<IInternalSessionControl> directControl;
14303 {
14304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14305 if (mData->mSession.mLockType == LockType_VM)
14306 directControl = mData->mSession.mDirectControl;
14307 }
14308
14309 /* ignore notifications sent after #OnSessionEnd() is called */
14310 if (!directControl)
14311 return S_OK;
14312
14313 return directControl->OnVideoCaptureChange();
14314}
14315
14316/**
14317 * @note Locks this object for reading.
14318 */
14319HRESULT SessionMachine::i_onUSBControllerChange()
14320{
14321 LogFlowThisFunc(("\n"));
14322
14323 AutoCaller autoCaller(this);
14324 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14325
14326 ComPtr<IInternalSessionControl> directControl;
14327 {
14328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14329 if (mData->mSession.mLockType == LockType_VM)
14330 directControl = mData->mSession.mDirectControl;
14331 }
14332
14333 /* ignore notifications sent after #OnSessionEnd() is called */
14334 if (!directControl)
14335 return S_OK;
14336
14337 return directControl->OnUSBControllerChange();
14338}
14339
14340/**
14341 * @note Locks this object for reading.
14342 */
14343HRESULT SessionMachine::i_onSharedFolderChange()
14344{
14345 LogFlowThisFunc(("\n"));
14346
14347 AutoCaller autoCaller(this);
14348 AssertComRCReturnRC(autoCaller.rc());
14349
14350 ComPtr<IInternalSessionControl> directControl;
14351 {
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353 if (mData->mSession.mLockType == LockType_VM)
14354 directControl = mData->mSession.mDirectControl;
14355 }
14356
14357 /* ignore notifications sent after #OnSessionEnd() is called */
14358 if (!directControl)
14359 return S_OK;
14360
14361 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14362}
14363
14364/**
14365 * @note Locks this object for reading.
14366 */
14367HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14368{
14369 LogFlowThisFunc(("\n"));
14370
14371 AutoCaller autoCaller(this);
14372 AssertComRCReturnRC(autoCaller.rc());
14373
14374 ComPtr<IInternalSessionControl> directControl;
14375 {
14376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14377 if (mData->mSession.mLockType == LockType_VM)
14378 directControl = mData->mSession.mDirectControl;
14379 }
14380
14381 /* ignore notifications sent after #OnSessionEnd() is called */
14382 if (!directControl)
14383 return S_OK;
14384
14385 return directControl->OnClipboardModeChange(aClipboardMode);
14386}
14387
14388/**
14389 * @note Locks this object for reading.
14390 */
14391HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14392{
14393 LogFlowThisFunc(("\n"));
14394
14395 AutoCaller autoCaller(this);
14396 AssertComRCReturnRC(autoCaller.rc());
14397
14398 ComPtr<IInternalSessionControl> directControl;
14399 {
14400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14401 if (mData->mSession.mLockType == LockType_VM)
14402 directControl = mData->mSession.mDirectControl;
14403 }
14404
14405 /* ignore notifications sent after #OnSessionEnd() is called */
14406 if (!directControl)
14407 return S_OK;
14408
14409 return directControl->OnDnDModeChange(aDnDMode);
14410}
14411
14412/**
14413 * @note Locks this object for reading.
14414 */
14415HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14416{
14417 LogFlowThisFunc(("\n"));
14418
14419 AutoCaller autoCaller(this);
14420 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14421
14422 ComPtr<IInternalSessionControl> directControl;
14423 {
14424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14425 if (mData->mSession.mLockType == LockType_VM)
14426 directControl = mData->mSession.mDirectControl;
14427 }
14428
14429 /* ignore notifications sent after #OnSessionEnd() is called */
14430 if (!directControl)
14431 return S_OK;
14432
14433 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14434}
14435
14436/**
14437 * @note Locks this object for reading.
14438 */
14439HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14440{
14441 LogFlowThisFunc(("\n"));
14442
14443 AutoCaller autoCaller(this);
14444 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14445
14446 ComPtr<IInternalSessionControl> directControl;
14447 {
14448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14449 if (mData->mSession.mLockType == LockType_VM)
14450 directControl = mData->mSession.mDirectControl;
14451 }
14452
14453 /* ignore notifications sent after #OnSessionEnd() is called */
14454 if (!directControl)
14455 return S_OK;
14456
14457 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14458}
14459
14460/**
14461 * Returns @c true if this machine's USB controller reports it has a matching
14462 * filter for the given USB device and @c false otherwise.
14463 *
14464 * @note locks this object for reading.
14465 */
14466bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14467{
14468 AutoCaller autoCaller(this);
14469 /* silently return if not ready -- this method may be called after the
14470 * direct machine session has been called */
14471 if (!autoCaller.isOk())
14472 return false;
14473
14474#ifdef VBOX_WITH_USB
14475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14476
14477 switch (mData->mMachineState)
14478 {
14479 case MachineState_Starting:
14480 case MachineState_Restoring:
14481 case MachineState_TeleportingIn:
14482 case MachineState_Paused:
14483 case MachineState_Running:
14484 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14485 * elsewhere... */
14486 alock.release();
14487 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14488 default: break;
14489 }
14490#else
14491 NOREF(aDevice);
14492 NOREF(aMaskedIfs);
14493#endif
14494 return false;
14495}
14496
14497/**
14498 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14499 */
14500HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14501 IVirtualBoxErrorInfo *aError,
14502 ULONG aMaskedIfs,
14503 const com::Utf8Str &aCaptureFilename)
14504{
14505 LogFlowThisFunc(("\n"));
14506
14507 AutoCaller autoCaller(this);
14508
14509 /* This notification may happen after the machine object has been
14510 * uninitialized (the session was closed), so don't assert. */
14511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14512
14513 ComPtr<IInternalSessionControl> directControl;
14514 {
14515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14516 if (mData->mSession.mLockType == LockType_VM)
14517 directControl = mData->mSession.mDirectControl;
14518 }
14519
14520 /* fail on notifications sent after #OnSessionEnd() is called, it is
14521 * expected by the caller */
14522 if (!directControl)
14523 return E_FAIL;
14524
14525 /* No locks should be held at this point. */
14526 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14527 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14528
14529 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14530}
14531
14532/**
14533 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14534 */
14535HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14536 IVirtualBoxErrorInfo *aError)
14537{
14538 LogFlowThisFunc(("\n"));
14539
14540 AutoCaller autoCaller(this);
14541
14542 /* This notification may happen after the machine object has been
14543 * uninitialized (the session was closed), so don't assert. */
14544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14545
14546 ComPtr<IInternalSessionControl> directControl;
14547 {
14548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14549 if (mData->mSession.mLockType == LockType_VM)
14550 directControl = mData->mSession.mDirectControl;
14551 }
14552
14553 /* fail on notifications sent after #OnSessionEnd() is called, it is
14554 * expected by the caller */
14555 if (!directControl)
14556 return E_FAIL;
14557
14558 /* No locks should be held at this point. */
14559 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14560 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14561
14562 return directControl->OnUSBDeviceDetach(aId, aError);
14563}
14564
14565// protected methods
14566/////////////////////////////////////////////////////////////////////////////
14567
14568/**
14569 * Deletes the given file if it is no longer in use by either the current machine state
14570 * (if the machine is "saved") or any of the machine's snapshots.
14571 *
14572 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14573 * but is different for each SnapshotMachine. When calling this, the order of calling this
14574 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14575 * is therefore critical. I know, it's all rather messy.
14576 *
14577 * @param strStateFile
14578 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14579 * the test for whether the saved state file is in use.
14580 */
14581void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14582 Snapshot *pSnapshotToIgnore)
14583{
14584 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14585 if ( (strStateFile.isNotEmpty())
14586 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14587 )
14588 // ... and it must also not be shared with other snapshots
14589 if ( !mData->mFirstSnapshot
14590 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14591 // this checks the SnapshotMachine's state file paths
14592 )
14593 RTFileDelete(strStateFile.c_str());
14594}
14595
14596/**
14597 * Locks the attached media.
14598 *
14599 * All attached hard disks are locked for writing and DVD/floppy are locked for
14600 * reading. Parents of attached hard disks (if any) are locked for reading.
14601 *
14602 * This method also performs accessibility check of all media it locks: if some
14603 * media is inaccessible, the method will return a failure and a bunch of
14604 * extended error info objects per each inaccessible medium.
14605 *
14606 * Note that this method is atomic: if it returns a success, all media are
14607 * locked as described above; on failure no media is locked at all (all
14608 * succeeded individual locks will be undone).
14609 *
14610 * The caller is responsible for doing the necessary state sanity checks.
14611 *
14612 * The locks made by this method must be undone by calling #unlockMedia() when
14613 * no more needed.
14614 */
14615HRESULT SessionMachine::i_lockMedia()
14616{
14617 AutoCaller autoCaller(this);
14618 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14619
14620 AutoMultiWriteLock2 alock(this->lockHandle(),
14621 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14622
14623 /* bail out if trying to lock things with already set up locking */
14624 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14625
14626 MultiResult mrc(S_OK);
14627
14628 /* Collect locking information for all medium objects attached to the VM. */
14629 for (MediumAttachmentList::const_iterator
14630 it = mMediumAttachments->begin();
14631 it != mMediumAttachments->end();
14632 ++it)
14633 {
14634 MediumAttachment *pAtt = *it;
14635 DeviceType_T devType = pAtt->i_getType();
14636 Medium *pMedium = pAtt->i_getMedium();
14637
14638 MediumLockList *pMediumLockList(new MediumLockList());
14639 // There can be attachments without a medium (floppy/dvd), and thus
14640 // it's impossible to create a medium lock list. It still makes sense
14641 // to have the empty medium lock list in the map in case a medium is
14642 // attached later.
14643 if (pMedium != NULL)
14644 {
14645 MediumType_T mediumType = pMedium->i_getType();
14646 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14647 || mediumType == MediumType_Shareable;
14648 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14649
14650 alock.release();
14651 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14652 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14653 false /* fMediumLockWriteAll */,
14654 NULL,
14655 *pMediumLockList);
14656 alock.acquire();
14657 if (FAILED(mrc))
14658 {
14659 delete pMediumLockList;
14660 mData->mSession.mLockedMedia.Clear();
14661 break;
14662 }
14663 }
14664
14665 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14666 if (FAILED(rc))
14667 {
14668 mData->mSession.mLockedMedia.Clear();
14669 mrc = setError(rc,
14670 tr("Collecting locking information for all attached media failed"));
14671 break;
14672 }
14673 }
14674
14675 if (SUCCEEDED(mrc))
14676 {
14677 /* Now lock all media. If this fails, nothing is locked. */
14678 alock.release();
14679 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14680 alock.acquire();
14681 if (FAILED(rc))
14682 {
14683 mrc = setError(rc,
14684 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14685 }
14686 }
14687
14688 return mrc;
14689}
14690
14691/**
14692 * Undoes the locks made by by #lockMedia().
14693 */
14694HRESULT SessionMachine::i_unlockMedia()
14695{
14696 AutoCaller autoCaller(this);
14697 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14698
14699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14700
14701 /* we may be holding important error info on the current thread;
14702 * preserve it */
14703 ErrorInfoKeeper eik;
14704
14705 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14706 AssertComRC(rc);
14707 return rc;
14708}
14709
14710/**
14711 * Helper to change the machine state (reimplementation).
14712 *
14713 * @note Locks this object for writing.
14714 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14715 * it can cause crashes in random places due to unexpectedly committing
14716 * the current settings. The caller is responsible for that. The call
14717 * to saveStateSettings is fine, because this method does not commit.
14718 */
14719HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14720{
14721 LogFlowThisFuncEnter();
14722 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14723
14724 AutoCaller autoCaller(this);
14725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14726
14727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14728
14729 MachineState_T oldMachineState = mData->mMachineState;
14730
14731 AssertMsgReturn(oldMachineState != aMachineState,
14732 ("oldMachineState=%s, aMachineState=%s\n",
14733 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14734 E_FAIL);
14735
14736 HRESULT rc = S_OK;
14737
14738 int stsFlags = 0;
14739 bool deleteSavedState = false;
14740
14741 /* detect some state transitions */
14742
14743 if ( ( oldMachineState == MachineState_Saved
14744 && aMachineState == MachineState_Restoring)
14745 || ( ( oldMachineState == MachineState_PoweredOff
14746 || oldMachineState == MachineState_Teleported
14747 || oldMachineState == MachineState_Aborted
14748 )
14749 && ( aMachineState == MachineState_TeleportingIn
14750 || aMachineState == MachineState_Starting
14751 )
14752 )
14753 )
14754 {
14755 /* The EMT thread is about to start */
14756
14757 /* Nothing to do here for now... */
14758
14759 /// @todo NEWMEDIA don't let mDVDDrive and other children
14760 /// change anything when in the Starting/Restoring state
14761 }
14762 else if ( ( oldMachineState == MachineState_Running
14763 || oldMachineState == MachineState_Paused
14764 || oldMachineState == MachineState_Teleporting
14765 || oldMachineState == MachineState_OnlineSnapshotting
14766 || oldMachineState == MachineState_LiveSnapshotting
14767 || oldMachineState == MachineState_Stuck
14768 || oldMachineState == MachineState_Starting
14769 || oldMachineState == MachineState_Stopping
14770 || oldMachineState == MachineState_Saving
14771 || oldMachineState == MachineState_Restoring
14772 || oldMachineState == MachineState_TeleportingPausedVM
14773 || oldMachineState == MachineState_TeleportingIn
14774 )
14775 && ( aMachineState == MachineState_PoweredOff
14776 || aMachineState == MachineState_Saved
14777 || aMachineState == MachineState_Teleported
14778 || aMachineState == MachineState_Aborted
14779 )
14780 )
14781 {
14782 /* The EMT thread has just stopped, unlock attached media. Note that as
14783 * opposed to locking that is done from Console, we do unlocking here
14784 * because the VM process may have aborted before having a chance to
14785 * properly unlock all media it locked. */
14786
14787 unlockMedia();
14788 }
14789
14790 if (oldMachineState == MachineState_Restoring)
14791 {
14792 if (aMachineState != MachineState_Saved)
14793 {
14794 /*
14795 * delete the saved state file once the machine has finished
14796 * restoring from it (note that Console sets the state from
14797 * Restoring to Saved if the VM couldn't restore successfully,
14798 * to give the user an ability to fix an error and retry --
14799 * we keep the saved state file in this case)
14800 */
14801 deleteSavedState = true;
14802 }
14803 }
14804 else if ( oldMachineState == MachineState_Saved
14805 && ( aMachineState == MachineState_PoweredOff
14806 || aMachineState == MachineState_Aborted
14807 || aMachineState == MachineState_Teleported
14808 )
14809 )
14810 {
14811 /*
14812 * delete the saved state after SessionMachine::ForgetSavedState() is called
14813 * or if the VM process (owning a direct VM session) crashed while the
14814 * VM was Saved
14815 */
14816
14817 /// @todo (dmik)
14818 // Not sure that deleting the saved state file just because of the
14819 // client death before it attempted to restore the VM is a good
14820 // thing. But when it crashes we need to go to the Aborted state
14821 // which cannot have the saved state file associated... The only
14822 // way to fix this is to make the Aborted condition not a VM state
14823 // but a bool flag: i.e., when a crash occurs, set it to true and
14824 // change the state to PoweredOff or Saved depending on the
14825 // saved state presence.
14826
14827 deleteSavedState = true;
14828 mData->mCurrentStateModified = TRUE;
14829 stsFlags |= SaveSTS_CurStateModified;
14830 }
14831
14832 if ( aMachineState == MachineState_Starting
14833 || aMachineState == MachineState_Restoring
14834 || aMachineState == MachineState_TeleportingIn
14835 )
14836 {
14837 /* set the current state modified flag to indicate that the current
14838 * state is no more identical to the state in the
14839 * current snapshot */
14840 if (!mData->mCurrentSnapshot.isNull())
14841 {
14842 mData->mCurrentStateModified = TRUE;
14843 stsFlags |= SaveSTS_CurStateModified;
14844 }
14845 }
14846
14847 if (deleteSavedState)
14848 {
14849 if (mRemoveSavedState)
14850 {
14851 Assert(!mSSData->strStateFilePath.isEmpty());
14852
14853 // it is safe to delete the saved state file if ...
14854 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14855 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14856 // ... none of the snapshots share the saved state file
14857 )
14858 RTFileDelete(mSSData->strStateFilePath.c_str());
14859 }
14860
14861 mSSData->strStateFilePath.setNull();
14862 stsFlags |= SaveSTS_StateFilePath;
14863 }
14864
14865 /* redirect to the underlying peer machine */
14866 mPeer->i_setMachineState(aMachineState);
14867
14868 if ( oldMachineState != MachineState_RestoringSnapshot
14869 && ( aMachineState == MachineState_PoweredOff
14870 || aMachineState == MachineState_Teleported
14871 || aMachineState == MachineState_Aborted
14872 || aMachineState == MachineState_Saved))
14873 {
14874 /* the machine has stopped execution
14875 * (or the saved state file was adopted) */
14876 stsFlags |= SaveSTS_StateTimeStamp;
14877 }
14878
14879 if ( ( oldMachineState == MachineState_PoweredOff
14880 || oldMachineState == MachineState_Aborted
14881 || oldMachineState == MachineState_Teleported
14882 )
14883 && aMachineState == MachineState_Saved)
14884 {
14885 /* the saved state file was adopted */
14886 Assert(!mSSData->strStateFilePath.isEmpty());
14887 stsFlags |= SaveSTS_StateFilePath;
14888 }
14889
14890#ifdef VBOX_WITH_GUEST_PROPS
14891 if ( aMachineState == MachineState_PoweredOff
14892 || aMachineState == MachineState_Aborted
14893 || aMachineState == MachineState_Teleported)
14894 {
14895 /* Make sure any transient guest properties get removed from the
14896 * property store on shutdown. */
14897 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14898
14899 /* remove it from the settings representation */
14900 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14901 for (settings::GuestPropertiesList::iterator
14902 it = llGuestProperties.begin();
14903 it != llGuestProperties.end();
14904 /*nothing*/)
14905 {
14906 const settings::GuestProperty &prop = *it;
14907 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14908 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14909 {
14910 it = llGuestProperties.erase(it);
14911 fNeedsSaving = true;
14912 }
14913 else
14914 {
14915 ++it;
14916 }
14917 }
14918
14919 /* Additionally remove it from the HWData representation. Required to
14920 * keep everything in sync, as this is what the API keeps using. */
14921 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14922 for (HWData::GuestPropertyMap::iterator
14923 it = llHWGuestProperties.begin();
14924 it != llHWGuestProperties.end();
14925 /*nothing*/)
14926 {
14927 uint32_t fFlags = it->second.mFlags;
14928 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14929 {
14930 /* iterator where we need to continue after the erase call
14931 * (C++03 is a fact still, and it doesn't return the iterator
14932 * which would allow continuing) */
14933 HWData::GuestPropertyMap::iterator it2 = it;
14934 ++it2;
14935 llHWGuestProperties.erase(it);
14936 it = it2;
14937 fNeedsSaving = true;
14938 }
14939 else
14940 {
14941 ++it;
14942 }
14943 }
14944
14945 if (fNeedsSaving)
14946 {
14947 mData->mCurrentStateModified = TRUE;
14948 stsFlags |= SaveSTS_CurStateModified;
14949 }
14950 }
14951#endif /* VBOX_WITH_GUEST_PROPS */
14952
14953 rc = i_saveStateSettings(stsFlags);
14954
14955 if ( ( oldMachineState != MachineState_PoweredOff
14956 && oldMachineState != MachineState_Aborted
14957 && oldMachineState != MachineState_Teleported
14958 )
14959 && ( aMachineState == MachineState_PoweredOff
14960 || aMachineState == MachineState_Aborted
14961 || aMachineState == MachineState_Teleported
14962 )
14963 )
14964 {
14965 /* we've been shut down for any reason */
14966 /* no special action so far */
14967 }
14968
14969 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14970 LogFlowThisFuncLeave();
14971 return rc;
14972}
14973
14974/**
14975 * Sends the current machine state value to the VM process.
14976 *
14977 * @note Locks this object for reading, then calls a client process.
14978 */
14979HRESULT SessionMachine::i_updateMachineStateOnClient()
14980{
14981 AutoCaller autoCaller(this);
14982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14983
14984 ComPtr<IInternalSessionControl> directControl;
14985 {
14986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14987 AssertReturn(!!mData, E_FAIL);
14988 if (mData->mSession.mLockType == LockType_VM)
14989 directControl = mData->mSession.mDirectControl;
14990
14991 /* directControl may be already set to NULL here in #OnSessionEnd()
14992 * called too early by the direct session process while there is still
14993 * some operation (like deleting the snapshot) in progress. The client
14994 * process in this case is waiting inside Session::close() for the
14995 * "end session" process object to complete, while #uninit() called by
14996 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14997 * operation to complete. For now, we accept this inconsistent behavior
14998 * and simply do nothing here. */
14999
15000 if (mData->mSession.mState == SessionState_Unlocking)
15001 return S_OK;
15002 }
15003
15004 /* ignore notifications sent after #OnSessionEnd() is called */
15005 if (!directControl)
15006 return S_OK;
15007
15008 return directControl->UpdateMachineState(mData->mMachineState);
15009}
15010
15011
15012/*static*/
15013HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15014{
15015 va_list args;
15016 va_start(args, pcszMsg);
15017 HRESULT rc = setErrorInternal(aResultCode,
15018 getStaticClassIID(),
15019 getStaticComponentName(),
15020 Utf8Str(pcszMsg, args),
15021 false /* aWarning */,
15022 true /* aLogIt */);
15023 va_end(args);
15024 return rc;
15025}
15026
15027
15028HRESULT Machine::updateState(MachineState_T aState)
15029{
15030 NOREF(aState);
15031 ReturnComNotImplemented();
15032}
15033
15034HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15035{
15036 NOREF(aProgress);
15037 ReturnComNotImplemented();
15038}
15039
15040HRESULT Machine::endPowerUp(LONG aResult)
15041{
15042 NOREF(aResult);
15043 ReturnComNotImplemented();
15044}
15045
15046HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15047{
15048 NOREF(aProgress);
15049 ReturnComNotImplemented();
15050}
15051
15052HRESULT Machine::endPoweringDown(LONG aResult,
15053 const com::Utf8Str &aErrMsg)
15054{
15055 NOREF(aResult);
15056 NOREF(aErrMsg);
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15061 BOOL *aMatched,
15062 ULONG *aMaskedInterfaces)
15063{
15064 NOREF(aDevice);
15065 NOREF(aMatched);
15066 NOREF(aMaskedInterfaces);
15067 ReturnComNotImplemented();
15068
15069}
15070
15071HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15072{
15073 NOREF(aId); NOREF(aCaptureFilename);
15074 ReturnComNotImplemented();
15075}
15076
15077HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15078 BOOL aDone)
15079{
15080 NOREF(aId);
15081 NOREF(aDone);
15082 ReturnComNotImplemented();
15083}
15084
15085HRESULT Machine::autoCaptureUSBDevices()
15086{
15087 ReturnComNotImplemented();
15088}
15089
15090HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15091{
15092 NOREF(aDone);
15093 ReturnComNotImplemented();
15094}
15095
15096HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15097 ComPtr<IProgress> &aProgress)
15098{
15099 NOREF(aSession);
15100 NOREF(aProgress);
15101 ReturnComNotImplemented();
15102}
15103
15104HRESULT Machine::finishOnlineMergeMedium()
15105{
15106 ReturnComNotImplemented();
15107}
15108
15109HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15110 std::vector<com::Utf8Str> &aValues,
15111 std::vector<LONG64> &aTimestamps,
15112 std::vector<com::Utf8Str> &aFlags)
15113{
15114 NOREF(aNames);
15115 NOREF(aValues);
15116 NOREF(aTimestamps);
15117 NOREF(aFlags);
15118 ReturnComNotImplemented();
15119}
15120
15121HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15122 const com::Utf8Str &aValue,
15123 LONG64 aTimestamp,
15124 const com::Utf8Str &aFlags)
15125{
15126 NOREF(aName);
15127 NOREF(aValue);
15128 NOREF(aTimestamp);
15129 NOREF(aFlags);
15130 ReturnComNotImplemented();
15131}
15132
15133HRESULT Machine::lockMedia()
15134{
15135 ReturnComNotImplemented();
15136}
15137
15138HRESULT Machine::unlockMedia()
15139{
15140 ReturnComNotImplemented();
15141}
15142
15143HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15144 ComPtr<IMediumAttachment> &aNewAttachment)
15145{
15146 NOREF(aAttachment);
15147 NOREF(aNewAttachment);
15148 ReturnComNotImplemented();
15149}
15150
15151HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15152 ULONG aCpuUser,
15153 ULONG aCpuKernel,
15154 ULONG aCpuIdle,
15155 ULONG aMemTotal,
15156 ULONG aMemFree,
15157 ULONG aMemBalloon,
15158 ULONG aMemShared,
15159 ULONG aMemCache,
15160 ULONG aPagedTotal,
15161 ULONG aMemAllocTotal,
15162 ULONG aMemFreeTotal,
15163 ULONG aMemBalloonTotal,
15164 ULONG aMemSharedTotal,
15165 ULONG aVmNetRx,
15166 ULONG aVmNetTx)
15167{
15168 NOREF(aValidStats);
15169 NOREF(aCpuUser);
15170 NOREF(aCpuKernel);
15171 NOREF(aCpuIdle);
15172 NOREF(aMemTotal);
15173 NOREF(aMemFree);
15174 NOREF(aMemBalloon);
15175 NOREF(aMemShared);
15176 NOREF(aMemCache);
15177 NOREF(aPagedTotal);
15178 NOREF(aMemAllocTotal);
15179 NOREF(aMemFreeTotal);
15180 NOREF(aMemBalloonTotal);
15181 NOREF(aMemSharedTotal);
15182 NOREF(aVmNetRx);
15183 NOREF(aVmNetTx);
15184 ReturnComNotImplemented();
15185}
15186
15187HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15188 com::Utf8Str &aResult)
15189{
15190 NOREF(aAuthParams);
15191 NOREF(aResult);
15192 ReturnComNotImplemented();
15193}
15194
15195HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15196{
15197 NOREF(aFlags);
15198 ReturnComNotImplemented();
15199}
15200
15201/* This isn't handled entirely by the wrapper generator yet. */
15202#ifdef VBOX_WITH_XPCOM
15203NS_DECL_CLASSINFO(SessionMachine)
15204NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15205
15206NS_DECL_CLASSINFO(SnapshotMachine)
15207NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15208#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