VirtualBox

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

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

Main/Machine: keep default '/' group if no group is specified on VM creation

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 527.0 KB
Line 
1/* $Id: MachineImpl.cpp 72700 2018-06-27 13:48:05Z 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 mHWVirtExUseNativeApi = false;
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mTripleFaultReset = false;
197 mAPIC = true;
198 mX2APIC = false;
199 mIBPBOnVMExit = false;
200 mIBPBOnVMEntry = false;
201 mSpecCtrl = false;
202 mSpecCtrlByHost = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
284 * scheme (includes the UUID).
285 *
286 * @return Success indicator. if not S_OK, the machine object is invalid
287 */
288HRESULT Machine::init(VirtualBox *aParent,
289 const Utf8Str &strConfigFile,
290 const Utf8Str &strName,
291 const StringsList &llGroups,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 if (llGroups.size())
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->i_applyDefaults(aOsType);
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355
356 /* Apply network adapters defaults */
357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
358 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
359
360 /* Apply serial port defaults */
361 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
362 mSerialPorts[slot]->i_applyDefaults(aOsType);
363
364 /* Apply parallel port defaults */
365 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
366 mParallelPorts[slot]->i_applyDefaults();
367
368 /* At this point the changing of the current state modification
369 * flag is allowed. */
370 i_allowStateModification();
371
372 /* commit all changes made during the initialization */
373 i_commit();
374 }
375
376 /* Confirm a successful initialization when it's the case */
377 if (SUCCEEDED(rc))
378 {
379 if (mData->mAccessible)
380 autoInitSpan.setSucceeded();
381 else
382 autoInitSpan.setLimited();
383 }
384
385 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
386 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
387 mData->mRegistered,
388 mData->mAccessible,
389 rc));
390
391 LogFlowThisFuncLeave();
392
393 return rc;
394}
395
396/**
397 * Initializes a new instance with data from machine XML (formerly Init_Registered).
398 * Gets called in two modes:
399 *
400 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
401 * UUID is specified and we mark the machine as "registered";
402 *
403 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
404 * and the machine remains unregistered until RegisterMachine() is called.
405 *
406 * @param aParent Associated parent object
407 * @param strConfigFile Local file system path to the VM settings file (can
408 * be relative to the VirtualBox config directory).
409 * @param aId UUID of the machine or NULL (see above).
410 *
411 * @return Success indicator. if not S_OK, the machine object is invalid
412 */
413HRESULT Machine::initFromSettings(VirtualBox *aParent,
414 const Utf8Str &strConfigFile,
415 const Guid *aId)
416{
417 LogFlowThisFuncEnter();
418 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
419
420 /* Enclose the state transition NotReady->InInit->Ready */
421 AutoInitSpan autoInitSpan(this);
422 AssertReturn(autoInitSpan.isOk(), E_FAIL);
423
424 HRESULT rc = initImpl(aParent, strConfigFile);
425 if (FAILED(rc)) return rc;
426
427 if (aId)
428 {
429 // loading a registered VM:
430 unconst(mData->mUuid) = *aId;
431 mData->mRegistered = TRUE;
432 // now load the settings from XML:
433 rc = i_registeredInit();
434 // this calls initDataAndChildObjects() and loadSettings()
435 }
436 else
437 {
438 // opening an unregistered VM (VirtualBox::OpenMachine()):
439 rc = initDataAndChildObjects();
440
441 if (SUCCEEDED(rc))
442 {
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 try
447 {
448 // load and parse machine XML; this will throw on XML or logic errors
449 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
450
451 // reject VM UUID duplicates, they can happen if someone
452 // tries to register an already known VM config again
453 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
454 true /* fPermitInaccessible */,
455 false /* aDoSetError */,
456 NULL) != VBOX_E_OBJECT_NOT_FOUND)
457 {
458 throw setError(E_FAIL,
459 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
460 mData->m_strConfigFile.c_str());
461 }
462
463 // use UUID from machine config
464 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
465
466 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
467 NULL /* puuidRegistry */);
468 if (FAILED(rc)) throw rc;
469
470 /* At this point the changing of the current state modification
471 * flag is allowed. */
472 i_allowStateModification();
473
474 i_commit();
475 }
476 catch (HRESULT err)
477 {
478 /* we assume that error info is set by the thrower */
479 rc = err;
480 }
481 catch (...)
482 {
483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
484 }
485 }
486 }
487
488 /* Confirm a successful initialization when it's the case */
489 if (SUCCEEDED(rc))
490 {
491 if (mData->mAccessible)
492 autoInitSpan.setSucceeded();
493 else
494 {
495 autoInitSpan.setLimited();
496
497 // uninit media from this machine's media registry, or else
498 // reloading the settings will fail
499 mParent->i_unregisterMachineMedia(i_getId());
500 }
501 }
502
503 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
504 "rc=%08X\n",
505 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
506 mData->mRegistered, mData->mAccessible, rc));
507
508 LogFlowThisFuncLeave();
509
510 return rc;
511}
512
513/**
514 * Initializes a new instance from a machine config that is already in memory
515 * (import OVF case). Since we are importing, the UUID in the machine
516 * config is ignored and we always generate a fresh one.
517 *
518 * @param aParent Associated parent object.
519 * @param strName Name for the new machine; this overrides what is specified in config.
520 * @param strSettingsFilename File name of .vbox file.
521 * @param config Machine configuration loaded and parsed from XML.
522 *
523 * @return Success indicator. if not S_OK, the machine object is invalid
524 */
525HRESULT Machine::init(VirtualBox *aParent,
526 const Utf8Str &strName,
527 const Utf8Str &strSettingsFilename,
528 const settings::MachineConfigFile &config)
529{
530 LogFlowThisFuncEnter();
531
532 /* Enclose the state transition NotReady->InInit->Ready */
533 AutoInitSpan autoInitSpan(this);
534 AssertReturn(autoInitSpan.isOk(), E_FAIL);
535
536 HRESULT rc = initImpl(aParent, strSettingsFilename);
537 if (FAILED(rc)) return rc;
538
539 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = i_loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 i_allowStateModification();
566
567 /* commit all changes made during the initialization */
568 i_commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 /* Ignore all errors from unregistering, they would destroy
580- * the more interesting error information we already have,
581- * pinpointing the issue with the VM config. */
582 ErrorInfoKeeper eik;
583
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->i_unregisterMachineMedia(i_getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent The VirtualBox object.
605 * @param strConfigFile Settings file.
606 * @return
607 */
608HRESULT Machine::initImpl(VirtualBox *aParent,
609 const Utf8Str &strConfigFile)
610{
611 LogFlowThisFuncEnter();
612
613 AssertReturn(aParent, E_INVALIDARG);
614 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
615
616 HRESULT rc = S_OK;
617
618 /* share the parent weakly */
619 unconst(mParent) = aParent;
620
621 /* allocate the essential machine data structure (the rest will be
622 * allocated later by initDataAndChildObjects() */
623 mData.allocate();
624
625 /* memorize the config file name (as provided) */
626 mData->m_strConfigFile = strConfigFile;
627
628 /* get the full file name */
629 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
630 if (RT_FAILURE(vrc1))
631 return setError(VBOX_E_FILE_ERROR,
632 tr("Invalid machine settings file name '%s' (%Rrc)"),
633 strConfigFile.c_str(),
634 vrc1);
635
636 LogFlowThisFuncLeave();
637
638 return rc;
639}
640
641/**
642 * Tries to create a machine settings file in the path stored in the machine
643 * instance data. Used when a new machine is created to fail gracefully if
644 * the settings file could not be written (e.g. because machine dir is read-only).
645 * @return
646 */
647HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
648{
649 HRESULT rc = S_OK;
650
651 // when we create a new machine, we must be able to create the settings file
652 RTFILE f = NIL_RTFILE;
653 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
654 if ( RT_SUCCESS(vrc)
655 || vrc == VERR_SHARING_VIOLATION
656 )
657 {
658 if (RT_SUCCESS(vrc))
659 RTFileClose(f);
660 if (!fForceOverwrite)
661 rc = setError(VBOX_E_FILE_ERROR,
662 tr("Machine settings file '%s' already exists"),
663 mData->m_strConfigFileFull.c_str());
664 else
665 {
666 /* try to delete the config file, as otherwise the creation
667 * of a new settings file will fail. */
668 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
669 if (RT_FAILURE(vrc2))
670 rc = setError(VBOX_E_FILE_ERROR,
671 tr("Could not delete the existing settings file '%s' (%Rrc)"),
672 mData->m_strConfigFileFull.c_str(), vrc2);
673 }
674 }
675 else if ( vrc != VERR_FILE_NOT_FOUND
676 && vrc != VERR_PATH_NOT_FOUND
677 )
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Invalid machine settings file name '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(),
681 vrc);
682 return rc;
683}
684
685/**
686 * Initializes the registered machine by loading the settings file.
687 * This method is separated from #init() in order to make it possible to
688 * retry the operation after VirtualBox startup instead of refusing to
689 * startup the whole VirtualBox server in case if the settings file of some
690 * registered VM is invalid or inaccessible.
691 *
692 * @note Must be always called from this object's write lock
693 * (unless called from #init() that doesn't need any locking).
694 * @note Locks the mUSBController method for writing.
695 * @note Subclasses must not call this method.
696 */
697HRESULT Machine::i_registeredInit()
698{
699 AssertReturn(!i_isSessionMachine(), E_FAIL);
700 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
701 AssertReturn(mData->mUuid.isValid(), E_FAIL);
702 AssertReturn(!mData->mAccessible, E_FAIL);
703
704 HRESULT rc = initDataAndChildObjects();
705
706 if (SUCCEEDED(rc))
707 {
708 /* Temporarily reset the registered flag in order to let setters
709 * potentially called from loadSettings() succeed (isMutable() used in
710 * all setters will return FALSE for a Machine instance if mRegistered
711 * is TRUE). */
712 mData->mRegistered = FALSE;
713
714 try
715 {
716 // load and parse machine XML; this will throw on XML or logic errors
717 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
718
719 if (mData->mUuid != mData->pMachineConfigFile->uuid)
720 throw setError(E_FAIL,
721 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
722 mData->pMachineConfigFile->uuid.raw(),
723 mData->m_strConfigFileFull.c_str(),
724 mData->mUuid.toString().c_str(),
725 mParent->i_settingsFilePath().c_str());
726
727 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
728 NULL /* const Guid *puuidRegistry */);
729 if (FAILED(rc)) throw rc;
730 }
731 catch (HRESULT err)
732 {
733 /* we assume that error info is set by the thrower */
734 rc = err;
735 }
736 catch (...)
737 {
738 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
739 }
740
741 /* Restore the registered flag (even on failure) */
742 mData->mRegistered = TRUE;
743 }
744
745 if (SUCCEEDED(rc))
746 {
747 /* Set mAccessible to TRUE only if we successfully locked and loaded
748 * the settings file */
749 mData->mAccessible = TRUE;
750
751 /* commit all changes made during loading the settings file */
752 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
753 /// @todo r=klaus for some reason the settings loading logic backs up
754 // the settings, and therefore a commit is needed. Should probably be changed.
755 }
756 else
757 {
758 /* If the machine is registered, then, instead of returning a
759 * failure, we mark it as inaccessible and set the result to
760 * success to give it a try later */
761
762 /* fetch the current error info */
763 mData->mAccessError = com::ErrorInfo();
764 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
765
766 /* rollback all changes */
767 i_rollback(false /* aNotify */);
768
769 // uninit media from this machine's media registry, or else
770 // reloading the settings will fail
771 mParent->i_unregisterMachineMedia(i_getId());
772
773 /* uninitialize the common part to make sure all data is reset to
774 * default (null) values */
775 uninitDataAndChildObjects();
776
777 rc = S_OK;
778 }
779
780 return rc;
781}
782
783/**
784 * Uninitializes the instance.
785 * Called either from FinalRelease() or by the parent when it gets destroyed.
786 *
787 * @note The caller of this method must make sure that this object
788 * a) doesn't have active callers on the current thread and b) is not locked
789 * by the current thread; otherwise uninit() will hang either a) due to
790 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
791 * a dead-lock caused by this thread waiting for all callers on the other
792 * threads are done but preventing them from doing so by holding a lock.
793 */
794void Machine::uninit()
795{
796 LogFlowThisFuncEnter();
797
798 Assert(!isWriteLockOnCurrentThread());
799
800 Assert(!uRegistryNeedsSaving);
801 if (uRegistryNeedsSaving)
802 {
803 AutoCaller autoCaller(this);
804 if (SUCCEEDED(autoCaller.rc()))
805 {
806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
807 i_saveSettings(NULL, Machine::SaveS_Force);
808 }
809 }
810
811 /* Enclose the state transition Ready->InUninit->NotReady */
812 AutoUninitSpan autoUninitSpan(this);
813 if (autoUninitSpan.uninitDone())
814 return;
815
816 Assert(!i_isSnapshotMachine());
817 Assert(!i_isSessionMachine());
818 Assert(!!mData);
819
820 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
821 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
822
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824
825 if (!mData->mSession.mMachine.isNull())
826 {
827 /* Theoretically, this can only happen if the VirtualBox server has been
828 * terminated while there were clients running that owned open direct
829 * sessions. Since in this case we are definitely called by
830 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
831 * won't happen on the client watcher thread (because it has a
832 * VirtualBox caller for the duration of the
833 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
834 * cannot happen until the VirtualBox caller is released). This is
835 * important, because SessionMachine::uninit() cannot correctly operate
836 * after we return from this method (it expects the Machine instance is
837 * still valid). We'll call it ourselves below.
838 */
839 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
840 (SessionMachine*)mData->mSession.mMachine));
841
842 if (Global::IsOnlineOrTransient(mData->mMachineState))
843 {
844 Log1WarningThisFunc(("Setting state to Aborted!\n"));
845 /* set machine state using SessionMachine reimplementation */
846 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
847 }
848
849 /*
850 * Uninitialize SessionMachine using public uninit() to indicate
851 * an unexpected uninitialization.
852 */
853 mData->mSession.mMachine->uninit();
854 /* SessionMachine::uninit() must set mSession.mMachine to null */
855 Assert(mData->mSession.mMachine.isNull());
856 }
857
858 // uninit media from this machine's media registry, if they're still there
859 Guid uuidMachine(i_getId());
860
861 /* the lock is no more necessary (SessionMachine is uninitialized) */
862 alock.release();
863
864 /* XXX This will fail with
865 * "cannot be closed because it is still attached to 1 virtual machines"
866 * because at this point we did not call uninitDataAndChildObjects() yet
867 * and therefore also removeBackReference() for all these mediums was not called! */
868
869 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
870 mParent->i_unregisterMachineMedia(uuidMachine);
871
872 // has machine been modified?
873 if (mData->flModifications)
874 {
875 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
876 i_rollback(false /* aNotify */);
877 }
878
879 if (mData->mAccessible)
880 uninitDataAndChildObjects();
881
882 /* free the essential data structure last */
883 mData.free();
884
885 LogFlowThisFuncLeave();
886}
887
888// Wrapped IMachine properties
889/////////////////////////////////////////////////////////////////////////////
890HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
891{
892 /* mParent is constant during life time, no need to lock */
893 ComObjPtr<VirtualBox> pVirtualBox(mParent);
894 aParent = pVirtualBox;
895
896 return S_OK;
897}
898
899
900HRESULT Machine::getAccessible(BOOL *aAccessible)
901{
902 /* In some cases (medium registry related), it is necessary to be able to
903 * go through the list of all machines. Happens when an inaccessible VM
904 * has a sensible medium registry. */
905 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
907
908 HRESULT rc = S_OK;
909
910 if (!mData->mAccessible)
911 {
912 /* try to initialize the VM once more if not accessible */
913
914 AutoReinitSpan autoReinitSpan(this);
915 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
916
917#ifdef DEBUG
918 LogFlowThisFunc(("Dumping media backreferences\n"));
919 mParent->i_dumpAllBackRefs();
920#endif
921
922 if (mData->pMachineConfigFile)
923 {
924 // reset the XML file to force loadSettings() (called from i_registeredInit())
925 // to parse it again; the file might have changed
926 delete mData->pMachineConfigFile;
927 mData->pMachineConfigFile = NULL;
928 }
929
930 rc = i_registeredInit();
931
932 if (SUCCEEDED(rc) && mData->mAccessible)
933 {
934 autoReinitSpan.setSucceeded();
935
936 /* make sure interesting parties will notice the accessibility
937 * state change */
938 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
939 mParent->i_onMachineDataChange(mData->mUuid);
940 }
941 }
942
943 if (SUCCEEDED(rc))
944 *aAccessible = mData->mAccessible;
945
946 LogFlowThisFuncLeave();
947
948 return rc;
949}
950
951HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
952{
953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
956 {
957 /* return shortly */
958 aAccessError = NULL;
959 return S_OK;
960 }
961
962 HRESULT rc = S_OK;
963
964 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
965 rc = errorInfo.createObject();
966 if (SUCCEEDED(rc))
967 {
968 errorInfo->init(mData->mAccessError.getResultCode(),
969 mData->mAccessError.getInterfaceID().ref(),
970 Utf8Str(mData->mAccessError.getComponent()).c_str(),
971 Utf8Str(mData->mAccessError.getText()));
972 aAccessError = errorInfo;
973 }
974
975 return rc;
976}
977
978HRESULT Machine::getName(com::Utf8Str &aName)
979{
980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
981
982 aName = mUserData->s.strName;
983
984 return S_OK;
985}
986
987HRESULT Machine::setName(const com::Utf8Str &aName)
988{
989 // prohibit setting a UUID only as the machine name, or else it can
990 // never be found by findMachine()
991 Guid test(aName);
992
993 if (test.isValid())
994 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
995
996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 HRESULT rc = i_checkStateDependency(MutableStateDep);
999 if (FAILED(rc)) return rc;
1000
1001 i_setModified(IsModified_MachineData);
1002 mUserData.backup();
1003 mUserData->s.strName = aName;
1004
1005 return S_OK;
1006}
1007
1008HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1009{
1010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 aDescription = mUserData->s.strDescription;
1013
1014 return S_OK;
1015}
1016
1017HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1018{
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 // this can be done in principle in any state as it doesn't affect the VM
1022 // significantly, but play safe by not messing around while complex
1023 // activities are going on
1024 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1025 if (FAILED(rc)) return rc;
1026
1027 i_setModified(IsModified_MachineData);
1028 mUserData.backup();
1029 mUserData->s.strDescription = aDescription;
1030
1031 return S_OK;
1032}
1033
1034HRESULT Machine::getId(com::Guid &aId)
1035{
1036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 aId = mData->mUuid;
1039
1040 return S_OK;
1041}
1042
1043HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1044{
1045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1046 aGroups.resize(mUserData->s.llGroups.size());
1047 size_t i = 0;
1048 for (StringsList::const_iterator
1049 it = mUserData->s.llGroups.begin();
1050 it != mUserData->s.llGroups.end();
1051 ++it, ++i)
1052 aGroups[i] = (*it);
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1058{
1059 StringsList llGroups;
1060 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1061 if (FAILED(rc))
1062 return rc;
1063
1064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1065
1066 rc = i_checkStateDependency(MutableOrSavedStateDep);
1067 if (FAILED(rc)) return rc;
1068
1069 i_setModified(IsModified_MachineData);
1070 mUserData.backup();
1071 mUserData->s.llGroups = llGroups;
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1077{
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 aOSTypeId = mUserData->s.strOsType;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1086{
1087 /* look up the object by Id to check it is valid */
1088 ComObjPtr<GuestOSType> pGuestOSType;
1089 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1090 pGuestOSType);
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Utf8Str osTypeId = pGuestOSType->i_id();
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 rc = i_checkStateDependency(MutableStateDep);
1100 if (FAILED(rc)) return rc;
1101
1102 i_setModified(IsModified_MachineData);
1103 mUserData.backup();
1104 mUserData->s.strOsType = osTypeId;
1105
1106 return S_OK;
1107}
1108
1109HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1110{
1111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 *aFirmwareType = mHWData->mFirmwareType;
1114
1115 return S_OK;
1116}
1117
1118HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1119{
1120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1121
1122 HRESULT rc = i_checkStateDependency(MutableStateDep);
1123 if (FAILED(rc)) return rc;
1124
1125 i_setModified(IsModified_MachineData);
1126 mHWData.backup();
1127 mHWData->mFirmwareType = aFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1133{
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1137
1138 return S_OK;
1139}
1140
1141HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1142{
1143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1144
1145 HRESULT rc = i_checkStateDependency(MutableStateDep);
1146 if (FAILED(rc)) return rc;
1147
1148 i_setModified(IsModified_MachineData);
1149 mHWData.backup();
1150 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1151
1152 return S_OK;
1153}
1154
1155HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1156{
1157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 *aPointingHIDType = mHWData->mPointingHIDType;
1160
1161 return S_OK;
1162}
1163
1164HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1165{
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = i_checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 i_setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 mHWData->mPointingHIDType = aPointingHIDType;
1174
1175 return S_OK;
1176}
1177
1178HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1179{
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aChipsetType = mHWData->mChipsetType;
1183
1184 return S_OK;
1185}
1186
1187HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1188{
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = i_checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 if (aChipsetType != mHWData->mChipsetType)
1195 {
1196 i_setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mChipsetType = aChipsetType;
1199
1200 // Resize network adapter array, to be finalized on commit/rollback.
1201 // We must not throw away entries yet, otherwise settings are lost
1202 // without a way to roll back.
1203 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1204 size_t oldCount = mNetworkAdapters.size();
1205 if (newCount > oldCount)
1206 {
1207 mNetworkAdapters.resize(newCount);
1208 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1209 {
1210 unconst(mNetworkAdapters[slot]).createObject();
1211 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1212 }
1213 }
1214 }
1215
1216 return S_OK;
1217}
1218
1219HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1220{
1221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 aParavirtDebug = mHWData->mParavirtDebug;
1224 return S_OK;
1225}
1226
1227HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 HRESULT rc = i_checkStateDependency(MutableStateDep);
1232 if (FAILED(rc)) return rc;
1233
1234 /** @todo Parse/validate options? */
1235 if (aParavirtDebug != mHWData->mParavirtDebug)
1236 {
1237 i_setModified(IsModified_MachineData);
1238 mHWData.backup();
1239 mHWData->mParavirtDebug = aParavirtDebug;
1240 }
1241
1242 return S_OK;
1243}
1244
1245HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1246{
1247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 *aParavirtProvider = mHWData->mParavirtProvider;
1250
1251 return S_OK;
1252}
1253
1254HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1255{
1256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1257
1258 HRESULT rc = i_checkStateDependency(MutableStateDep);
1259 if (FAILED(rc)) return rc;
1260
1261 if (aParavirtProvider != mHWData->mParavirtProvider)
1262 {
1263 i_setModified(IsModified_MachineData);
1264 mHWData.backup();
1265 mHWData->mParavirtProvider = aParavirtProvider;
1266 }
1267
1268 return S_OK;
1269}
1270
1271HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1272{
1273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 *aParavirtProvider = mHWData->mParavirtProvider;
1276 switch (mHWData->mParavirtProvider)
1277 {
1278 case ParavirtProvider_None:
1279 case ParavirtProvider_HyperV:
1280 case ParavirtProvider_KVM:
1281 case ParavirtProvider_Minimal:
1282 break;
1283
1284 /* Resolve dynamic provider types to the effective types. */
1285 default:
1286 {
1287 ComObjPtr<GuestOSType> pGuestOSType;
1288 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1289 pGuestOSType);
1290 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1291
1292 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1293 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1294
1295 switch (mHWData->mParavirtProvider)
1296 {
1297 case ParavirtProvider_Legacy:
1298 {
1299 if (fOsXGuest)
1300 *aParavirtProvider = ParavirtProvider_Minimal;
1301 else
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 case ParavirtProvider_Default:
1307 {
1308 if (fOsXGuest)
1309 *aParavirtProvider = ParavirtProvider_Minimal;
1310 else if ( mUserData->s.strOsType == "Windows10"
1311 || mUserData->s.strOsType == "Windows10_64"
1312 || mUserData->s.strOsType == "Windows81"
1313 || mUserData->s.strOsType == "Windows81_64"
1314 || mUserData->s.strOsType == "Windows8"
1315 || mUserData->s.strOsType == "Windows8_64"
1316 || mUserData->s.strOsType == "Windows7"
1317 || mUserData->s.strOsType == "Windows7_64"
1318 || mUserData->s.strOsType == "WindowsVista"
1319 || mUserData->s.strOsType == "WindowsVista_64"
1320 || mUserData->s.strOsType == "Windows2012"
1321 || mUserData->s.strOsType == "Windows2012_64"
1322 || mUserData->s.strOsType == "Windows2008"
1323 || mUserData->s.strOsType == "Windows2008_64")
1324 {
1325 *aParavirtProvider = ParavirtProvider_HyperV;
1326 }
1327 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1328 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1329 || mUserData->s.strOsType == "Linux"
1330 || mUserData->s.strOsType == "Linux_64"
1331 || mUserData->s.strOsType == "ArchLinux"
1332 || mUserData->s.strOsType == "ArchLinux_64"
1333 || mUserData->s.strOsType == "Debian"
1334 || mUserData->s.strOsType == "Debian_64"
1335 || mUserData->s.strOsType == "Fedora"
1336 || mUserData->s.strOsType == "Fedora_64"
1337 || mUserData->s.strOsType == "Gentoo"
1338 || mUserData->s.strOsType == "Gentoo_64"
1339 || mUserData->s.strOsType == "Mandriva"
1340 || mUserData->s.strOsType == "Mandriva_64"
1341 || mUserData->s.strOsType == "OpenSUSE"
1342 || mUserData->s.strOsType == "OpenSUSE_64"
1343 || mUserData->s.strOsType == "Oracle"
1344 || mUserData->s.strOsType == "Oracle_64"
1345 || mUserData->s.strOsType == "RedHat"
1346 || mUserData->s.strOsType == "RedHat_64"
1347 || mUserData->s.strOsType == "Turbolinux"
1348 || mUserData->s.strOsType == "Turbolinux_64"
1349 || mUserData->s.strOsType == "Ubuntu"
1350 || mUserData->s.strOsType == "Ubuntu_64"
1351 || mUserData->s.strOsType == "Xandros"
1352 || mUserData->s.strOsType == "Xandros_64")
1353 {
1354 *aParavirtProvider = ParavirtProvider_KVM;
1355 }
1356 else
1357 *aParavirtProvider = ParavirtProvider_None;
1358 break;
1359 }
1360
1361 default: AssertFailedBreak(); /* Shut up MSC. */
1362 }
1363 break;
1364 }
1365 }
1366
1367 Assert( *aParavirtProvider == ParavirtProvider_None
1368 || *aParavirtProvider == ParavirtProvider_Minimal
1369 || *aParavirtProvider == ParavirtProvider_HyperV
1370 || *aParavirtProvider == ParavirtProvider_KVM);
1371 return S_OK;
1372}
1373
1374HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 aHardwareVersion = mHWData->mHWVersion;
1379
1380 return S_OK;
1381}
1382
1383HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1384{
1385 /* check known version */
1386 Utf8Str hwVersion = aHardwareVersion;
1387 if ( hwVersion.compare("1") != 0
1388 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1389 return setError(E_INVALIDARG,
1390 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1391
1392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 HRESULT rc = i_checkStateDependency(MutableStateDep);
1395 if (FAILED(rc)) return rc;
1396
1397 i_setModified(IsModified_MachineData);
1398 mHWData.backup();
1399 mHWData->mHWVersion = aHardwareVersion;
1400
1401 return S_OK;
1402}
1403
1404HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1405{
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 if (!mHWData->mHardwareUUID.isZero())
1409 aHardwareUUID = mHWData->mHardwareUUID;
1410 else
1411 aHardwareUUID = mData->mUuid;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1417{
1418 if (!aHardwareUUID.isValid())
1419 return E_INVALIDARG;
1420
1421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 HRESULT rc = i_checkStateDependency(MutableStateDep);
1424 if (FAILED(rc)) return rc;
1425
1426 i_setModified(IsModified_MachineData);
1427 mHWData.backup();
1428 if (aHardwareUUID == mData->mUuid)
1429 mHWData->mHardwareUUID.clear();
1430 else
1431 mHWData->mHardwareUUID = aHardwareUUID;
1432
1433 return S_OK;
1434}
1435
1436HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1437{
1438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 *aMemorySize = mHWData->mMemorySize;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::setMemorySize(ULONG aMemorySize)
1446{
1447 /* check RAM limits */
1448 if ( aMemorySize < MM_RAM_MIN_IN_MB
1449 || aMemorySize > MM_RAM_MAX_IN_MB
1450 )
1451 return setError(E_INVALIDARG,
1452 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1453 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1454
1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 HRESULT rc = i_checkStateDependency(MutableStateDep);
1458 if (FAILED(rc)) return rc;
1459
1460 i_setModified(IsModified_MachineData);
1461 mHWData.backup();
1462 mHWData->mMemorySize = aMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1468{
1469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 *aCPUCount = mHWData->mCPUCount;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::setCPUCount(ULONG aCPUCount)
1477{
1478 /* check CPU limits */
1479 if ( aCPUCount < SchemaDefs::MinCPUCount
1480 || aCPUCount > SchemaDefs::MaxCPUCount
1481 )
1482 return setError(E_INVALIDARG,
1483 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1484 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1485
1486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1489 if (mHWData->mCPUHotPlugEnabled)
1490 {
1491 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1492 {
1493 if (mHWData->mCPUAttached[idx])
1494 return setError(E_INVALIDARG,
1495 tr("There is still a CPU attached to socket %lu."
1496 "Detach the CPU before removing the socket"),
1497 aCPUCount, idx+1);
1498 }
1499 }
1500
1501 HRESULT rc = i_checkStateDependency(MutableStateDep);
1502 if (FAILED(rc)) return rc;
1503
1504 i_setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mCPUCount = aCPUCount;
1507
1508 return S_OK;
1509}
1510
1511HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1512{
1513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1514
1515 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1521{
1522 HRESULT rc = S_OK;
1523
1524 /* check throttle limits */
1525 if ( aCPUExecutionCap < 1
1526 || aCPUExecutionCap > 100
1527 )
1528 return setError(E_INVALIDARG,
1529 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1530 aCPUExecutionCap, 1, 100);
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 alock.release();
1535 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1536 alock.acquire();
1537 if (FAILED(rc)) return rc;
1538
1539 i_setModified(IsModified_MachineData);
1540 mHWData.backup();
1541 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1542
1543 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1544 if (Global::IsOnline(mData->mMachineState))
1545 i_saveSettings(NULL);
1546
1547 return S_OK;
1548}
1549
1550HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1551{
1552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1560{
1561 HRESULT rc = S_OK;
1562
1563 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 rc = i_checkStateDependency(MutableStateDep);
1566 if (FAILED(rc)) return rc;
1567
1568 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1569 {
1570 if (aCPUHotPlugEnabled)
1571 {
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574
1575 /* Add the amount of CPUs currently attached */
1576 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1577 mHWData->mCPUAttached[i] = true;
1578 }
1579 else
1580 {
1581 /*
1582 * We can disable hotplug only if the amount of maximum CPUs is equal
1583 * to the amount of attached CPUs
1584 */
1585 unsigned cCpusAttached = 0;
1586 unsigned iHighestId = 0;
1587
1588 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1589 {
1590 if (mHWData->mCPUAttached[i])
1591 {
1592 cCpusAttached++;
1593 iHighestId = i;
1594 }
1595 }
1596
1597 if ( (cCpusAttached != mHWData->mCPUCount)
1598 || (iHighestId >= mHWData->mCPUCount))
1599 return setError(E_INVALIDARG,
1600 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1601
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604 }
1605 }
1606
1607 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1608
1609 return rc;
1610}
1611
1612HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1622{
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1626 if (SUCCEEDED(hrc))
1627 {
1628 i_setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1631 }
1632 return hrc;
1633}
1634
1635HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1636{
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638 aCPUProfile = mHWData->mCpuProfile;
1639 return S_OK;
1640}
1641
1642HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1643{
1644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1645 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1646 if (SUCCEEDED(hrc))
1647 {
1648 i_setModified(IsModified_MachineData);
1649 mHWData.backup();
1650 /* Empty equals 'host'. */
1651 if (aCPUProfile.isNotEmpty())
1652 mHWData->mCpuProfile = aCPUProfile;
1653 else
1654 mHWData->mCpuProfile = "host";
1655 }
1656 return hrc;
1657}
1658
1659HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1660{
1661#ifdef VBOX_WITH_USB_CARDREADER
1662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEmulatedUSBCardReaderEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1679 if (FAILED(rc)) return rc;
1680
1681 i_setModified(IsModified_MachineData);
1682 mHWData.backup();
1683 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1684
1685 return S_OK;
1686#else
1687 NOREF(aEmulatedUSBCardReaderEnabled);
1688 return E_NOTIMPL;
1689#endif
1690}
1691
1692HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1693{
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 *aHPETEnabled = mHWData->mHPETEnabled;
1697
1698 return S_OK;
1699}
1700
1701HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1702{
1703 HRESULT rc = S_OK;
1704
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706
1707 rc = i_checkStateDependency(MutableStateDep);
1708 if (FAILED(rc)) return rc;
1709
1710 i_setModified(IsModified_MachineData);
1711 mHWData.backup();
1712
1713 mHWData->mHPETEnabled = aHPETEnabled;
1714
1715 return rc;
1716}
1717
1718HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1719{
1720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1723 return S_OK;
1724}
1725
1726HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1727{
1728 HRESULT rc = S_OK;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1735
1736 alock.release();
1737 rc = i_onVideoCaptureChange();
1738 alock.acquire();
1739 if (FAILED(rc))
1740 {
1741 /*
1742 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1743 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1744 * determine if it should start or stop capturing. Therefore we need to manually
1745 * undo change.
1746 */
1747 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1748 return rc;
1749 }
1750
1751 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1752 if (Global::IsOnline(mData->mMachineState))
1753 i_saveSettings(NULL);
1754
1755 return rc;
1756}
1757
1758HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1759{
1760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1761 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1762 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1763 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1764 return S_OK;
1765}
1766
1767HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1768{
1769 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1770 bool fChanged = false;
1771
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1775 {
1776 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1777 {
1778 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1779 fChanged = true;
1780 }
1781 }
1782 if (fChanged)
1783 {
1784 alock.release();
1785 HRESULT rc = i_onVideoCaptureChange();
1786 alock.acquire();
1787 if (FAILED(rc)) return rc;
1788 i_setModified(IsModified_MachineData);
1789
1790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1791 if (Global::IsOnline(mData->mMachineState))
1792 i_saveSettings(NULL);
1793 }
1794
1795 return S_OK;
1796}
1797
1798HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801 if (mHWData->mVideoCaptureFile.isEmpty())
1802 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1803 else
1804 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1805 return S_OK;
1806}
1807
1808HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1809{
1810 Utf8Str strFile(aVideoCaptureFile);
1811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1812
1813 if ( Global::IsOnline(mData->mMachineState)
1814 && mHWData->mVideoCaptureEnabled)
1815 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1816
1817 if (!RTPathStartsWithRoot(strFile.c_str()))
1818 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1819
1820 if (!strFile.isEmpty())
1821 {
1822 Utf8Str defaultFile;
1823 i_getDefaultVideoCaptureFile(defaultFile);
1824 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1825 strFile.setNull();
1826 }
1827
1828 i_setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mVideoCaptureFile = strFile;
1831
1832 return S_OK;
1833}
1834
1835HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1836{
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1843{
1844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1845
1846 if ( Global::IsOnline(mData->mMachineState)
1847 && mHWData->mVideoCaptureEnabled)
1848 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1849
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1858{
1859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1860 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1861 return S_OK;
1862}
1863
1864HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1865{
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 i_setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1875
1876 return S_OK;
1877}
1878
1879HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1880{
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1883 return S_OK;
1884}
1885
1886HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1887{
1888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 if ( Global::IsOnline(mData->mMachineState)
1891 && mHWData->mVideoCaptureEnabled)
1892 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1893
1894 i_setModified(IsModified_MachineData);
1895 mHWData.backup();
1896 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1897
1898 return S_OK;
1899}
1900
1901HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1902{
1903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1904 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1905 return S_OK;
1906}
1907
1908HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1909{
1910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 if ( Global::IsOnline(mData->mMachineState)
1913 && mHWData->mVideoCaptureEnabled)
1914 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1915
1916 i_setModified(IsModified_MachineData);
1917 mHWData.backup();
1918 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1919
1920 return S_OK;
1921}
1922
1923HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1924{
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1927 return S_OK;
1928}
1929
1930HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1931{
1932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1933
1934 if ( Global::IsOnline(mData->mMachineState)
1935 && mHWData->mVideoCaptureEnabled)
1936 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1937
1938 i_setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1941
1942 return S_OK;
1943}
1944
1945HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1946{
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1949 return S_OK;
1950}
1951
1952HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1953{
1954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1955
1956 if ( Global::IsOnline(mData->mMachineState)
1957 && mHWData->mVideoCaptureEnabled)
1958 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1959
1960 i_setModified(IsModified_MachineData);
1961 mHWData.backup();
1962 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1963
1964 return S_OK;
1965}
1966
1967HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1968{
1969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1972 return S_OK;
1973}
1974
1975HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1976{
1977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 if ( Global::IsOnline(mData->mMachineState)
1980 && mHWData->mVideoCaptureEnabled)
1981 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1982
1983 i_setModified(IsModified_MachineData);
1984 mHWData.backup();
1985 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1986
1987 return S_OK;
1988}
1989
1990HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1991{
1992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1995
1996 return S_OK;
1997}
1998
1999HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2000{
2001 switch (aGraphicsControllerType)
2002 {
2003 case GraphicsControllerType_Null:
2004 case GraphicsControllerType_VBoxVGA:
2005#ifdef VBOX_WITH_VMSVGA
2006 case GraphicsControllerType_VMSVGA:
2007#endif
2008 break;
2009 default:
2010 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2011 }
2012
2013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2014
2015 HRESULT rc = i_checkStateDependency(MutableStateDep);
2016 if (FAILED(rc)) return rc;
2017
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2021
2022 return S_OK;
2023}
2024
2025HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2026{
2027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 *aVRAMSize = mHWData->mVRAMSize;
2030
2031 return S_OK;
2032}
2033
2034HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2035{
2036 /* check VRAM limits */
2037 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2038 return setError(E_INVALIDARG,
2039 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2040 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 HRESULT rc = i_checkStateDependency(MutableStateDep);
2045 if (FAILED(rc)) return rc;
2046
2047 i_setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mVRAMSize = aVRAMSize;
2050
2051 return S_OK;
2052}
2053
2054/** @todo this method should not be public */
2055HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2060
2061 return S_OK;
2062}
2063
2064/**
2065 * Set the memory balloon size.
2066 *
2067 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2068 * we have to make sure that we never call IGuest from here.
2069 */
2070HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2071{
2072 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2073#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2074 /* check limits */
2075 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2076 return setError(E_INVALIDARG,
2077 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2078 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2079
2080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2085
2086 return S_OK;
2087#else
2088 NOREF(aMemoryBalloonSize);
2089 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2090#endif
2091}
2092
2093HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2094{
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2098 return S_OK;
2099}
2100
2101HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2102{
2103#ifdef VBOX_WITH_PAGE_SHARING
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2110 return S_OK;
2111#else
2112 NOREF(aPageFusionEnabled);
2113 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2114#endif
2115}
2116
2117HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2118{
2119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2122
2123 return S_OK;
2124}
2125
2126HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2127{
2128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 HRESULT rc = i_checkStateDependency(MutableStateDep);
2131 if (FAILED(rc)) return rc;
2132
2133 /** @todo check validity! */
2134
2135 i_setModified(IsModified_MachineData);
2136 mHWData.backup();
2137 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2138
2139 return S_OK;
2140}
2141
2142
2143HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2144{
2145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2148
2149 return S_OK;
2150}
2151
2152HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2153{
2154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 HRESULT rc = i_checkStateDependency(MutableStateDep);
2157 if (FAILED(rc)) return rc;
2158
2159 /** @todo check validity! */
2160 i_setModified(IsModified_MachineData);
2161 mHWData.backup();
2162 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2168{
2169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 *aMonitorCount = mHWData->mMonitorCount;
2172
2173 return S_OK;
2174}
2175
2176HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2177{
2178 /* make sure monitor count is a sensible number */
2179 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2180 return setError(E_INVALIDARG,
2181 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2182 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2183
2184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 HRESULT rc = i_checkStateDependency(MutableStateDep);
2187 if (FAILED(rc)) return rc;
2188
2189 i_setModified(IsModified_MachineData);
2190 mHWData.backup();
2191 mHWData->mMonitorCount = aMonitorCount;
2192
2193 return S_OK;
2194}
2195
2196HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2197{
2198 /* mBIOSSettings is constant during life time, no need to lock */
2199 aBIOSSettings = mBIOSSettings;
2200
2201 return S_OK;
2202}
2203
2204HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2205{
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 switch (aProperty)
2209 {
2210 case CPUPropertyType_PAE:
2211 *aValue = mHWData->mPAEEnabled;
2212 break;
2213
2214 case CPUPropertyType_LongMode:
2215 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2216 *aValue = TRUE;
2217 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2218 *aValue = FALSE;
2219#if HC_ARCH_BITS == 64
2220 else
2221 *aValue = TRUE;
2222#else
2223 else
2224 {
2225 *aValue = FALSE;
2226
2227 ComObjPtr<GuestOSType> pGuestOSType;
2228 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2229 pGuestOSType);
2230 if (SUCCEEDED(hrc2))
2231 {
2232 if (pGuestOSType->i_is64Bit())
2233 {
2234 ComObjPtr<Host> pHost = mParent->i_host();
2235 alock.release();
2236
2237 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2238 if (FAILED(hrc2))
2239 *aValue = FALSE;
2240 }
2241 }
2242 }
2243#endif
2244 break;
2245
2246 case CPUPropertyType_TripleFaultReset:
2247 *aValue = mHWData->mTripleFaultReset;
2248 break;
2249
2250 case CPUPropertyType_APIC:
2251 *aValue = mHWData->mAPIC;
2252 break;
2253
2254 case CPUPropertyType_X2APIC:
2255 *aValue = mHWData->mX2APIC;
2256 break;
2257
2258 case CPUPropertyType_IBPBOnVMExit:
2259 *aValue = mHWData->mIBPBOnVMExit;
2260 break;
2261
2262 case CPUPropertyType_IBPBOnVMEntry:
2263 *aValue = mHWData->mIBPBOnVMEntry;
2264 break;
2265
2266 case CPUPropertyType_SpecCtrl:
2267 *aValue = mHWData->mSpecCtrl;
2268 break;
2269
2270 case CPUPropertyType_SpecCtrlByHost:
2271 *aValue = mHWData->mSpecCtrlByHost;
2272 break;
2273
2274 case CPUPropertyType_HWVirt:
2275 *aValue = mHWData->mNestedHWVirt;
2276 break;
2277
2278 default:
2279 return E_INVALIDARG;
2280 }
2281 return S_OK;
2282}
2283
2284HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2285{
2286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2287
2288 HRESULT rc = i_checkStateDependency(MutableStateDep);
2289 if (FAILED(rc)) return rc;
2290
2291 switch (aProperty)
2292 {
2293 case CPUPropertyType_PAE:
2294 i_setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mPAEEnabled = !!aValue;
2297 break;
2298
2299 case CPUPropertyType_LongMode:
2300 i_setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2303 break;
2304
2305 case CPUPropertyType_TripleFaultReset:
2306 i_setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mTripleFaultReset = !!aValue;
2309 break;
2310
2311 case CPUPropertyType_APIC:
2312 if (mHWData->mX2APIC)
2313 aValue = TRUE;
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mAPIC = !!aValue;
2317 break;
2318
2319 case CPUPropertyType_X2APIC:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mX2APIC = !!aValue;
2323 if (aValue)
2324 mHWData->mAPIC = !!aValue;
2325 break;
2326
2327 case CPUPropertyType_IBPBOnVMExit:
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mIBPBOnVMExit = !!aValue;
2331 break;
2332
2333 case CPUPropertyType_IBPBOnVMEntry:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mIBPBOnVMEntry = !!aValue;
2337 break;
2338
2339 case CPUPropertyType_SpecCtrl:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mSpecCtrl = !!aValue;
2343 break;
2344
2345 case CPUPropertyType_SpecCtrlByHost:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mSpecCtrlByHost = !!aValue;
2349 break;
2350
2351 case CPUPropertyType_HWVirt:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mNestedHWVirt = !!aValue;
2355 break;
2356
2357 default:
2358 return E_INVALIDARG;
2359 }
2360 return S_OK;
2361}
2362
2363HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2364 ULONG *aValEcx, ULONG *aValEdx)
2365{
2366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2367 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2368 {
2369 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2370 it != mHWData->mCpuIdLeafList.end();
2371 ++it)
2372 {
2373 if (aOrdinal == 0)
2374 {
2375 const settings::CpuIdLeaf &rLeaf= *it;
2376 *aIdx = rLeaf.idx;
2377 *aSubIdx = rLeaf.idxSub;
2378 *aValEax = rLeaf.uEax;
2379 *aValEbx = rLeaf.uEbx;
2380 *aValEcx = rLeaf.uEcx;
2381 *aValEdx = rLeaf.uEdx;
2382 return S_OK;
2383 }
2384 aOrdinal--;
2385 }
2386 }
2387 return E_INVALIDARG;
2388}
2389
2390HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2391{
2392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2393
2394 /*
2395 * Search the list.
2396 */
2397 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2398 {
2399 const settings::CpuIdLeaf &rLeaf= *it;
2400 if ( rLeaf.idx == aIdx
2401 && ( aSubIdx == UINT32_MAX
2402 || rLeaf.idxSub == aSubIdx) )
2403 {
2404 *aValEax = rLeaf.uEax;
2405 *aValEbx = rLeaf.uEbx;
2406 *aValEcx = rLeaf.uEcx;
2407 *aValEdx = rLeaf.uEdx;
2408 return S_OK;
2409 }
2410 }
2411
2412 return E_INVALIDARG;
2413}
2414
2415
2416HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2417{
2418 /*
2419 * Validate input before taking locks and checking state.
2420 */
2421 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2422 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2423 if ( aIdx >= UINT32_C(0x20)
2424 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2425 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2426 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2427
2428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2429 HRESULT rc = i_checkStateDependency(MutableStateDep);
2430 if (FAILED(rc)) return rc;
2431
2432 /*
2433 * Impose a maximum number of leaves.
2434 */
2435 if (mHWData->mCpuIdLeafList.size() > 256)
2436 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2437
2438 /*
2439 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2440 */
2441 i_setModified(IsModified_MachineData);
2442 mHWData.backup();
2443
2444 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2445 {
2446 settings::CpuIdLeaf &rLeaf= *it;
2447 if ( rLeaf.idx == aIdx
2448 && ( aSubIdx == UINT32_MAX
2449 || rLeaf.idxSub == aSubIdx) )
2450 it = mHWData->mCpuIdLeafList.erase(it);
2451 else
2452 ++it;
2453 }
2454
2455 settings::CpuIdLeaf NewLeaf;
2456 NewLeaf.idx = aIdx;
2457 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2458 NewLeaf.uEax = aValEax;
2459 NewLeaf.uEbx = aValEbx;
2460 NewLeaf.uEcx = aValEcx;
2461 NewLeaf.uEdx = aValEdx;
2462 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2463 return S_OK;
2464}
2465
2466HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2467{
2468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 HRESULT rc = i_checkStateDependency(MutableStateDep);
2471 if (FAILED(rc)) return rc;
2472
2473 /*
2474 * Do the removal.
2475 */
2476 bool fModified = false;
2477 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2478 {
2479 settings::CpuIdLeaf &rLeaf= *it;
2480 if ( rLeaf.idx == aIdx
2481 && ( aSubIdx == UINT32_MAX
2482 || rLeaf.idxSub == aSubIdx) )
2483 {
2484 if (!fModified)
2485 {
2486 fModified = true;
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 }
2490 it = mHWData->mCpuIdLeafList.erase(it);
2491 }
2492 else
2493 ++it;
2494 }
2495
2496 return S_OK;
2497}
2498
2499HRESULT Machine::removeAllCPUIDLeaves()
2500{
2501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2502
2503 HRESULT rc = i_checkStateDependency(MutableStateDep);
2504 if (FAILED(rc)) return rc;
2505
2506 if (mHWData->mCpuIdLeafList.size() > 0)
2507 {
2508 i_setModified(IsModified_MachineData);
2509 mHWData.backup();
2510
2511 mHWData->mCpuIdLeafList.clear();
2512 }
2513
2514 return S_OK;
2515}
2516HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 switch(aProperty)
2521 {
2522 case HWVirtExPropertyType_Enabled:
2523 *aValue = mHWData->mHWVirtExEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_VPID:
2527 *aValue = mHWData->mHWVirtExVPIDEnabled;
2528 break;
2529
2530 case HWVirtExPropertyType_NestedPaging:
2531 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2532 break;
2533
2534 case HWVirtExPropertyType_UnrestrictedExecution:
2535 *aValue = mHWData->mHWVirtExUXEnabled;
2536 break;
2537
2538 case HWVirtExPropertyType_LargePages:
2539 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2540#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2541 *aValue = FALSE;
2542#endif
2543 break;
2544
2545 case HWVirtExPropertyType_Force:
2546 *aValue = mHWData->mHWVirtExForceEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_UseNativeApi:
2550 *aValue = mHWData->mHWVirtExUseNativeApi;
2551 break;
2552
2553 default:
2554 return E_INVALIDARG;
2555 }
2556 return S_OK;
2557}
2558
2559HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2560{
2561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 HRESULT rc = i_checkStateDependency(MutableStateDep);
2564 if (FAILED(rc)) return rc;
2565
2566 switch (aProperty)
2567 {
2568 case HWVirtExPropertyType_Enabled:
2569 i_setModified(IsModified_MachineData);
2570 mHWData.backup();
2571 mHWData->mHWVirtExEnabled = !!aValue;
2572 break;
2573
2574 case HWVirtExPropertyType_VPID:
2575 i_setModified(IsModified_MachineData);
2576 mHWData.backup();
2577 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2578 break;
2579
2580 case HWVirtExPropertyType_NestedPaging:
2581 i_setModified(IsModified_MachineData);
2582 mHWData.backup();
2583 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2584 break;
2585
2586 case HWVirtExPropertyType_UnrestrictedExecution:
2587 i_setModified(IsModified_MachineData);
2588 mHWData.backup();
2589 mHWData->mHWVirtExUXEnabled = !!aValue;
2590 break;
2591
2592 case HWVirtExPropertyType_LargePages:
2593 i_setModified(IsModified_MachineData);
2594 mHWData.backup();
2595 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2596 break;
2597
2598 case HWVirtExPropertyType_Force:
2599 i_setModified(IsModified_MachineData);
2600 mHWData.backup();
2601 mHWData->mHWVirtExForceEnabled = !!aValue;
2602 break;
2603
2604 case HWVirtExPropertyType_UseNativeApi:
2605 i_setModified(IsModified_MachineData);
2606 mHWData.backup();
2607 mHWData->mHWVirtExUseNativeApi = !!aValue;
2608 break;
2609
2610 default:
2611 return E_INVALIDARG;
2612 }
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2622
2623 return S_OK;
2624}
2625
2626HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2627{
2628 /** @todo (r=dmik):
2629 * 1. Allow to change the name of the snapshot folder containing snapshots
2630 * 2. Rename the folder on disk instead of just changing the property
2631 * value (to be smart and not to leave garbage). Note that it cannot be
2632 * done here because the change may be rolled back. Thus, the right
2633 * place is #saveSettings().
2634 */
2635
2636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 HRESULT rc = i_checkStateDependency(MutableStateDep);
2639 if (FAILED(rc)) return rc;
2640
2641 if (!mData->mCurrentSnapshot.isNull())
2642 return setError(E_FAIL,
2643 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2644
2645 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2646
2647 if (strSnapshotFolder.isEmpty())
2648 strSnapshotFolder = "Snapshots";
2649 int vrc = i_calculateFullPath(strSnapshotFolder,
2650 strSnapshotFolder);
2651 if (RT_FAILURE(vrc))
2652 return setError(E_FAIL,
2653 tr("Invalid snapshot folder '%s' (%Rrc)"),
2654 strSnapshotFolder.c_str(), vrc);
2655
2656 i_setModified(IsModified_MachineData);
2657 mUserData.backup();
2658
2659 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 aMediumAttachments.resize(mMediumAttachments->size());
2669 size_t i = 0;
2670 for (MediumAttachmentList::const_iterator
2671 it = mMediumAttachments->begin();
2672 it != mMediumAttachments->end();
2673 ++it, ++i)
2674 aMediumAttachments[i] = *it;
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 Assert(!!mVRDEServer);
2684
2685 aVRDEServer = mVRDEServer;
2686
2687 return S_OK;
2688}
2689
2690HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2691{
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 aAudioAdapter = mAudioAdapter;
2695
2696 return S_OK;
2697}
2698
2699HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2700{
2701#ifdef VBOX_WITH_VUSB
2702 clearError();
2703 MultiResult rc(S_OK);
2704
2705# ifdef VBOX_WITH_USB
2706 rc = mParent->i_host()->i_checkUSBProxyService();
2707 if (FAILED(rc)) return rc;
2708# endif
2709
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 aUSBControllers.resize(mUSBControllers->size());
2713 size_t i = 0;
2714 for (USBControllerList::const_iterator
2715 it = mUSBControllers->begin();
2716 it != mUSBControllers->end();
2717 ++it, ++i)
2718 aUSBControllers[i] = *it;
2719
2720 return S_OK;
2721#else
2722 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2723 * extended error info to indicate that USB is simply not available
2724 * (w/o treating it as a failure), for example, as in OSE */
2725 NOREF(aUSBControllers);
2726 ReturnComNotImplemented();
2727#endif /* VBOX_WITH_VUSB */
2728}
2729
2730HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2731{
2732#ifdef VBOX_WITH_VUSB
2733 clearError();
2734 MultiResult rc(S_OK);
2735
2736# ifdef VBOX_WITH_USB
2737 rc = mParent->i_host()->i_checkUSBProxyService();
2738 if (FAILED(rc)) return rc;
2739# endif
2740
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aUSBDeviceFilters = mUSBDeviceFilters;
2744 return rc;
2745#else
2746 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2747 * extended error info to indicate that USB is simply not available
2748 * (w/o treating it as a failure), for example, as in OSE */
2749 NOREF(aUSBDeviceFilters);
2750 ReturnComNotImplemented();
2751#endif /* VBOX_WITH_VUSB */
2752}
2753
2754HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 aSettingsFilePath = mData->m_strConfigFileFull;
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2764{
2765 RT_NOREF(aSettingsFilePath);
2766 ReturnComNotImplemented();
2767}
2768
2769HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2770{
2771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2772
2773 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2774 if (FAILED(rc)) return rc;
2775
2776 if (!mData->pMachineConfigFile->fileExists())
2777 // this is a new machine, and no config file exists yet:
2778 *aSettingsModified = TRUE;
2779 else
2780 *aSettingsModified = (mData->flModifications != 0);
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 *aSessionState = mData->mSession.mState;
2790
2791 return S_OK;
2792}
2793
2794HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2795{
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 aSessionName = mData->mSession.mName;
2799
2800 return S_OK;
2801}
2802
2803HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2804{
2805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2806
2807 *aSessionPID = mData->mSession.mPID;
2808
2809 return S_OK;
2810}
2811
2812HRESULT Machine::getState(MachineState_T *aState)
2813{
2814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2815
2816 *aState = mData->mMachineState;
2817 Assert(mData->mMachineState != MachineState_Null);
2818
2819 return S_OK;
2820}
2821
2822HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2823{
2824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2825
2826 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834
2835 aStateFilePath = mSSData->strStateFilePath;
2836
2837 return S_OK;
2838}
2839
2840HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2841{
2842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2843
2844 i_getLogFolder(aLogFolder);
2845
2846 return S_OK;
2847}
2848
2849HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2850{
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 aCurrentSnapshot = mData->mCurrentSnapshot;
2854
2855 return S_OK;
2856}
2857
2858HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2863 ? 0
2864 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 /* Note: for machines with no snapshots, we always return FALSE
2874 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2875 * reasons :) */
2876
2877 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2878 ? FALSE
2879 : mData->mCurrentStateModified;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 aSharedFolders.resize(mHWData->mSharedFolders.size());
2889 size_t i = 0;
2890 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2891 it = mHWData->mSharedFolders.begin();
2892 it != mHWData->mSharedFolders.end();
2893 ++it, ++i)
2894 aSharedFolders[i] = *it;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 *aClipboardMode = mHWData->mClipboardMode;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2909{
2910 HRESULT rc = S_OK;
2911
2912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 alock.release();
2915 rc = i_onClipboardModeChange(aClipboardMode);
2916 alock.acquire();
2917 if (FAILED(rc)) return rc;
2918
2919 i_setModified(IsModified_MachineData);
2920 mHWData.backup();
2921 mHWData->mClipboardMode = aClipboardMode;
2922
2923 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2924 if (Global::IsOnline(mData->mMachineState))
2925 i_saveSettings(NULL);
2926
2927 return S_OK;
2928}
2929
2930HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2931{
2932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2933
2934 *aDnDMode = mHWData->mDnDMode;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2940{
2941 HRESULT rc = S_OK;
2942
2943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 alock.release();
2946 rc = i_onDnDModeChange(aDnDMode);
2947
2948 alock.acquire();
2949 if (FAILED(rc)) return rc;
2950
2951 i_setModified(IsModified_MachineData);
2952 mHWData.backup();
2953 mHWData->mDnDMode = aDnDMode;
2954
2955 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2956 if (Global::IsOnline(mData->mMachineState))
2957 i_saveSettings(NULL);
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965
2966 aStorageControllers.resize(mStorageControllers->size());
2967 size_t i = 0;
2968 for (StorageControllerList::const_iterator
2969 it = mStorageControllers->begin();
2970 it != mStorageControllers->end();
2971 ++it, ++i)
2972 aStorageControllers[i] = *it;
2973
2974 return S_OK;
2975}
2976
2977HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 *aEnabled = mUserData->s.fTeleporterEnabled;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2987{
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 /* Only allow it to be set to true when PoweredOff or Aborted.
2991 (Clearing it is always permitted.) */
2992 if ( aTeleporterEnabled
2993 && mData->mRegistered
2994 && ( !i_isSessionMachine()
2995 || ( mData->mMachineState != MachineState_PoweredOff
2996 && mData->mMachineState != MachineState_Teleported
2997 && mData->mMachineState != MachineState_Aborted
2998 )
2999 )
3000 )
3001 return setError(VBOX_E_INVALID_VM_STATE,
3002 tr("The machine is not powered off (state is %s)"),
3003 Global::stringifyMachineState(mData->mMachineState));
3004
3005 i_setModified(IsModified_MachineData);
3006 mUserData.backup();
3007 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3008
3009 return S_OK;
3010}
3011
3012HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3013{
3014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3017
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3022{
3023 if (aTeleporterPort >= _64K)
3024 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3025
3026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3027
3028 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3029 if (FAILED(rc)) return rc;
3030
3031 i_setModified(IsModified_MachineData);
3032 mUserData.backup();
3033 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3034
3035 return S_OK;
3036}
3037
3038HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3039{
3040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3043
3044 return S_OK;
3045}
3046
3047HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3048{
3049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3050
3051 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3052 if (FAILED(rc)) return rc;
3053
3054 i_setModified(IsModified_MachineData);
3055 mUserData.backup();
3056 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3057
3058 return S_OK;
3059}
3060
3061HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3062{
3063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3064 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3065
3066 return S_OK;
3067}
3068
3069HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3070{
3071 /*
3072 * Hash the password first.
3073 */
3074 com::Utf8Str aT = aTeleporterPassword;
3075
3076 if (!aT.isEmpty())
3077 {
3078 if (VBoxIsPasswordHashed(&aT))
3079 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3080 VBoxHashPassword(&aT);
3081 }
3082
3083 /*
3084 * Do the update.
3085 */
3086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3087 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3088 if (SUCCEEDED(hrc))
3089 {
3090 i_setModified(IsModified_MachineData);
3091 mUserData.backup();
3092 mUserData->s.strTeleporterPassword = aT;
3093 }
3094
3095 return hrc;
3096}
3097
3098HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3099{
3100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3103 return S_OK;
3104}
3105
3106HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3107{
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 /** @todo deal with running state change. */
3111 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3112 if (FAILED(rc)) return rc;
3113
3114 i_setModified(IsModified_MachineData);
3115 mUserData.backup();
3116 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3121{
3122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3125 return S_OK;
3126}
3127
3128HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3129{
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /** @todo deal with running state change. */
3133 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3134 if (FAILED(rc)) return rc;
3135
3136 i_setModified(IsModified_MachineData);
3137 mUserData.backup();
3138 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3139 return S_OK;
3140}
3141
3142HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3143{
3144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3145
3146 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3147 return S_OK;
3148}
3149
3150HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3151{
3152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 /** @todo deal with running state change. */
3155 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3156 if (FAILED(rc)) return rc;
3157
3158 i_setModified(IsModified_MachineData);
3159 mUserData.backup();
3160 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3161 return S_OK;
3162}
3163
3164HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3165{
3166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3169
3170 return S_OK;
3171}
3172
3173HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3174{
3175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3176
3177 /** @todo deal with running state change. */
3178 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3179 if (FAILED(rc)) return rc;
3180
3181 i_setModified(IsModified_MachineData);
3182 mUserData.backup();
3183 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3184
3185 return S_OK;
3186}
3187
3188HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3189{
3190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3191
3192 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3193 return S_OK;
3194}
3195
3196HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3197{
3198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3199
3200 /** @todo deal with running state change. */
3201 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3202 if (FAILED(rc)) return rc;
3203
3204 i_setModified(IsModified_MachineData);
3205 mUserData.backup();
3206 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3207 return S_OK;
3208}
3209
3210HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3211{
3212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3215
3216 return S_OK;
3217}
3218
3219HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3220{
3221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3222
3223 /* Only allow it to be set to true when PoweredOff or Aborted.
3224 (Clearing it is always permitted.) */
3225 if ( aRTCUseUTC
3226 && mData->mRegistered
3227 && ( !i_isSessionMachine()
3228 || ( mData->mMachineState != MachineState_PoweredOff
3229 && mData->mMachineState != MachineState_Teleported
3230 && mData->mMachineState != MachineState_Aborted
3231 )
3232 )
3233 )
3234 return setError(VBOX_E_INVALID_VM_STATE,
3235 tr("The machine is not powered off (state is %s)"),
3236 Global::stringifyMachineState(mData->mMachineState));
3237
3238 i_setModified(IsModified_MachineData);
3239 mUserData.backup();
3240 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3241
3242 return S_OK;
3243}
3244
3245HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3246{
3247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3248
3249 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3250
3251 return S_OK;
3252}
3253
3254HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3255{
3256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3257
3258 HRESULT rc = i_checkStateDependency(MutableStateDep);
3259 if (FAILED(rc)) return rc;
3260
3261 i_setModified(IsModified_MachineData);
3262 mHWData.backup();
3263 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3264
3265 return S_OK;
3266}
3267
3268HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3269{
3270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3271
3272 *aIOCacheSize = mHWData->mIOCacheSize;
3273
3274 return S_OK;
3275}
3276
3277HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3278{
3279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 HRESULT rc = i_checkStateDependency(MutableStateDep);
3282 if (FAILED(rc)) return rc;
3283
3284 i_setModified(IsModified_MachineData);
3285 mHWData.backup();
3286 mHWData->mIOCacheSize = aIOCacheSize;
3287
3288 return S_OK;
3289}
3290
3291
3292/**
3293 * @note Locks objects!
3294 */
3295HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3296 LockType_T aLockType)
3297{
3298 /* check the session state */
3299 SessionState_T state;
3300 HRESULT rc = aSession->COMGETTER(State)(&state);
3301 if (FAILED(rc)) return rc;
3302
3303 if (state != SessionState_Unlocked)
3304 return setError(VBOX_E_INVALID_OBJECT_STATE,
3305 tr("The given session is busy"));
3306
3307 // get the client's IInternalSessionControl interface
3308 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3309 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3310 E_INVALIDARG);
3311
3312 // session name (only used in some code paths)
3313 Utf8Str strSessionName;
3314
3315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3316
3317 if (!mData->mRegistered)
3318 return setError(E_UNEXPECTED,
3319 tr("The machine '%s' is not registered"),
3320 mUserData->s.strName.c_str());
3321
3322 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3323
3324 SessionState_T oldState = mData->mSession.mState;
3325 /* Hack: in case the session is closing and there is a progress object
3326 * which allows waiting for the session to be closed, take the opportunity
3327 * and do a limited wait (max. 1 second). This helps a lot when the system
3328 * is busy and thus session closing can take a little while. */
3329 if ( mData->mSession.mState == SessionState_Unlocking
3330 && mData->mSession.mProgress)
3331 {
3332 alock.release();
3333 mData->mSession.mProgress->WaitForCompletion(1000);
3334 alock.acquire();
3335 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3336 }
3337
3338 // try again now
3339 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3340 // (i.e. session machine exists)
3341 && (aLockType == LockType_Shared) // caller wants a shared link to the
3342 // existing session that holds the write lock:
3343 )
3344 {
3345 // OK, share the session... we are now dealing with three processes:
3346 // 1) VBoxSVC (where this code runs);
3347 // 2) process C: the caller's client process (who wants a shared session);
3348 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3349
3350 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3351 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3352 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3353 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3354 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3355
3356 /*
3357 * Release the lock before calling the client process. It's safe here
3358 * since the only thing to do after we get the lock again is to add
3359 * the remote control to the list (which doesn't directly influence
3360 * anything).
3361 */
3362 alock.release();
3363
3364 // get the console of the session holding the write lock (this is a remote call)
3365 ComPtr<IConsole> pConsoleW;
3366 if (mData->mSession.mLockType == LockType_VM)
3367 {
3368 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3369 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3370 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3371 if (FAILED(rc))
3372 // the failure may occur w/o any error info (from RPC), so provide one
3373 return setError(VBOX_E_VM_ERROR,
3374 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3375 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3376 }
3377
3378 // share the session machine and W's console with the caller's session
3379 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3380 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3381 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3382
3383 if (FAILED(rc))
3384 // the failure may occur w/o any error info (from RPC), so provide one
3385 return setError(VBOX_E_VM_ERROR,
3386 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3387 alock.acquire();
3388
3389 // need to revalidate the state after acquiring the lock again
3390 if (mData->mSession.mState != SessionState_Locked)
3391 {
3392 pSessionControl->Uninitialize();
3393 return setError(VBOX_E_INVALID_SESSION_STATE,
3394 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3395 mUserData->s.strName.c_str());
3396 }
3397
3398 // add the caller's session to the list
3399 mData->mSession.mRemoteControls.push_back(pSessionControl);
3400 }
3401 else if ( mData->mSession.mState == SessionState_Locked
3402 || mData->mSession.mState == SessionState_Unlocking
3403 )
3404 {
3405 // sharing not permitted, or machine still unlocking:
3406 return setError(VBOX_E_INVALID_OBJECT_STATE,
3407 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3408 mUserData->s.strName.c_str());
3409 }
3410 else
3411 {
3412 // machine is not locked: then write-lock the machine (create the session machine)
3413
3414 // must not be busy
3415 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3416
3417 // get the caller's session PID
3418 RTPROCESS pid = NIL_RTPROCESS;
3419 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3420 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3421 Assert(pid != NIL_RTPROCESS);
3422
3423 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3424
3425 if (fLaunchingVMProcess)
3426 {
3427 if (mData->mSession.mPID == NIL_RTPROCESS)
3428 {
3429 // two or more clients racing for a lock, the one which set the
3430 // session state to Spawning will win, the others will get an
3431 // error as we can't decide here if waiting a little would help
3432 // (only for shared locks this would avoid an error)
3433 return setError(VBOX_E_INVALID_OBJECT_STATE,
3434 tr("The machine '%s' already has a lock request pending"),
3435 mUserData->s.strName.c_str());
3436 }
3437
3438 // this machine is awaiting for a spawning session to be opened:
3439 // then the calling process must be the one that got started by
3440 // LaunchVMProcess()
3441
3442 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3443 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3444
3445#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3446 /* Hardened windows builds spawns three processes when a VM is
3447 launched, the 3rd one is the one that will end up here. */
3448 RTPROCESS ppid;
3449 int rc = RTProcQueryParent(pid, &ppid);
3450 if (RT_SUCCESS(rc))
3451 rc = RTProcQueryParent(ppid, &ppid);
3452 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3453 || rc == VERR_ACCESS_DENIED)
3454 {
3455 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3456 mData->mSession.mPID = pid;
3457 }
3458#endif
3459
3460 if (mData->mSession.mPID != pid)
3461 return setError(E_ACCESSDENIED,
3462 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3463 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3464 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3465 }
3466
3467 // create the mutable SessionMachine from the current machine
3468 ComObjPtr<SessionMachine> sessionMachine;
3469 sessionMachine.createObject();
3470 rc = sessionMachine->init(this);
3471 AssertComRC(rc);
3472
3473 /* NOTE: doing return from this function after this point but
3474 * before the end is forbidden since it may call SessionMachine::uninit()
3475 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3476 * lock while still holding the Machine lock in alock so that a deadlock
3477 * is possible due to the wrong lock order. */
3478
3479 if (SUCCEEDED(rc))
3480 {
3481 /*
3482 * Set the session state to Spawning to protect against subsequent
3483 * attempts to open a session and to unregister the machine after
3484 * we release the lock.
3485 */
3486 SessionState_T origState = mData->mSession.mState;
3487 mData->mSession.mState = SessionState_Spawning;
3488
3489#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3490 /* Get the client token ID to be passed to the client process */
3491 Utf8Str strTokenId;
3492 sessionMachine->i_getTokenId(strTokenId);
3493 Assert(!strTokenId.isEmpty());
3494#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3495 /* Get the client token to be passed to the client process */
3496 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3497 /* The token is now "owned" by pToken, fix refcount */
3498 if (!pToken.isNull())
3499 pToken->Release();
3500#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3501
3502 /*
3503 * Release the lock before calling the client process -- it will call
3504 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3505 * because the state is Spawning, so that LaunchVMProcess() and
3506 * LockMachine() calls will fail. This method, called before we
3507 * acquire the lock again, will fail because of the wrong PID.
3508 *
3509 * Note that mData->mSession.mRemoteControls accessed outside
3510 * the lock may not be modified when state is Spawning, so it's safe.
3511 */
3512 alock.release();
3513
3514 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3515#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3516 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3517#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3518 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3519 /* Now the token is owned by the client process. */
3520 pToken.setNull();
3521#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3522 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3523
3524 /* The failure may occur w/o any error info (from RPC), so provide one */
3525 if (FAILED(rc))
3526 setError(VBOX_E_VM_ERROR,
3527 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3528
3529 // get session name, either to remember or to compare against
3530 // the already known session name.
3531 {
3532 Bstr bstrSessionName;
3533 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3534 if (SUCCEEDED(rc2))
3535 strSessionName = bstrSessionName;
3536 }
3537
3538 if ( SUCCEEDED(rc)
3539 && fLaunchingVMProcess
3540 )
3541 {
3542 /* complete the remote session initialization */
3543
3544 /* get the console from the direct session */
3545 ComPtr<IConsole> console;
3546 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3547 ComAssertComRC(rc);
3548
3549 if (SUCCEEDED(rc) && !console)
3550 {
3551 ComAssert(!!console);
3552 rc = E_FAIL;
3553 }
3554
3555 /* assign machine & console to the remote session */
3556 if (SUCCEEDED(rc))
3557 {
3558 /*
3559 * after LaunchVMProcess(), the first and the only
3560 * entry in remoteControls is that remote session
3561 */
3562 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3563 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3564 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3565
3566 /* The failure may occur w/o any error info (from RPC), so provide one */
3567 if (FAILED(rc))
3568 setError(VBOX_E_VM_ERROR,
3569 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3570 }
3571
3572 if (FAILED(rc))
3573 pSessionControl->Uninitialize();
3574 }
3575
3576 /* acquire the lock again */
3577 alock.acquire();
3578
3579 /* Restore the session state */
3580 mData->mSession.mState = origState;
3581 }
3582
3583 // finalize spawning anyway (this is why we don't return on errors above)
3584 if (fLaunchingVMProcess)
3585 {
3586 Assert(mData->mSession.mName == strSessionName);
3587 /* Note that the progress object is finalized later */
3588 /** @todo Consider checking mData->mSession.mProgress for cancellation
3589 * around here. */
3590
3591 /* We don't reset mSession.mPID here because it is necessary for
3592 * SessionMachine::uninit() to reap the child process later. */
3593
3594 if (FAILED(rc))
3595 {
3596 /* Close the remote session, remove the remote control from the list
3597 * and reset session state to Closed (@note keep the code in sync
3598 * with the relevant part in checkForSpawnFailure()). */
3599
3600 Assert(mData->mSession.mRemoteControls.size() == 1);
3601 if (mData->mSession.mRemoteControls.size() == 1)
3602 {
3603 ErrorInfoKeeper eik;
3604 mData->mSession.mRemoteControls.front()->Uninitialize();
3605 }
3606
3607 mData->mSession.mRemoteControls.clear();
3608 mData->mSession.mState = SessionState_Unlocked;
3609 }
3610 }
3611 else
3612 {
3613 /* memorize PID of the directly opened session */
3614 if (SUCCEEDED(rc))
3615 mData->mSession.mPID = pid;
3616 }
3617
3618 if (SUCCEEDED(rc))
3619 {
3620 mData->mSession.mLockType = aLockType;
3621 /* memorize the direct session control and cache IUnknown for it */
3622 mData->mSession.mDirectControl = pSessionControl;
3623 mData->mSession.mState = SessionState_Locked;
3624 if (!fLaunchingVMProcess)
3625 mData->mSession.mName = strSessionName;
3626 /* associate the SessionMachine with this Machine */
3627 mData->mSession.mMachine = sessionMachine;
3628
3629 /* request an IUnknown pointer early from the remote party for later
3630 * identity checks (it will be internally cached within mDirectControl
3631 * at least on XPCOM) */
3632 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3633 NOREF(unk);
3634 }
3635
3636 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3637 * would break the lock order */
3638 alock.release();
3639
3640 /* uninitialize the created session machine on failure */
3641 if (FAILED(rc))
3642 sessionMachine->uninit();
3643 }
3644
3645 if (SUCCEEDED(rc))
3646 {
3647 /*
3648 * tell the client watcher thread to update the set of
3649 * machines that have open sessions
3650 */
3651 mParent->i_updateClientWatcher();
3652
3653 if (oldState != SessionState_Locked)
3654 /* fire an event */
3655 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3656 }
3657
3658 return rc;
3659}
3660
3661/**
3662 * @note Locks objects!
3663 */
3664HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3665 const com::Utf8Str &aName,
3666 const com::Utf8Str &aEnvironment,
3667 ComPtr<IProgress> &aProgress)
3668{
3669 Utf8Str strFrontend(aName);
3670 /* "emergencystop" doesn't need the session, so skip the checks/interface
3671 * retrieval. This code doesn't quite fit in here, but introducing a
3672 * special API method would be even more effort, and would require explicit
3673 * support by every API client. It's better to hide the feature a bit. */
3674 if (strFrontend != "emergencystop")
3675 CheckComArgNotNull(aSession);
3676
3677 HRESULT rc = S_OK;
3678 if (strFrontend.isEmpty())
3679 {
3680 Bstr bstrFrontend;
3681 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3682 if (FAILED(rc))
3683 return rc;
3684 strFrontend = bstrFrontend;
3685 if (strFrontend.isEmpty())
3686 {
3687 ComPtr<ISystemProperties> systemProperties;
3688 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3689 if (FAILED(rc))
3690 return rc;
3691 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3692 if (FAILED(rc))
3693 return rc;
3694 strFrontend = bstrFrontend;
3695 }
3696 /* paranoia - emergencystop is not a valid default */
3697 if (strFrontend == "emergencystop")
3698 strFrontend = Utf8Str::Empty;
3699 }
3700 /* default frontend: Qt GUI */
3701 if (strFrontend.isEmpty())
3702 strFrontend = "GUI/Qt";
3703
3704 if (strFrontend != "emergencystop")
3705 {
3706 /* check the session state */
3707 SessionState_T state;
3708 rc = aSession->COMGETTER(State)(&state);
3709 if (FAILED(rc))
3710 return rc;
3711
3712 if (state != SessionState_Unlocked)
3713 return setError(VBOX_E_INVALID_OBJECT_STATE,
3714 tr("The given session is busy"));
3715
3716 /* get the IInternalSessionControl interface */
3717 ComPtr<IInternalSessionControl> control(aSession);
3718 ComAssertMsgRet(!control.isNull(),
3719 ("No IInternalSessionControl interface"),
3720 E_INVALIDARG);
3721
3722 /* get the teleporter enable state for the progress object init. */
3723 BOOL fTeleporterEnabled;
3724 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3725 if (FAILED(rc))
3726 return rc;
3727
3728 /* create a progress object */
3729 ComObjPtr<ProgressProxy> progress;
3730 progress.createObject();
3731 rc = progress->init(mParent,
3732 static_cast<IMachine*>(this),
3733 Bstr(tr("Starting VM")).raw(),
3734 TRUE /* aCancelable */,
3735 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3736 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3737 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3738 2 /* uFirstOperationWeight */,
3739 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3740
3741 if (SUCCEEDED(rc))
3742 {
3743 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3744 if (SUCCEEDED(rc))
3745 {
3746 aProgress = progress;
3747
3748 /* signal the client watcher thread */
3749 mParent->i_updateClientWatcher();
3750
3751 /* fire an event */
3752 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3753 }
3754 }
3755 }
3756 else
3757 {
3758 /* no progress object - either instant success or failure */
3759 aProgress = NULL;
3760
3761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3762
3763 if (mData->mSession.mState != SessionState_Locked)
3764 return setError(VBOX_E_INVALID_OBJECT_STATE,
3765 tr("The machine '%s' is not locked by a session"),
3766 mUserData->s.strName.c_str());
3767
3768 /* must have a VM process associated - do not kill normal API clients
3769 * with an open session */
3770 if (!Global::IsOnline(mData->mMachineState))
3771 return setError(VBOX_E_INVALID_OBJECT_STATE,
3772 tr("The machine '%s' does not have a VM process"),
3773 mUserData->s.strName.c_str());
3774
3775 /* forcibly terminate the VM process */
3776 if (mData->mSession.mPID != NIL_RTPROCESS)
3777 RTProcTerminate(mData->mSession.mPID);
3778
3779 /* signal the client watcher thread, as most likely the client has
3780 * been terminated */
3781 mParent->i_updateClientWatcher();
3782 }
3783
3784 return rc;
3785}
3786
3787HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3788{
3789 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3790 return setError(E_INVALIDARG,
3791 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3792 aPosition, SchemaDefs::MaxBootPosition);
3793
3794 if (aDevice == DeviceType_USB)
3795 return setError(E_NOTIMPL,
3796 tr("Booting from USB device is currently not supported"));
3797
3798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3799
3800 HRESULT rc = i_checkStateDependency(MutableStateDep);
3801 if (FAILED(rc)) return rc;
3802
3803 i_setModified(IsModified_MachineData);
3804 mHWData.backup();
3805 mHWData->mBootOrder[aPosition - 1] = aDevice;
3806
3807 return S_OK;
3808}
3809
3810HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3811{
3812 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3813 return setError(E_INVALIDARG,
3814 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3815 aPosition, SchemaDefs::MaxBootPosition);
3816
3817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3818
3819 *aDevice = mHWData->mBootOrder[aPosition - 1];
3820
3821 return S_OK;
3822}
3823
3824HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3825 LONG aControllerPort,
3826 LONG aDevice,
3827 DeviceType_T aType,
3828 const ComPtr<IMedium> &aMedium)
3829{
3830 IMedium *aM = aMedium;
3831 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3832 aName.c_str(), aControllerPort, aDevice, aType, aM));
3833
3834 // request the host lock first, since might be calling Host methods for getting host drives;
3835 // next, protect the media tree all the while we're in here, as well as our member variables
3836 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3837 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3838
3839 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3840 if (FAILED(rc)) return rc;
3841
3842 /// @todo NEWMEDIA implicit machine registration
3843 if (!mData->mRegistered)
3844 return setError(VBOX_E_INVALID_OBJECT_STATE,
3845 tr("Cannot attach storage devices to an unregistered machine"));
3846
3847 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3848
3849 /* Check for an existing controller. */
3850 ComObjPtr<StorageController> ctl;
3851 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3852 if (FAILED(rc)) return rc;
3853
3854 StorageControllerType_T ctrlType;
3855 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3856 if (FAILED(rc))
3857 return setError(E_FAIL,
3858 tr("Could not get type of controller '%s'"),
3859 aName.c_str());
3860
3861 bool fSilent = false;
3862 Utf8Str strReconfig;
3863
3864 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3865 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3866 if ( mData->mMachineState == MachineState_Paused
3867 && strReconfig == "1")
3868 fSilent = true;
3869
3870 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3871 bool fHotplug = false;
3872 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3873 fHotplug = true;
3874
3875 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3876 return setError(VBOX_E_INVALID_VM_STATE,
3877 tr("Controller '%s' does not support hotplugging"),
3878 aName.c_str());
3879
3880 // check that the port and device are not out of range
3881 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3882 if (FAILED(rc)) return rc;
3883
3884 /* check if the device slot is already busy */
3885 MediumAttachment *pAttachTemp;
3886 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3887 aName,
3888 aControllerPort,
3889 aDevice)))
3890 {
3891 Medium *pMedium = pAttachTemp->i_getMedium();
3892 if (pMedium)
3893 {
3894 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3895 return setError(VBOX_E_OBJECT_IN_USE,
3896 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3897 pMedium->i_getLocationFull().c_str(),
3898 aControllerPort,
3899 aDevice,
3900 aName.c_str());
3901 }
3902 else
3903 return setError(VBOX_E_OBJECT_IN_USE,
3904 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3905 aControllerPort, aDevice, aName.c_str());
3906 }
3907
3908 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3909 if (aMedium && medium.isNull())
3910 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3911
3912 AutoCaller mediumCaller(medium);
3913 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3914
3915 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3916
3917 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3918 && !medium.isNull()
3919 )
3920 return setError(VBOX_E_OBJECT_IN_USE,
3921 tr("Medium '%s' is already attached to this virtual machine"),
3922 medium->i_getLocationFull().c_str());
3923
3924 if (!medium.isNull())
3925 {
3926 MediumType_T mtype = medium->i_getType();
3927 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3928 // For DVDs it's not written to the config file, so needs no global config
3929 // version bump. For floppies it's a new attribute "type", which is ignored
3930 // by older VirtualBox version, so needs no global config version bump either.
3931 // For hard disks this type is not accepted.
3932 if (mtype == MediumType_MultiAttach)
3933 {
3934 // This type is new with VirtualBox 4.0 and therefore requires settings
3935 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3936 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3937 // two reasons: The medium type is a property of the media registry tree, which
3938 // can reside in the global config file (for pre-4.0 media); we would therefore
3939 // possibly need to bump the global config version. We don't want to do that though
3940 // because that might make downgrading to pre-4.0 impossible.
3941 // As a result, we can only use these two new types if the medium is NOT in the
3942 // global registry:
3943 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3944 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3945 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3946 )
3947 return setError(VBOX_E_INVALID_OBJECT_STATE,
3948 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3949 "to machines that were created with VirtualBox 4.0 or later"),
3950 medium->i_getLocationFull().c_str());
3951 }
3952 }
3953
3954 bool fIndirect = false;
3955 if (!medium.isNull())
3956 fIndirect = medium->i_isReadOnly();
3957 bool associate = true;
3958
3959 do
3960 {
3961 if ( aType == DeviceType_HardDisk
3962 && mMediumAttachments.isBackedUp())
3963 {
3964 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3965
3966 /* check if the medium was attached to the VM before we started
3967 * changing attachments in which case the attachment just needs to
3968 * be restored */
3969 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3970 {
3971 AssertReturn(!fIndirect, E_FAIL);
3972
3973 /* see if it's the same bus/channel/device */
3974 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3975 {
3976 /* the simplest case: restore the whole attachment
3977 * and return, nothing else to do */
3978 mMediumAttachments->push_back(pAttachTemp);
3979
3980 /* Reattach the medium to the VM. */
3981 if (fHotplug || fSilent)
3982 {
3983 mediumLock.release();
3984 treeLock.release();
3985 alock.release();
3986
3987 MediumLockList *pMediumLockList(new MediumLockList());
3988
3989 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3990 medium /* pToLockWrite */,
3991 false /* fMediumLockWriteAll */,
3992 NULL,
3993 *pMediumLockList);
3994 alock.acquire();
3995 if (FAILED(rc))
3996 delete pMediumLockList;
3997 else
3998 {
3999 mData->mSession.mLockedMedia.Unlock();
4000 alock.release();
4001 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4002 mData->mSession.mLockedMedia.Lock();
4003 alock.acquire();
4004 }
4005 alock.release();
4006
4007 if (SUCCEEDED(rc))
4008 {
4009 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4010 /* Remove lock list in case of error. */
4011 if (FAILED(rc))
4012 {
4013 mData->mSession.mLockedMedia.Unlock();
4014 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4015 mData->mSession.mLockedMedia.Lock();
4016 }
4017 }
4018 }
4019
4020 return S_OK;
4021 }
4022
4023 /* bus/channel/device differ; we need a new attachment object,
4024 * but don't try to associate it again */
4025 associate = false;
4026 break;
4027 }
4028 }
4029
4030 /* go further only if the attachment is to be indirect */
4031 if (!fIndirect)
4032 break;
4033
4034 /* perform the so called smart attachment logic for indirect
4035 * attachments. Note that smart attachment is only applicable to base
4036 * hard disks. */
4037
4038 if (medium->i_getParent().isNull())
4039 {
4040 /* first, investigate the backup copy of the current hard disk
4041 * attachments to make it possible to re-attach existing diffs to
4042 * another device slot w/o losing their contents */
4043 if (mMediumAttachments.isBackedUp())
4044 {
4045 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4046
4047 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4048 uint32_t foundLevel = 0;
4049
4050 for (MediumAttachmentList::const_iterator
4051 it = oldAtts.begin();
4052 it != oldAtts.end();
4053 ++it)
4054 {
4055 uint32_t level = 0;
4056 MediumAttachment *pAttach = *it;
4057 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4058 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4059 if (pMedium.isNull())
4060 continue;
4061
4062 if (pMedium->i_getBase(&level) == medium)
4063 {
4064 /* skip the hard disk if its currently attached (we
4065 * cannot attach the same hard disk twice) */
4066 if (i_findAttachment(*mMediumAttachments.data(),
4067 pMedium))
4068 continue;
4069
4070 /* matched device, channel and bus (i.e. attached to the
4071 * same place) will win and immediately stop the search;
4072 * otherwise the attachment that has the youngest
4073 * descendant of medium will be used
4074 */
4075 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4076 {
4077 /* the simplest case: restore the whole attachment
4078 * and return, nothing else to do */
4079 mMediumAttachments->push_back(*it);
4080
4081 /* Reattach the medium to the VM. */
4082 if (fHotplug || fSilent)
4083 {
4084 mediumLock.release();
4085 treeLock.release();
4086 alock.release();
4087
4088 MediumLockList *pMediumLockList(new MediumLockList());
4089
4090 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4091 medium /* pToLockWrite */,
4092 false /* fMediumLockWriteAll */,
4093 NULL,
4094 *pMediumLockList);
4095 alock.acquire();
4096 if (FAILED(rc))
4097 delete pMediumLockList;
4098 else
4099 {
4100 mData->mSession.mLockedMedia.Unlock();
4101 alock.release();
4102 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4103 mData->mSession.mLockedMedia.Lock();
4104 alock.acquire();
4105 }
4106 alock.release();
4107
4108 if (SUCCEEDED(rc))
4109 {
4110 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4111 /* Remove lock list in case of error. */
4112 if (FAILED(rc))
4113 {
4114 mData->mSession.mLockedMedia.Unlock();
4115 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4116 mData->mSession.mLockedMedia.Lock();
4117 }
4118 }
4119 }
4120
4121 return S_OK;
4122 }
4123 else if ( foundIt == oldAtts.end()
4124 || level > foundLevel /* prefer younger */
4125 )
4126 {
4127 foundIt = it;
4128 foundLevel = level;
4129 }
4130 }
4131 }
4132
4133 if (foundIt != oldAtts.end())
4134 {
4135 /* use the previously attached hard disk */
4136 medium = (*foundIt)->i_getMedium();
4137 mediumCaller.attach(medium);
4138 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4139 mediumLock.attach(medium);
4140 /* not implicit, doesn't require association with this VM */
4141 fIndirect = false;
4142 associate = false;
4143 /* go right to the MediumAttachment creation */
4144 break;
4145 }
4146 }
4147
4148 /* must give up the medium lock and medium tree lock as below we
4149 * go over snapshots, which needs a lock with higher lock order. */
4150 mediumLock.release();
4151 treeLock.release();
4152
4153 /* then, search through snapshots for the best diff in the given
4154 * hard disk's chain to base the new diff on */
4155
4156 ComObjPtr<Medium> base;
4157 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4158 while (snap)
4159 {
4160 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4161
4162 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4163
4164 MediumAttachment *pAttachFound = NULL;
4165 uint32_t foundLevel = 0;
4166
4167 for (MediumAttachmentList::const_iterator
4168 it = snapAtts.begin();
4169 it != snapAtts.end();
4170 ++it)
4171 {
4172 MediumAttachment *pAttach = *it;
4173 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4174 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4175 if (pMedium.isNull())
4176 continue;
4177
4178 uint32_t level = 0;
4179 if (pMedium->i_getBase(&level) == medium)
4180 {
4181 /* matched device, channel and bus (i.e. attached to the
4182 * same place) will win and immediately stop the search;
4183 * otherwise the attachment that has the youngest
4184 * descendant of medium will be used
4185 */
4186 if ( pAttach->i_getDevice() == aDevice
4187 && pAttach->i_getPort() == aControllerPort
4188 && pAttach->i_getControllerName() == aName
4189 )
4190 {
4191 pAttachFound = pAttach;
4192 break;
4193 }
4194 else if ( !pAttachFound
4195 || level > foundLevel /* prefer younger */
4196 )
4197 {
4198 pAttachFound = pAttach;
4199 foundLevel = level;
4200 }
4201 }
4202 }
4203
4204 if (pAttachFound)
4205 {
4206 base = pAttachFound->i_getMedium();
4207 break;
4208 }
4209
4210 snap = snap->i_getParent();
4211 }
4212
4213 /* re-lock medium tree and the medium, as we need it below */
4214 treeLock.acquire();
4215 mediumLock.acquire();
4216
4217 /* found a suitable diff, use it as a base */
4218 if (!base.isNull())
4219 {
4220 medium = base;
4221 mediumCaller.attach(medium);
4222 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4223 mediumLock.attach(medium);
4224 }
4225 }
4226
4227 Utf8Str strFullSnapshotFolder;
4228 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4229
4230 ComObjPtr<Medium> diff;
4231 diff.createObject();
4232 // store this diff in the same registry as the parent
4233 Guid uuidRegistryParent;
4234 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4235 {
4236 // parent image has no registry: this can happen if we're attaching a new immutable
4237 // image that has not yet been attached (medium then points to the base and we're
4238 // creating the diff image for the immutable, and the parent is not yet registered);
4239 // put the parent in the machine registry then
4240 mediumLock.release();
4241 treeLock.release();
4242 alock.release();
4243 i_addMediumToRegistry(medium);
4244 alock.acquire();
4245 treeLock.acquire();
4246 mediumLock.acquire();
4247 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4248 }
4249 rc = diff->init(mParent,
4250 medium->i_getPreferredDiffFormat(),
4251 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4252 uuidRegistryParent,
4253 DeviceType_HardDisk);
4254 if (FAILED(rc)) return rc;
4255
4256 /* Apply the normal locking logic to the entire chain. */
4257 MediumLockList *pMediumLockList(new MediumLockList());
4258 mediumLock.release();
4259 treeLock.release();
4260 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4261 diff /* pToLockWrite */,
4262 false /* fMediumLockWriteAll */,
4263 medium,
4264 *pMediumLockList);
4265 treeLock.acquire();
4266 mediumLock.acquire();
4267 if (SUCCEEDED(rc))
4268 {
4269 mediumLock.release();
4270 treeLock.release();
4271 rc = pMediumLockList->Lock();
4272 treeLock.acquire();
4273 mediumLock.acquire();
4274 if (FAILED(rc))
4275 setError(rc,
4276 tr("Could not lock medium when creating diff '%s'"),
4277 diff->i_getLocationFull().c_str());
4278 else
4279 {
4280 /* will release the lock before the potentially lengthy
4281 * operation, so protect with the special state */
4282 MachineState_T oldState = mData->mMachineState;
4283 i_setMachineState(MachineState_SettingUp);
4284
4285 mediumLock.release();
4286 treeLock.release();
4287 alock.release();
4288
4289 rc = medium->i_createDiffStorage(diff,
4290 medium->i_getPreferredDiffVariant(),
4291 pMediumLockList,
4292 NULL /* aProgress */,
4293 true /* aWait */);
4294
4295 alock.acquire();
4296 treeLock.acquire();
4297 mediumLock.acquire();
4298
4299 i_setMachineState(oldState);
4300 }
4301 }
4302
4303 /* Unlock the media and free the associated memory. */
4304 delete pMediumLockList;
4305
4306 if (FAILED(rc)) return rc;
4307
4308 /* use the created diff for the actual attachment */
4309 medium = diff;
4310 mediumCaller.attach(medium);
4311 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4312 mediumLock.attach(medium);
4313 }
4314 while (0);
4315
4316 ComObjPtr<MediumAttachment> attachment;
4317 attachment.createObject();
4318 rc = attachment->init(this,
4319 medium,
4320 aName,
4321 aControllerPort,
4322 aDevice,
4323 aType,
4324 fIndirect,
4325 false /* fPassthrough */,
4326 false /* fTempEject */,
4327 false /* fNonRotational */,
4328 false /* fDiscard */,
4329 fHotplug /* fHotPluggable */,
4330 Utf8Str::Empty);
4331 if (FAILED(rc)) return rc;
4332
4333 if (associate && !medium.isNull())
4334 {
4335 // as the last step, associate the medium to the VM
4336 rc = medium->i_addBackReference(mData->mUuid);
4337 // here we can fail because of Deleting, or being in process of creating a Diff
4338 if (FAILED(rc)) return rc;
4339
4340 mediumLock.release();
4341 treeLock.release();
4342 alock.release();
4343 i_addMediumToRegistry(medium);
4344 alock.acquire();
4345 treeLock.acquire();
4346 mediumLock.acquire();
4347 }
4348
4349 /* success: finally remember the attachment */
4350 i_setModified(IsModified_Storage);
4351 mMediumAttachments.backup();
4352 mMediumAttachments->push_back(attachment);
4353
4354 mediumLock.release();
4355 treeLock.release();
4356 alock.release();
4357
4358 if (fHotplug || fSilent)
4359 {
4360 if (!medium.isNull())
4361 {
4362 MediumLockList *pMediumLockList(new MediumLockList());
4363
4364 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4365 medium /* pToLockWrite */,
4366 false /* fMediumLockWriteAll */,
4367 NULL,
4368 *pMediumLockList);
4369 alock.acquire();
4370 if (FAILED(rc))
4371 delete pMediumLockList;
4372 else
4373 {
4374 mData->mSession.mLockedMedia.Unlock();
4375 alock.release();
4376 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4377 mData->mSession.mLockedMedia.Lock();
4378 alock.acquire();
4379 }
4380 alock.release();
4381 }
4382
4383 if (SUCCEEDED(rc))
4384 {
4385 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4386 /* Remove lock list in case of error. */
4387 if (FAILED(rc))
4388 {
4389 mData->mSession.mLockedMedia.Unlock();
4390 mData->mSession.mLockedMedia.Remove(attachment);
4391 mData->mSession.mLockedMedia.Lock();
4392 }
4393 }
4394 }
4395
4396 /* Save modified registries, but skip this machine as it's the caller's
4397 * job to save its settings like all other settings changes. */
4398 mParent->i_unmarkRegistryModified(i_getId());
4399 mParent->i_saveModifiedRegistries();
4400
4401 return rc;
4402}
4403
4404HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4405 LONG aDevice)
4406{
4407 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4408 aName.c_str(), aControllerPort, aDevice));
4409
4410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4411
4412 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4413 if (FAILED(rc)) return rc;
4414
4415 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4416
4417 /* Check for an existing controller. */
4418 ComObjPtr<StorageController> ctl;
4419 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4420 if (FAILED(rc)) return rc;
4421
4422 StorageControllerType_T ctrlType;
4423 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4424 if (FAILED(rc))
4425 return setError(E_FAIL,
4426 tr("Could not get type of controller '%s'"),
4427 aName.c_str());
4428
4429 bool fSilent = false;
4430 Utf8Str strReconfig;
4431
4432 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4433 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4434 if ( mData->mMachineState == MachineState_Paused
4435 && strReconfig == "1")
4436 fSilent = true;
4437
4438 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4439 bool fHotplug = false;
4440 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4441 fHotplug = true;
4442
4443 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4444 return setError(VBOX_E_INVALID_VM_STATE,
4445 tr("Controller '%s' does not support hotplugging"),
4446 aName.c_str());
4447
4448 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4449 aName,
4450 aControllerPort,
4451 aDevice);
4452 if (!pAttach)
4453 return setError(VBOX_E_OBJECT_NOT_FOUND,
4454 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 if (fHotplug && !pAttach->i_getHotPluggable())
4458 return setError(VBOX_E_NOT_SUPPORTED,
4459 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4460 aDevice, aControllerPort, aName.c_str());
4461
4462 /*
4463 * The VM has to detach the device before we delete any implicit diffs.
4464 * If this fails we can roll back without loosing data.
4465 */
4466 if (fHotplug || fSilent)
4467 {
4468 alock.release();
4469 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4470 alock.acquire();
4471 }
4472 if (FAILED(rc)) return rc;
4473
4474 /* If we are here everything went well and we can delete the implicit now. */
4475 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4476
4477 alock.release();
4478
4479 /* Save modified registries, but skip this machine as it's the caller's
4480 * job to save its settings like all other settings changes. */
4481 mParent->i_unmarkRegistryModified(i_getId());
4482 mParent->i_saveModifiedRegistries();
4483
4484 return rc;
4485}
4486
4487HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4488 LONG aDevice, BOOL aPassthrough)
4489{
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4491 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = i_checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4506 aName,
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4512 aDevice, aControllerPort, aName.c_str());
4513
4514
4515 i_setModified(IsModified_Storage);
4516 mMediumAttachments.backup();
4517
4518 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4519
4520 if (pAttach->i_getType() != DeviceType_DVD)
4521 return setError(E_INVALIDARG,
4522 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4523 aDevice, aControllerPort, aName.c_str());
4524 pAttach->i_updatePassthrough(!!aPassthrough);
4525
4526 return S_OK;
4527}
4528
4529HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4530 LONG aDevice, BOOL aTemporaryEject)
4531{
4532
4533 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4534 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4535
4536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4537
4538 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4539 if (FAILED(rc)) return rc;
4540
4541 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4542 aName,
4543 aControllerPort,
4544 aDevice);
4545 if (!pAttach)
4546 return setError(VBOX_E_OBJECT_NOT_FOUND,
4547 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550
4551 i_setModified(IsModified_Storage);
4552 mMediumAttachments.backup();
4553
4554 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4555
4556 if (pAttach->i_getType() != DeviceType_DVD)
4557 return setError(E_INVALIDARG,
4558 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4559 aDevice, aControllerPort, aName.c_str());
4560 pAttach->i_updateTempEject(!!aTemporaryEject);
4561
4562 return S_OK;
4563}
4564
4565HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4566 LONG aDevice, BOOL aNonRotational)
4567{
4568
4569 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4570 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4571
4572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4573
4574 HRESULT rc = i_checkStateDependency(MutableStateDep);
4575 if (FAILED(rc)) return rc;
4576
4577 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4578
4579 if (Global::IsOnlineOrTransient(mData->mMachineState))
4580 return setError(VBOX_E_INVALID_VM_STATE,
4581 tr("Invalid machine state: %s"),
4582 Global::stringifyMachineState(mData->mMachineState));
4583
4584 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4585 aName,
4586 aControllerPort,
4587 aDevice);
4588 if (!pAttach)
4589 return setError(VBOX_E_OBJECT_NOT_FOUND,
4590 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4591 aDevice, aControllerPort, aName.c_str());
4592
4593
4594 i_setModified(IsModified_Storage);
4595 mMediumAttachments.backup();
4596
4597 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4598
4599 if (pAttach->i_getType() != DeviceType_HardDisk)
4600 return setError(E_INVALIDARG,
4601 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"),
4602 aDevice, aControllerPort, aName.c_str());
4603 pAttach->i_updateNonRotational(!!aNonRotational);
4604
4605 return S_OK;
4606}
4607
4608HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4609 LONG aDevice, BOOL aDiscard)
4610{
4611
4612 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4613 aName.c_str(), aControllerPort, aDevice, aDiscard));
4614
4615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 HRESULT rc = i_checkStateDependency(MutableStateDep);
4618 if (FAILED(rc)) return rc;
4619
4620 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4621
4622 if (Global::IsOnlineOrTransient(mData->mMachineState))
4623 return setError(VBOX_E_INVALID_VM_STATE,
4624 tr("Invalid machine state: %s"),
4625 Global::stringifyMachineState(mData->mMachineState));
4626
4627 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4628 aName,
4629 aControllerPort,
4630 aDevice);
4631 if (!pAttach)
4632 return setError(VBOX_E_OBJECT_NOT_FOUND,
4633 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4634 aDevice, aControllerPort, aName.c_str());
4635
4636
4637 i_setModified(IsModified_Storage);
4638 mMediumAttachments.backup();
4639
4640 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4641
4642 if (pAttach->i_getType() != DeviceType_HardDisk)
4643 return setError(E_INVALIDARG,
4644 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"),
4645 aDevice, aControllerPort, aName.c_str());
4646 pAttach->i_updateDiscard(!!aDiscard);
4647
4648 return S_OK;
4649}
4650
4651HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4652 LONG aDevice, BOOL aHotPluggable)
4653{
4654 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4655 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4656
4657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4658
4659 HRESULT rc = i_checkStateDependency(MutableStateDep);
4660 if (FAILED(rc)) return rc;
4661
4662 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4663
4664 if (Global::IsOnlineOrTransient(mData->mMachineState))
4665 return setError(VBOX_E_INVALID_VM_STATE,
4666 tr("Invalid machine state: %s"),
4667 Global::stringifyMachineState(mData->mMachineState));
4668
4669 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4670 aName,
4671 aControllerPort,
4672 aDevice);
4673 if (!pAttach)
4674 return setError(VBOX_E_OBJECT_NOT_FOUND,
4675 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4676 aDevice, aControllerPort, aName.c_str());
4677
4678 /* Check for an existing controller. */
4679 ComObjPtr<StorageController> ctl;
4680 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4681 if (FAILED(rc)) return rc;
4682
4683 StorageControllerType_T ctrlType;
4684 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4685 if (FAILED(rc))
4686 return setError(E_FAIL,
4687 tr("Could not get type of controller '%s'"),
4688 aName.c_str());
4689
4690 if (!i_isControllerHotplugCapable(ctrlType))
4691 return setError(VBOX_E_NOT_SUPPORTED,
4692 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4693 aName.c_str());
4694
4695 i_setModified(IsModified_Storage);
4696 mMediumAttachments.backup();
4697
4698 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4699
4700 if (pAttach->i_getType() == DeviceType_Floppy)
4701 return setError(E_INVALIDARG,
4702 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"),
4703 aDevice, aControllerPort, aName.c_str());
4704 pAttach->i_updateHotPluggable(!!aHotPluggable);
4705
4706 return S_OK;
4707}
4708
4709HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4710 LONG aDevice)
4711{
4712 int rc = S_OK;
4713 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4714 aName.c_str(), aControllerPort, aDevice));
4715
4716 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4717
4718 return rc;
4719}
4720
4721HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4722 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4723{
4724 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4725 aName.c_str(), aControllerPort, aDevice));
4726
4727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4728
4729 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4730 if (FAILED(rc)) return rc;
4731
4732 if (Global::IsOnlineOrTransient(mData->mMachineState))
4733 return setError(VBOX_E_INVALID_VM_STATE,
4734 tr("Invalid machine state: %s"),
4735 Global::stringifyMachineState(mData->mMachineState));
4736
4737 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4738 aName,
4739 aControllerPort,
4740 aDevice);
4741 if (!pAttach)
4742 return setError(VBOX_E_OBJECT_NOT_FOUND,
4743 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4744 aDevice, aControllerPort, aName.c_str());
4745
4746
4747 i_setModified(IsModified_Storage);
4748 mMediumAttachments.backup();
4749
4750 IBandwidthGroup *iB = aBandwidthGroup;
4751 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4752 if (aBandwidthGroup && group.isNull())
4753 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4754
4755 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4756
4757 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4758 if (strBandwidthGroupOld.isNotEmpty())
4759 {
4760 /* Get the bandwidth group object and release it - this must not fail. */
4761 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4762 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4763 Assert(SUCCEEDED(rc));
4764
4765 pBandwidthGroupOld->i_release();
4766 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4767 }
4768
4769 if (!group.isNull())
4770 {
4771 group->i_reference();
4772 pAttach->i_updateBandwidthGroup(group->i_getName());
4773 }
4774
4775 return S_OK;
4776}
4777
4778HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4779 LONG aControllerPort,
4780 LONG aDevice,
4781 DeviceType_T aType)
4782{
4783 HRESULT rc = S_OK;
4784
4785 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4786 aName.c_str(), aControllerPort, aDevice, aType));
4787
4788 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4789
4790 return rc;
4791}
4792
4793
4794HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4795 LONG aControllerPort,
4796 LONG aDevice,
4797 BOOL aForce)
4798{
4799 int rc = S_OK;
4800 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4801 aName.c_str(), aControllerPort, aForce));
4802
4803 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4804
4805 return rc;
4806}
4807
4808HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4809 LONG aControllerPort,
4810 LONG aDevice,
4811 const ComPtr<IMedium> &aMedium,
4812 BOOL aForce)
4813{
4814 int rc = S_OK;
4815 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4816 aName.c_str(), aControllerPort, aDevice, aForce));
4817
4818 // request the host lock first, since might be calling Host methods for getting host drives;
4819 // next, protect the media tree all the while we're in here, as well as our member variables
4820 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4821 this->lockHandle(),
4822 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4823
4824 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4825 aName,
4826 aControllerPort,
4827 aDevice);
4828 if (pAttach.isNull())
4829 return setError(VBOX_E_OBJECT_NOT_FOUND,
4830 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4831 aDevice, aControllerPort, aName.c_str());
4832
4833 /* Remember previously mounted medium. The medium before taking the
4834 * backup is not necessarily the same thing. */
4835 ComObjPtr<Medium> oldmedium;
4836 oldmedium = pAttach->i_getMedium();
4837
4838 IMedium *iM = aMedium;
4839 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4840 if (aMedium && pMedium.isNull())
4841 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4842
4843 AutoCaller mediumCaller(pMedium);
4844 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4845
4846 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4847 if (pMedium)
4848 {
4849 DeviceType_T mediumType = pAttach->i_getType();
4850 switch (mediumType)
4851 {
4852 case DeviceType_DVD:
4853 case DeviceType_Floppy:
4854 break;
4855
4856 default:
4857 return setError(VBOX_E_INVALID_OBJECT_STATE,
4858 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4859 aControllerPort,
4860 aDevice,
4861 aName.c_str());
4862 }
4863 }
4864
4865 i_setModified(IsModified_Storage);
4866 mMediumAttachments.backup();
4867
4868 {
4869 // The backup operation makes the pAttach reference point to the
4870 // old settings. Re-get the correct reference.
4871 pAttach = i_findAttachment(*mMediumAttachments.data(),
4872 aName,
4873 aControllerPort,
4874 aDevice);
4875 if (!oldmedium.isNull())
4876 oldmedium->i_removeBackReference(mData->mUuid);
4877 if (!pMedium.isNull())
4878 {
4879 pMedium->i_addBackReference(mData->mUuid);
4880
4881 mediumLock.release();
4882 multiLock.release();
4883 i_addMediumToRegistry(pMedium);
4884 multiLock.acquire();
4885 mediumLock.acquire();
4886 }
4887
4888 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4889 pAttach->i_updateMedium(pMedium);
4890 }
4891
4892 i_setModified(IsModified_Storage);
4893
4894 mediumLock.release();
4895 multiLock.release();
4896 rc = i_onMediumChange(pAttach, aForce);
4897 multiLock.acquire();
4898 mediumLock.acquire();
4899
4900 /* On error roll back this change only. */
4901 if (FAILED(rc))
4902 {
4903 if (!pMedium.isNull())
4904 pMedium->i_removeBackReference(mData->mUuid);
4905 pAttach = i_findAttachment(*mMediumAttachments.data(),
4906 aName,
4907 aControllerPort,
4908 aDevice);
4909 /* If the attachment is gone in the meantime, bail out. */
4910 if (pAttach.isNull())
4911 return rc;
4912 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4913 if (!oldmedium.isNull())
4914 oldmedium->i_addBackReference(mData->mUuid);
4915 pAttach->i_updateMedium(oldmedium);
4916 }
4917
4918 mediumLock.release();
4919 multiLock.release();
4920
4921 /* Save modified registries, but skip this machine as it's the caller's
4922 * job to save its settings like all other settings changes. */
4923 mParent->i_unmarkRegistryModified(i_getId());
4924 mParent->i_saveModifiedRegistries();
4925
4926 return rc;
4927}
4928HRESULT Machine::getMedium(const com::Utf8Str &aName,
4929 LONG aControllerPort,
4930 LONG aDevice,
4931 ComPtr<IMedium> &aMedium)
4932{
4933 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4934 aName.c_str(), aControllerPort, aDevice));
4935
4936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4937
4938 aMedium = NULL;
4939
4940 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4941 aName,
4942 aControllerPort,
4943 aDevice);
4944 if (pAttach.isNull())
4945 return setError(VBOX_E_OBJECT_NOT_FOUND,
4946 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4947 aDevice, aControllerPort, aName.c_str());
4948
4949 aMedium = pAttach->i_getMedium();
4950
4951 return S_OK;
4952}
4953
4954HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4955{
4956
4957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4958
4959 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4960
4961 return S_OK;
4962}
4963
4964HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4965{
4966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4967
4968 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4969
4970 return S_OK;
4971}
4972
4973HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4974{
4975 /* Do not assert if slot is out of range, just return the advertised
4976 status. testdriver/vbox.py triggers this in logVmInfo. */
4977 if (aSlot >= mNetworkAdapters.size())
4978 return setError(E_INVALIDARG,
4979 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4980 aSlot, mNetworkAdapters.size());
4981
4982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4983
4984 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4985
4986 return S_OK;
4987}
4988
4989HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4990{
4991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4992
4993 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4994 size_t i = 0;
4995 for (settings::StringsMap::const_iterator
4996 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4997 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4998 ++it, ++i)
4999 aKeys[i] = it->first;
5000
5001 return S_OK;
5002}
5003
5004 /**
5005 * @note Locks this object for reading.
5006 */
5007HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5008 com::Utf8Str &aValue)
5009{
5010 /* start with nothing found */
5011 aValue = "";
5012
5013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5014
5015 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5016 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5017 // found:
5018 aValue = it->second; // source is a Utf8Str
5019
5020 /* return the result to caller (may be empty) */
5021 return S_OK;
5022}
5023
5024 /**
5025 * @note Locks mParent for writing + this object for writing.
5026 */
5027HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5028{
5029 Utf8Str strOldValue; // empty
5030
5031 // locking note: we only hold the read lock briefly to look up the old value,
5032 // then release it and call the onExtraCanChange callbacks. There is a small
5033 // chance of a race insofar as the callback might be called twice if two callers
5034 // change the same key at the same time, but that's a much better solution
5035 // than the deadlock we had here before. The actual changing of the extradata
5036 // is then performed under the write lock and race-free.
5037
5038 // look up the old value first; if nothing has changed then we need not do anything
5039 {
5040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5041
5042 // For snapshots don't even think about allowing changes, extradata
5043 // is global for a machine, so there is nothing snapshot specific.
5044 if (i_isSnapshotMachine())
5045 return setError(VBOX_E_INVALID_VM_STATE,
5046 tr("Cannot set extradata for a snapshot"));
5047
5048 // check if the right IMachine instance is used
5049 if (mData->mRegistered && !i_isSessionMachine())
5050 return setError(VBOX_E_INVALID_VM_STATE,
5051 tr("Cannot set extradata for an immutable machine"));
5052
5053 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5054 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5055 strOldValue = it->second;
5056 }
5057
5058 bool fChanged;
5059 if ((fChanged = (strOldValue != aValue)))
5060 {
5061 // ask for permission from all listeners outside the locks;
5062 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5063 // lock to copy the list of callbacks to invoke
5064 Bstr error;
5065 Bstr bstrValue(aValue);
5066
5067 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5068 {
5069 const char *sep = error.isEmpty() ? "" : ": ";
5070 CBSTR err = error.raw();
5071 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5072 return setError(E_ACCESSDENIED,
5073 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5074 aKey.c_str(),
5075 aValue.c_str(),
5076 sep,
5077 err);
5078 }
5079
5080 // data is changing and change not vetoed: then write it out under the lock
5081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5082
5083 if (aValue.isEmpty())
5084 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5085 else
5086 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5087 // creates a new key if needed
5088
5089 bool fNeedsGlobalSaveSettings = false;
5090 // This saving of settings is tricky: there is no "old state" for the
5091 // extradata items at all (unlike all other settings), so the old/new
5092 // settings comparison would give a wrong result!
5093 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5094
5095 if (fNeedsGlobalSaveSettings)
5096 {
5097 // save the global settings; for that we should hold only the VirtualBox lock
5098 alock.release();
5099 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5100 mParent->i_saveSettings();
5101 }
5102 }
5103
5104 // fire notification outside the lock
5105 if (fChanged)
5106 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5107
5108 return S_OK;
5109}
5110
5111HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5112{
5113 aProgress = NULL;
5114 NOREF(aSettingsFilePath);
5115 ReturnComNotImplemented();
5116}
5117
5118HRESULT Machine::saveSettings()
5119{
5120 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5121
5122 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5123 if (FAILED(rc)) return rc;
5124
5125 /* the settings file path may never be null */
5126 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5127
5128 /* save all VM data excluding snapshots */
5129 bool fNeedsGlobalSaveSettings = false;
5130 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5131 mlock.release();
5132
5133 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5134 {
5135 // save the global settings; for that we should hold only the VirtualBox lock
5136 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5137 rc = mParent->i_saveSettings();
5138 }
5139
5140 return rc;
5141}
5142
5143
5144HRESULT Machine::discardSettings()
5145{
5146 /*
5147 * We need to take the machine list lock here as well as the machine one
5148 * or we'll get into trouble should any media stuff require rolling back.
5149 *
5150 * Details:
5151 *
5152 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5153 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5154 * 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]
5155 * 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
5156 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5157 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5158 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5159 * 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
5160 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5161 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5162 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5163 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5164 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5165 * 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]
5166 * 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] (*)
5167 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5168 * 0:005> k
5169 * # Child-SP RetAddr Call Site
5170 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5171 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5172 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5173 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5174 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5175 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5176 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5177 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5178 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5179 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5180 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5181 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5182 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5183 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5184 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5185 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5186 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5187 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5188 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5189 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5190 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5191 *
5192 */
5193 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5195
5196 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5197 if (FAILED(rc)) return rc;
5198
5199 /*
5200 * during this rollback, the session will be notified if data has
5201 * been actually changed
5202 */
5203 i_rollback(true /* aNotify */);
5204
5205 return S_OK;
5206}
5207
5208/** @note Locks objects! */
5209HRESULT Machine::unregister(AutoCaller &autoCaller,
5210 CleanupMode_T aCleanupMode,
5211 std::vector<ComPtr<IMedium> > &aMedia)
5212{
5213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5214
5215 Guid id(i_getId());
5216
5217 if (mData->mSession.mState != SessionState_Unlocked)
5218 return setError(VBOX_E_INVALID_OBJECT_STATE,
5219 tr("Cannot unregister the machine '%s' while it is locked"),
5220 mUserData->s.strName.c_str());
5221
5222 // wait for state dependents to drop to zero
5223 i_ensureNoStateDependencies();
5224
5225 if (!mData->mAccessible)
5226 {
5227 // inaccessible maschines can only be unregistered; uninitialize ourselves
5228 // here because currently there may be no unregistered that are inaccessible
5229 // (this state combination is not supported). Note releasing the caller and
5230 // leaving the lock before calling uninit()
5231 alock.release();
5232 autoCaller.release();
5233
5234 uninit();
5235
5236 mParent->i_unregisterMachine(this, id);
5237 // calls VirtualBox::i_saveSettings()
5238
5239 return S_OK;
5240 }
5241
5242 HRESULT rc = S_OK;
5243
5244 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5245 // discard saved state
5246 if (mData->mMachineState == MachineState_Saved)
5247 {
5248 // add the saved state file to the list of files the caller should delete
5249 Assert(!mSSData->strStateFilePath.isEmpty());
5250 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5251
5252 mSSData->strStateFilePath.setNull();
5253
5254 // unconditionally set the machine state to powered off, we now
5255 // know no session has locked the machine
5256 mData->mMachineState = MachineState_PoweredOff;
5257 }
5258
5259 size_t cSnapshots = 0;
5260 if (mData->mFirstSnapshot)
5261 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5262 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5263 // fail now before we start detaching media
5264 return setError(VBOX_E_INVALID_OBJECT_STATE,
5265 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5266 mUserData->s.strName.c_str(), cSnapshots);
5267
5268 // This list collects the medium objects from all medium attachments
5269 // which we will detach from the machine and its snapshots, in a specific
5270 // order which allows for closing all media without getting "media in use"
5271 // errors, simply by going through the list from the front to the back:
5272 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5273 // and must be closed before the parent media from the snapshots, or closing the parents
5274 // will fail because they still have children);
5275 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5276 // the root ("first") snapshot of the machine.
5277 MediaList llMedia;
5278
5279 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5280 && mMediumAttachments->size()
5281 )
5282 {
5283 // we have media attachments: detach them all and add the Medium objects to our list
5284 if (aCleanupMode != CleanupMode_UnregisterOnly)
5285 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5286 else
5287 return setError(VBOX_E_INVALID_OBJECT_STATE,
5288 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5289 mUserData->s.strName.c_str(), mMediumAttachments->size());
5290 }
5291
5292 if (cSnapshots)
5293 {
5294 // add the media from the medium attachments of the snapshots to llMedia
5295 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5296 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5297 // into the children first
5298
5299 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5300 MachineState_T oldState = mData->mMachineState;
5301 mData->mMachineState = MachineState_DeletingSnapshot;
5302
5303 // make a copy of the first snapshot so the refcount does not drop to 0
5304 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5305 // because of the AutoCaller voodoo)
5306 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5307
5308 // GO!
5309 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5310
5311 mData->mMachineState = oldState;
5312 }
5313
5314 if (FAILED(rc))
5315 {
5316 i_rollbackMedia();
5317 return rc;
5318 }
5319
5320 // commit all the media changes made above
5321 i_commitMedia();
5322
5323 mData->mRegistered = false;
5324
5325 // machine lock no longer needed
5326 alock.release();
5327
5328 // return media to caller
5329 aMedia.resize(llMedia.size());
5330 size_t i = 0;
5331 for (MediaList::const_iterator
5332 it = llMedia.begin();
5333 it != llMedia.end();
5334 ++it, ++i)
5335 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5336
5337 mParent->i_unregisterMachine(this, id);
5338 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5339
5340 return S_OK;
5341}
5342
5343/**
5344 * Task record for deleting a machine config.
5345 */
5346class Machine::DeleteConfigTask
5347 : public Machine::Task
5348{
5349public:
5350 DeleteConfigTask(Machine *m,
5351 Progress *p,
5352 const Utf8Str &t,
5353 const RTCList<ComPtr<IMedium> > &llMediums,
5354 const StringsList &llFilesToDelete)
5355 : Task(m, p, t),
5356 m_llMediums(llMediums),
5357 m_llFilesToDelete(llFilesToDelete)
5358 {}
5359
5360private:
5361 void handler()
5362 {
5363 try
5364 {
5365 m_pMachine->i_deleteConfigHandler(*this);
5366 }
5367 catch (...)
5368 {
5369 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5370 }
5371 }
5372
5373 RTCList<ComPtr<IMedium> > m_llMediums;
5374 StringsList m_llFilesToDelete;
5375
5376 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5377};
5378
5379/**
5380 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5381 * SessionMachine::taskHandler().
5382 *
5383 * @note Locks this object for writing.
5384 *
5385 * @param task
5386 * @return
5387 */
5388void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5389{
5390 LogFlowThisFuncEnter();
5391
5392 AutoCaller autoCaller(this);
5393 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5394 if (FAILED(autoCaller.rc()))
5395 {
5396 /* we might have been uninitialized because the session was accidentally
5397 * closed by the client, so don't assert */
5398 HRESULT rc = setError(E_FAIL,
5399 tr("The session has been accidentally closed"));
5400 task.m_pProgress->i_notifyComplete(rc);
5401 LogFlowThisFuncLeave();
5402 return;
5403 }
5404
5405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5406
5407 HRESULT rc = S_OK;
5408
5409 try
5410 {
5411 ULONG uLogHistoryCount = 3;
5412 ComPtr<ISystemProperties> systemProperties;
5413 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5414 if (FAILED(rc)) throw rc;
5415
5416 if (!systemProperties.isNull())
5417 {
5418 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5419 if (FAILED(rc)) throw rc;
5420 }
5421
5422 MachineState_T oldState = mData->mMachineState;
5423 i_setMachineState(MachineState_SettingUp);
5424 alock.release();
5425 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5426 {
5427 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5428 {
5429 AutoCaller mac(pMedium);
5430 if (FAILED(mac.rc())) throw mac.rc();
5431 Utf8Str strLocation = pMedium->i_getLocationFull();
5432 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5433 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5434 if (FAILED(rc)) throw rc;
5435 }
5436 if (pMedium->i_isMediumFormatFile())
5437 {
5438 ComPtr<IProgress> pProgress2;
5439 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5440 if (FAILED(rc)) throw rc;
5441 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5442 if (FAILED(rc)) throw rc;
5443 /* Check the result of the asynchronous process. */
5444 LONG iRc;
5445 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5446 if (FAILED(rc)) throw rc;
5447 /* If the thread of the progress object has an error, then
5448 * retrieve the error info from there, or it'll be lost. */
5449 if (FAILED(iRc))
5450 throw setError(ProgressErrorInfo(pProgress2));
5451 }
5452
5453 /* Close the medium, deliberately without checking the return
5454 * code, and without leaving any trace in the error info, as
5455 * a failure here is a very minor issue, which shouldn't happen
5456 * as above we even managed to delete the medium. */
5457 {
5458 ErrorInfoKeeper eik;
5459 pMedium->Close();
5460 }
5461 }
5462 i_setMachineState(oldState);
5463 alock.acquire();
5464
5465 // delete the files pushed on the task list by Machine::Delete()
5466 // (this includes saved states of the machine and snapshots and
5467 // medium storage files from the IMedium list passed in, and the
5468 // machine XML file)
5469 for (StringsList::const_iterator
5470 it = task.m_llFilesToDelete.begin();
5471 it != task.m_llFilesToDelete.end();
5472 ++it)
5473 {
5474 const Utf8Str &strFile = *it;
5475 LogFunc(("Deleting file %s\n", strFile.c_str()));
5476 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5477 if (FAILED(rc)) throw rc;
5478
5479 int vrc = RTFileDelete(strFile.c_str());
5480 if (RT_FAILURE(vrc))
5481 throw setError(VBOX_E_IPRT_ERROR,
5482 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5483 }
5484
5485 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5486 if (FAILED(rc)) throw rc;
5487
5488 /* delete the settings only when the file actually exists */
5489 if (mData->pMachineConfigFile->fileExists())
5490 {
5491 /* Delete any backup or uncommitted XML files. Ignore failures.
5492 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5493 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5494 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5495 RTFileDelete(otherXml.c_str());
5496 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5497 RTFileDelete(otherXml.c_str());
5498
5499 /* delete the Logs folder, nothing important should be left
5500 * there (we don't check for errors because the user might have
5501 * some private files there that we don't want to delete) */
5502 Utf8Str logFolder;
5503 getLogFolder(logFolder);
5504 Assert(logFolder.length());
5505 if (RTDirExists(logFolder.c_str()))
5506 {
5507 /* Delete all VBox.log[.N] files from the Logs folder
5508 * (this must be in sync with the rotation logic in
5509 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5510 * files that may have been created by the GUI. */
5511 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5512 logFolder.c_str(), RTPATH_DELIMITER);
5513 RTFileDelete(log.c_str());
5514 log = Utf8StrFmt("%s%cVBox.png",
5515 logFolder.c_str(), RTPATH_DELIMITER);
5516 RTFileDelete(log.c_str());
5517 for (int i = uLogHistoryCount; i > 0; i--)
5518 {
5519 log = Utf8StrFmt("%s%cVBox.log.%d",
5520 logFolder.c_str(), RTPATH_DELIMITER, i);
5521 RTFileDelete(log.c_str());
5522 log = Utf8StrFmt("%s%cVBox.png.%d",
5523 logFolder.c_str(), RTPATH_DELIMITER, i);
5524 RTFileDelete(log.c_str());
5525 }
5526#if defined(RT_OS_WINDOWS)
5527 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5528 RTFileDelete(log.c_str());
5529 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5530 RTFileDelete(log.c_str());
5531#endif
5532
5533 RTDirRemove(logFolder.c_str());
5534 }
5535
5536 /* delete the Snapshots folder, nothing important should be left
5537 * there (we don't check for errors because the user might have
5538 * some private files there that we don't want to delete) */
5539 Utf8Str strFullSnapshotFolder;
5540 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5541 Assert(!strFullSnapshotFolder.isEmpty());
5542 if (RTDirExists(strFullSnapshotFolder.c_str()))
5543 RTDirRemove(strFullSnapshotFolder.c_str());
5544
5545 // delete the directory that contains the settings file, but only
5546 // if it matches the VM name
5547 Utf8Str settingsDir;
5548 if (i_isInOwnDir(&settingsDir))
5549 RTDirRemove(settingsDir.c_str());
5550 }
5551
5552 alock.release();
5553
5554 mParent->i_saveModifiedRegistries();
5555 }
5556 catch (HRESULT aRC) { rc = aRC; }
5557
5558 task.m_pProgress->i_notifyComplete(rc);
5559
5560 LogFlowThisFuncLeave();
5561}
5562
5563HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5564{
5565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5566
5567 HRESULT rc = i_checkStateDependency(MutableStateDep);
5568 if (FAILED(rc)) return rc;
5569
5570 if (mData->mRegistered)
5571 return setError(VBOX_E_INVALID_VM_STATE,
5572 tr("Cannot delete settings of a registered machine"));
5573
5574 // collect files to delete
5575 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5576 if (mData->pMachineConfigFile->fileExists())
5577 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5578
5579 RTCList<ComPtr<IMedium> > llMediums;
5580 for (size_t i = 0; i < aMedia.size(); ++i)
5581 {
5582 IMedium *pIMedium(aMedia[i]);
5583 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5584 if (pMedium.isNull())
5585 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5586 SafeArray<BSTR> ids;
5587 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5588 if (FAILED(rc)) return rc;
5589 /* At this point the medium should not have any back references
5590 * anymore. If it has it is attached to another VM and *must* not
5591 * deleted. */
5592 if (ids.size() < 1)
5593 llMediums.append(pMedium);
5594 }
5595
5596 ComObjPtr<Progress> pProgress;
5597 pProgress.createObject();
5598 rc = pProgress->init(i_getVirtualBox(),
5599 static_cast<IMachine*>(this) /* aInitiator */,
5600 tr("Deleting files"),
5601 true /* fCancellable */,
5602 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5603 tr("Collecting file inventory"));
5604 if (FAILED(rc))
5605 return rc;
5606
5607 /* create and start the task on a separate thread (note that it will not
5608 * start working until we release alock) */
5609 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5610 rc = pTask->createThread();
5611 if (FAILED(rc))
5612 return rc;
5613
5614 pProgress.queryInterfaceTo(aProgress.asOutParam());
5615
5616 LogFlowFuncLeave();
5617
5618 return S_OK;
5619}
5620
5621HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5622{
5623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5624
5625 ComObjPtr<Snapshot> pSnapshot;
5626 HRESULT rc;
5627
5628 if (aNameOrId.isEmpty())
5629 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5630 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5631 else
5632 {
5633 Guid uuid(aNameOrId);
5634 if (uuid.isValid())
5635 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5636 else
5637 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5638 }
5639 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5640
5641 return rc;
5642}
5643
5644HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5645{
5646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5647
5648 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5649 if (FAILED(rc)) return rc;
5650
5651 ComObjPtr<SharedFolder> sharedFolder;
5652 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5653 if (SUCCEEDED(rc))
5654 return setError(VBOX_E_OBJECT_IN_USE,
5655 tr("Shared folder named '%s' already exists"),
5656 aName.c_str());
5657
5658 sharedFolder.createObject();
5659 rc = sharedFolder->init(i_getMachine(),
5660 aName,
5661 aHostPath,
5662 !!aWritable,
5663 !!aAutomount,
5664 true /* fFailOnError */);
5665 if (FAILED(rc)) return rc;
5666
5667 i_setModified(IsModified_SharedFolders);
5668 mHWData.backup();
5669 mHWData->mSharedFolders.push_back(sharedFolder);
5670
5671 /* inform the direct session if any */
5672 alock.release();
5673 i_onSharedFolderChange();
5674
5675 return S_OK;
5676}
5677
5678HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5679{
5680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5681
5682 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5683 if (FAILED(rc)) return rc;
5684
5685 ComObjPtr<SharedFolder> sharedFolder;
5686 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5687 if (FAILED(rc)) return rc;
5688
5689 i_setModified(IsModified_SharedFolders);
5690 mHWData.backup();
5691 mHWData->mSharedFolders.remove(sharedFolder);
5692
5693 /* inform the direct session if any */
5694 alock.release();
5695 i_onSharedFolderChange();
5696
5697 return S_OK;
5698}
5699
5700HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5701{
5702 /* start with No */
5703 *aCanShow = FALSE;
5704
5705 ComPtr<IInternalSessionControl> directControl;
5706 {
5707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5708
5709 if (mData->mSession.mState != SessionState_Locked)
5710 return setError(VBOX_E_INVALID_VM_STATE,
5711 tr("Machine is not locked for session (session state: %s)"),
5712 Global::stringifySessionState(mData->mSession.mState));
5713
5714 if (mData->mSession.mLockType == LockType_VM)
5715 directControl = mData->mSession.mDirectControl;
5716 }
5717
5718 /* ignore calls made after #OnSessionEnd() is called */
5719 if (!directControl)
5720 return S_OK;
5721
5722 LONG64 dummy;
5723 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5724}
5725
5726HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5727{
5728 ComPtr<IInternalSessionControl> directControl;
5729 {
5730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5731
5732 if (mData->mSession.mState != SessionState_Locked)
5733 return setError(E_FAIL,
5734 tr("Machine is not locked for session (session state: %s)"),
5735 Global::stringifySessionState(mData->mSession.mState));
5736
5737 if (mData->mSession.mLockType == LockType_VM)
5738 directControl = mData->mSession.mDirectControl;
5739 }
5740
5741 /* ignore calls made after #OnSessionEnd() is called */
5742 if (!directControl)
5743 return S_OK;
5744
5745 BOOL dummy;
5746 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5747}
5748
5749#ifdef VBOX_WITH_GUEST_PROPS
5750/**
5751 * Look up a guest property in VBoxSVC's internal structures.
5752 */
5753HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5754 com::Utf8Str &aValue,
5755 LONG64 *aTimestamp,
5756 com::Utf8Str &aFlags) const
5757{
5758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5759
5760 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5761 if (it != mHWData->mGuestProperties.end())
5762 {
5763 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5764 aValue = it->second.strValue;
5765 *aTimestamp = it->second.mTimestamp;
5766 GuestPropWriteFlags(it->second.mFlags, szFlags);
5767 aFlags = Utf8Str(szFlags);
5768 }
5769
5770 return S_OK;
5771}
5772
5773/**
5774 * Query the VM that a guest property belongs to for the property.
5775 * @returns E_ACCESSDENIED if the VM process is not available or not
5776 * currently handling queries and the lookup should then be done in
5777 * VBoxSVC.
5778 */
5779HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5780 com::Utf8Str &aValue,
5781 LONG64 *aTimestamp,
5782 com::Utf8Str &aFlags) const
5783{
5784 HRESULT rc = S_OK;
5785 BSTR bValue = NULL;
5786 BSTR bFlags = NULL;
5787
5788 ComPtr<IInternalSessionControl> directControl;
5789 {
5790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5791 if (mData->mSession.mLockType == LockType_VM)
5792 directControl = mData->mSession.mDirectControl;
5793 }
5794
5795 /* ignore calls made after #OnSessionEnd() is called */
5796 if (!directControl)
5797 rc = E_ACCESSDENIED;
5798 else
5799 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5800 0 /* accessMode */,
5801 &bValue, aTimestamp, &bFlags);
5802
5803 aValue = bValue;
5804 aFlags = bFlags;
5805
5806 return rc;
5807}
5808#endif // VBOX_WITH_GUEST_PROPS
5809
5810HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5811 com::Utf8Str &aValue,
5812 LONG64 *aTimestamp,
5813 com::Utf8Str &aFlags)
5814{
5815#ifndef VBOX_WITH_GUEST_PROPS
5816 ReturnComNotImplemented();
5817#else // VBOX_WITH_GUEST_PROPS
5818
5819 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5820
5821 if (rc == E_ACCESSDENIED)
5822 /* The VM is not running or the service is not (yet) accessible */
5823 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5824 return rc;
5825#endif // VBOX_WITH_GUEST_PROPS
5826}
5827
5828HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5829{
5830 LONG64 dummyTimestamp;
5831 com::Utf8Str dummyFlags;
5832 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5833 return rc;
5834
5835}
5836HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5837{
5838 com::Utf8Str dummyFlags;
5839 com::Utf8Str dummyValue;
5840 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5841 return rc;
5842}
5843
5844#ifdef VBOX_WITH_GUEST_PROPS
5845/**
5846 * Set a guest property in VBoxSVC's internal structures.
5847 */
5848HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5849 const com::Utf8Str &aFlags, bool fDelete)
5850{
5851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5852 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5853 if (FAILED(rc)) return rc;
5854
5855 try
5856 {
5857 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5858 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5859 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5860
5861 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5862 if (it == mHWData->mGuestProperties.end())
5863 {
5864 if (!fDelete)
5865 {
5866 i_setModified(IsModified_MachineData);
5867 mHWData.backupEx();
5868
5869 RTTIMESPEC time;
5870 HWData::GuestProperty prop;
5871 prop.strValue = Bstr(aValue).raw();
5872 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5873 prop.mFlags = fFlags;
5874 mHWData->mGuestProperties[aName] = prop;
5875 }
5876 }
5877 else
5878 {
5879 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5880 {
5881 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5882 }
5883 else
5884 {
5885 i_setModified(IsModified_MachineData);
5886 mHWData.backupEx();
5887
5888 /* The backupEx() operation invalidates our iterator,
5889 * so get a new one. */
5890 it = mHWData->mGuestProperties.find(aName);
5891 Assert(it != mHWData->mGuestProperties.end());
5892
5893 if (!fDelete)
5894 {
5895 RTTIMESPEC time;
5896 it->second.strValue = aValue;
5897 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5898 it->second.mFlags = fFlags;
5899 }
5900 else
5901 mHWData->mGuestProperties.erase(it);
5902 }
5903 }
5904
5905 if (SUCCEEDED(rc))
5906 {
5907 alock.release();
5908
5909 mParent->i_onGuestPropertyChange(mData->mUuid,
5910 Bstr(aName).raw(),
5911 Bstr(aValue).raw(),
5912 Bstr(aFlags).raw());
5913 }
5914 }
5915 catch (std::bad_alloc &)
5916 {
5917 rc = E_OUTOFMEMORY;
5918 }
5919
5920 return rc;
5921}
5922
5923/**
5924 * Set a property on the VM that that property belongs to.
5925 * @returns E_ACCESSDENIED if the VM process is not available or not
5926 * currently handling queries and the setting should then be done in
5927 * VBoxSVC.
5928 */
5929HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5930 const com::Utf8Str &aFlags, bool fDelete)
5931{
5932 HRESULT rc;
5933
5934 try
5935 {
5936 ComPtr<IInternalSessionControl> directControl;
5937 {
5938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5939 if (mData->mSession.mLockType == LockType_VM)
5940 directControl = mData->mSession.mDirectControl;
5941 }
5942
5943 BSTR dummy = NULL; /* will not be changed (setter) */
5944 LONG64 dummy64;
5945 if (!directControl)
5946 rc = E_ACCESSDENIED;
5947 else
5948 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5949 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5950 fDelete? 2: 1 /* accessMode */,
5951 &dummy, &dummy64, &dummy);
5952 }
5953 catch (std::bad_alloc &)
5954 {
5955 rc = E_OUTOFMEMORY;
5956 }
5957
5958 return rc;
5959}
5960#endif // VBOX_WITH_GUEST_PROPS
5961
5962HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5963 const com::Utf8Str &aFlags)
5964{
5965#ifndef VBOX_WITH_GUEST_PROPS
5966 ReturnComNotImplemented();
5967#else // VBOX_WITH_GUEST_PROPS
5968 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5969 if (rc == E_ACCESSDENIED)
5970 /* The VM is not running or the service is not (yet) accessible */
5971 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5972 return rc;
5973#endif // VBOX_WITH_GUEST_PROPS
5974}
5975
5976HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5977{
5978 return setGuestProperty(aProperty, aValue, "");
5979}
5980
5981HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5982{
5983#ifndef VBOX_WITH_GUEST_PROPS
5984 ReturnComNotImplemented();
5985#else // VBOX_WITH_GUEST_PROPS
5986 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5987 if (rc == E_ACCESSDENIED)
5988 /* The VM is not running or the service is not (yet) accessible */
5989 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5990 return rc;
5991#endif // VBOX_WITH_GUEST_PROPS
5992}
5993
5994#ifdef VBOX_WITH_GUEST_PROPS
5995/**
5996 * Enumerate the guest properties in VBoxSVC's internal structures.
5997 */
5998HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5999 std::vector<com::Utf8Str> &aNames,
6000 std::vector<com::Utf8Str> &aValues,
6001 std::vector<LONG64> &aTimestamps,
6002 std::vector<com::Utf8Str> &aFlags)
6003{
6004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6005 Utf8Str strPatterns(aPatterns);
6006
6007 /*
6008 * Look for matching patterns and build up a list.
6009 */
6010 HWData::GuestPropertyMap propMap;
6011 for (HWData::GuestPropertyMap::const_iterator
6012 it = mHWData->mGuestProperties.begin();
6013 it != mHWData->mGuestProperties.end();
6014 ++it)
6015 {
6016 if ( strPatterns.isEmpty()
6017 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6018 RTSTR_MAX,
6019 it->first.c_str(),
6020 RTSTR_MAX,
6021 NULL)
6022 )
6023 propMap.insert(*it);
6024 }
6025
6026 alock.release();
6027
6028 /*
6029 * And build up the arrays for returning the property information.
6030 */
6031 size_t cEntries = propMap.size();
6032
6033 aNames.resize(cEntries);
6034 aValues.resize(cEntries);
6035 aTimestamps.resize(cEntries);
6036 aFlags.resize(cEntries);
6037
6038 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6039 size_t i = 0;
6040 for (HWData::GuestPropertyMap::const_iterator
6041 it = propMap.begin();
6042 it != propMap.end();
6043 ++it, ++i)
6044 {
6045 aNames[i] = it->first;
6046 aValues[i] = it->second.strValue;
6047 aTimestamps[i] = it->second.mTimestamp;
6048 GuestPropWriteFlags(it->second.mFlags, szFlags);
6049 aFlags[i] = Utf8Str(szFlags);
6050 }
6051
6052 return S_OK;
6053}
6054
6055/**
6056 * Enumerate the properties managed by a VM.
6057 * @returns E_ACCESSDENIED if the VM process is not available or not
6058 * currently handling queries and the setting should then be done in
6059 * VBoxSVC.
6060 */
6061HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6062 std::vector<com::Utf8Str> &aNames,
6063 std::vector<com::Utf8Str> &aValues,
6064 std::vector<LONG64> &aTimestamps,
6065 std::vector<com::Utf8Str> &aFlags)
6066{
6067 HRESULT rc;
6068 ComPtr<IInternalSessionControl> directControl;
6069 {
6070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6071 if (mData->mSession.mLockType == LockType_VM)
6072 directControl = mData->mSession.mDirectControl;
6073 }
6074
6075 com::SafeArray<BSTR> bNames;
6076 com::SafeArray<BSTR> bValues;
6077 com::SafeArray<LONG64> bTimestamps;
6078 com::SafeArray<BSTR> bFlags;
6079
6080 if (!directControl)
6081 rc = E_ACCESSDENIED;
6082 else
6083 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6084 ComSafeArrayAsOutParam(bNames),
6085 ComSafeArrayAsOutParam(bValues),
6086 ComSafeArrayAsOutParam(bTimestamps),
6087 ComSafeArrayAsOutParam(bFlags));
6088 size_t i;
6089 aNames.resize(bNames.size());
6090 for (i = 0; i < bNames.size(); ++i)
6091 aNames[i] = Utf8Str(bNames[i]);
6092 aValues.resize(bValues.size());
6093 for (i = 0; i < bValues.size(); ++i)
6094 aValues[i] = Utf8Str(bValues[i]);
6095 aTimestamps.resize(bTimestamps.size());
6096 for (i = 0; i < bTimestamps.size(); ++i)
6097 aTimestamps[i] = bTimestamps[i];
6098 aFlags.resize(bFlags.size());
6099 for (i = 0; i < bFlags.size(); ++i)
6100 aFlags[i] = Utf8Str(bFlags[i]);
6101
6102 return rc;
6103}
6104#endif // VBOX_WITH_GUEST_PROPS
6105HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6106 std::vector<com::Utf8Str> &aNames,
6107 std::vector<com::Utf8Str> &aValues,
6108 std::vector<LONG64> &aTimestamps,
6109 std::vector<com::Utf8Str> &aFlags)
6110{
6111#ifndef VBOX_WITH_GUEST_PROPS
6112 ReturnComNotImplemented();
6113#else // VBOX_WITH_GUEST_PROPS
6114
6115 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6116
6117 if (rc == E_ACCESSDENIED)
6118 /* The VM is not running or the service is not (yet) accessible */
6119 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6120 return rc;
6121#endif // VBOX_WITH_GUEST_PROPS
6122}
6123
6124HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6125 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6126{
6127 MediumAttachmentList atts;
6128
6129 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6130 if (FAILED(rc)) return rc;
6131
6132 aMediumAttachments.resize(atts.size());
6133 size_t i = 0;
6134 for (MediumAttachmentList::const_iterator
6135 it = atts.begin();
6136 it != atts.end();
6137 ++it, ++i)
6138 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6139
6140 return S_OK;
6141}
6142
6143HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6144 LONG aControllerPort,
6145 LONG aDevice,
6146 ComPtr<IMediumAttachment> &aAttachment)
6147{
6148 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6149 aName.c_str(), aControllerPort, aDevice));
6150
6151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6152
6153 aAttachment = NULL;
6154
6155 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6156 aName,
6157 aControllerPort,
6158 aDevice);
6159 if (pAttach.isNull())
6160 return setError(VBOX_E_OBJECT_NOT_FOUND,
6161 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6162 aDevice, aControllerPort, aName.c_str());
6163
6164 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6165
6166 return S_OK;
6167}
6168
6169
6170HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6171 StorageBus_T aConnectionType,
6172 ComPtr<IStorageController> &aController)
6173{
6174 if ( (aConnectionType <= StorageBus_Null)
6175 || (aConnectionType > StorageBus_PCIe))
6176 return setError(E_INVALIDARG,
6177 tr("Invalid connection type: %d"),
6178 aConnectionType);
6179
6180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6181
6182 HRESULT rc = i_checkStateDependency(MutableStateDep);
6183 if (FAILED(rc)) return rc;
6184
6185 /* try to find one with the name first. */
6186 ComObjPtr<StorageController> ctrl;
6187
6188 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6189 if (SUCCEEDED(rc))
6190 return setError(VBOX_E_OBJECT_IN_USE,
6191 tr("Storage controller named '%s' already exists"),
6192 aName.c_str());
6193
6194 ctrl.createObject();
6195
6196 /* get a new instance number for the storage controller */
6197 ULONG ulInstance = 0;
6198 bool fBootable = true;
6199 for (StorageControllerList::const_iterator
6200 it = mStorageControllers->begin();
6201 it != mStorageControllers->end();
6202 ++it)
6203 {
6204 if ((*it)->i_getStorageBus() == aConnectionType)
6205 {
6206 ULONG ulCurInst = (*it)->i_getInstance();
6207
6208 if (ulCurInst >= ulInstance)
6209 ulInstance = ulCurInst + 1;
6210
6211 /* Only one controller of each type can be marked as bootable. */
6212 if ((*it)->i_getBootable())
6213 fBootable = false;
6214 }
6215 }
6216
6217 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6218 if (FAILED(rc)) return rc;
6219
6220 i_setModified(IsModified_Storage);
6221 mStorageControllers.backup();
6222 mStorageControllers->push_back(ctrl);
6223
6224 ctrl.queryInterfaceTo(aController.asOutParam());
6225
6226 /* inform the direct session if any */
6227 alock.release();
6228 i_onStorageControllerChange();
6229
6230 return S_OK;
6231}
6232
6233HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6234 ComPtr<IStorageController> &aStorageController)
6235{
6236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6237
6238 ComObjPtr<StorageController> ctrl;
6239
6240 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6241 if (SUCCEEDED(rc))
6242 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6243
6244 return rc;
6245}
6246
6247HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6248 ULONG aInstance,
6249 ComPtr<IStorageController> &aStorageController)
6250{
6251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6252
6253 for (StorageControllerList::const_iterator
6254 it = mStorageControllers->begin();
6255 it != mStorageControllers->end();
6256 ++it)
6257 {
6258 if ( (*it)->i_getStorageBus() == aConnectionType
6259 && (*it)->i_getInstance() == aInstance)
6260 {
6261 (*it).queryInterfaceTo(aStorageController.asOutParam());
6262 return S_OK;
6263 }
6264 }
6265
6266 return setError(VBOX_E_OBJECT_NOT_FOUND,
6267 tr("Could not find a storage controller with instance number '%lu'"),
6268 aInstance);
6269}
6270
6271HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6272{
6273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 HRESULT rc = i_checkStateDependency(MutableStateDep);
6276 if (FAILED(rc)) return rc;
6277
6278 ComObjPtr<StorageController> ctrl;
6279
6280 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6281 if (SUCCEEDED(rc))
6282 {
6283 /* Ensure that only one controller of each type is marked as bootable. */
6284 if (aBootable == TRUE)
6285 {
6286 for (StorageControllerList::const_iterator
6287 it = mStorageControllers->begin();
6288 it != mStorageControllers->end();
6289 ++it)
6290 {
6291 ComObjPtr<StorageController> aCtrl = (*it);
6292
6293 if ( (aCtrl->i_getName() != aName)
6294 && aCtrl->i_getBootable() == TRUE
6295 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6296 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6297 {
6298 aCtrl->i_setBootable(FALSE);
6299 break;
6300 }
6301 }
6302 }
6303
6304 if (SUCCEEDED(rc))
6305 {
6306 ctrl->i_setBootable(aBootable);
6307 i_setModified(IsModified_Storage);
6308 }
6309 }
6310
6311 if (SUCCEEDED(rc))
6312 {
6313 /* inform the direct session if any */
6314 alock.release();
6315 i_onStorageControllerChange();
6316 }
6317
6318 return rc;
6319}
6320
6321HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6322{
6323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 HRESULT rc = i_checkStateDependency(MutableStateDep);
6326 if (FAILED(rc)) return rc;
6327
6328 ComObjPtr<StorageController> ctrl;
6329 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6330 if (FAILED(rc)) return rc;
6331
6332 {
6333 /* find all attached devices to the appropriate storage controller and detach them all */
6334 // make a temporary list because detachDevice invalidates iterators into
6335 // mMediumAttachments
6336 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6337
6338 for (MediumAttachmentList::const_iterator
6339 it = llAttachments2.begin();
6340 it != llAttachments2.end();
6341 ++it)
6342 {
6343 MediumAttachment *pAttachTemp = *it;
6344
6345 AutoCaller localAutoCaller(pAttachTemp);
6346 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6347
6348 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6349
6350 if (pAttachTemp->i_getControllerName() == aName)
6351 {
6352 rc = i_detachDevice(pAttachTemp, alock, NULL);
6353 if (FAILED(rc)) return rc;
6354 }
6355 }
6356 }
6357
6358 /* We can remove it now. */
6359 i_setModified(IsModified_Storage);
6360 mStorageControllers.backup();
6361
6362 ctrl->i_unshare();
6363
6364 mStorageControllers->remove(ctrl);
6365
6366 /* inform the direct session if any */
6367 alock.release();
6368 i_onStorageControllerChange();
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6374 ComPtr<IUSBController> &aController)
6375{
6376 if ( (aType <= USBControllerType_Null)
6377 || (aType >= USBControllerType_Last))
6378 return setError(E_INVALIDARG,
6379 tr("Invalid USB controller type: %d"),
6380 aType);
6381
6382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 HRESULT rc = i_checkStateDependency(MutableStateDep);
6385 if (FAILED(rc)) return rc;
6386
6387 /* try to find one with the same type first. */
6388 ComObjPtr<USBController> ctrl;
6389
6390 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6391 if (SUCCEEDED(rc))
6392 return setError(VBOX_E_OBJECT_IN_USE,
6393 tr("USB controller named '%s' already exists"),
6394 aName.c_str());
6395
6396 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6397 ULONG maxInstances;
6398 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6399 if (FAILED(rc))
6400 return rc;
6401
6402 ULONG cInstances = i_getUSBControllerCountByType(aType);
6403 if (cInstances >= maxInstances)
6404 return setError(E_INVALIDARG,
6405 tr("Too many USB controllers of this type"));
6406
6407 ctrl.createObject();
6408
6409 rc = ctrl->init(this, aName, aType);
6410 if (FAILED(rc)) return rc;
6411
6412 i_setModified(IsModified_USB);
6413 mUSBControllers.backup();
6414 mUSBControllers->push_back(ctrl);
6415
6416 ctrl.queryInterfaceTo(aController.asOutParam());
6417
6418 /* inform the direct session if any */
6419 alock.release();
6420 i_onUSBControllerChange();
6421
6422 return S_OK;
6423}
6424
6425HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6426{
6427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6428
6429 ComObjPtr<USBController> ctrl;
6430
6431 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6432 if (SUCCEEDED(rc))
6433 ctrl.queryInterfaceTo(aController.asOutParam());
6434
6435 return rc;
6436}
6437
6438HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6439 ULONG *aControllers)
6440{
6441 if ( (aType <= USBControllerType_Null)
6442 || (aType >= USBControllerType_Last))
6443 return setError(E_INVALIDARG,
6444 tr("Invalid USB controller type: %d"),
6445 aType);
6446
6447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6448
6449 ComObjPtr<USBController> ctrl;
6450
6451 *aControllers = i_getUSBControllerCountByType(aType);
6452
6453 return S_OK;
6454}
6455
6456HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6457{
6458
6459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 HRESULT rc = i_checkStateDependency(MutableStateDep);
6462 if (FAILED(rc)) return rc;
6463
6464 ComObjPtr<USBController> ctrl;
6465 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6466 if (FAILED(rc)) return rc;
6467
6468 i_setModified(IsModified_USB);
6469 mUSBControllers.backup();
6470
6471 ctrl->i_unshare();
6472
6473 mUSBControllers->remove(ctrl);
6474
6475 /* inform the direct session if any */
6476 alock.release();
6477 i_onUSBControllerChange();
6478
6479 return S_OK;
6480}
6481
6482HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6483 ULONG *aOriginX,
6484 ULONG *aOriginY,
6485 ULONG *aWidth,
6486 ULONG *aHeight,
6487 BOOL *aEnabled)
6488{
6489 uint32_t u32OriginX= 0;
6490 uint32_t u32OriginY= 0;
6491 uint32_t u32Width = 0;
6492 uint32_t u32Height = 0;
6493 uint16_t u16Flags = 0;
6494
6495 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6496 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6497 if (RT_FAILURE(vrc))
6498 {
6499#ifdef RT_OS_WINDOWS
6500 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6501 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6502 * So just assign fEnable to TRUE again.
6503 * The right fix would be to change GUI API wrappers to make sure that parameters
6504 * are changed only if API succeeds.
6505 */
6506 *aEnabled = TRUE;
6507#endif
6508 return setError(VBOX_E_IPRT_ERROR,
6509 tr("Saved guest size is not available (%Rrc)"),
6510 vrc);
6511 }
6512
6513 *aOriginX = u32OriginX;
6514 *aOriginY = u32OriginY;
6515 *aWidth = u32Width;
6516 *aHeight = u32Height;
6517 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6518
6519 return S_OK;
6520}
6521
6522HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6523 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6524{
6525 if (aScreenId != 0)
6526 return E_NOTIMPL;
6527
6528 if ( aBitmapFormat != BitmapFormat_BGR0
6529 && aBitmapFormat != BitmapFormat_BGRA
6530 && aBitmapFormat != BitmapFormat_RGBA
6531 && aBitmapFormat != BitmapFormat_PNG)
6532 return setError(E_NOTIMPL,
6533 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6534
6535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6536
6537 uint8_t *pu8Data = NULL;
6538 uint32_t cbData = 0;
6539 uint32_t u32Width = 0;
6540 uint32_t u32Height = 0;
6541
6542 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6543
6544 if (RT_FAILURE(vrc))
6545 return setError(VBOX_E_IPRT_ERROR,
6546 tr("Saved thumbnail data is not available (%Rrc)"),
6547 vrc);
6548
6549 HRESULT hr = S_OK;
6550
6551 *aWidth = u32Width;
6552 *aHeight = u32Height;
6553
6554 if (cbData > 0)
6555 {
6556 /* Convert pixels to the format expected by the API caller. */
6557 if (aBitmapFormat == BitmapFormat_BGR0)
6558 {
6559 /* [0] B, [1] G, [2] R, [3] 0. */
6560 aData.resize(cbData);
6561 memcpy(&aData.front(), pu8Data, cbData);
6562 }
6563 else if (aBitmapFormat == BitmapFormat_BGRA)
6564 {
6565 /* [0] B, [1] G, [2] R, [3] A. */
6566 aData.resize(cbData);
6567 for (uint32_t i = 0; i < cbData; i += 4)
6568 {
6569 aData[i] = pu8Data[i];
6570 aData[i + 1] = pu8Data[i + 1];
6571 aData[i + 2] = pu8Data[i + 2];
6572 aData[i + 3] = 0xff;
6573 }
6574 }
6575 else if (aBitmapFormat == BitmapFormat_RGBA)
6576 {
6577 /* [0] R, [1] G, [2] B, [3] A. */
6578 aData.resize(cbData);
6579 for (uint32_t i = 0; i < cbData; i += 4)
6580 {
6581 aData[i] = pu8Data[i + 2];
6582 aData[i + 1] = pu8Data[i + 1];
6583 aData[i + 2] = pu8Data[i];
6584 aData[i + 3] = 0xff;
6585 }
6586 }
6587 else if (aBitmapFormat == BitmapFormat_PNG)
6588 {
6589 uint8_t *pu8PNG = NULL;
6590 uint32_t cbPNG = 0;
6591 uint32_t cxPNG = 0;
6592 uint32_t cyPNG = 0;
6593
6594 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6595
6596 if (RT_SUCCESS(vrc))
6597 {
6598 aData.resize(cbPNG);
6599 if (cbPNG)
6600 memcpy(&aData.front(), pu8PNG, cbPNG);
6601 }
6602 else
6603 hr = setError(VBOX_E_IPRT_ERROR,
6604 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6605 vrc);
6606
6607 RTMemFree(pu8PNG);
6608 }
6609 }
6610
6611 freeSavedDisplayScreenshot(pu8Data);
6612
6613 return hr;
6614}
6615
6616HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6617 ULONG *aWidth,
6618 ULONG *aHeight,
6619 std::vector<BitmapFormat_T> &aBitmapFormats)
6620{
6621 if (aScreenId != 0)
6622 return E_NOTIMPL;
6623
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625
6626 uint8_t *pu8Data = NULL;
6627 uint32_t cbData = 0;
6628 uint32_t u32Width = 0;
6629 uint32_t u32Height = 0;
6630
6631 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6632
6633 if (RT_FAILURE(vrc))
6634 return setError(VBOX_E_IPRT_ERROR,
6635 tr("Saved screenshot data is not available (%Rrc)"),
6636 vrc);
6637
6638 *aWidth = u32Width;
6639 *aHeight = u32Height;
6640 aBitmapFormats.resize(1);
6641 aBitmapFormats[0] = BitmapFormat_PNG;
6642
6643 freeSavedDisplayScreenshot(pu8Data);
6644
6645 return S_OK;
6646}
6647
6648HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6649 BitmapFormat_T aBitmapFormat,
6650 ULONG *aWidth,
6651 ULONG *aHeight,
6652 std::vector<BYTE> &aData)
6653{
6654 if (aScreenId != 0)
6655 return E_NOTIMPL;
6656
6657 if (aBitmapFormat != BitmapFormat_PNG)
6658 return E_NOTIMPL;
6659
6660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6661
6662 uint8_t *pu8Data = NULL;
6663 uint32_t cbData = 0;
6664 uint32_t u32Width = 0;
6665 uint32_t u32Height = 0;
6666
6667 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6668
6669 if (RT_FAILURE(vrc))
6670 return setError(VBOX_E_IPRT_ERROR,
6671 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6672 vrc);
6673
6674 *aWidth = u32Width;
6675 *aHeight = u32Height;
6676
6677 aData.resize(cbData);
6678 if (cbData)
6679 memcpy(&aData.front(), pu8Data, cbData);
6680
6681 freeSavedDisplayScreenshot(pu8Data);
6682
6683 return S_OK;
6684}
6685
6686HRESULT Machine::hotPlugCPU(ULONG aCpu)
6687{
6688 HRESULT rc = S_OK;
6689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6690
6691 if (!mHWData->mCPUHotPlugEnabled)
6692 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6693
6694 if (aCpu >= mHWData->mCPUCount)
6695 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6696
6697 if (mHWData->mCPUAttached[aCpu])
6698 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6699
6700 alock.release();
6701 rc = i_onCPUChange(aCpu, false);
6702 alock.acquire();
6703 if (FAILED(rc)) return rc;
6704
6705 i_setModified(IsModified_MachineData);
6706 mHWData.backup();
6707 mHWData->mCPUAttached[aCpu] = true;
6708
6709 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6710 if (Global::IsOnline(mData->mMachineState))
6711 i_saveSettings(NULL);
6712
6713 return S_OK;
6714}
6715
6716HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6717{
6718 HRESULT rc = S_OK;
6719
6720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 if (!mHWData->mCPUHotPlugEnabled)
6723 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6724
6725 if (aCpu >= SchemaDefs::MaxCPUCount)
6726 return setError(E_INVALIDARG,
6727 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6728 SchemaDefs::MaxCPUCount);
6729
6730 if (!mHWData->mCPUAttached[aCpu])
6731 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6732
6733 /* CPU 0 can't be detached */
6734 if (aCpu == 0)
6735 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6736
6737 alock.release();
6738 rc = i_onCPUChange(aCpu, true);
6739 alock.acquire();
6740 if (FAILED(rc)) return rc;
6741
6742 i_setModified(IsModified_MachineData);
6743 mHWData.backup();
6744 mHWData->mCPUAttached[aCpu] = false;
6745
6746 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6747 if (Global::IsOnline(mData->mMachineState))
6748 i_saveSettings(NULL);
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6754{
6755 *aAttached = false;
6756
6757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 /* If hotplug is enabled the CPU is always enabled. */
6760 if (!mHWData->mCPUHotPlugEnabled)
6761 {
6762 if (aCpu < mHWData->mCPUCount)
6763 *aAttached = true;
6764 }
6765 else
6766 {
6767 if (aCpu < SchemaDefs::MaxCPUCount)
6768 *aAttached = mHWData->mCPUAttached[aCpu];
6769 }
6770
6771 return S_OK;
6772}
6773
6774HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6775{
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777
6778 Utf8Str log = i_getLogFilename(aIdx);
6779 if (!RTFileExists(log.c_str()))
6780 log.setNull();
6781 aFilename = log;
6782
6783 return S_OK;
6784}
6785
6786HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6787{
6788 if (aSize < 0)
6789 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6790
6791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 HRESULT rc = S_OK;
6794 Utf8Str log = i_getLogFilename(aIdx);
6795
6796 /* do not unnecessarily hold the lock while doing something which does
6797 * not need the lock and potentially takes a long time. */
6798 alock.release();
6799
6800 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6801 * keeps the SOAP reply size under 1M for the webservice (we're using
6802 * base64 encoded strings for binary data for years now, avoiding the
6803 * expansion of each byte array element to approx. 25 bytes of XML. */
6804 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6805 aData.resize(cbData);
6806
6807 RTFILE LogFile;
6808 int vrc = RTFileOpen(&LogFile, log.c_str(),
6809 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6810 if (RT_SUCCESS(vrc))
6811 {
6812 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6813 if (RT_SUCCESS(vrc))
6814 aData.resize(cbData);
6815 else
6816 rc = setError(VBOX_E_IPRT_ERROR,
6817 tr("Could not read log file '%s' (%Rrc)"),
6818 log.c_str(), vrc);
6819 RTFileClose(LogFile);
6820 }
6821 else
6822 rc = setError(VBOX_E_IPRT_ERROR,
6823 tr("Could not open log file '%s' (%Rrc)"),
6824 log.c_str(), vrc);
6825
6826 if (FAILED(rc))
6827 aData.resize(0);
6828
6829 return rc;
6830}
6831
6832
6833/**
6834 * Currently this method doesn't attach device to the running VM,
6835 * just makes sure it's plugged on next VM start.
6836 */
6837HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6838{
6839 // lock scope
6840 {
6841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 HRESULT rc = i_checkStateDependency(MutableStateDep);
6844 if (FAILED(rc)) return rc;
6845
6846 ChipsetType_T aChipset = ChipsetType_PIIX3;
6847 COMGETTER(ChipsetType)(&aChipset);
6848
6849 if (aChipset != ChipsetType_ICH9)
6850 {
6851 return setError(E_INVALIDARG,
6852 tr("Host PCI attachment only supported with ICH9 chipset"));
6853 }
6854
6855 // check if device with this host PCI address already attached
6856 for (HWData::PCIDeviceAssignmentList::const_iterator
6857 it = mHWData->mPCIDeviceAssignments.begin();
6858 it != mHWData->mPCIDeviceAssignments.end();
6859 ++it)
6860 {
6861 LONG iHostAddress = -1;
6862 ComPtr<PCIDeviceAttachment> pAttach;
6863 pAttach = *it;
6864 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6865 if (iHostAddress == aHostAddress)
6866 return setError(E_INVALIDARG,
6867 tr("Device with host PCI address already attached to this VM"));
6868 }
6869
6870 ComObjPtr<PCIDeviceAttachment> pda;
6871 char name[32];
6872
6873 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6874 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6875 pda.createObject();
6876 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6877 i_setModified(IsModified_MachineData);
6878 mHWData.backup();
6879 mHWData->mPCIDeviceAssignments.push_back(pda);
6880 }
6881
6882 return S_OK;
6883}
6884
6885/**
6886 * Currently this method doesn't detach device from the running VM,
6887 * just makes sure it's not plugged on next VM start.
6888 */
6889HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6890{
6891 ComObjPtr<PCIDeviceAttachment> pAttach;
6892 bool fRemoved = false;
6893 HRESULT rc;
6894
6895 // lock scope
6896 {
6897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6898
6899 rc = i_checkStateDependency(MutableStateDep);
6900 if (FAILED(rc)) return rc;
6901
6902 for (HWData::PCIDeviceAssignmentList::const_iterator
6903 it = mHWData->mPCIDeviceAssignments.begin();
6904 it != mHWData->mPCIDeviceAssignments.end();
6905 ++it)
6906 {
6907 LONG iHostAddress = -1;
6908 pAttach = *it;
6909 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6910 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6911 {
6912 i_setModified(IsModified_MachineData);
6913 mHWData.backup();
6914 mHWData->mPCIDeviceAssignments.remove(pAttach);
6915 fRemoved = true;
6916 break;
6917 }
6918 }
6919 }
6920
6921
6922 /* Fire event outside of the lock */
6923 if (fRemoved)
6924 {
6925 Assert(!pAttach.isNull());
6926 ComPtr<IEventSource> es;
6927 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6928 Assert(SUCCEEDED(rc));
6929 Bstr mid;
6930 rc = this->COMGETTER(Id)(mid.asOutParam());
6931 Assert(SUCCEEDED(rc));
6932 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6933 }
6934
6935 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6936 tr("No host PCI device %08x attached"),
6937 aHostAddress
6938 );
6939}
6940
6941HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6942{
6943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6944
6945 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6946 size_t i = 0;
6947 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6948 it = mHWData->mPCIDeviceAssignments.begin();
6949 it != mHWData->mPCIDeviceAssignments.end();
6950 ++it, ++i)
6951 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6952
6953 return S_OK;
6954}
6955
6956HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6957{
6958 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6959
6960 return S_OK;
6961}
6962
6963HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6964{
6965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6966
6967 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6968
6969 return S_OK;
6970}
6971
6972HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6973{
6974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6976 if (SUCCEEDED(hrc))
6977 {
6978 hrc = mHWData.backupEx();
6979 if (SUCCEEDED(hrc))
6980 {
6981 i_setModified(IsModified_MachineData);
6982 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6983 }
6984 }
6985 return hrc;
6986}
6987
6988HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6989{
6990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6992 return S_OK;
6993}
6994
6995HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6996{
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6999 if (SUCCEEDED(hrc))
7000 {
7001 hrc = mHWData.backupEx();
7002 if (SUCCEEDED(hrc))
7003 {
7004 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7005 if (SUCCEEDED(hrc))
7006 i_setModified(IsModified_MachineData);
7007 }
7008 }
7009 return hrc;
7010}
7011
7012HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7013{
7014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7015
7016 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7017
7018 return S_OK;
7019}
7020
7021HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7022{
7023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7024 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7025 if (SUCCEEDED(hrc))
7026 {
7027 hrc = mHWData.backupEx();
7028 if (SUCCEEDED(hrc))
7029 {
7030 i_setModified(IsModified_MachineData);
7031 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7032 }
7033 }
7034 return hrc;
7035}
7036
7037HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7038{
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7042
7043 return S_OK;
7044}
7045
7046HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7047{
7048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7049
7050 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7051 if ( SUCCEEDED(hrc)
7052 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7053 {
7054 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7055 int vrc;
7056
7057 if (aAutostartEnabled)
7058 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7059 else
7060 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7061
7062 if (RT_SUCCESS(vrc))
7063 {
7064 hrc = mHWData.backupEx();
7065 if (SUCCEEDED(hrc))
7066 {
7067 i_setModified(IsModified_MachineData);
7068 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7069 }
7070 }
7071 else if (vrc == VERR_NOT_SUPPORTED)
7072 hrc = setError(VBOX_E_NOT_SUPPORTED,
7073 tr("The VM autostart feature is not supported on this platform"));
7074 else if (vrc == VERR_PATH_NOT_FOUND)
7075 hrc = setError(E_FAIL,
7076 tr("The path to the autostart database is not set"));
7077 else
7078 hrc = setError(E_UNEXPECTED,
7079 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7080 aAutostartEnabled ? "Adding" : "Removing",
7081 mUserData->s.strName.c_str(), vrc);
7082 }
7083 return hrc;
7084}
7085
7086HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7087{
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7091
7092 return S_OK;
7093}
7094
7095HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7096{
7097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7098 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7099 if (SUCCEEDED(hrc))
7100 {
7101 hrc = mHWData.backupEx();
7102 if (SUCCEEDED(hrc))
7103 {
7104 i_setModified(IsModified_MachineData);
7105 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7106 }
7107 }
7108 return hrc;
7109}
7110
7111HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7112{
7113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7114
7115 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7116
7117 return S_OK;
7118}
7119
7120HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7121{
7122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7123 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7124 if ( SUCCEEDED(hrc)
7125 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7126 {
7127 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7128 int vrc;
7129
7130 if (aAutostopType != AutostopType_Disabled)
7131 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7132 else
7133 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7134
7135 if (RT_SUCCESS(vrc))
7136 {
7137 hrc = mHWData.backupEx();
7138 if (SUCCEEDED(hrc))
7139 {
7140 i_setModified(IsModified_MachineData);
7141 mHWData->mAutostart.enmAutostopType = aAutostopType;
7142 }
7143 }
7144 else if (vrc == VERR_NOT_SUPPORTED)
7145 hrc = setError(VBOX_E_NOT_SUPPORTED,
7146 tr("The VM autostop feature is not supported on this platform"));
7147 else if (vrc == VERR_PATH_NOT_FOUND)
7148 hrc = setError(E_FAIL,
7149 tr("The path to the autostart database is not set"));
7150 else
7151 hrc = setError(E_UNEXPECTED,
7152 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7153 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7154 mUserData->s.strName.c_str(), vrc);
7155 }
7156 return hrc;
7157}
7158
7159HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7160{
7161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7162
7163 aDefaultFrontend = mHWData->mDefaultFrontend;
7164
7165 return S_OK;
7166}
7167
7168HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7169{
7170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7171 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7172 if (SUCCEEDED(hrc))
7173 {
7174 hrc = mHWData.backupEx();
7175 if (SUCCEEDED(hrc))
7176 {
7177 i_setModified(IsModified_MachineData);
7178 mHWData->mDefaultFrontend = aDefaultFrontend;
7179 }
7180 }
7181 return hrc;
7182}
7183
7184HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7185{
7186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7187 size_t cbIcon = mUserData->s.ovIcon.size();
7188 aIcon.resize(cbIcon);
7189 if (cbIcon)
7190 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7191 return S_OK;
7192}
7193
7194HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7195{
7196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7197 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7198 if (SUCCEEDED(hrc))
7199 {
7200 i_setModified(IsModified_MachineData);
7201 mUserData.backup();
7202 size_t cbIcon = aIcon.size();
7203 mUserData->s.ovIcon.resize(cbIcon);
7204 if (cbIcon)
7205 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7206 }
7207 return hrc;
7208}
7209
7210HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7211{
7212#ifdef VBOX_WITH_USB
7213 *aUSBProxyAvailable = true;
7214#else
7215 *aUSBProxyAvailable = false;
7216#endif
7217 return S_OK;
7218}
7219
7220HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7221{
7222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 aVMProcessPriority = mUserData->s.strVMPriority;
7225
7226 return S_OK;
7227}
7228
7229HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7230{
7231 RT_NOREF(aVMProcessPriority);
7232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7233 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7234 if (SUCCEEDED(hrc))
7235 {
7236 /** @todo r=klaus: currently this is marked as not implemented, as
7237 * the code for setting the priority of the process is not there
7238 * (neither when starting the VM nor at runtime). */
7239 ReturnComNotImplemented();
7240#if 0
7241 hrc = mUserData.backupEx();
7242 if (SUCCEEDED(hrc))
7243 {
7244 i_setModified(IsModified_MachineData);
7245 mUserData->s.strVMPriority = aVMProcessPriority;
7246 }
7247#endif
7248 }
7249 return hrc;
7250}
7251
7252HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7253 ComPtr<IProgress> &aProgress)
7254{
7255 ComObjPtr<Progress> pP;
7256 Progress *ppP = pP;
7257 IProgress *iP = static_cast<IProgress *>(ppP);
7258 IProgress **pProgress = &iP;
7259
7260 IMachine *pTarget = aTarget;
7261
7262 /* Convert the options. */
7263 RTCList<CloneOptions_T> optList;
7264 if (aOptions.size())
7265 for (size_t i = 0; i < aOptions.size(); ++i)
7266 optList.append(aOptions[i]);
7267
7268 if (optList.contains(CloneOptions_Link))
7269 {
7270 if (!i_isSnapshotMachine())
7271 return setError(E_INVALIDARG,
7272 tr("Linked clone can only be created from a snapshot"));
7273 if (aMode != CloneMode_MachineState)
7274 return setError(E_INVALIDARG,
7275 tr("Linked clone can only be created for a single machine state"));
7276 }
7277 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7278
7279 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7280
7281 HRESULT rc = pWorker->start(pProgress);
7282
7283 pP = static_cast<Progress *>(*pProgress);
7284 pP.queryInterfaceTo(aProgress.asOutParam());
7285
7286 return rc;
7287
7288}
7289
7290HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7291 const com::Utf8Str &aType,
7292 ComPtr<IProgress> &aProgress)
7293{
7294 LogFlowThisFuncEnter();
7295
7296 ComObjPtr<Progress> progress;
7297
7298 progress.createObject();
7299
7300 HRESULT rc = S_OK;
7301 Utf8Str targetPath = aTargetPath;
7302 Utf8Str type = aType;
7303
7304 /* Initialize our worker task */
7305 MachineMoveVM* task = NULL;
7306 try
7307 {
7308 task = new MachineMoveVM(this, targetPath, type, progress);
7309 }
7310 catch(...)
7311 {
7312 delete task;
7313 return rc;
7314 }
7315
7316 /*
7317 * task pointer will be owned by the ThreadTask class.
7318 * There is no need to call operator "delete" in the end.
7319 */
7320 rc = task->init();
7321 if (SUCCEEDED(rc))
7322 {
7323 rc = task->createThread();
7324 if (FAILED(rc))
7325 {
7326 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7327 }
7328
7329 /* Return progress to the caller */
7330 progress.queryInterfaceTo(aProgress.asOutParam());
7331 }
7332
7333 LogFlowThisFuncLeave();
7334 return rc;
7335
7336}
7337
7338HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7339{
7340 NOREF(aProgress);
7341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7342
7343 // This check should always fail.
7344 HRESULT rc = i_checkStateDependency(MutableStateDep);
7345 if (FAILED(rc)) return rc;
7346
7347 AssertFailedReturn(E_NOTIMPL);
7348}
7349
7350HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7351{
7352 NOREF(aSavedStateFile);
7353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7354
7355 // This check should always fail.
7356 HRESULT rc = i_checkStateDependency(MutableStateDep);
7357 if (FAILED(rc)) return rc;
7358
7359 AssertFailedReturn(E_NOTIMPL);
7360}
7361
7362HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7363{
7364 NOREF(aFRemoveFile);
7365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7366
7367 // This check should always fail.
7368 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7369 if (FAILED(rc)) return rc;
7370
7371 AssertFailedReturn(E_NOTIMPL);
7372}
7373
7374// public methods for internal purposes
7375/////////////////////////////////////////////////////////////////////////////
7376
7377/**
7378 * Adds the given IsModified_* flag to the dirty flags of the machine.
7379 * This must be called either during i_loadSettings or under the machine write lock.
7380 * @param fl Flag
7381 * @param fAllowStateModification If state modifications are allowed.
7382 */
7383void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7384{
7385 mData->flModifications |= fl;
7386 if (fAllowStateModification && i_isStateModificationAllowed())
7387 mData->mCurrentStateModified = true;
7388}
7389
7390/**
7391 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7392 * care of the write locking.
7393 *
7394 * @param fModification The flag to add.
7395 * @param fAllowStateModification If state modifications are allowed.
7396 */
7397void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7398{
7399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 i_setModified(fModification, fAllowStateModification);
7401}
7402
7403/**
7404 * Saves the registry entry of this machine to the given configuration node.
7405 *
7406 * @param data Machine registry data.
7407 *
7408 * @note locks this object for reading.
7409 */
7410HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7411{
7412 AutoLimitedCaller autoCaller(this);
7413 AssertComRCReturnRC(autoCaller.rc());
7414
7415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 data.uuid = mData->mUuid;
7418 data.strSettingsFile = mData->m_strConfigFile;
7419
7420 return S_OK;
7421}
7422
7423/**
7424 * Calculates the absolute path of the given path taking the directory of the
7425 * machine settings file as the current directory.
7426 *
7427 * @param strPath Path to calculate the absolute path for.
7428 * @param aResult Where to put the result (used only on success, can be the
7429 * same Utf8Str instance as passed in @a aPath).
7430 * @return IPRT result.
7431 *
7432 * @note Locks this object for reading.
7433 */
7434int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7435{
7436 AutoCaller autoCaller(this);
7437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7438
7439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7440
7441 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7442
7443 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7444
7445 strSettingsDir.stripFilename();
7446 char folder[RTPATH_MAX];
7447 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7448 if (RT_SUCCESS(vrc))
7449 aResult = folder;
7450
7451 return vrc;
7452}
7453
7454/**
7455 * Copies strSource to strTarget, making it relative to the machine folder
7456 * if it is a subdirectory thereof, or simply copying it otherwise.
7457 *
7458 * @param strSource Path to evaluate and copy.
7459 * @param strTarget Buffer to receive target path.
7460 *
7461 * @note Locks this object for reading.
7462 */
7463void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7464 Utf8Str &strTarget)
7465{
7466 AutoCaller autoCaller(this);
7467 AssertComRCReturn(autoCaller.rc(), (void)0);
7468
7469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7470
7471 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7472 // use strTarget as a temporary buffer to hold the machine settings dir
7473 strTarget = mData->m_strConfigFileFull;
7474 strTarget.stripFilename();
7475 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7476 {
7477 // is relative: then append what's left
7478 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7479 // for empty paths (only possible for subdirs) use "." to avoid
7480 // triggering default settings for not present config attributes.
7481 if (strTarget.isEmpty())
7482 strTarget = ".";
7483 }
7484 else
7485 // is not relative: then overwrite
7486 strTarget = strSource;
7487}
7488
7489/**
7490 * Returns the full path to the machine's log folder in the
7491 * \a aLogFolder argument.
7492 */
7493void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7494{
7495 AutoCaller autoCaller(this);
7496 AssertComRCReturnVoid(autoCaller.rc());
7497
7498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7499
7500 char szTmp[RTPATH_MAX];
7501 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7502 if (RT_SUCCESS(vrc))
7503 {
7504 if (szTmp[0] && !mUserData.isNull())
7505 {
7506 char szTmp2[RTPATH_MAX];
7507 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7508 if (RT_SUCCESS(vrc))
7509 aLogFolder = Utf8StrFmt("%s%c%s",
7510 szTmp2,
7511 RTPATH_DELIMITER,
7512 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7513 }
7514 else
7515 vrc = VERR_PATH_IS_RELATIVE;
7516 }
7517
7518 if (RT_FAILURE(vrc))
7519 {
7520 // fallback if VBOX_USER_LOGHOME is not set or invalid
7521 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7522 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7523 aLogFolder.append(RTPATH_DELIMITER);
7524 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7525 }
7526}
7527
7528/**
7529 * Returns the full path to the machine's log file for an given index.
7530 */
7531Utf8Str Machine::i_getLogFilename(ULONG idx)
7532{
7533 Utf8Str logFolder;
7534 getLogFolder(logFolder);
7535 Assert(logFolder.length());
7536
7537 Utf8Str log;
7538 if (idx == 0)
7539 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7540#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7541 else if (idx == 1)
7542 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7543 else
7544 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7545#else
7546 else
7547 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7548#endif
7549 return log;
7550}
7551
7552/**
7553 * Returns the full path to the machine's hardened log file.
7554 */
7555Utf8Str Machine::i_getHardeningLogFilename(void)
7556{
7557 Utf8Str strFilename;
7558 getLogFolder(strFilename);
7559 Assert(strFilename.length());
7560 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7561 return strFilename;
7562}
7563
7564
7565/**
7566 * Composes a unique saved state filename based on the current system time. The filename is
7567 * granular to the second so this will work so long as no more than one snapshot is taken on
7568 * a machine per second.
7569 *
7570 * Before version 4.1, we used this formula for saved state files:
7571 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7572 * which no longer works because saved state files can now be shared between the saved state of the
7573 * "saved" machine and an online snapshot, and the following would cause problems:
7574 * 1) save machine
7575 * 2) create online snapshot from that machine state --> reusing saved state file
7576 * 3) save machine again --> filename would be reused, breaking the online snapshot
7577 *
7578 * So instead we now use a timestamp.
7579 *
7580 * @param strStateFilePath
7581 */
7582
7583void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7584{
7585 AutoCaller autoCaller(this);
7586 AssertComRCReturnVoid(autoCaller.rc());
7587
7588 {
7589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7590 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7591 }
7592
7593 RTTIMESPEC ts;
7594 RTTimeNow(&ts);
7595 RTTIME time;
7596 RTTimeExplode(&time, &ts);
7597
7598 strStateFilePath += RTPATH_DELIMITER;
7599 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7600 time.i32Year, time.u8Month, time.u8MonthDay,
7601 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7602}
7603
7604/**
7605 * Returns the full path to the default video capture file.
7606 */
7607void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7608{
7609 AutoCaller autoCaller(this);
7610 AssertComRCReturnVoid(autoCaller.rc());
7611
7612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7613
7614 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7615 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7616 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7617}
7618
7619/**
7620 * Returns whether at least one USB controller is present for the VM.
7621 */
7622bool Machine::i_isUSBControllerPresent()
7623{
7624 AutoCaller autoCaller(this);
7625 AssertComRCReturn(autoCaller.rc(), false);
7626
7627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7628
7629 return (mUSBControllers->size() > 0);
7630}
7631
7632/**
7633 * @note Locks this object for writing, calls the client process
7634 * (inside the lock).
7635 */
7636HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7637 const Utf8Str &strFrontend,
7638 const Utf8Str &strEnvironment,
7639 ProgressProxy *aProgress)
7640{
7641 LogFlowThisFuncEnter();
7642
7643 AssertReturn(aControl, E_FAIL);
7644 AssertReturn(aProgress, E_FAIL);
7645 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7646
7647 AutoCaller autoCaller(this);
7648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7649
7650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7651
7652 if (!mData->mRegistered)
7653 return setError(E_UNEXPECTED,
7654 tr("The machine '%s' is not registered"),
7655 mUserData->s.strName.c_str());
7656
7657 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7658
7659 /* The process started when launching a VM with separate UI/VM processes is always
7660 * the UI process, i.e. needs special handling as it won't claim the session. */
7661 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7662
7663 if (fSeparate)
7664 {
7665 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7666 return setError(VBOX_E_INVALID_OBJECT_STATE,
7667 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7668 mUserData->s.strName.c_str());
7669 }
7670 else
7671 {
7672 if ( mData->mSession.mState == SessionState_Locked
7673 || mData->mSession.mState == SessionState_Spawning
7674 || mData->mSession.mState == SessionState_Unlocking)
7675 return setError(VBOX_E_INVALID_OBJECT_STATE,
7676 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7677 mUserData->s.strName.c_str());
7678
7679 /* may not be busy */
7680 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7681 }
7682
7683 /* get the path to the executable */
7684 char szPath[RTPATH_MAX];
7685 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7686 size_t cchBufLeft = strlen(szPath);
7687 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7688 szPath[cchBufLeft] = 0;
7689 char *pszNamePart = szPath + cchBufLeft;
7690 cchBufLeft = sizeof(szPath) - cchBufLeft;
7691
7692 int vrc = VINF_SUCCESS;
7693 RTPROCESS pid = NIL_RTPROCESS;
7694
7695 RTENV env = RTENV_DEFAULT;
7696
7697 if (!strEnvironment.isEmpty())
7698 {
7699 char *newEnvStr = NULL;
7700
7701 do
7702 {
7703 /* clone the current environment */
7704 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7705 AssertRCBreakStmt(vrc2, vrc = vrc2);
7706
7707 newEnvStr = RTStrDup(strEnvironment.c_str());
7708 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7709
7710 /* put new variables to the environment
7711 * (ignore empty variable names here since RTEnv API
7712 * intentionally doesn't do that) */
7713 char *var = newEnvStr;
7714 for (char *p = newEnvStr; *p; ++p)
7715 {
7716 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7717 {
7718 *p = '\0';
7719 if (*var)
7720 {
7721 char *val = strchr(var, '=');
7722 if (val)
7723 {
7724 *val++ = '\0';
7725 vrc2 = RTEnvSetEx(env, var, val);
7726 }
7727 else
7728 vrc2 = RTEnvUnsetEx(env, var);
7729 if (RT_FAILURE(vrc2))
7730 break;
7731 }
7732 var = p + 1;
7733 }
7734 }
7735 if (RT_SUCCESS(vrc2) && *var)
7736 vrc2 = RTEnvPutEx(env, var);
7737
7738 AssertRCBreakStmt(vrc2, vrc = vrc2);
7739 }
7740 while (0);
7741
7742 if (newEnvStr != NULL)
7743 RTStrFree(newEnvStr);
7744 }
7745
7746 /* Hardening logging */
7747#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7748 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7749 {
7750 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7751 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7752 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7753 {
7754 Utf8Str strStartupLogDir = strHardeningLogFile;
7755 strStartupLogDir.stripFilename();
7756 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7757 file without stripping the file. */
7758 }
7759 strSupHardeningLogArg.append(strHardeningLogFile);
7760
7761 /* Remove legacy log filename to avoid confusion. */
7762 Utf8Str strOldStartupLogFile;
7763 getLogFolder(strOldStartupLogFile);
7764 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7765 RTFileDelete(strOldStartupLogFile.c_str());
7766 }
7767 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7768#else
7769 const char *pszSupHardeningLogArg = NULL;
7770#endif
7771
7772 Utf8Str strCanonicalName;
7773
7774#ifdef VBOX_WITH_QTGUI
7775 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7776 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7777 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7778 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7779 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7780 {
7781 strCanonicalName = "GUI/Qt";
7782# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7783 /* Modify the base path so that we don't need to use ".." below. */
7784 RTPathStripTrailingSlash(szPath);
7785 RTPathStripFilename(szPath);
7786 cchBufLeft = strlen(szPath);
7787 pszNamePart = szPath + cchBufLeft;
7788 cchBufLeft = sizeof(szPath) - cchBufLeft;
7789
7790# define OSX_APP_NAME "VirtualBoxVM"
7791# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7792
7793 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7794 if ( strAppOverride.contains(".")
7795 || strAppOverride.contains("/")
7796 || strAppOverride.contains("\\")
7797 || strAppOverride.contains(":"))
7798 strAppOverride.setNull();
7799 Utf8Str strAppPath;
7800 if (!strAppOverride.isEmpty())
7801 {
7802 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7803 Utf8Str strFullPath(szPath);
7804 strFullPath.append(strAppPath);
7805 /* there is a race, but people using this deserve the failure */
7806 if (!RTFileExists(strFullPath.c_str()))
7807 strAppOverride.setNull();
7808 }
7809 if (strAppOverride.isEmpty())
7810 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7811 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7812 strcpy(pszNamePart, strAppPath.c_str());
7813# else
7814# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7815 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7816# else
7817 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7818# endif
7819 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7820 strcpy(pszNamePart, s_szVirtualBox_exe);
7821# endif
7822
7823 Utf8Str idStr = mData->mUuid.toString();
7824 const char *apszArgs[] =
7825 {
7826 szPath,
7827 "--comment", mUserData->s.strName.c_str(),
7828 "--startvm", idStr.c_str(),
7829 "--no-startvm-errormsgbox",
7830 NULL, /* For "--separate". */
7831 NULL, /* For "--sup-startup-log". */
7832 NULL
7833 };
7834 unsigned iArg = 6;
7835 if (fSeparate)
7836 apszArgs[iArg++] = "--separate";
7837 apszArgs[iArg++] = pszSupHardeningLogArg;
7838
7839 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7840 }
7841#else /* !VBOX_WITH_QTGUI */
7842 if (0)
7843 ;
7844#endif /* VBOX_WITH_QTGUI */
7845
7846 else
7847
7848#ifdef VBOX_WITH_VBOXSDL
7849 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7850 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7851 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7852 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7853 {
7854 strCanonicalName = "GUI/SDL";
7855 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7856 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7857 strcpy(pszNamePart, s_szVBoxSDL_exe);
7858
7859 Utf8Str idStr = mData->mUuid.toString();
7860 const char *apszArgs[] =
7861 {
7862 szPath,
7863 "--comment", mUserData->s.strName.c_str(),
7864 "--startvm", idStr.c_str(),
7865 NULL, /* For "--separate". */
7866 NULL, /* For "--sup-startup-log". */
7867 NULL
7868 };
7869 unsigned iArg = 5;
7870 if (fSeparate)
7871 apszArgs[iArg++] = "--separate";
7872 apszArgs[iArg++] = pszSupHardeningLogArg;
7873
7874 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7875 }
7876#else /* !VBOX_WITH_VBOXSDL */
7877 if (0)
7878 ;
7879#endif /* !VBOX_WITH_VBOXSDL */
7880
7881 else
7882
7883#ifdef VBOX_WITH_HEADLESS
7884 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7885 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7886 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7887 )
7888 {
7889 strCanonicalName = "headless";
7890 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7891 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7892 * and a VM works even if the server has not been installed.
7893 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7894 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7895 * differently in 4.0 and 3.x.
7896 */
7897 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7898 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7899 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7900
7901 Utf8Str idStr = mData->mUuid.toString();
7902 const char *apszArgs[] =
7903 {
7904 szPath,
7905 "--comment", mUserData->s.strName.c_str(),
7906 "--startvm", idStr.c_str(),
7907 "--vrde", "config",
7908 NULL, /* For "--capture". */
7909 NULL, /* For "--sup-startup-log". */
7910 NULL
7911 };
7912 unsigned iArg = 7;
7913 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7914 apszArgs[iArg++] = "--capture";
7915 apszArgs[iArg++] = pszSupHardeningLogArg;
7916
7917# ifdef RT_OS_WINDOWS
7918 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7919# else
7920 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7921# endif
7922 }
7923#else /* !VBOX_WITH_HEADLESS */
7924 if (0)
7925 ;
7926#endif /* !VBOX_WITH_HEADLESS */
7927 else
7928 {
7929 RTEnvDestroy(env);
7930 return setError(E_INVALIDARG,
7931 tr("Invalid frontend name: '%s'"),
7932 strFrontend.c_str());
7933 }
7934
7935 RTEnvDestroy(env);
7936
7937 if (RT_FAILURE(vrc))
7938 return setError(VBOX_E_IPRT_ERROR,
7939 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7940 mUserData->s.strName.c_str(), vrc);
7941
7942 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7943
7944 if (!fSeparate)
7945 {
7946 /*
7947 * Note that we don't release the lock here before calling the client,
7948 * because it doesn't need to call us back if called with a NULL argument.
7949 * Releasing the lock here is dangerous because we didn't prepare the
7950 * launch data yet, but the client we've just started may happen to be
7951 * too fast and call LockMachine() that will fail (because of PID, etc.),
7952 * so that the Machine will never get out of the Spawning session state.
7953 */
7954
7955 /* inform the session that it will be a remote one */
7956 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7957#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7958 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7959#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7960 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7961#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7962 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7963
7964 if (FAILED(rc))
7965 {
7966 /* restore the session state */
7967 mData->mSession.mState = SessionState_Unlocked;
7968 alock.release();
7969 mParent->i_addProcessToReap(pid);
7970 /* The failure may occur w/o any error info (from RPC), so provide one */
7971 return setError(VBOX_E_VM_ERROR,
7972 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7973 }
7974
7975 /* attach launch data to the machine */
7976 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7977 mData->mSession.mRemoteControls.push_back(aControl);
7978 mData->mSession.mProgress = aProgress;
7979 mData->mSession.mPID = pid;
7980 mData->mSession.mState = SessionState_Spawning;
7981 Assert(strCanonicalName.isNotEmpty());
7982 mData->mSession.mName = strCanonicalName;
7983 }
7984 else
7985 {
7986 /* For separate UI process we declare the launch as completed instantly, as the
7987 * actual headless VM start may or may not come. No point in remembering anything
7988 * yet, as what matters for us is when the headless VM gets started. */
7989 aProgress->i_notifyComplete(S_OK);
7990 }
7991
7992 alock.release();
7993 mParent->i_addProcessToReap(pid);
7994
7995 LogFlowThisFuncLeave();
7996 return S_OK;
7997}
7998
7999/**
8000 * Returns @c true if the given session machine instance has an open direct
8001 * session (and optionally also for direct sessions which are closing) and
8002 * returns the session control machine instance if so.
8003 *
8004 * Note that when the method returns @c false, the arguments remain unchanged.
8005 *
8006 * @param aMachine Session machine object.
8007 * @param aControl Direct session control object (optional).
8008 * @param aRequireVM If true then only allow VM sessions.
8009 * @param aAllowClosing If true then additionally a session which is currently
8010 * being closed will also be allowed.
8011 *
8012 * @note locks this object for reading.
8013 */
8014bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8015 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8016 bool aRequireVM /*= false*/,
8017 bool aAllowClosing /*= false*/)
8018{
8019 AutoLimitedCaller autoCaller(this);
8020 AssertComRCReturn(autoCaller.rc(), false);
8021
8022 /* just return false for inaccessible machines */
8023 if (getObjectState().getState() != ObjectState::Ready)
8024 return false;
8025
8026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8027
8028 if ( ( mData->mSession.mState == SessionState_Locked
8029 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8030 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8031 )
8032 {
8033 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8034
8035 aMachine = mData->mSession.mMachine;
8036
8037 if (aControl != NULL)
8038 *aControl = mData->mSession.mDirectControl;
8039
8040 return true;
8041 }
8042
8043 return false;
8044}
8045
8046/**
8047 * Returns @c true if the given machine has an spawning direct session.
8048 *
8049 * @note locks this object for reading.
8050 */
8051bool Machine::i_isSessionSpawning()
8052{
8053 AutoLimitedCaller autoCaller(this);
8054 AssertComRCReturn(autoCaller.rc(), false);
8055
8056 /* just return false for inaccessible machines */
8057 if (getObjectState().getState() != ObjectState::Ready)
8058 return false;
8059
8060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8061
8062 if (mData->mSession.mState == SessionState_Spawning)
8063 return true;
8064
8065 return false;
8066}
8067
8068/**
8069 * Called from the client watcher thread to check for unexpected client process
8070 * death during Session_Spawning state (e.g. before it successfully opened a
8071 * direct session).
8072 *
8073 * On Win32 and on OS/2, this method is called only when we've got the
8074 * direct client's process termination notification, so it always returns @c
8075 * true.
8076 *
8077 * On other platforms, this method returns @c true if the client process is
8078 * terminated and @c false if it's still alive.
8079 *
8080 * @note Locks this object for writing.
8081 */
8082bool Machine::i_checkForSpawnFailure()
8083{
8084 AutoCaller autoCaller(this);
8085 if (!autoCaller.isOk())
8086 {
8087 /* nothing to do */
8088 LogFlowThisFunc(("Already uninitialized!\n"));
8089 return true;
8090 }
8091
8092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8093
8094 if (mData->mSession.mState != SessionState_Spawning)
8095 {
8096 /* nothing to do */
8097 LogFlowThisFunc(("Not spawning any more!\n"));
8098 return true;
8099 }
8100
8101 HRESULT rc = S_OK;
8102
8103 /* PID not yet initialized, skip check. */
8104 if (mData->mSession.mPID == NIL_RTPROCESS)
8105 return false;
8106
8107 RTPROCSTATUS status;
8108 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8109
8110 if (vrc != VERR_PROCESS_RUNNING)
8111 {
8112 Utf8Str strExtraInfo;
8113
8114#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8115 /* If the startup logfile exists and is of non-zero length, tell the
8116 user to look there for more details to encourage them to attach it
8117 when reporting startup issues. */
8118 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8119 uint64_t cbStartupLogFile = 0;
8120 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8121 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8122 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8123#endif
8124
8125 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8126 rc = setError(E_FAIL,
8127 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8128 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8129 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8130 rc = setError(E_FAIL,
8131 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8132 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8133 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8134 rc = setError(E_FAIL,
8135 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8136 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8137 else
8138 rc = setError(E_FAIL,
8139 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8140 i_getName().c_str(), vrc, strExtraInfo.c_str());
8141 }
8142
8143 if (FAILED(rc))
8144 {
8145 /* Close the remote session, remove the remote control from the list
8146 * and reset session state to Closed (@note keep the code in sync with
8147 * the relevant part in LockMachine()). */
8148
8149 Assert(mData->mSession.mRemoteControls.size() == 1);
8150 if (mData->mSession.mRemoteControls.size() == 1)
8151 {
8152 ErrorInfoKeeper eik;
8153 mData->mSession.mRemoteControls.front()->Uninitialize();
8154 }
8155
8156 mData->mSession.mRemoteControls.clear();
8157 mData->mSession.mState = SessionState_Unlocked;
8158
8159 /* finalize the progress after setting the state */
8160 if (!mData->mSession.mProgress.isNull())
8161 {
8162 mData->mSession.mProgress->notifyComplete(rc);
8163 mData->mSession.mProgress.setNull();
8164 }
8165
8166 mData->mSession.mPID = NIL_RTPROCESS;
8167
8168 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8169 return true;
8170 }
8171
8172 return false;
8173}
8174
8175/**
8176 * Checks whether the machine can be registered. If so, commits and saves
8177 * all settings.
8178 *
8179 * @note Must be called from mParent's write lock. Locks this object and
8180 * children for writing.
8181 */
8182HRESULT Machine::i_prepareRegister()
8183{
8184 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8185
8186 AutoLimitedCaller autoCaller(this);
8187 AssertComRCReturnRC(autoCaller.rc());
8188
8189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8190
8191 /* wait for state dependents to drop to zero */
8192 i_ensureNoStateDependencies();
8193
8194 if (!mData->mAccessible)
8195 return setError(VBOX_E_INVALID_OBJECT_STATE,
8196 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8197 mUserData->s.strName.c_str(),
8198 mData->mUuid.toString().c_str());
8199
8200 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8201
8202 if (mData->mRegistered)
8203 return setError(VBOX_E_INVALID_OBJECT_STATE,
8204 tr("The machine '%s' with UUID {%s} is already registered"),
8205 mUserData->s.strName.c_str(),
8206 mData->mUuid.toString().c_str());
8207
8208 HRESULT rc = S_OK;
8209
8210 // Ensure the settings are saved. If we are going to be registered and
8211 // no config file exists yet, create it by calling i_saveSettings() too.
8212 if ( (mData->flModifications)
8213 || (!mData->pMachineConfigFile->fileExists())
8214 )
8215 {
8216 rc = i_saveSettings(NULL);
8217 // no need to check whether VirtualBox.xml needs saving too since
8218 // we can't have a machine XML file rename pending
8219 if (FAILED(rc)) return rc;
8220 }
8221
8222 /* more config checking goes here */
8223
8224 if (SUCCEEDED(rc))
8225 {
8226 /* we may have had implicit modifications we want to fix on success */
8227 i_commit();
8228
8229 mData->mRegistered = true;
8230 }
8231 else
8232 {
8233 /* we may have had implicit modifications we want to cancel on failure*/
8234 i_rollback(false /* aNotify */);
8235 }
8236
8237 return rc;
8238}
8239
8240/**
8241 * Increases the number of objects dependent on the machine state or on the
8242 * registered state. Guarantees that these two states will not change at least
8243 * until #i_releaseStateDependency() is called.
8244 *
8245 * Depending on the @a aDepType value, additional state checks may be made.
8246 * These checks will set extended error info on failure. See
8247 * #i_checkStateDependency() for more info.
8248 *
8249 * If this method returns a failure, the dependency is not added and the caller
8250 * is not allowed to rely on any particular machine state or registration state
8251 * value and may return the failed result code to the upper level.
8252 *
8253 * @param aDepType Dependency type to add.
8254 * @param aState Current machine state (NULL if not interested).
8255 * @param aRegistered Current registered state (NULL if not interested).
8256 *
8257 * @note Locks this object for writing.
8258 */
8259HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8260 MachineState_T *aState /* = NULL */,
8261 BOOL *aRegistered /* = NULL */)
8262{
8263 AutoCaller autoCaller(this);
8264 AssertComRCReturnRC(autoCaller.rc());
8265
8266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8267
8268 HRESULT rc = i_checkStateDependency(aDepType);
8269 if (FAILED(rc)) return rc;
8270
8271 {
8272 if (mData->mMachineStateChangePending != 0)
8273 {
8274 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8275 * drop to zero so don't add more. It may make sense to wait a bit
8276 * and retry before reporting an error (since the pending state
8277 * transition should be really quick) but let's just assert for
8278 * now to see if it ever happens on practice. */
8279
8280 AssertFailed();
8281
8282 return setError(E_ACCESSDENIED,
8283 tr("Machine state change is in progress. Please retry the operation later."));
8284 }
8285
8286 ++mData->mMachineStateDeps;
8287 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8288 }
8289
8290 if (aState)
8291 *aState = mData->mMachineState;
8292 if (aRegistered)
8293 *aRegistered = mData->mRegistered;
8294
8295 return S_OK;
8296}
8297
8298/**
8299 * Decreases the number of objects dependent on the machine state.
8300 * Must always complete the #i_addStateDependency() call after the state
8301 * dependency is no more necessary.
8302 */
8303void Machine::i_releaseStateDependency()
8304{
8305 AutoCaller autoCaller(this);
8306 AssertComRCReturnVoid(autoCaller.rc());
8307
8308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8309
8310 /* releaseStateDependency() w/o addStateDependency()? */
8311 AssertReturnVoid(mData->mMachineStateDeps != 0);
8312 -- mData->mMachineStateDeps;
8313
8314 if (mData->mMachineStateDeps == 0)
8315 {
8316 /* inform i_ensureNoStateDependencies() that there are no more deps */
8317 if (mData->mMachineStateChangePending != 0)
8318 {
8319 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8320 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8321 }
8322 }
8323}
8324
8325Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8326{
8327 /* start with nothing found */
8328 Utf8Str strResult("");
8329
8330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8331
8332 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8333 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8334 // found:
8335 strResult = it->second; // source is a Utf8Str
8336
8337 return strResult;
8338}
8339
8340// protected methods
8341/////////////////////////////////////////////////////////////////////////////
8342
8343/**
8344 * Performs machine state checks based on the @a aDepType value. If a check
8345 * fails, this method will set extended error info, otherwise it will return
8346 * S_OK. It is supposed, that on failure, the caller will immediately return
8347 * the return value of this method to the upper level.
8348 *
8349 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8350 *
8351 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8352 * current state of this machine object allows to change settings of the
8353 * machine (i.e. the machine is not registered, or registered but not running
8354 * and not saved). It is useful to call this method from Machine setters
8355 * before performing any change.
8356 *
8357 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8358 * as for MutableStateDep except that if the machine is saved, S_OK is also
8359 * returned. This is useful in setters which allow changing machine
8360 * properties when it is in the saved state.
8361 *
8362 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8363 * if the current state of this machine object allows to change runtime
8364 * changeable settings of the machine (i.e. the machine is not registered, or
8365 * registered but either running or not running and not saved). It is useful
8366 * to call this method from Machine setters before performing any changes to
8367 * runtime changeable settings.
8368 *
8369 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8370 * the same as for MutableOrRunningStateDep except that if the machine is
8371 * saved, S_OK is also returned. This is useful in setters which allow
8372 * changing runtime and saved state changeable machine properties.
8373 *
8374 * @param aDepType Dependency type to check.
8375 *
8376 * @note Non Machine based classes should use #i_addStateDependency() and
8377 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8378 * template.
8379 *
8380 * @note This method must be called from under this object's read or write
8381 * lock.
8382 */
8383HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8384{
8385 switch (aDepType)
8386 {
8387 case AnyStateDep:
8388 {
8389 break;
8390 }
8391 case MutableStateDep:
8392 {
8393 if ( mData->mRegistered
8394 && ( !i_isSessionMachine()
8395 || ( mData->mMachineState != MachineState_Aborted
8396 && mData->mMachineState != MachineState_Teleported
8397 && mData->mMachineState != MachineState_PoweredOff
8398 )
8399 )
8400 )
8401 return setError(VBOX_E_INVALID_VM_STATE,
8402 tr("The machine is not mutable (state is %s)"),
8403 Global::stringifyMachineState(mData->mMachineState));
8404 break;
8405 }
8406 case MutableOrSavedStateDep:
8407 {
8408 if ( mData->mRegistered
8409 && ( !i_isSessionMachine()
8410 || ( mData->mMachineState != MachineState_Aborted
8411 && mData->mMachineState != MachineState_Teleported
8412 && mData->mMachineState != MachineState_Saved
8413 && mData->mMachineState != MachineState_PoweredOff
8414 )
8415 )
8416 )
8417 return setError(VBOX_E_INVALID_VM_STATE,
8418 tr("The machine is not mutable or saved (state is %s)"),
8419 Global::stringifyMachineState(mData->mMachineState));
8420 break;
8421 }
8422 case MutableOrRunningStateDep:
8423 {
8424 if ( mData->mRegistered
8425 && ( !i_isSessionMachine()
8426 || ( mData->mMachineState != MachineState_Aborted
8427 && mData->mMachineState != MachineState_Teleported
8428 && mData->mMachineState != MachineState_PoweredOff
8429 && !Global::IsOnline(mData->mMachineState)
8430 )
8431 )
8432 )
8433 return setError(VBOX_E_INVALID_VM_STATE,
8434 tr("The machine is not mutable or running (state is %s)"),
8435 Global::stringifyMachineState(mData->mMachineState));
8436 break;
8437 }
8438 case MutableOrSavedOrRunningStateDep:
8439 {
8440 if ( mData->mRegistered
8441 && ( !i_isSessionMachine()
8442 || ( mData->mMachineState != MachineState_Aborted
8443 && mData->mMachineState != MachineState_Teleported
8444 && mData->mMachineState != MachineState_Saved
8445 && mData->mMachineState != MachineState_PoweredOff
8446 && !Global::IsOnline(mData->mMachineState)
8447 )
8448 )
8449 )
8450 return setError(VBOX_E_INVALID_VM_STATE,
8451 tr("The machine is not mutable, saved or running (state is %s)"),
8452 Global::stringifyMachineState(mData->mMachineState));
8453 break;
8454 }
8455 }
8456
8457 return S_OK;
8458}
8459
8460/**
8461 * Helper to initialize all associated child objects and allocate data
8462 * structures.
8463 *
8464 * This method must be called as a part of the object's initialization procedure
8465 * (usually done in the #init() method).
8466 *
8467 * @note Must be called only from #init() or from #i_registeredInit().
8468 */
8469HRESULT Machine::initDataAndChildObjects()
8470{
8471 AutoCaller autoCaller(this);
8472 AssertComRCReturnRC(autoCaller.rc());
8473 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8474 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8475
8476 AssertReturn(!mData->mAccessible, E_FAIL);
8477
8478 /* allocate data structures */
8479 mSSData.allocate();
8480 mUserData.allocate();
8481 mHWData.allocate();
8482 mMediumAttachments.allocate();
8483 mStorageControllers.allocate();
8484 mUSBControllers.allocate();
8485
8486 /* initialize mOSTypeId */
8487 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8488
8489/** @todo r=bird: init() methods never fails, right? Why don't we make them
8490 * return void then! */
8491
8492 /* create associated BIOS settings object */
8493 unconst(mBIOSSettings).createObject();
8494 mBIOSSettings->init(this);
8495
8496 /* create an associated VRDE object (default is disabled) */
8497 unconst(mVRDEServer).createObject();
8498 mVRDEServer->init(this);
8499
8500 /* create associated serial port objects */
8501 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8502 {
8503 unconst(mSerialPorts[slot]).createObject();
8504 mSerialPorts[slot]->init(this, slot);
8505 }
8506
8507 /* create associated parallel port objects */
8508 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8509 {
8510 unconst(mParallelPorts[slot]).createObject();
8511 mParallelPorts[slot]->init(this, slot);
8512 }
8513
8514 /* create the audio adapter object (always present, default is disabled) */
8515 unconst(mAudioAdapter).createObject();
8516 mAudioAdapter->init(this);
8517
8518 /* create the USB device filters object (always present) */
8519 unconst(mUSBDeviceFilters).createObject();
8520 mUSBDeviceFilters->init(this);
8521
8522 /* create associated network adapter objects */
8523 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8524 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8525 {
8526 unconst(mNetworkAdapters[slot]).createObject();
8527 mNetworkAdapters[slot]->init(this, slot);
8528 }
8529
8530 /* create the bandwidth control */
8531 unconst(mBandwidthControl).createObject();
8532 mBandwidthControl->init(this);
8533
8534 return S_OK;
8535}
8536
8537/**
8538 * Helper to uninitialize all associated child objects and to free all data
8539 * structures.
8540 *
8541 * This method must be called as a part of the object's uninitialization
8542 * procedure (usually done in the #uninit() method).
8543 *
8544 * @note Must be called only from #uninit() or from #i_registeredInit().
8545 */
8546void Machine::uninitDataAndChildObjects()
8547{
8548 AutoCaller autoCaller(this);
8549 AssertComRCReturnVoid(autoCaller.rc());
8550 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8551 || getObjectState().getState() == ObjectState::Limited);
8552
8553 /* tell all our other child objects we've been uninitialized */
8554 if (mBandwidthControl)
8555 {
8556 mBandwidthControl->uninit();
8557 unconst(mBandwidthControl).setNull();
8558 }
8559
8560 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8561 {
8562 if (mNetworkAdapters[slot])
8563 {
8564 mNetworkAdapters[slot]->uninit();
8565 unconst(mNetworkAdapters[slot]).setNull();
8566 }
8567 }
8568
8569 if (mUSBDeviceFilters)
8570 {
8571 mUSBDeviceFilters->uninit();
8572 unconst(mUSBDeviceFilters).setNull();
8573 }
8574
8575 if (mAudioAdapter)
8576 {
8577 mAudioAdapter->uninit();
8578 unconst(mAudioAdapter).setNull();
8579 }
8580
8581 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8582 {
8583 if (mParallelPorts[slot])
8584 {
8585 mParallelPorts[slot]->uninit();
8586 unconst(mParallelPorts[slot]).setNull();
8587 }
8588 }
8589
8590 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8591 {
8592 if (mSerialPorts[slot])
8593 {
8594 mSerialPorts[slot]->uninit();
8595 unconst(mSerialPorts[slot]).setNull();
8596 }
8597 }
8598
8599 if (mVRDEServer)
8600 {
8601 mVRDEServer->uninit();
8602 unconst(mVRDEServer).setNull();
8603 }
8604
8605 if (mBIOSSettings)
8606 {
8607 mBIOSSettings->uninit();
8608 unconst(mBIOSSettings).setNull();
8609 }
8610
8611 /* Deassociate media (only when a real Machine or a SnapshotMachine
8612 * instance is uninitialized; SessionMachine instances refer to real
8613 * Machine media). This is necessary for a clean re-initialization of
8614 * the VM after successfully re-checking the accessibility state. Note
8615 * that in case of normal Machine or SnapshotMachine uninitialization (as
8616 * a result of unregistering or deleting the snapshot), outdated media
8617 * attachments will already be uninitialized and deleted, so this
8618 * code will not affect them. */
8619 if ( !mMediumAttachments.isNull()
8620 && !i_isSessionMachine()
8621 )
8622 {
8623 for (MediumAttachmentList::const_iterator
8624 it = mMediumAttachments->begin();
8625 it != mMediumAttachments->end();
8626 ++it)
8627 {
8628 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8629 if (pMedium.isNull())
8630 continue;
8631 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8632 AssertComRC(rc);
8633 }
8634 }
8635
8636 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8637 {
8638 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8639 if (mData->mFirstSnapshot)
8640 {
8641 // snapshots tree is protected by machine write lock; strictly
8642 // this isn't necessary here since we're deleting the entire
8643 // machine, but otherwise we assert in Snapshot::uninit()
8644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8645 mData->mFirstSnapshot->uninit();
8646 mData->mFirstSnapshot.setNull();
8647 }
8648
8649 mData->mCurrentSnapshot.setNull();
8650 }
8651
8652 /* free data structures (the essential mData structure is not freed here
8653 * since it may be still in use) */
8654 mMediumAttachments.free();
8655 mStorageControllers.free();
8656 mUSBControllers.free();
8657 mHWData.free();
8658 mUserData.free();
8659 mSSData.free();
8660}
8661
8662/**
8663 * Returns a pointer to the Machine object for this machine that acts like a
8664 * parent for complex machine data objects such as shared folders, etc.
8665 *
8666 * For primary Machine objects and for SnapshotMachine objects, returns this
8667 * object's pointer itself. For SessionMachine objects, returns the peer
8668 * (primary) machine pointer.
8669 */
8670Machine *Machine::i_getMachine()
8671{
8672 if (i_isSessionMachine())
8673 return (Machine*)mPeer;
8674 return this;
8675}
8676
8677/**
8678 * Makes sure that there are no machine state dependents. If necessary, waits
8679 * for the number of dependents to drop to zero.
8680 *
8681 * Make sure this method is called from under this object's write lock to
8682 * guarantee that no new dependents may be added when this method returns
8683 * control to the caller.
8684 *
8685 * @note Locks this object for writing. The lock will be released while waiting
8686 * (if necessary).
8687 *
8688 * @warning To be used only in methods that change the machine state!
8689 */
8690void Machine::i_ensureNoStateDependencies()
8691{
8692 AssertReturnVoid(isWriteLockOnCurrentThread());
8693
8694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8695
8696 /* Wait for all state dependents if necessary */
8697 if (mData->mMachineStateDeps != 0)
8698 {
8699 /* lazy semaphore creation */
8700 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8701 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8702
8703 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8704 mData->mMachineStateDeps));
8705
8706 ++mData->mMachineStateChangePending;
8707
8708 /* reset the semaphore before waiting, the last dependent will signal
8709 * it */
8710 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8711
8712 alock.release();
8713
8714 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8715
8716 alock.acquire();
8717
8718 -- mData->mMachineStateChangePending;
8719 }
8720}
8721
8722/**
8723 * Changes the machine state and informs callbacks.
8724 *
8725 * This method is not intended to fail so it either returns S_OK or asserts (and
8726 * returns a failure).
8727 *
8728 * @note Locks this object for writing.
8729 */
8730HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8731{
8732 LogFlowThisFuncEnter();
8733 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8734 Assert(aMachineState != MachineState_Null);
8735
8736 AutoCaller autoCaller(this);
8737 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8738
8739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8740
8741 /* wait for state dependents to drop to zero */
8742 i_ensureNoStateDependencies();
8743
8744 MachineState_T const enmOldState = mData->mMachineState;
8745 if (enmOldState != aMachineState)
8746 {
8747 mData->mMachineState = aMachineState;
8748 RTTimeNow(&mData->mLastStateChange);
8749
8750#ifdef VBOX_WITH_DTRACE_R3_MAIN
8751 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8752#endif
8753 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8754 }
8755
8756 LogFlowThisFuncLeave();
8757 return S_OK;
8758}
8759
8760/**
8761 * Searches for a shared folder with the given logical name
8762 * in the collection of shared folders.
8763 *
8764 * @param aName logical name of the shared folder
8765 * @param aSharedFolder where to return the found object
8766 * @param aSetError whether to set the error info if the folder is
8767 * not found
8768 * @return
8769 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8770 *
8771 * @note
8772 * must be called from under the object's lock!
8773 */
8774HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8775 ComObjPtr<SharedFolder> &aSharedFolder,
8776 bool aSetError /* = false */)
8777{
8778 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8779 for (HWData::SharedFolderList::const_iterator
8780 it = mHWData->mSharedFolders.begin();
8781 it != mHWData->mSharedFolders.end();
8782 ++it)
8783 {
8784 SharedFolder *pSF = *it;
8785 AutoCaller autoCaller(pSF);
8786 if (pSF->i_getName() == aName)
8787 {
8788 aSharedFolder = pSF;
8789 rc = S_OK;
8790 break;
8791 }
8792 }
8793
8794 if (aSetError && FAILED(rc))
8795 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8796
8797 return rc;
8798}
8799
8800/**
8801 * Initializes all machine instance data from the given settings structures
8802 * from XML. The exception is the machine UUID which needs special handling
8803 * depending on the caller's use case, so the caller needs to set that herself.
8804 *
8805 * This gets called in several contexts during machine initialization:
8806 *
8807 * -- When machine XML exists on disk already and needs to be loaded into memory,
8808 * for example, from #i_registeredInit() to load all registered machines on
8809 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8810 * attached to the machine should be part of some media registry already.
8811 *
8812 * -- During OVF import, when a machine config has been constructed from an
8813 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8814 * ensure that the media listed as attachments in the config (which have
8815 * been imported from the OVF) receive the correct registry ID.
8816 *
8817 * -- During VM cloning.
8818 *
8819 * @param config Machine settings from XML.
8820 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8821 * for each attached medium in the config.
8822 * @return
8823 */
8824HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8825 const Guid *puuidRegistry)
8826{
8827 // copy name, description, OS type, teleporter, UTC etc.
8828 mUserData->s = config.machineUserData;
8829
8830 // look up the object by Id to check it is valid
8831 ComObjPtr<GuestOSType> pGuestOSType;
8832 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8833 pGuestOSType);
8834 if (FAILED(rc)) return rc;
8835 mUserData->s.strOsType = pGuestOSType->i_id();
8836
8837 // stateFile (optional)
8838 if (config.strStateFile.isEmpty())
8839 mSSData->strStateFilePath.setNull();
8840 else
8841 {
8842 Utf8Str stateFilePathFull(config.strStateFile);
8843 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8844 if (RT_FAILURE(vrc))
8845 return setError(E_FAIL,
8846 tr("Invalid saved state file path '%s' (%Rrc)"),
8847 config.strStateFile.c_str(),
8848 vrc);
8849 mSSData->strStateFilePath = stateFilePathFull;
8850 }
8851
8852 // snapshot folder needs special processing so set it again
8853 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8854 if (FAILED(rc)) return rc;
8855
8856 /* Copy the extra data items (config may or may not be the same as
8857 * mData->pMachineConfigFile) if necessary. When loading the XML files
8858 * from disk they are the same, but not for OVF import. */
8859 if (mData->pMachineConfigFile != &config)
8860 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8861
8862 /* currentStateModified (optional, default is true) */
8863 mData->mCurrentStateModified = config.fCurrentStateModified;
8864
8865 mData->mLastStateChange = config.timeLastStateChange;
8866
8867 /*
8868 * note: all mUserData members must be assigned prior this point because
8869 * we need to commit changes in order to let mUserData be shared by all
8870 * snapshot machine instances.
8871 */
8872 mUserData.commitCopy();
8873
8874 // machine registry, if present (must be loaded before snapshots)
8875 if (config.canHaveOwnMediaRegistry())
8876 {
8877 // determine machine folder
8878 Utf8Str strMachineFolder = i_getSettingsFileFull();
8879 strMachineFolder.stripFilename();
8880 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8881 config.mediaRegistry,
8882 strMachineFolder);
8883 if (FAILED(rc)) return rc;
8884 }
8885
8886 /* Snapshot node (optional) */
8887 size_t cRootSnapshots;
8888 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8889 {
8890 // there must be only one root snapshot
8891 Assert(cRootSnapshots == 1);
8892
8893 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8894
8895 rc = i_loadSnapshot(snap,
8896 config.uuidCurrentSnapshot,
8897 NULL); // no parent == first snapshot
8898 if (FAILED(rc)) return rc;
8899 }
8900
8901 // hardware data
8902 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8903 if (FAILED(rc)) return rc;
8904
8905 /*
8906 * NOTE: the assignment below must be the last thing to do,
8907 * otherwise it will be not possible to change the settings
8908 * somewhere in the code above because all setters will be
8909 * blocked by i_checkStateDependency(MutableStateDep).
8910 */
8911
8912 /* set the machine state to Aborted or Saved when appropriate */
8913 if (config.fAborted)
8914 {
8915 mSSData->strStateFilePath.setNull();
8916
8917 /* no need to use i_setMachineState() during init() */
8918 mData->mMachineState = MachineState_Aborted;
8919 }
8920 else if (!mSSData->strStateFilePath.isEmpty())
8921 {
8922 /* no need to use i_setMachineState() during init() */
8923 mData->mMachineState = MachineState_Saved;
8924 }
8925
8926 // after loading settings, we are no longer different from the XML on disk
8927 mData->flModifications = 0;
8928
8929 return S_OK;
8930}
8931
8932/**
8933 * Recursively loads all snapshots starting from the given.
8934 *
8935 * @param data snapshot settings.
8936 * @param aCurSnapshotId Current snapshot ID from the settings file.
8937 * @param aParentSnapshot Parent snapshot.
8938 */
8939HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8940 const Guid &aCurSnapshotId,
8941 Snapshot *aParentSnapshot)
8942{
8943 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8944 AssertReturn(!i_isSessionMachine(), E_FAIL);
8945
8946 HRESULT rc = S_OK;
8947
8948 Utf8Str strStateFile;
8949 if (!data.strStateFile.isEmpty())
8950 {
8951 /* optional */
8952 strStateFile = data.strStateFile;
8953 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8954 if (RT_FAILURE(vrc))
8955 return setError(E_FAIL,
8956 tr("Invalid saved state file path '%s' (%Rrc)"),
8957 strStateFile.c_str(),
8958 vrc);
8959 }
8960
8961 /* create a snapshot machine object */
8962 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8963 pSnapshotMachine.createObject();
8964 rc = pSnapshotMachine->initFromSettings(this,
8965 data.hardware,
8966 &data.debugging,
8967 &data.autostart,
8968 data.uuid.ref(),
8969 strStateFile);
8970 if (FAILED(rc)) return rc;
8971
8972 /* create a snapshot object */
8973 ComObjPtr<Snapshot> pSnapshot;
8974 pSnapshot.createObject();
8975 /* initialize the snapshot */
8976 rc = pSnapshot->init(mParent, // VirtualBox object
8977 data.uuid,
8978 data.strName,
8979 data.strDescription,
8980 data.timestamp,
8981 pSnapshotMachine,
8982 aParentSnapshot);
8983 if (FAILED(rc)) return rc;
8984
8985 /* memorize the first snapshot if necessary */
8986 if (!mData->mFirstSnapshot)
8987 mData->mFirstSnapshot = pSnapshot;
8988
8989 /* memorize the current snapshot when appropriate */
8990 if ( !mData->mCurrentSnapshot
8991 && pSnapshot->i_getId() == aCurSnapshotId
8992 )
8993 mData->mCurrentSnapshot = pSnapshot;
8994
8995 // now create the children
8996 for (settings::SnapshotsList::const_iterator
8997 it = data.llChildSnapshots.begin();
8998 it != data.llChildSnapshots.end();
8999 ++it)
9000 {
9001 const settings::Snapshot &childData = *it;
9002 // recurse
9003 rc = i_loadSnapshot(childData,
9004 aCurSnapshotId,
9005 pSnapshot); // parent = the one we created above
9006 if (FAILED(rc)) return rc;
9007 }
9008
9009 return rc;
9010}
9011
9012/**
9013 * Loads settings into mHWData.
9014 *
9015 * @param puuidRegistry Registry ID.
9016 * @param puuidSnapshot Snapshot ID
9017 * @param data Reference to the hardware settings.
9018 * @param pDbg Pointer to the debugging settings.
9019 * @param pAutostart Pointer to the autostart settings.
9020 */
9021HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9022 const Guid *puuidSnapshot,
9023 const settings::Hardware &data,
9024 const settings::Debugging *pDbg,
9025 const settings::Autostart *pAutostart)
9026{
9027 AssertReturn(!i_isSessionMachine(), E_FAIL);
9028
9029 HRESULT rc = S_OK;
9030
9031 try
9032 {
9033 ComObjPtr<GuestOSType> pGuestOSType;
9034 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9035 pGuestOSType);
9036 if (FAILED(rc))
9037 return rc;
9038
9039 /* The hardware version attribute (optional). */
9040 mHWData->mHWVersion = data.strVersion;
9041 mHWData->mHardwareUUID = data.uuid;
9042
9043 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9044 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9045 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9046 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9047 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9048 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9049 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9050 mHWData->mPAEEnabled = data.fPAE;
9051 mHWData->mLongMode = data.enmLongMode;
9052 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9053 mHWData->mAPIC = data.fAPIC;
9054 mHWData->mX2APIC = data.fX2APIC;
9055 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9056 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9057 mHWData->mSpecCtrl = data.fSpecCtrl;
9058 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9059 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9060 mHWData->mCPUCount = data.cCPUs;
9061 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9062 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9063 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9064 mHWData->mCpuProfile = data.strCpuProfile;
9065
9066 // cpu
9067 if (mHWData->mCPUHotPlugEnabled)
9068 {
9069 for (settings::CpuList::const_iterator
9070 it = data.llCpus.begin();
9071 it != data.llCpus.end();
9072 ++it)
9073 {
9074 const settings::Cpu &cpu = *it;
9075
9076 mHWData->mCPUAttached[cpu.ulId] = true;
9077 }
9078 }
9079
9080 // cpuid leafs
9081 for (settings::CpuIdLeafsList::const_iterator
9082 it = data.llCpuIdLeafs.begin();
9083 it != data.llCpuIdLeafs.end();
9084 ++it)
9085 {
9086 const settings::CpuIdLeaf &rLeaf= *it;
9087 if ( rLeaf.idx < UINT32_C(0x20)
9088 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9089 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9090 mHWData->mCpuIdLeafList.push_back(rLeaf);
9091 /* else: just ignore */
9092 }
9093
9094 mHWData->mMemorySize = data.ulMemorySizeMB;
9095 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9096
9097 // boot order
9098 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9099 {
9100 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9101 if (it == data.mapBootOrder.end())
9102 mHWData->mBootOrder[i] = DeviceType_Null;
9103 else
9104 mHWData->mBootOrder[i] = it->second;
9105 }
9106
9107 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9108 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9109 mHWData->mMonitorCount = data.cMonitors;
9110 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9111 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9112 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9113 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9114 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9115 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9116 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9117 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9118 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9119 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9120 if (!data.strVideoCaptureFile.isEmpty())
9121 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9122 else
9123 mHWData->mVideoCaptureFile.setNull();
9124 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9125 mHWData->mFirmwareType = data.firmwareType;
9126 mHWData->mPointingHIDType = data.pointingHIDType;
9127 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9128 mHWData->mChipsetType = data.chipsetType;
9129 mHWData->mParavirtProvider = data.paravirtProvider;
9130 mHWData->mParavirtDebug = data.strParavirtDebug;
9131 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9132 mHWData->mHPETEnabled = data.fHPETEnabled;
9133
9134 /* VRDEServer */
9135 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9136 if (FAILED(rc)) return rc;
9137
9138 /* BIOS */
9139 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9140 if (FAILED(rc)) return rc;
9141
9142 // Bandwidth control (must come before network adapters)
9143 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9144 if (FAILED(rc)) return rc;
9145
9146 /* Shared folders */
9147 for (settings::USBControllerList::const_iterator
9148 it = data.usbSettings.llUSBControllers.begin();
9149 it != data.usbSettings.llUSBControllers.end();
9150 ++it)
9151 {
9152 const settings::USBController &settingsCtrl = *it;
9153 ComObjPtr<USBController> newCtrl;
9154
9155 newCtrl.createObject();
9156 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9157 mUSBControllers->push_back(newCtrl);
9158 }
9159
9160 /* USB device filters */
9161 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9162 if (FAILED(rc)) return rc;
9163
9164 // network adapters (establish array size first and apply defaults, to
9165 // ensure reading the same settings as we saved, since the list skips
9166 // adapters having defaults)
9167 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9168 size_t oldCount = mNetworkAdapters.size();
9169 if (newCount > oldCount)
9170 {
9171 mNetworkAdapters.resize(newCount);
9172 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9173 {
9174 unconst(mNetworkAdapters[slot]).createObject();
9175 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9176 }
9177 }
9178 else if (newCount < oldCount)
9179 mNetworkAdapters.resize(newCount);
9180 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9181 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9182 for (settings::NetworkAdaptersList::const_iterator
9183 it = data.llNetworkAdapters.begin();
9184 it != data.llNetworkAdapters.end();
9185 ++it)
9186 {
9187 const settings::NetworkAdapter &nic = *it;
9188
9189 /* slot uniqueness is guaranteed by XML Schema */
9190 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9191 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9192 if (FAILED(rc)) return rc;
9193 }
9194
9195 // serial ports (establish defaults first, to ensure reading the same
9196 // settings as we saved, since the list skips ports having defaults)
9197 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9198 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9199 for (settings::SerialPortsList::const_iterator
9200 it = data.llSerialPorts.begin();
9201 it != data.llSerialPorts.end();
9202 ++it)
9203 {
9204 const settings::SerialPort &s = *it;
9205
9206 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9207 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9208 if (FAILED(rc)) return rc;
9209 }
9210
9211 // parallel ports (establish defaults first, to ensure reading the same
9212 // settings as we saved, since the list skips ports having defaults)
9213 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9214 mParallelPorts[i]->i_applyDefaults();
9215 for (settings::ParallelPortsList::const_iterator
9216 it = data.llParallelPorts.begin();
9217 it != data.llParallelPorts.end();
9218 ++it)
9219 {
9220 const settings::ParallelPort &p = *it;
9221
9222 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9223 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9224 if (FAILED(rc)) return rc;
9225 }
9226
9227 /* AudioAdapter */
9228 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9229 if (FAILED(rc)) return rc;
9230
9231 /* storage controllers */
9232 rc = i_loadStorageControllers(data.storage,
9233 puuidRegistry,
9234 puuidSnapshot);
9235 if (FAILED(rc)) return rc;
9236
9237 /* Shared folders */
9238 for (settings::SharedFoldersList::const_iterator
9239 it = data.llSharedFolders.begin();
9240 it != data.llSharedFolders.end();
9241 ++it)
9242 {
9243 const settings::SharedFolder &sf = *it;
9244
9245 ComObjPtr<SharedFolder> sharedFolder;
9246 /* Check for double entries. Not allowed! */
9247 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9248 if (SUCCEEDED(rc))
9249 return setError(VBOX_E_OBJECT_IN_USE,
9250 tr("Shared folder named '%s' already exists"),
9251 sf.strName.c_str());
9252
9253 /* Create the new shared folder. Don't break on error. This will be
9254 * reported when the machine starts. */
9255 sharedFolder.createObject();
9256 rc = sharedFolder->init(i_getMachine(),
9257 sf.strName,
9258 sf.strHostPath,
9259 RT_BOOL(sf.fWritable),
9260 RT_BOOL(sf.fAutoMount),
9261 false /* fFailOnError */);
9262 if (FAILED(rc)) return rc;
9263 mHWData->mSharedFolders.push_back(sharedFolder);
9264 }
9265
9266 // Clipboard
9267 mHWData->mClipboardMode = data.clipboardMode;
9268
9269 // drag'n'drop
9270 mHWData->mDnDMode = data.dndMode;
9271
9272 // guest settings
9273 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9274
9275 // IO settings
9276 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9277 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9278
9279 // Host PCI devices
9280 for (settings::HostPCIDeviceAttachmentList::const_iterator
9281 it = data.pciAttachments.begin();
9282 it != data.pciAttachments.end();
9283 ++it)
9284 {
9285 const settings::HostPCIDeviceAttachment &hpda = *it;
9286 ComObjPtr<PCIDeviceAttachment> pda;
9287
9288 pda.createObject();
9289 pda->i_loadSettings(this, hpda);
9290 mHWData->mPCIDeviceAssignments.push_back(pda);
9291 }
9292
9293 /*
9294 * (The following isn't really real hardware, but it lives in HWData
9295 * for reasons of convenience.)
9296 */
9297
9298#ifdef VBOX_WITH_GUEST_PROPS
9299 /* Guest properties (optional) */
9300
9301 /* Only load transient guest properties for configs which have saved
9302 * state, because there shouldn't be any for powered off VMs. The same
9303 * logic applies for snapshots, as offline snapshots shouldn't have
9304 * any such properties. They confuse the code in various places.
9305 * Note: can't rely on the machine state, as it isn't set yet. */
9306 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9307 /* apologies for the hacky unconst() usage, but this needs hacking
9308 * actually inconsistent settings into consistency, otherwise there
9309 * will be some corner cases where the inconsistency survives
9310 * surprisingly long without getting fixed, especially for snapshots
9311 * as there are no config changes. */
9312 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9313 for (settings::GuestPropertiesList::iterator
9314 it = llGuestProperties.begin();
9315 it != llGuestProperties.end();
9316 /*nothing*/)
9317 {
9318 const settings::GuestProperty &prop = *it;
9319 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9320 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9321 if ( fSkipTransientGuestProperties
9322 && ( fFlags & GUEST_PROP_F_TRANSIENT
9323 || fFlags & GUEST_PROP_F_TRANSRESET))
9324 {
9325 it = llGuestProperties.erase(it);
9326 continue;
9327 }
9328 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9329 mHWData->mGuestProperties[prop.strName] = property;
9330 ++it;
9331 }
9332#endif /* VBOX_WITH_GUEST_PROPS defined */
9333
9334 rc = i_loadDebugging(pDbg);
9335 if (FAILED(rc))
9336 return rc;
9337
9338 mHWData->mAutostart = *pAutostart;
9339
9340 /* default frontend */
9341 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9342 }
9343 catch (std::bad_alloc &)
9344 {
9345 return E_OUTOFMEMORY;
9346 }
9347
9348 AssertComRC(rc);
9349 return rc;
9350}
9351
9352/**
9353 * Called from i_loadHardware() to load the debugging settings of the
9354 * machine.
9355 *
9356 * @param pDbg Pointer to the settings.
9357 */
9358HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9359{
9360 mHWData->mDebugging = *pDbg;
9361 /* no more processing currently required, this will probably change. */
9362 return S_OK;
9363}
9364
9365/**
9366 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9367 *
9368 * @param data storage settings.
9369 * @param puuidRegistry media registry ID to set media to or NULL;
9370 * see Machine::i_loadMachineDataFromSettings()
9371 * @param puuidSnapshot snapshot ID
9372 * @return
9373 */
9374HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9375 const Guid *puuidRegistry,
9376 const Guid *puuidSnapshot)
9377{
9378 AssertReturn(!i_isSessionMachine(), E_FAIL);
9379
9380 HRESULT rc = S_OK;
9381
9382 for (settings::StorageControllersList::const_iterator
9383 it = data.llStorageControllers.begin();
9384 it != data.llStorageControllers.end();
9385 ++it)
9386 {
9387 const settings::StorageController &ctlData = *it;
9388
9389 ComObjPtr<StorageController> pCtl;
9390 /* Try to find one with the name first. */
9391 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9392 if (SUCCEEDED(rc))
9393 return setError(VBOX_E_OBJECT_IN_USE,
9394 tr("Storage controller named '%s' already exists"),
9395 ctlData.strName.c_str());
9396
9397 pCtl.createObject();
9398 rc = pCtl->init(this,
9399 ctlData.strName,
9400 ctlData.storageBus,
9401 ctlData.ulInstance,
9402 ctlData.fBootable);
9403 if (FAILED(rc)) return rc;
9404
9405 mStorageControllers->push_back(pCtl);
9406
9407 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9408 if (FAILED(rc)) return rc;
9409
9410 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9411 if (FAILED(rc)) return rc;
9412
9413 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9414 if (FAILED(rc)) return rc;
9415
9416 /* Load the attached devices now. */
9417 rc = i_loadStorageDevices(pCtl,
9418 ctlData,
9419 puuidRegistry,
9420 puuidSnapshot);
9421 if (FAILED(rc)) return rc;
9422 }
9423
9424 return S_OK;
9425}
9426
9427/**
9428 * Called from i_loadStorageControllers for a controller's devices.
9429 *
9430 * @param aStorageController
9431 * @param data
9432 * @param puuidRegistry media registry ID to set media to or NULL; see
9433 * Machine::i_loadMachineDataFromSettings()
9434 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9435 * @return
9436 */
9437HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9438 const settings::StorageController &data,
9439 const Guid *puuidRegistry,
9440 const Guid *puuidSnapshot)
9441{
9442 HRESULT rc = S_OK;
9443
9444 /* paranoia: detect duplicate attachments */
9445 for (settings::AttachedDevicesList::const_iterator
9446 it = data.llAttachedDevices.begin();
9447 it != data.llAttachedDevices.end();
9448 ++it)
9449 {
9450 const settings::AttachedDevice &ad = *it;
9451
9452 for (settings::AttachedDevicesList::const_iterator it2 = it;
9453 it2 != data.llAttachedDevices.end();
9454 ++it2)
9455 {
9456 if (it == it2)
9457 continue;
9458
9459 const settings::AttachedDevice &ad2 = *it2;
9460
9461 if ( ad.lPort == ad2.lPort
9462 && ad.lDevice == ad2.lDevice)
9463 {
9464 return setError(E_FAIL,
9465 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9466 aStorageController->i_getName().c_str(),
9467 ad.lPort,
9468 ad.lDevice,
9469 mUserData->s.strName.c_str());
9470 }
9471 }
9472 }
9473
9474 for (settings::AttachedDevicesList::const_iterator
9475 it = data.llAttachedDevices.begin();
9476 it != data.llAttachedDevices.end();
9477 ++it)
9478 {
9479 const settings::AttachedDevice &dev = *it;
9480 ComObjPtr<Medium> medium;
9481
9482 switch (dev.deviceType)
9483 {
9484 case DeviceType_Floppy:
9485 case DeviceType_DVD:
9486 if (dev.strHostDriveSrc.isNotEmpty())
9487 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9488 false /* fRefresh */, medium);
9489 else
9490 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9491 dev.uuid,
9492 false /* fRefresh */,
9493 false /* aSetError */,
9494 medium);
9495 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9496 // This is not an error. The host drive or UUID might have vanished, so just go
9497 // ahead without this removeable medium attachment
9498 rc = S_OK;
9499 break;
9500
9501 case DeviceType_HardDisk:
9502 {
9503 /* find a hard disk by UUID */
9504 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9505 if (FAILED(rc))
9506 {
9507 if (i_isSnapshotMachine())
9508 {
9509 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9510 // so the user knows that the bad disk is in a snapshot somewhere
9511 com::ErrorInfo info;
9512 return setError(E_FAIL,
9513 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9514 puuidSnapshot->raw(),
9515 info.getText().raw());
9516 }
9517 else
9518 return rc;
9519 }
9520
9521 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9522
9523 if (medium->i_getType() == MediumType_Immutable)
9524 {
9525 if (i_isSnapshotMachine())
9526 return setError(E_FAIL,
9527 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9528 "of the virtual machine '%s' ('%s')"),
9529 medium->i_getLocationFull().c_str(),
9530 dev.uuid.raw(),
9531 puuidSnapshot->raw(),
9532 mUserData->s.strName.c_str(),
9533 mData->m_strConfigFileFull.c_str());
9534
9535 return setError(E_FAIL,
9536 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9537 medium->i_getLocationFull().c_str(),
9538 dev.uuid.raw(),
9539 mUserData->s.strName.c_str(),
9540 mData->m_strConfigFileFull.c_str());
9541 }
9542
9543 if (medium->i_getType() == MediumType_MultiAttach)
9544 {
9545 if (i_isSnapshotMachine())
9546 return setError(E_FAIL,
9547 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9548 "of the virtual machine '%s' ('%s')"),
9549 medium->i_getLocationFull().c_str(),
9550 dev.uuid.raw(),
9551 puuidSnapshot->raw(),
9552 mUserData->s.strName.c_str(),
9553 mData->m_strConfigFileFull.c_str());
9554
9555 return setError(E_FAIL,
9556 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9557 medium->i_getLocationFull().c_str(),
9558 dev.uuid.raw(),
9559 mUserData->s.strName.c_str(),
9560 mData->m_strConfigFileFull.c_str());
9561 }
9562
9563 if ( !i_isSnapshotMachine()
9564 && medium->i_getChildren().size() != 0
9565 )
9566 return setError(E_FAIL,
9567 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9568 "because it has %d differencing child hard disks"),
9569 medium->i_getLocationFull().c_str(),
9570 dev.uuid.raw(),
9571 mUserData->s.strName.c_str(),
9572 mData->m_strConfigFileFull.c_str(),
9573 medium->i_getChildren().size());
9574
9575 if (i_findAttachment(*mMediumAttachments.data(),
9576 medium))
9577 return setError(E_FAIL,
9578 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9579 medium->i_getLocationFull().c_str(),
9580 dev.uuid.raw(),
9581 mUserData->s.strName.c_str(),
9582 mData->m_strConfigFileFull.c_str());
9583
9584 break;
9585 }
9586
9587 default:
9588 return setError(E_FAIL,
9589 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9590 medium->i_getLocationFull().c_str(),
9591 mUserData->s.strName.c_str(),
9592 mData->m_strConfigFileFull.c_str());
9593 }
9594
9595 if (FAILED(rc))
9596 break;
9597
9598 /* Bandwidth groups are loaded at this point. */
9599 ComObjPtr<BandwidthGroup> pBwGroup;
9600
9601 if (!dev.strBwGroup.isEmpty())
9602 {
9603 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9604 if (FAILED(rc))
9605 return setError(E_FAIL,
9606 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9607 medium->i_getLocationFull().c_str(),
9608 dev.strBwGroup.c_str(),
9609 mUserData->s.strName.c_str(),
9610 mData->m_strConfigFileFull.c_str());
9611 pBwGroup->i_reference();
9612 }
9613
9614 const Utf8Str controllerName = aStorageController->i_getName();
9615 ComObjPtr<MediumAttachment> pAttachment;
9616 pAttachment.createObject();
9617 rc = pAttachment->init(this,
9618 medium,
9619 controllerName,
9620 dev.lPort,
9621 dev.lDevice,
9622 dev.deviceType,
9623 false,
9624 dev.fPassThrough,
9625 dev.fTempEject,
9626 dev.fNonRotational,
9627 dev.fDiscard,
9628 dev.fHotPluggable,
9629 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9630 if (FAILED(rc)) break;
9631
9632 /* associate the medium with this machine and snapshot */
9633 if (!medium.isNull())
9634 {
9635 AutoCaller medCaller(medium);
9636 if (FAILED(medCaller.rc())) return medCaller.rc();
9637 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9638
9639 if (i_isSnapshotMachine())
9640 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9641 else
9642 rc = medium->i_addBackReference(mData->mUuid);
9643 /* If the medium->addBackReference fails it sets an appropriate
9644 * error message, so no need to do any guesswork here. */
9645
9646 if (puuidRegistry)
9647 // caller wants registry ID to be set on all attached media (OVF import case)
9648 medium->i_addRegistry(*puuidRegistry);
9649 }
9650
9651 if (FAILED(rc))
9652 break;
9653
9654 /* back up mMediumAttachments to let registeredInit() properly rollback
9655 * on failure (= limited accessibility) */
9656 i_setModified(IsModified_Storage);
9657 mMediumAttachments.backup();
9658 mMediumAttachments->push_back(pAttachment);
9659 }
9660
9661 return rc;
9662}
9663
9664/**
9665 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9666 *
9667 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9668 * @param aSnapshot where to return the found snapshot
9669 * @param aSetError true to set extended error info on failure
9670 */
9671HRESULT Machine::i_findSnapshotById(const Guid &aId,
9672 ComObjPtr<Snapshot> &aSnapshot,
9673 bool aSetError /* = false */)
9674{
9675 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9676
9677 if (!mData->mFirstSnapshot)
9678 {
9679 if (aSetError)
9680 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9681 return E_FAIL;
9682 }
9683
9684 if (aId.isZero())
9685 aSnapshot = mData->mFirstSnapshot;
9686 else
9687 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9688
9689 if (!aSnapshot)
9690 {
9691 if (aSetError)
9692 return setError(E_FAIL,
9693 tr("Could not find a snapshot with UUID {%s}"),
9694 aId.toString().c_str());
9695 return E_FAIL;
9696 }
9697
9698 return S_OK;
9699}
9700
9701/**
9702 * Returns the snapshot with the given name or fails of no such snapshot.
9703 *
9704 * @param strName snapshot name to find
9705 * @param aSnapshot where to return the found snapshot
9706 * @param aSetError true to set extended error info on failure
9707 */
9708HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9709 ComObjPtr<Snapshot> &aSnapshot,
9710 bool aSetError /* = false */)
9711{
9712 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9713
9714 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9715
9716 if (!mData->mFirstSnapshot)
9717 {
9718 if (aSetError)
9719 return setError(VBOX_E_OBJECT_NOT_FOUND,
9720 tr("This machine does not have any snapshots"));
9721 return VBOX_E_OBJECT_NOT_FOUND;
9722 }
9723
9724 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9725
9726 if (!aSnapshot)
9727 {
9728 if (aSetError)
9729 return setError(VBOX_E_OBJECT_NOT_FOUND,
9730 tr("Could not find a snapshot named '%s'"), strName.c_str());
9731 return VBOX_E_OBJECT_NOT_FOUND;
9732 }
9733
9734 return S_OK;
9735}
9736
9737/**
9738 * Returns a storage controller object with the given name.
9739 *
9740 * @param aName storage controller name to find
9741 * @param aStorageController where to return the found storage controller
9742 * @param aSetError true to set extended error info on failure
9743 */
9744HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9745 ComObjPtr<StorageController> &aStorageController,
9746 bool aSetError /* = false */)
9747{
9748 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9749
9750 for (StorageControllerList::const_iterator
9751 it = mStorageControllers->begin();
9752 it != mStorageControllers->end();
9753 ++it)
9754 {
9755 if ((*it)->i_getName() == aName)
9756 {
9757 aStorageController = (*it);
9758 return S_OK;
9759 }
9760 }
9761
9762 if (aSetError)
9763 return setError(VBOX_E_OBJECT_NOT_FOUND,
9764 tr("Could not find a storage controller named '%s'"),
9765 aName.c_str());
9766 return VBOX_E_OBJECT_NOT_FOUND;
9767}
9768
9769/**
9770 * Returns a USB controller object with the given name.
9771 *
9772 * @param aName USB controller name to find
9773 * @param aUSBController where to return the found USB controller
9774 * @param aSetError true to set extended error info on failure
9775 */
9776HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9777 ComObjPtr<USBController> &aUSBController,
9778 bool aSetError /* = false */)
9779{
9780 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9781
9782 for (USBControllerList::const_iterator
9783 it = mUSBControllers->begin();
9784 it != mUSBControllers->end();
9785 ++it)
9786 {
9787 if ((*it)->i_getName() == aName)
9788 {
9789 aUSBController = (*it);
9790 return S_OK;
9791 }
9792 }
9793
9794 if (aSetError)
9795 return setError(VBOX_E_OBJECT_NOT_FOUND,
9796 tr("Could not find a storage controller named '%s'"),
9797 aName.c_str());
9798 return VBOX_E_OBJECT_NOT_FOUND;
9799}
9800
9801/**
9802 * Returns the number of USB controller instance of the given type.
9803 *
9804 * @param enmType USB controller type.
9805 */
9806ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9807{
9808 ULONG cCtrls = 0;
9809
9810 for (USBControllerList::const_iterator
9811 it = mUSBControllers->begin();
9812 it != mUSBControllers->end();
9813 ++it)
9814 {
9815 if ((*it)->i_getControllerType() == enmType)
9816 cCtrls++;
9817 }
9818
9819 return cCtrls;
9820}
9821
9822HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9823 MediumAttachmentList &atts)
9824{
9825 AutoCaller autoCaller(this);
9826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9827
9828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9829
9830 for (MediumAttachmentList::const_iterator
9831 it = mMediumAttachments->begin();
9832 it != mMediumAttachments->end();
9833 ++it)
9834 {
9835 const ComObjPtr<MediumAttachment> &pAtt = *it;
9836 // should never happen, but deal with NULL pointers in the list.
9837 AssertContinue(!pAtt.isNull());
9838
9839 // getControllerName() needs caller+read lock
9840 AutoCaller autoAttCaller(pAtt);
9841 if (FAILED(autoAttCaller.rc()))
9842 {
9843 atts.clear();
9844 return autoAttCaller.rc();
9845 }
9846 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9847
9848 if (pAtt->i_getControllerName() == aName)
9849 atts.push_back(pAtt);
9850 }
9851
9852 return S_OK;
9853}
9854
9855
9856/**
9857 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9858 * file if the machine name was changed and about creating a new settings file
9859 * if this is a new machine.
9860 *
9861 * @note Must be never called directly but only from #saveSettings().
9862 */
9863HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9864{
9865 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9866
9867 HRESULT rc = S_OK;
9868
9869 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9870
9871 /// @todo need to handle primary group change, too
9872
9873 /* attempt to rename the settings file if machine name is changed */
9874 if ( mUserData->s.fNameSync
9875 && mUserData.isBackedUp()
9876 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9877 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9878 )
9879 {
9880 bool dirRenamed = false;
9881 bool fileRenamed = false;
9882
9883 Utf8Str configFile, newConfigFile;
9884 Utf8Str configFilePrev, newConfigFilePrev;
9885 Utf8Str configDir, newConfigDir;
9886
9887 do
9888 {
9889 int vrc = VINF_SUCCESS;
9890
9891 Utf8Str name = mUserData.backedUpData()->s.strName;
9892 Utf8Str newName = mUserData->s.strName;
9893 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9894 if (group == "/")
9895 group.setNull();
9896 Utf8Str newGroup = mUserData->s.llGroups.front();
9897 if (newGroup == "/")
9898 newGroup.setNull();
9899
9900 configFile = mData->m_strConfigFileFull;
9901
9902 /* first, rename the directory if it matches the group and machine name */
9903 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9904 group.c_str(), RTPATH_DELIMITER, name.c_str());
9905 /** @todo hack, make somehow use of ComposeMachineFilename */
9906 if (mUserData->s.fDirectoryIncludesUUID)
9907 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9908 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9909 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9910 /** @todo hack, make somehow use of ComposeMachineFilename */
9911 if (mUserData->s.fDirectoryIncludesUUID)
9912 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9913 configDir = configFile;
9914 configDir.stripFilename();
9915 newConfigDir = configDir;
9916 if ( configDir.length() >= groupPlusName.length()
9917 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9918 groupPlusName.c_str()))
9919 {
9920 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9921 Utf8Str newConfigBaseDir(newConfigDir);
9922 newConfigDir.append(newGroupPlusName);
9923 /* consistency: use \ if appropriate on the platform */
9924 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9925 /* new dir and old dir cannot be equal here because of 'if'
9926 * above and because name != newName */
9927 Assert(configDir != newConfigDir);
9928 if (!fSettingsFileIsNew)
9929 {
9930 /* perform real rename only if the machine is not new */
9931 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9932 if ( vrc == VERR_FILE_NOT_FOUND
9933 || vrc == VERR_PATH_NOT_FOUND)
9934 {
9935 /* create the parent directory, then retry renaming */
9936 Utf8Str parent(newConfigDir);
9937 parent.stripFilename();
9938 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9939 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9940 }
9941 if (RT_FAILURE(vrc))
9942 {
9943 rc = setError(E_FAIL,
9944 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9945 configDir.c_str(),
9946 newConfigDir.c_str(),
9947 vrc);
9948 break;
9949 }
9950 /* delete subdirectories which are no longer needed */
9951 Utf8Str dir(configDir);
9952 dir.stripFilename();
9953 while (dir != newConfigBaseDir && dir != ".")
9954 {
9955 vrc = RTDirRemove(dir.c_str());
9956 if (RT_FAILURE(vrc))
9957 break;
9958 dir.stripFilename();
9959 }
9960 dirRenamed = true;
9961 }
9962 }
9963
9964 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9965 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9966
9967 /* then try to rename the settings file itself */
9968 if (newConfigFile != configFile)
9969 {
9970 /* get the path to old settings file in renamed directory */
9971 configFile = Utf8StrFmt("%s%c%s",
9972 newConfigDir.c_str(),
9973 RTPATH_DELIMITER,
9974 RTPathFilename(configFile.c_str()));
9975 if (!fSettingsFileIsNew)
9976 {
9977 /* perform real rename only if the machine is not new */
9978 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9979 if (RT_FAILURE(vrc))
9980 {
9981 rc = setError(E_FAIL,
9982 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9983 configFile.c_str(),
9984 newConfigFile.c_str(),
9985 vrc);
9986 break;
9987 }
9988 fileRenamed = true;
9989 configFilePrev = configFile;
9990 configFilePrev += "-prev";
9991 newConfigFilePrev = newConfigFile;
9992 newConfigFilePrev += "-prev";
9993 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9994 }
9995 }
9996
9997 // update m_strConfigFileFull amd mConfigFile
9998 mData->m_strConfigFileFull = newConfigFile;
9999 // compute the relative path too
10000 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10001
10002 // store the old and new so that VirtualBox::i_saveSettings() can update
10003 // the media registry
10004 if ( mData->mRegistered
10005 && (configDir != newConfigDir || configFile != newConfigFile))
10006 {
10007 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10008
10009 if (pfNeedsGlobalSaveSettings)
10010 *pfNeedsGlobalSaveSettings = true;
10011 }
10012
10013 // in the saved state file path, replace the old directory with the new directory
10014 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10015 {
10016 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10017 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10018 }
10019
10020 // and do the same thing for the saved state file paths of all the online snapshots
10021 if (mData->mFirstSnapshot)
10022 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10023 newConfigDir.c_str());
10024 }
10025 while (0);
10026
10027 if (FAILED(rc))
10028 {
10029 /* silently try to rename everything back */
10030 if (fileRenamed)
10031 {
10032 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10033 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10034 }
10035 if (dirRenamed)
10036 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10037 }
10038
10039 if (FAILED(rc)) return rc;
10040 }
10041
10042 if (fSettingsFileIsNew)
10043 {
10044 /* create a virgin config file */
10045 int vrc = VINF_SUCCESS;
10046
10047 /* ensure the settings directory exists */
10048 Utf8Str path(mData->m_strConfigFileFull);
10049 path.stripFilename();
10050 if (!RTDirExists(path.c_str()))
10051 {
10052 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10053 if (RT_FAILURE(vrc))
10054 {
10055 return setError(E_FAIL,
10056 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10057 path.c_str(),
10058 vrc);
10059 }
10060 }
10061
10062 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10063 path = Utf8Str(mData->m_strConfigFileFull);
10064 RTFILE f = NIL_RTFILE;
10065 vrc = RTFileOpen(&f, path.c_str(),
10066 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10067 if (RT_FAILURE(vrc))
10068 return setError(E_FAIL,
10069 tr("Could not create the settings file '%s' (%Rrc)"),
10070 path.c_str(),
10071 vrc);
10072 RTFileClose(f);
10073 }
10074
10075 return rc;
10076}
10077
10078/**
10079 * Saves and commits machine data, user data and hardware data.
10080 *
10081 * Note that on failure, the data remains uncommitted.
10082 *
10083 * @a aFlags may combine the following flags:
10084 *
10085 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10086 * Used when saving settings after an operation that makes them 100%
10087 * correspond to the settings from the current snapshot.
10088 * - SaveS_Force: settings will be saved without doing a deep compare of the
10089 * settings structures. This is used when this is called because snapshots
10090 * have changed to avoid the overhead of the deep compare.
10091 *
10092 * @note Must be called from under this object's write lock. Locks children for
10093 * writing.
10094 *
10095 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10096 * initialized to false and that will be set to true by this function if
10097 * the caller must invoke VirtualBox::i_saveSettings() because the global
10098 * settings have changed. This will happen if a machine rename has been
10099 * saved and the global machine and media registries will therefore need
10100 * updating.
10101 * @param aFlags Flags.
10102 */
10103HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10104 int aFlags /*= 0*/)
10105{
10106 LogFlowThisFuncEnter();
10107
10108 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10109
10110 /* make sure child objects are unable to modify the settings while we are
10111 * saving them */
10112 i_ensureNoStateDependencies();
10113
10114 AssertReturn(!i_isSnapshotMachine(),
10115 E_FAIL);
10116
10117 HRESULT rc = S_OK;
10118 bool fNeedsWrite = false;
10119
10120 /* First, prepare to save settings. It will care about renaming the
10121 * settings directory and file if the machine name was changed and about
10122 * creating a new settings file if this is a new machine. */
10123 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10124 if (FAILED(rc)) return rc;
10125
10126 // keep a pointer to the current settings structures
10127 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10128 settings::MachineConfigFile *pNewConfig = NULL;
10129
10130 try
10131 {
10132 // make a fresh one to have everyone write stuff into
10133 pNewConfig = new settings::MachineConfigFile(NULL);
10134 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10135
10136 // now go and copy all the settings data from COM to the settings structures
10137 // (this calls i_saveSettings() on all the COM objects in the machine)
10138 i_copyMachineDataToSettings(*pNewConfig);
10139
10140 if (aFlags & SaveS_ResetCurStateModified)
10141 {
10142 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10143 mData->mCurrentStateModified = FALSE;
10144 fNeedsWrite = true; // always, no need to compare
10145 }
10146 else if (aFlags & SaveS_Force)
10147 {
10148 fNeedsWrite = true; // always, no need to compare
10149 }
10150 else
10151 {
10152 if (!mData->mCurrentStateModified)
10153 {
10154 // do a deep compare of the settings that we just saved with the settings
10155 // previously stored in the config file; this invokes MachineConfigFile::operator==
10156 // which does a deep compare of all the settings, which is expensive but less expensive
10157 // than writing out XML in vain
10158 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10159
10160 // could still be modified if any settings changed
10161 mData->mCurrentStateModified = fAnySettingsChanged;
10162
10163 fNeedsWrite = fAnySettingsChanged;
10164 }
10165 else
10166 fNeedsWrite = true;
10167 }
10168
10169 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10170
10171 if (fNeedsWrite)
10172 // now spit it all out!
10173 pNewConfig->write(mData->m_strConfigFileFull);
10174
10175 mData->pMachineConfigFile = pNewConfig;
10176 delete pOldConfig;
10177 i_commit();
10178
10179 // after saving settings, we are no longer different from the XML on disk
10180 mData->flModifications = 0;
10181 }
10182 catch (HRESULT err)
10183 {
10184 // we assume that error info is set by the thrower
10185 rc = err;
10186
10187 // restore old config
10188 delete pNewConfig;
10189 mData->pMachineConfigFile = pOldConfig;
10190 }
10191 catch (...)
10192 {
10193 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10194 }
10195
10196 if (fNeedsWrite)
10197 {
10198 /* Fire the data change event, even on failure (since we've already
10199 * committed all data). This is done only for SessionMachines because
10200 * mutable Machine instances are always not registered (i.e. private
10201 * to the client process that creates them) and thus don't need to
10202 * inform callbacks. */
10203 if (i_isSessionMachine())
10204 mParent->i_onMachineDataChange(mData->mUuid);
10205 }
10206
10207 LogFlowThisFunc(("rc=%08X\n", rc));
10208 LogFlowThisFuncLeave();
10209 return rc;
10210}
10211
10212/**
10213 * Implementation for saving the machine settings into the given
10214 * settings::MachineConfigFile instance. This copies machine extradata
10215 * from the previous machine config file in the instance data, if any.
10216 *
10217 * This gets called from two locations:
10218 *
10219 * -- Machine::i_saveSettings(), during the regular XML writing;
10220 *
10221 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10222 * exported to OVF and we write the VirtualBox proprietary XML
10223 * into a <vbox:Machine> tag.
10224 *
10225 * This routine fills all the fields in there, including snapshots, *except*
10226 * for the following:
10227 *
10228 * -- fCurrentStateModified. There is some special logic associated with that.
10229 *
10230 * The caller can then call MachineConfigFile::write() or do something else
10231 * with it.
10232 *
10233 * Caller must hold the machine lock!
10234 *
10235 * This throws XML errors and HRESULT, so the caller must have a catch block!
10236 */
10237void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10238{
10239 // deep copy extradata, being extra careful with self assignment (the STL
10240 // map assignment on Mac OS X clang based Xcode isn't checking)
10241 if (&config != mData->pMachineConfigFile)
10242 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10243
10244 config.uuid = mData->mUuid;
10245
10246 // copy name, description, OS type, teleport, UTC etc.
10247 config.machineUserData = mUserData->s;
10248
10249 if ( mData->mMachineState == MachineState_Saved
10250 || mData->mMachineState == MachineState_Restoring
10251 // when doing certain snapshot operations we may or may not have
10252 // a saved state in the current state, so keep everything as is
10253 || ( ( mData->mMachineState == MachineState_Snapshotting
10254 || mData->mMachineState == MachineState_DeletingSnapshot
10255 || mData->mMachineState == MachineState_RestoringSnapshot)
10256 && (!mSSData->strStateFilePath.isEmpty())
10257 )
10258 )
10259 {
10260 Assert(!mSSData->strStateFilePath.isEmpty());
10261 /* try to make the file name relative to the settings file dir */
10262 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10263 }
10264 else
10265 {
10266 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10267 config.strStateFile.setNull();
10268 }
10269
10270 if (mData->mCurrentSnapshot)
10271 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10272 else
10273 config.uuidCurrentSnapshot.clear();
10274
10275 config.timeLastStateChange = mData->mLastStateChange;
10276 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10277 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10278
10279 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10280 if (FAILED(rc)) throw rc;
10281
10282 // save machine's media registry if this is VirtualBox 4.0 or later
10283 if (config.canHaveOwnMediaRegistry())
10284 {
10285 // determine machine folder
10286 Utf8Str strMachineFolder = i_getSettingsFileFull();
10287 strMachineFolder.stripFilename();
10288 mParent->i_saveMediaRegistry(config.mediaRegistry,
10289 i_getId(), // only media with registry ID == machine UUID
10290 strMachineFolder);
10291 // this throws HRESULT
10292 }
10293
10294 // save snapshots
10295 rc = i_saveAllSnapshots(config);
10296 if (FAILED(rc)) throw rc;
10297}
10298
10299/**
10300 * Saves all snapshots of the machine into the given machine config file. Called
10301 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10302 * @param config
10303 * @return
10304 */
10305HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10306{
10307 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10308
10309 HRESULT rc = S_OK;
10310
10311 try
10312 {
10313 config.llFirstSnapshot.clear();
10314
10315 if (mData->mFirstSnapshot)
10316 {
10317 // the settings use a list for "the first snapshot"
10318 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10319
10320 // get reference to the snapshot on the list and work on that
10321 // element straight in the list to avoid excessive copying later
10322 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10323 if (FAILED(rc)) throw rc;
10324 }
10325
10326// if (mType == IsSessionMachine)
10327// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10328
10329 }
10330 catch (HRESULT err)
10331 {
10332 /* we assume that error info is set by the thrower */
10333 rc = err;
10334 }
10335 catch (...)
10336 {
10337 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10338 }
10339
10340 return rc;
10341}
10342
10343/**
10344 * Saves the VM hardware configuration. It is assumed that the
10345 * given node is empty.
10346 *
10347 * @param data Reference to the settings object for the hardware config.
10348 * @param pDbg Pointer to the settings object for the debugging config
10349 * which happens to live in mHWData.
10350 * @param pAutostart Pointer to the settings object for the autostart config
10351 * which happens to live in mHWData.
10352 */
10353HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10354 settings::Autostart *pAutostart)
10355{
10356 HRESULT rc = S_OK;
10357
10358 try
10359 {
10360 /* The hardware version attribute (optional).
10361 Automatically upgrade from 1 to current default hardware version
10362 when there is no saved state. (ugly!) */
10363 if ( mHWData->mHWVersion == "1"
10364 && mSSData->strStateFilePath.isEmpty()
10365 )
10366 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10367
10368 data.strVersion = mHWData->mHWVersion;
10369 data.uuid = mHWData->mHardwareUUID;
10370
10371 // CPU
10372 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10373 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10374 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10375 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10376 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10377 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10378 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10379 data.fPAE = !!mHWData->mPAEEnabled;
10380 data.enmLongMode = mHWData->mLongMode;
10381 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10382 data.fAPIC = !!mHWData->mAPIC;
10383 data.fX2APIC = !!mHWData->mX2APIC;
10384 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10385 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10386 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10387 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10388 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10389 data.cCPUs = mHWData->mCPUCount;
10390 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10391 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10392 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10393 data.strCpuProfile = mHWData->mCpuProfile;
10394
10395 data.llCpus.clear();
10396 if (data.fCpuHotPlug)
10397 {
10398 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10399 {
10400 if (mHWData->mCPUAttached[idx])
10401 {
10402 settings::Cpu cpu;
10403 cpu.ulId = idx;
10404 data.llCpus.push_back(cpu);
10405 }
10406 }
10407 }
10408
10409 /* Standard and Extended CPUID leafs. */
10410 data.llCpuIdLeafs.clear();
10411 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10412
10413 // memory
10414 data.ulMemorySizeMB = mHWData->mMemorySize;
10415 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10416
10417 // firmware
10418 data.firmwareType = mHWData->mFirmwareType;
10419
10420 // HID
10421 data.pointingHIDType = mHWData->mPointingHIDType;
10422 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10423
10424 // chipset
10425 data.chipsetType = mHWData->mChipsetType;
10426
10427 // paravirt
10428 data.paravirtProvider = mHWData->mParavirtProvider;
10429 data.strParavirtDebug = mHWData->mParavirtDebug;
10430
10431 // emulated USB card reader
10432 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10433
10434 // HPET
10435 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10436
10437 // boot order
10438 data.mapBootOrder.clear();
10439 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10440 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10441
10442 // display
10443 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10444 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10445 data.cMonitors = mHWData->mMonitorCount;
10446 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10447 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10448 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10449 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10450 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10451 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10452 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10453 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10454 {
10455 if (mHWData->maVideoCaptureScreens[i])
10456 ASMBitSet(&data.u64VideoCaptureScreens, i);
10457 else
10458 ASMBitClear(&data.u64VideoCaptureScreens, i);
10459 }
10460 /* store relative video capture file if possible */
10461 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10462 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10463
10464 /* VRDEServer settings (optional) */
10465 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10466 if (FAILED(rc)) throw rc;
10467
10468 /* BIOS (required) */
10469 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10470 if (FAILED(rc)) throw rc;
10471
10472 /* USB Controller (required) */
10473 data.usbSettings.llUSBControllers.clear();
10474 for (USBControllerList::const_iterator
10475 it = mUSBControllers->begin();
10476 it != mUSBControllers->end();
10477 ++it)
10478 {
10479 ComObjPtr<USBController> ctrl = *it;
10480 settings::USBController settingsCtrl;
10481
10482 settingsCtrl.strName = ctrl->i_getName();
10483 settingsCtrl.enmType = ctrl->i_getControllerType();
10484
10485 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10486 }
10487
10488 /* USB device filters (required) */
10489 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10490 if (FAILED(rc)) throw rc;
10491
10492 /* Network adapters (required) */
10493 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10494 data.llNetworkAdapters.clear();
10495 /* Write out only the nominal number of network adapters for this
10496 * chipset type. Since Machine::commit() hasn't been called there
10497 * may be extra NIC settings in the vector. */
10498 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10499 {
10500 settings::NetworkAdapter nic;
10501 nic.ulSlot = (uint32_t)slot;
10502 /* paranoia check... must not be NULL, but must not crash either. */
10503 if (mNetworkAdapters[slot])
10504 {
10505 if (mNetworkAdapters[slot]->i_hasDefaults())
10506 continue;
10507
10508 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10509 if (FAILED(rc)) throw rc;
10510
10511 data.llNetworkAdapters.push_back(nic);
10512 }
10513 }
10514
10515 /* Serial ports */
10516 data.llSerialPorts.clear();
10517 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10518 {
10519 if (mSerialPorts[slot]->i_hasDefaults())
10520 continue;
10521
10522 settings::SerialPort s;
10523 s.ulSlot = slot;
10524 rc = mSerialPorts[slot]->i_saveSettings(s);
10525 if (FAILED(rc)) return rc;
10526
10527 data.llSerialPorts.push_back(s);
10528 }
10529
10530 /* Parallel ports */
10531 data.llParallelPorts.clear();
10532 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10533 {
10534 if (mParallelPorts[slot]->i_hasDefaults())
10535 continue;
10536
10537 settings::ParallelPort p;
10538 p.ulSlot = slot;
10539 rc = mParallelPorts[slot]->i_saveSettings(p);
10540 if (FAILED(rc)) return rc;
10541
10542 data.llParallelPorts.push_back(p);
10543 }
10544
10545 /* Audio adapter */
10546 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10547 if (FAILED(rc)) return rc;
10548
10549 rc = i_saveStorageControllers(data.storage);
10550 if (FAILED(rc)) return rc;
10551
10552 /* Shared folders */
10553 data.llSharedFolders.clear();
10554 for (HWData::SharedFolderList::const_iterator
10555 it = mHWData->mSharedFolders.begin();
10556 it != mHWData->mSharedFolders.end();
10557 ++it)
10558 {
10559 SharedFolder *pSF = *it;
10560 AutoCaller sfCaller(pSF);
10561 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10562 settings::SharedFolder sf;
10563 sf.strName = pSF->i_getName();
10564 sf.strHostPath = pSF->i_getHostPath();
10565 sf.fWritable = !!pSF->i_isWritable();
10566 sf.fAutoMount = !!pSF->i_isAutoMounted();
10567
10568 data.llSharedFolders.push_back(sf);
10569 }
10570
10571 // clipboard
10572 data.clipboardMode = mHWData->mClipboardMode;
10573
10574 // drag'n'drop
10575 data.dndMode = mHWData->mDnDMode;
10576
10577 /* Guest */
10578 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10579
10580 // IO settings
10581 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10582 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10583
10584 /* BandwidthControl (required) */
10585 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10586 if (FAILED(rc)) throw rc;
10587
10588 /* Host PCI devices */
10589 data.pciAttachments.clear();
10590 for (HWData::PCIDeviceAssignmentList::const_iterator
10591 it = mHWData->mPCIDeviceAssignments.begin();
10592 it != mHWData->mPCIDeviceAssignments.end();
10593 ++it)
10594 {
10595 ComObjPtr<PCIDeviceAttachment> pda = *it;
10596 settings::HostPCIDeviceAttachment hpda;
10597
10598 rc = pda->i_saveSettings(hpda);
10599 if (FAILED(rc)) throw rc;
10600
10601 data.pciAttachments.push_back(hpda);
10602 }
10603
10604 // guest properties
10605 data.llGuestProperties.clear();
10606#ifdef VBOX_WITH_GUEST_PROPS
10607 for (HWData::GuestPropertyMap::const_iterator
10608 it = mHWData->mGuestProperties.begin();
10609 it != mHWData->mGuestProperties.end();
10610 ++it)
10611 {
10612 HWData::GuestProperty property = it->second;
10613
10614 /* Remove transient guest properties at shutdown unless we
10615 * are saving state. Note that restoring snapshot intentionally
10616 * keeps them, they will be removed if appropriate once the final
10617 * machine state is set (as crashes etc. need to work). */
10618 if ( ( mData->mMachineState == MachineState_PoweredOff
10619 || mData->mMachineState == MachineState_Aborted
10620 || mData->mMachineState == MachineState_Teleported)
10621 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10622 continue;
10623 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10624 prop.strName = it->first;
10625 prop.strValue = property.strValue;
10626 prop.timestamp = property.mTimestamp;
10627 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10628 GuestPropWriteFlags(property.mFlags, szFlags);
10629 prop.strFlags = szFlags;
10630
10631 data.llGuestProperties.push_back(prop);
10632 }
10633
10634 /* I presume this doesn't require a backup(). */
10635 mData->mGuestPropertiesModified = FALSE;
10636#endif /* VBOX_WITH_GUEST_PROPS defined */
10637
10638 *pDbg = mHWData->mDebugging;
10639 *pAutostart = mHWData->mAutostart;
10640
10641 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10642 }
10643 catch (std::bad_alloc &)
10644 {
10645 return E_OUTOFMEMORY;
10646 }
10647
10648 AssertComRC(rc);
10649 return rc;
10650}
10651
10652/**
10653 * Saves the storage controller configuration.
10654 *
10655 * @param data storage settings.
10656 */
10657HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10658{
10659 data.llStorageControllers.clear();
10660
10661 for (StorageControllerList::const_iterator
10662 it = mStorageControllers->begin();
10663 it != mStorageControllers->end();
10664 ++it)
10665 {
10666 HRESULT rc;
10667 ComObjPtr<StorageController> pCtl = *it;
10668
10669 settings::StorageController ctl;
10670 ctl.strName = pCtl->i_getName();
10671 ctl.controllerType = pCtl->i_getControllerType();
10672 ctl.storageBus = pCtl->i_getStorageBus();
10673 ctl.ulInstance = pCtl->i_getInstance();
10674 ctl.fBootable = pCtl->i_getBootable();
10675
10676 /* Save the port count. */
10677 ULONG portCount;
10678 rc = pCtl->COMGETTER(PortCount)(&portCount);
10679 ComAssertComRCRet(rc, rc);
10680 ctl.ulPortCount = portCount;
10681
10682 /* Save fUseHostIOCache */
10683 BOOL fUseHostIOCache;
10684 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10685 ComAssertComRCRet(rc, rc);
10686 ctl.fUseHostIOCache = !!fUseHostIOCache;
10687
10688 /* save the devices now. */
10689 rc = i_saveStorageDevices(pCtl, ctl);
10690 ComAssertComRCRet(rc, rc);
10691
10692 data.llStorageControllers.push_back(ctl);
10693 }
10694
10695 return S_OK;
10696}
10697
10698/**
10699 * Saves the hard disk configuration.
10700 */
10701HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10702 settings::StorageController &data)
10703{
10704 MediumAttachmentList atts;
10705
10706 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10707 if (FAILED(rc)) return rc;
10708
10709 data.llAttachedDevices.clear();
10710 for (MediumAttachmentList::const_iterator
10711 it = atts.begin();
10712 it != atts.end();
10713 ++it)
10714 {
10715 settings::AttachedDevice dev;
10716 IMediumAttachment *iA = *it;
10717 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10718 Medium *pMedium = pAttach->i_getMedium();
10719
10720 dev.deviceType = pAttach->i_getType();
10721 dev.lPort = pAttach->i_getPort();
10722 dev.lDevice = pAttach->i_getDevice();
10723 dev.fPassThrough = pAttach->i_getPassthrough();
10724 dev.fHotPluggable = pAttach->i_getHotPluggable();
10725 if (pMedium)
10726 {
10727 if (pMedium->i_isHostDrive())
10728 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10729 else
10730 dev.uuid = pMedium->i_getId();
10731 dev.fTempEject = pAttach->i_getTempEject();
10732 dev.fNonRotational = pAttach->i_getNonRotational();
10733 dev.fDiscard = pAttach->i_getDiscard();
10734 }
10735
10736 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10737
10738 data.llAttachedDevices.push_back(dev);
10739 }
10740
10741 return S_OK;
10742}
10743
10744/**
10745 * Saves machine state settings as defined by aFlags
10746 * (SaveSTS_* values).
10747 *
10748 * @param aFlags Combination of SaveSTS_* flags.
10749 *
10750 * @note Locks objects for writing.
10751 */
10752HRESULT Machine::i_saveStateSettings(int aFlags)
10753{
10754 if (aFlags == 0)
10755 return S_OK;
10756
10757 AutoCaller autoCaller(this);
10758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10759
10760 /* This object's write lock is also necessary to serialize file access
10761 * (prevent concurrent reads and writes) */
10762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10763
10764 HRESULT rc = S_OK;
10765
10766 Assert(mData->pMachineConfigFile);
10767
10768 try
10769 {
10770 if (aFlags & SaveSTS_CurStateModified)
10771 mData->pMachineConfigFile->fCurrentStateModified = true;
10772
10773 if (aFlags & SaveSTS_StateFilePath)
10774 {
10775 if (!mSSData->strStateFilePath.isEmpty())
10776 /* try to make the file name relative to the settings file dir */
10777 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10778 else
10779 mData->pMachineConfigFile->strStateFile.setNull();
10780 }
10781
10782 if (aFlags & SaveSTS_StateTimeStamp)
10783 {
10784 Assert( mData->mMachineState != MachineState_Aborted
10785 || mSSData->strStateFilePath.isEmpty());
10786
10787 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10788
10789 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10790/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10791 }
10792
10793 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10794 }
10795 catch (...)
10796 {
10797 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10798 }
10799
10800 return rc;
10801}
10802
10803/**
10804 * Ensures that the given medium is added to a media registry. If this machine
10805 * was created with 4.0 or later, then the machine registry is used. Otherwise
10806 * the global VirtualBox media registry is used.
10807 *
10808 * Caller must NOT hold machine lock, media tree or any medium locks!
10809 *
10810 * @param pMedium
10811 */
10812void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10813{
10814 /* Paranoia checks: do not hold machine or media tree locks. */
10815 AssertReturnVoid(!isWriteLockOnCurrentThread());
10816 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10817
10818 ComObjPtr<Medium> pBase;
10819 {
10820 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10821 pBase = pMedium->i_getBase();
10822 }
10823
10824 /* Paranoia checks: do not hold medium locks. */
10825 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10826 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10827
10828 // decide which medium registry to use now that the medium is attached:
10829 Guid uuid;
10830 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10831 if (fCanHaveOwnMediaRegistry)
10832 // machine XML is VirtualBox 4.0 or higher:
10833 uuid = i_getId(); // machine UUID
10834 else
10835 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10836
10837 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10838 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10839 if (pMedium->i_addRegistry(uuid))
10840 mParent->i_markRegistryModified(uuid);
10841
10842 /* For more complex hard disk structures it can happen that the base
10843 * medium isn't yet associated with any medium registry. Do that now. */
10844 if (pMedium != pBase)
10845 {
10846 /* Tree lock needed by Medium::addRegistry when recursing. */
10847 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10848 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10849 {
10850 treeLock.release();
10851 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10852 treeLock.acquire();
10853 }
10854 if (pBase->i_addRegistryRecursive(uuid))
10855 {
10856 treeLock.release();
10857 mParent->i_markRegistryModified(uuid);
10858 }
10859 }
10860}
10861
10862/**
10863 * Creates differencing hard disks for all normal hard disks attached to this
10864 * machine and a new set of attachments to refer to created disks.
10865 *
10866 * Used when taking a snapshot or when deleting the current state. Gets called
10867 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10868 *
10869 * This method assumes that mMediumAttachments contains the original hard disk
10870 * attachments it needs to create diffs for. On success, these attachments will
10871 * be replaced with the created diffs.
10872 *
10873 * Attachments with non-normal hard disks are left as is.
10874 *
10875 * If @a aOnline is @c false then the original hard disks that require implicit
10876 * diffs will be locked for reading. Otherwise it is assumed that they are
10877 * already locked for writing (when the VM was started). Note that in the latter
10878 * case it is responsibility of the caller to lock the newly created diffs for
10879 * writing if this method succeeds.
10880 *
10881 * @param aProgress Progress object to run (must contain at least as
10882 * many operations left as the number of hard disks
10883 * attached).
10884 * @param aWeight Weight of this operation.
10885 * @param aOnline Whether the VM was online prior to this operation.
10886 *
10887 * @note The progress object is not marked as completed, neither on success nor
10888 * on failure. This is a responsibility of the caller.
10889 *
10890 * @note Locks this object and the media tree for writing.
10891 */
10892HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10893 ULONG aWeight,
10894 bool aOnline)
10895{
10896 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10897
10898 AutoCaller autoCaller(this);
10899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10900
10901 AutoMultiWriteLock2 alock(this->lockHandle(),
10902 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10903
10904 /* must be in a protective state because we release the lock below */
10905 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10906 || mData->mMachineState == MachineState_OnlineSnapshotting
10907 || mData->mMachineState == MachineState_LiveSnapshotting
10908 || mData->mMachineState == MachineState_RestoringSnapshot
10909 || mData->mMachineState == MachineState_DeletingSnapshot
10910 , E_FAIL);
10911
10912 HRESULT rc = S_OK;
10913
10914 // use appropriate locked media map (online or offline)
10915 MediumLockListMap lockedMediaOffline;
10916 MediumLockListMap *lockedMediaMap;
10917 if (aOnline)
10918 lockedMediaMap = &mData->mSession.mLockedMedia;
10919 else
10920 lockedMediaMap = &lockedMediaOffline;
10921
10922 try
10923 {
10924 if (!aOnline)
10925 {
10926 /* lock all attached hard disks early to detect "in use"
10927 * situations before creating actual diffs */
10928 for (MediumAttachmentList::const_iterator
10929 it = mMediumAttachments->begin();
10930 it != mMediumAttachments->end();
10931 ++it)
10932 {
10933 MediumAttachment *pAtt = *it;
10934 if (pAtt->i_getType() == DeviceType_HardDisk)
10935 {
10936 Medium *pMedium = pAtt->i_getMedium();
10937 Assert(pMedium);
10938
10939 MediumLockList *pMediumLockList(new MediumLockList());
10940 alock.release();
10941 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10942 NULL /* pToLockWrite */,
10943 false /* fMediumLockWriteAll */,
10944 NULL,
10945 *pMediumLockList);
10946 alock.acquire();
10947 if (FAILED(rc))
10948 {
10949 delete pMediumLockList;
10950 throw rc;
10951 }
10952 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10953 if (FAILED(rc))
10954 {
10955 throw setError(rc,
10956 tr("Collecting locking information for all attached media failed"));
10957 }
10958 }
10959 }
10960
10961 /* Now lock all media. If this fails, nothing is locked. */
10962 alock.release();
10963 rc = lockedMediaMap->Lock();
10964 alock.acquire();
10965 if (FAILED(rc))
10966 {
10967 throw setError(rc,
10968 tr("Locking of attached media failed"));
10969 }
10970 }
10971
10972 /* remember the current list (note that we don't use backup() since
10973 * mMediumAttachments may be already backed up) */
10974 MediumAttachmentList atts = *mMediumAttachments.data();
10975
10976 /* start from scratch */
10977 mMediumAttachments->clear();
10978
10979 /* go through remembered attachments and create diffs for normal hard
10980 * disks and attach them */
10981 for (MediumAttachmentList::const_iterator
10982 it = atts.begin();
10983 it != atts.end();
10984 ++it)
10985 {
10986 MediumAttachment *pAtt = *it;
10987
10988 DeviceType_T devType = pAtt->i_getType();
10989 Medium *pMedium = pAtt->i_getMedium();
10990
10991 if ( devType != DeviceType_HardDisk
10992 || pMedium == NULL
10993 || pMedium->i_getType() != MediumType_Normal)
10994 {
10995 /* copy the attachment as is */
10996
10997 /** @todo the progress object created in SessionMachine::TakeSnaphot
10998 * only expects operations for hard disks. Later other
10999 * device types need to show up in the progress as well. */
11000 if (devType == DeviceType_HardDisk)
11001 {
11002 if (pMedium == NULL)
11003 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11004 aWeight); // weight
11005 else
11006 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11007 pMedium->i_getBase()->i_getName().c_str()).raw(),
11008 aWeight); // weight
11009 }
11010
11011 mMediumAttachments->push_back(pAtt);
11012 continue;
11013 }
11014
11015 /* need a diff */
11016 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11017 pMedium->i_getBase()->i_getName().c_str()).raw(),
11018 aWeight); // weight
11019
11020 Utf8Str strFullSnapshotFolder;
11021 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11022
11023 ComObjPtr<Medium> diff;
11024 diff.createObject();
11025 // store the diff in the same registry as the parent
11026 // (this cannot fail here because we can't create implicit diffs for
11027 // unregistered images)
11028 Guid uuidRegistryParent;
11029 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11030 Assert(fInRegistry); NOREF(fInRegistry);
11031 rc = diff->init(mParent,
11032 pMedium->i_getPreferredDiffFormat(),
11033 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11034 uuidRegistryParent,
11035 DeviceType_HardDisk);
11036 if (FAILED(rc)) throw rc;
11037
11038 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11039 * the push_back? Looks like we're going to release medium with the
11040 * wrong kind of lock (general issue with if we fail anywhere at all)
11041 * and an orphaned VDI in the snapshots folder. */
11042
11043 /* update the appropriate lock list */
11044 MediumLockList *pMediumLockList;
11045 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11046 AssertComRCThrowRC(rc);
11047 if (aOnline)
11048 {
11049 alock.release();
11050 /* The currently attached medium will be read-only, change
11051 * the lock type to read. */
11052 rc = pMediumLockList->Update(pMedium, false);
11053 alock.acquire();
11054 AssertComRCThrowRC(rc);
11055 }
11056
11057 /* release the locks before the potentially lengthy operation */
11058 alock.release();
11059 rc = pMedium->i_createDiffStorage(diff,
11060 pMedium->i_getPreferredDiffVariant(),
11061 pMediumLockList,
11062 NULL /* aProgress */,
11063 true /* aWait */);
11064 alock.acquire();
11065 if (FAILED(rc)) throw rc;
11066
11067 /* actual lock list update is done in Machine::i_commitMedia */
11068
11069 rc = diff->i_addBackReference(mData->mUuid);
11070 AssertComRCThrowRC(rc);
11071
11072 /* add a new attachment */
11073 ComObjPtr<MediumAttachment> attachment;
11074 attachment.createObject();
11075 rc = attachment->init(this,
11076 diff,
11077 pAtt->i_getControllerName(),
11078 pAtt->i_getPort(),
11079 pAtt->i_getDevice(),
11080 DeviceType_HardDisk,
11081 true /* aImplicit */,
11082 false /* aPassthrough */,
11083 false /* aTempEject */,
11084 pAtt->i_getNonRotational(),
11085 pAtt->i_getDiscard(),
11086 pAtt->i_getHotPluggable(),
11087 pAtt->i_getBandwidthGroup());
11088 if (FAILED(rc)) throw rc;
11089
11090 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11091 AssertComRCThrowRC(rc);
11092 mMediumAttachments->push_back(attachment);
11093 }
11094 }
11095 catch (HRESULT aRC) { rc = aRC; }
11096
11097 /* unlock all hard disks we locked when there is no VM */
11098 if (!aOnline)
11099 {
11100 ErrorInfoKeeper eik;
11101
11102 HRESULT rc1 = lockedMediaMap->Clear();
11103 AssertComRC(rc1);
11104 }
11105
11106 return rc;
11107}
11108
11109/**
11110 * Deletes implicit differencing hard disks created either by
11111 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11112 * mMediumAttachments.
11113 *
11114 * Note that to delete hard disks created by #attachDevice() this method is
11115 * called from #i_rollbackMedia() when the changes are rolled back.
11116 *
11117 * @note Locks this object and the media tree for writing.
11118 */
11119HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11120{
11121 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11122
11123 AutoCaller autoCaller(this);
11124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11125
11126 AutoMultiWriteLock2 alock(this->lockHandle(),
11127 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11128
11129 /* We absolutely must have backed up state. */
11130 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11131
11132 /* Check if there are any implicitly created diff images. */
11133 bool fImplicitDiffs = false;
11134 for (MediumAttachmentList::const_iterator
11135 it = mMediumAttachments->begin();
11136 it != mMediumAttachments->end();
11137 ++it)
11138 {
11139 const ComObjPtr<MediumAttachment> &pAtt = *it;
11140 if (pAtt->i_isImplicit())
11141 {
11142 fImplicitDiffs = true;
11143 break;
11144 }
11145 }
11146 /* If there is nothing to do, leave early. This saves lots of image locking
11147 * effort. It also avoids a MachineStateChanged event without real reason.
11148 * This is important e.g. when loading a VM config, because there should be
11149 * no events. Otherwise API clients can become thoroughly confused for
11150 * inaccessible VMs (the code for loading VM configs uses this method for
11151 * cleanup if the config makes no sense), as they take such events as an
11152 * indication that the VM is alive, and they would force the VM config to
11153 * be reread, leading to an endless loop. */
11154 if (!fImplicitDiffs)
11155 return S_OK;
11156
11157 HRESULT rc = S_OK;
11158 MachineState_T oldState = mData->mMachineState;
11159
11160 /* will release the lock before the potentially lengthy operation,
11161 * so protect with the special state (unless already protected) */
11162 if ( oldState != MachineState_Snapshotting
11163 && oldState != MachineState_OnlineSnapshotting
11164 && oldState != MachineState_LiveSnapshotting
11165 && oldState != MachineState_RestoringSnapshot
11166 && oldState != MachineState_DeletingSnapshot
11167 && oldState != MachineState_DeletingSnapshotOnline
11168 && oldState != MachineState_DeletingSnapshotPaused
11169 )
11170 i_setMachineState(MachineState_SettingUp);
11171
11172 // use appropriate locked media map (online or offline)
11173 MediumLockListMap lockedMediaOffline;
11174 MediumLockListMap *lockedMediaMap;
11175 if (aOnline)
11176 lockedMediaMap = &mData->mSession.mLockedMedia;
11177 else
11178 lockedMediaMap = &lockedMediaOffline;
11179
11180 try
11181 {
11182 if (!aOnline)
11183 {
11184 /* lock all attached hard disks early to detect "in use"
11185 * situations before deleting actual diffs */
11186 for (MediumAttachmentList::const_iterator
11187 it = mMediumAttachments->begin();
11188 it != mMediumAttachments->end();
11189 ++it)
11190 {
11191 MediumAttachment *pAtt = *it;
11192 if (pAtt->i_getType() == DeviceType_HardDisk)
11193 {
11194 Medium *pMedium = pAtt->i_getMedium();
11195 Assert(pMedium);
11196
11197 MediumLockList *pMediumLockList(new MediumLockList());
11198 alock.release();
11199 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11200 NULL /* pToLockWrite */,
11201 false /* fMediumLockWriteAll */,
11202 NULL,
11203 *pMediumLockList);
11204 alock.acquire();
11205
11206 if (FAILED(rc))
11207 {
11208 delete pMediumLockList;
11209 throw rc;
11210 }
11211
11212 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11213 if (FAILED(rc))
11214 throw rc;
11215 }
11216 }
11217
11218 if (FAILED(rc))
11219 throw rc;
11220 } // end of offline
11221
11222 /* Lock lists are now up to date and include implicitly created media */
11223
11224 /* Go through remembered attachments and delete all implicitly created
11225 * diffs and fix up the attachment information */
11226 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11227 MediumAttachmentList implicitAtts;
11228 for (MediumAttachmentList::const_iterator
11229 it = mMediumAttachments->begin();
11230 it != mMediumAttachments->end();
11231 ++it)
11232 {
11233 ComObjPtr<MediumAttachment> pAtt = *it;
11234 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11235 if (pMedium.isNull())
11236 continue;
11237
11238 // Implicit attachments go on the list for deletion and back references are removed.
11239 if (pAtt->i_isImplicit())
11240 {
11241 /* Deassociate and mark for deletion */
11242 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11243 rc = pMedium->i_removeBackReference(mData->mUuid);
11244 if (FAILED(rc))
11245 throw rc;
11246 implicitAtts.push_back(pAtt);
11247 continue;
11248 }
11249
11250 /* Was this medium attached before? */
11251 if (!i_findAttachment(oldAtts, pMedium))
11252 {
11253 /* no: de-associate */
11254 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11255 rc = pMedium->i_removeBackReference(mData->mUuid);
11256 if (FAILED(rc))
11257 throw rc;
11258 continue;
11259 }
11260 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11261 }
11262
11263 /* If there are implicit attachments to delete, throw away the lock
11264 * map contents (which will unlock all media) since the medium
11265 * attachments will be rolled back. Below we need to completely
11266 * recreate the lock map anyway since it is infinitely complex to
11267 * do this incrementally (would need reconstructing each attachment
11268 * change, which would be extremely hairy). */
11269 if (implicitAtts.size() != 0)
11270 {
11271 ErrorInfoKeeper eik;
11272
11273 HRESULT rc1 = lockedMediaMap->Clear();
11274 AssertComRC(rc1);
11275 }
11276
11277 /* rollback hard disk changes */
11278 mMediumAttachments.rollback();
11279
11280 MultiResult mrc(S_OK);
11281
11282 // Delete unused implicit diffs.
11283 if (implicitAtts.size() != 0)
11284 {
11285 alock.release();
11286
11287 for (MediumAttachmentList::const_iterator
11288 it = implicitAtts.begin();
11289 it != implicitAtts.end();
11290 ++it)
11291 {
11292 // Remove medium associated with this attachment.
11293 ComObjPtr<MediumAttachment> pAtt = *it;
11294 Assert(pAtt);
11295 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11296 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11297 Assert(pMedium);
11298
11299 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11300 // continue on delete failure, just collect error messages
11301 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11302 pMedium->i_getLocationFull().c_str() ));
11303 mrc = rc;
11304 }
11305 // Clear the list of deleted implicit attachments now, while not
11306 // holding the lock, as it will ultimately trigger Medium::uninit()
11307 // calls which assume that the media tree lock isn't held.
11308 implicitAtts.clear();
11309
11310 alock.acquire();
11311
11312 /* if there is a VM recreate media lock map as mentioned above,
11313 * otherwise it is a waste of time and we leave things unlocked */
11314 if (aOnline)
11315 {
11316 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11317 /* must never be NULL, but better safe than sorry */
11318 if (!pMachine.isNull())
11319 {
11320 alock.release();
11321 rc = mData->mSession.mMachine->i_lockMedia();
11322 alock.acquire();
11323 if (FAILED(rc))
11324 throw rc;
11325 }
11326 }
11327 }
11328 }
11329 catch (HRESULT aRC) {rc = aRC;}
11330
11331 if (mData->mMachineState == MachineState_SettingUp)
11332 i_setMachineState(oldState);
11333
11334 /* unlock all hard disks we locked when there is no VM */
11335 if (!aOnline)
11336 {
11337 ErrorInfoKeeper eik;
11338
11339 HRESULT rc1 = lockedMediaMap->Clear();
11340 AssertComRC(rc1);
11341 }
11342
11343 return rc;
11344}
11345
11346
11347/**
11348 * Looks through the given list of media attachments for one with the given parameters
11349 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11350 * can be searched as well if needed.
11351 *
11352 * @param ll
11353 * @param aControllerName
11354 * @param aControllerPort
11355 * @param aDevice
11356 * @return
11357 */
11358MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11359 const Utf8Str &aControllerName,
11360 LONG aControllerPort,
11361 LONG aDevice)
11362{
11363 for (MediumAttachmentList::const_iterator
11364 it = ll.begin();
11365 it != ll.end();
11366 ++it)
11367 {
11368 MediumAttachment *pAttach = *it;
11369 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11370 return pAttach;
11371 }
11372
11373 return NULL;
11374}
11375
11376/**
11377 * Looks through the given list of media attachments for one with the given parameters
11378 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11379 * can be searched as well if needed.
11380 *
11381 * @param ll
11382 * @param pMedium
11383 * @return
11384 */
11385MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11386 ComObjPtr<Medium> pMedium)
11387{
11388 for (MediumAttachmentList::const_iterator
11389 it = ll.begin();
11390 it != ll.end();
11391 ++it)
11392 {
11393 MediumAttachment *pAttach = *it;
11394 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11395 if (pMediumThis == pMedium)
11396 return pAttach;
11397 }
11398
11399 return NULL;
11400}
11401
11402/**
11403 * Looks through the given list of media attachments for one with the given parameters
11404 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11405 * can be searched as well if needed.
11406 *
11407 * @param ll
11408 * @param id
11409 * @return
11410 */
11411MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11412 Guid &id)
11413{
11414 for (MediumAttachmentList::const_iterator
11415 it = ll.begin();
11416 it != ll.end();
11417 ++it)
11418 {
11419 MediumAttachment *pAttach = *it;
11420 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11421 if (pMediumThis->i_getId() == id)
11422 return pAttach;
11423 }
11424
11425 return NULL;
11426}
11427
11428/**
11429 * Main implementation for Machine::DetachDevice. This also gets called
11430 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11431 *
11432 * @param pAttach Medium attachment to detach.
11433 * @param writeLock Machine write lock which the caller must have locked once.
11434 * This may be released temporarily in here.
11435 * @param pSnapshot If NULL, then the detachment is for the current machine.
11436 * Otherwise this is for a SnapshotMachine, and this must be
11437 * its snapshot.
11438 * @return
11439 */
11440HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11441 AutoWriteLock &writeLock,
11442 Snapshot *pSnapshot)
11443{
11444 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11445 DeviceType_T mediumType = pAttach->i_getType();
11446
11447 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11448
11449 if (pAttach->i_isImplicit())
11450 {
11451 /* attempt to implicitly delete the implicitly created diff */
11452
11453 /// @todo move the implicit flag from MediumAttachment to Medium
11454 /// and forbid any hard disk operation when it is implicit. Or maybe
11455 /// a special media state for it to make it even more simple.
11456
11457 Assert(mMediumAttachments.isBackedUp());
11458
11459 /* will release the lock before the potentially lengthy operation, so
11460 * protect with the special state */
11461 MachineState_T oldState = mData->mMachineState;
11462 i_setMachineState(MachineState_SettingUp);
11463
11464 writeLock.release();
11465
11466 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11467 true /*aWait*/);
11468
11469 writeLock.acquire();
11470
11471 i_setMachineState(oldState);
11472
11473 if (FAILED(rc)) return rc;
11474 }
11475
11476 i_setModified(IsModified_Storage);
11477 mMediumAttachments.backup();
11478 mMediumAttachments->remove(pAttach);
11479
11480 if (!oldmedium.isNull())
11481 {
11482 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11483 if (pSnapshot)
11484 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11485 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11486 else if (mediumType != DeviceType_HardDisk)
11487 oldmedium->i_removeBackReference(mData->mUuid);
11488 }
11489
11490 return S_OK;
11491}
11492
11493/**
11494 * Goes thru all media of the given list and
11495 *
11496 * 1) calls i_detachDevice() on each of them for this machine and
11497 * 2) adds all Medium objects found in the process to the given list,
11498 * depending on cleanupMode.
11499 *
11500 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11501 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11502 * media to the list.
11503 *
11504 * This gets called from Machine::Unregister, both for the actual Machine and
11505 * the SnapshotMachine objects that might be found in the snapshots.
11506 *
11507 * Requires caller and locking. The machine lock must be passed in because it
11508 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11509 *
11510 * @param writeLock Machine lock from top-level caller; this gets passed to
11511 * i_detachDevice.
11512 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11513 * object if called for a SnapshotMachine.
11514 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11515 * added to llMedia; if Full, then all media get added;
11516 * otherwise no media get added.
11517 * @param llMedia Caller's list to receive Medium objects which got detached so
11518 * caller can close() them, depending on cleanupMode.
11519 * @return
11520 */
11521HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11522 Snapshot *pSnapshot,
11523 CleanupMode_T cleanupMode,
11524 MediaList &llMedia)
11525{
11526 Assert(isWriteLockOnCurrentThread());
11527
11528 HRESULT rc;
11529
11530 // make a temporary list because i_detachDevice invalidates iterators into
11531 // mMediumAttachments
11532 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11533
11534 for (MediumAttachmentList::iterator
11535 it = llAttachments2.begin();
11536 it != llAttachments2.end();
11537 ++it)
11538 {
11539 ComObjPtr<MediumAttachment> &pAttach = *it;
11540 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11541
11542 if (!pMedium.isNull())
11543 {
11544 AutoCaller mac(pMedium);
11545 if (FAILED(mac.rc())) return mac.rc();
11546 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11547 DeviceType_T devType = pMedium->i_getDeviceType();
11548 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11549 && devType == DeviceType_HardDisk)
11550 || (cleanupMode == CleanupMode_Full)
11551 )
11552 {
11553 llMedia.push_back(pMedium);
11554 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11555 /* Not allowed to keep this lock as below we need the parent
11556 * medium lock, and the lock order is parent to child. */
11557 lock.release();
11558 /*
11559 * Search for medias which are not attached to any machine, but
11560 * in the chain to an attached disk. Mediums are only consided
11561 * if they are:
11562 * - have only one child
11563 * - no references to any machines
11564 * - are of normal medium type
11565 */
11566 while (!pParent.isNull())
11567 {
11568 AutoCaller mac1(pParent);
11569 if (FAILED(mac1.rc())) return mac1.rc();
11570 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11571 if (pParent->i_getChildren().size() == 1)
11572 {
11573 if ( pParent->i_getMachineBackRefCount() == 0
11574 && pParent->i_getType() == MediumType_Normal
11575 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11576 llMedia.push_back(pParent);
11577 }
11578 else
11579 break;
11580 pParent = pParent->i_getParent();
11581 }
11582 }
11583 }
11584
11585 // real machine: then we need to use the proper method
11586 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11587
11588 if (FAILED(rc))
11589 return rc;
11590 }
11591
11592 return S_OK;
11593}
11594
11595/**
11596 * Perform deferred hard disk detachments.
11597 *
11598 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11599 * changed (not backed up).
11600 *
11601 * If @a aOnline is @c true then this method will also unlock the old hard
11602 * disks for which the new implicit diffs were created and will lock these new
11603 * diffs for writing.
11604 *
11605 * @param aOnline Whether the VM was online prior to this operation.
11606 *
11607 * @note Locks this object for writing!
11608 */
11609void Machine::i_commitMedia(bool aOnline /*= false*/)
11610{
11611 AutoCaller autoCaller(this);
11612 AssertComRCReturnVoid(autoCaller.rc());
11613
11614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11615
11616 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11617
11618 HRESULT rc = S_OK;
11619
11620 /* no attach/detach operations -- nothing to do */
11621 if (!mMediumAttachments.isBackedUp())
11622 return;
11623
11624 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11625 bool fMediaNeedsLocking = false;
11626
11627 /* enumerate new attachments */
11628 for (MediumAttachmentList::const_iterator
11629 it = mMediumAttachments->begin();
11630 it != mMediumAttachments->end();
11631 ++it)
11632 {
11633 MediumAttachment *pAttach = *it;
11634
11635 pAttach->i_commit();
11636
11637 Medium *pMedium = pAttach->i_getMedium();
11638 bool fImplicit = pAttach->i_isImplicit();
11639
11640 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11641 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11642 fImplicit));
11643
11644 /** @todo convert all this Machine-based voodoo to MediumAttachment
11645 * based commit logic. */
11646 if (fImplicit)
11647 {
11648 /* convert implicit attachment to normal */
11649 pAttach->i_setImplicit(false);
11650
11651 if ( aOnline
11652 && pMedium
11653 && pAttach->i_getType() == DeviceType_HardDisk
11654 )
11655 {
11656 /* update the appropriate lock list */
11657 MediumLockList *pMediumLockList;
11658 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11659 AssertComRC(rc);
11660 if (pMediumLockList)
11661 {
11662 /* unlock if there's a need to change the locking */
11663 if (!fMediaNeedsLocking)
11664 {
11665 rc = mData->mSession.mLockedMedia.Unlock();
11666 AssertComRC(rc);
11667 fMediaNeedsLocking = true;
11668 }
11669 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11670 AssertComRC(rc);
11671 rc = pMediumLockList->Append(pMedium, true);
11672 AssertComRC(rc);
11673 }
11674 }
11675
11676 continue;
11677 }
11678
11679 if (pMedium)
11680 {
11681 /* was this medium attached before? */
11682 for (MediumAttachmentList::iterator
11683 oldIt = oldAtts.begin();
11684 oldIt != oldAtts.end();
11685 ++oldIt)
11686 {
11687 MediumAttachment *pOldAttach = *oldIt;
11688 if (pOldAttach->i_getMedium() == pMedium)
11689 {
11690 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11691
11692 /* yes: remove from old to avoid de-association */
11693 oldAtts.erase(oldIt);
11694 break;
11695 }
11696 }
11697 }
11698 }
11699
11700 /* enumerate remaining old attachments and de-associate from the
11701 * current machine state */
11702 for (MediumAttachmentList::const_iterator
11703 it = oldAtts.begin();
11704 it != oldAtts.end();
11705 ++it)
11706 {
11707 MediumAttachment *pAttach = *it;
11708 Medium *pMedium = pAttach->i_getMedium();
11709
11710 /* Detach only hard disks, since DVD/floppy media is detached
11711 * instantly in MountMedium. */
11712 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11713 {
11714 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11715
11716 /* now de-associate from the current machine state */
11717 rc = pMedium->i_removeBackReference(mData->mUuid);
11718 AssertComRC(rc);
11719
11720 if (aOnline)
11721 {
11722 /* unlock since medium is not used anymore */
11723 MediumLockList *pMediumLockList;
11724 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11725 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11726 {
11727 /* this happens for online snapshots, there the attachment
11728 * is changing, but only to a diff image created under
11729 * the old one, so there is no separate lock list */
11730 Assert(!pMediumLockList);
11731 }
11732 else
11733 {
11734 AssertComRC(rc);
11735 if (pMediumLockList)
11736 {
11737 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11738 AssertComRC(rc);
11739 }
11740 }
11741 }
11742 }
11743 }
11744
11745 /* take media locks again so that the locking state is consistent */
11746 if (fMediaNeedsLocking)
11747 {
11748 Assert(aOnline);
11749 rc = mData->mSession.mLockedMedia.Lock();
11750 AssertComRC(rc);
11751 }
11752
11753 /* commit the hard disk changes */
11754 mMediumAttachments.commit();
11755
11756 if (i_isSessionMachine())
11757 {
11758 /*
11759 * Update the parent machine to point to the new owner.
11760 * This is necessary because the stored parent will point to the
11761 * session machine otherwise and cause crashes or errors later
11762 * when the session machine gets invalid.
11763 */
11764 /** @todo Change the MediumAttachment class to behave like any other
11765 * class in this regard by creating peer MediumAttachment
11766 * objects for session machines and share the data with the peer
11767 * machine.
11768 */
11769 for (MediumAttachmentList::const_iterator
11770 it = mMediumAttachments->begin();
11771 it != mMediumAttachments->end();
11772 ++it)
11773 (*it)->i_updateParentMachine(mPeer);
11774
11775 /* attach new data to the primary machine and reshare it */
11776 mPeer->mMediumAttachments.attach(mMediumAttachments);
11777 }
11778
11779 return;
11780}
11781
11782/**
11783 * Perform deferred deletion of implicitly created diffs.
11784 *
11785 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11786 * changed (not backed up).
11787 *
11788 * @note Locks this object for writing!
11789 */
11790void Machine::i_rollbackMedia()
11791{
11792 AutoCaller autoCaller(this);
11793 AssertComRCReturnVoid(autoCaller.rc());
11794
11795 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11796 LogFlowThisFunc(("Entering rollbackMedia\n"));
11797
11798 HRESULT rc = S_OK;
11799
11800 /* no attach/detach operations -- nothing to do */
11801 if (!mMediumAttachments.isBackedUp())
11802 return;
11803
11804 /* enumerate new attachments */
11805 for (MediumAttachmentList::const_iterator
11806 it = mMediumAttachments->begin();
11807 it != mMediumAttachments->end();
11808 ++it)
11809 {
11810 MediumAttachment *pAttach = *it;
11811 /* Fix up the backrefs for DVD/floppy media. */
11812 if (pAttach->i_getType() != DeviceType_HardDisk)
11813 {
11814 Medium *pMedium = pAttach->i_getMedium();
11815 if (pMedium)
11816 {
11817 rc = pMedium->i_removeBackReference(mData->mUuid);
11818 AssertComRC(rc);
11819 }
11820 }
11821
11822 (*it)->i_rollback();
11823
11824 pAttach = *it;
11825 /* Fix up the backrefs for DVD/floppy media. */
11826 if (pAttach->i_getType() != DeviceType_HardDisk)
11827 {
11828 Medium *pMedium = pAttach->i_getMedium();
11829 if (pMedium)
11830 {
11831 rc = pMedium->i_addBackReference(mData->mUuid);
11832 AssertComRC(rc);
11833 }
11834 }
11835 }
11836
11837 /** @todo convert all this Machine-based voodoo to MediumAttachment
11838 * based rollback logic. */
11839 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11840
11841 return;
11842}
11843
11844/**
11845 * Returns true if the settings file is located in the directory named exactly
11846 * as the machine; this means, among other things, that the machine directory
11847 * should be auto-renamed.
11848 *
11849 * @param aSettingsDir if not NULL, the full machine settings file directory
11850 * name will be assigned there.
11851 *
11852 * @note Doesn't lock anything.
11853 * @note Not thread safe (must be called from this object's lock).
11854 */
11855bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11856{
11857 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11858 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11859 if (aSettingsDir)
11860 *aSettingsDir = strMachineDirName;
11861 strMachineDirName.stripPath(); // vmname
11862 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11863 strConfigFileOnly.stripPath() // vmname.vbox
11864 .stripSuffix(); // vmname
11865 /** @todo hack, make somehow use of ComposeMachineFilename */
11866 if (mUserData->s.fDirectoryIncludesUUID)
11867 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11868
11869 AssertReturn(!strMachineDirName.isEmpty(), false);
11870 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11871
11872 return strMachineDirName == strConfigFileOnly;
11873}
11874
11875/**
11876 * Discards all changes to machine settings.
11877 *
11878 * @param aNotify Whether to notify the direct session about changes or not.
11879 *
11880 * @note Locks objects for writing!
11881 */
11882void Machine::i_rollback(bool aNotify)
11883{
11884 AutoCaller autoCaller(this);
11885 AssertComRCReturn(autoCaller.rc(), (void)0);
11886
11887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11888
11889 if (!mStorageControllers.isNull())
11890 {
11891 if (mStorageControllers.isBackedUp())
11892 {
11893 /* unitialize all new devices (absent in the backed up list). */
11894 StorageControllerList *backedList = mStorageControllers.backedUpData();
11895 for (StorageControllerList::const_iterator
11896 it = mStorageControllers->begin();
11897 it != mStorageControllers->end();
11898 ++it)
11899 {
11900 if ( std::find(backedList->begin(), backedList->end(), *it)
11901 == backedList->end()
11902 )
11903 {
11904 (*it)->uninit();
11905 }
11906 }
11907
11908 /* restore the list */
11909 mStorageControllers.rollback();
11910 }
11911
11912 /* rollback any changes to devices after restoring the list */
11913 if (mData->flModifications & IsModified_Storage)
11914 {
11915 for (StorageControllerList::const_iterator
11916 it = mStorageControllers->begin();
11917 it != mStorageControllers->end();
11918 ++it)
11919 {
11920 (*it)->i_rollback();
11921 }
11922 }
11923 }
11924
11925 if (!mUSBControllers.isNull())
11926 {
11927 if (mUSBControllers.isBackedUp())
11928 {
11929 /* unitialize all new devices (absent in the backed up list). */
11930 USBControllerList *backedList = mUSBControllers.backedUpData();
11931 for (USBControllerList::const_iterator
11932 it = mUSBControllers->begin();
11933 it != mUSBControllers->end();
11934 ++it)
11935 {
11936 if ( std::find(backedList->begin(), backedList->end(), *it)
11937 == backedList->end()
11938 )
11939 {
11940 (*it)->uninit();
11941 }
11942 }
11943
11944 /* restore the list */
11945 mUSBControllers.rollback();
11946 }
11947
11948 /* rollback any changes to devices after restoring the list */
11949 if (mData->flModifications & IsModified_USB)
11950 {
11951 for (USBControllerList::const_iterator
11952 it = mUSBControllers->begin();
11953 it != mUSBControllers->end();
11954 ++it)
11955 {
11956 (*it)->i_rollback();
11957 }
11958 }
11959 }
11960
11961 mUserData.rollback();
11962
11963 mHWData.rollback();
11964
11965 if (mData->flModifications & IsModified_Storage)
11966 i_rollbackMedia();
11967
11968 if (mBIOSSettings)
11969 mBIOSSettings->i_rollback();
11970
11971 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11972 mVRDEServer->i_rollback();
11973
11974 if (mAudioAdapter)
11975 mAudioAdapter->i_rollback();
11976
11977 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11978 mUSBDeviceFilters->i_rollback();
11979
11980 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11981 mBandwidthControl->i_rollback();
11982
11983 if (!mHWData.isNull())
11984 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11985 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11986 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11987 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11988
11989 if (mData->flModifications & IsModified_NetworkAdapters)
11990 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11991 if ( mNetworkAdapters[slot]
11992 && mNetworkAdapters[slot]->i_isModified())
11993 {
11994 mNetworkAdapters[slot]->i_rollback();
11995 networkAdapters[slot] = mNetworkAdapters[slot];
11996 }
11997
11998 if (mData->flModifications & IsModified_SerialPorts)
11999 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12000 if ( mSerialPorts[slot]
12001 && mSerialPorts[slot]->i_isModified())
12002 {
12003 mSerialPorts[slot]->i_rollback();
12004 serialPorts[slot] = mSerialPorts[slot];
12005 }
12006
12007 if (mData->flModifications & IsModified_ParallelPorts)
12008 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12009 if ( mParallelPorts[slot]
12010 && mParallelPorts[slot]->i_isModified())
12011 {
12012 mParallelPorts[slot]->i_rollback();
12013 parallelPorts[slot] = mParallelPorts[slot];
12014 }
12015
12016 if (aNotify)
12017 {
12018 /* inform the direct session about changes */
12019
12020 ComObjPtr<Machine> that = this;
12021 uint32_t flModifications = mData->flModifications;
12022 alock.release();
12023
12024 if (flModifications & IsModified_SharedFolders)
12025 that->i_onSharedFolderChange();
12026
12027 if (flModifications & IsModified_VRDEServer)
12028 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12029 if (flModifications & IsModified_USB)
12030 that->i_onUSBControllerChange();
12031
12032 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12033 if (networkAdapters[slot])
12034 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12035 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12036 if (serialPorts[slot])
12037 that->i_onSerialPortChange(serialPorts[slot]);
12038 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12039 if (parallelPorts[slot])
12040 that->i_onParallelPortChange(parallelPorts[slot]);
12041
12042 if (flModifications & IsModified_Storage)
12043 that->i_onStorageControllerChange();
12044
12045#if 0
12046 if (flModifications & IsModified_BandwidthControl)
12047 that->onBandwidthControlChange();
12048#endif
12049 }
12050}
12051
12052/**
12053 * Commits all the changes to machine settings.
12054 *
12055 * Note that this operation is supposed to never fail.
12056 *
12057 * @note Locks this object and children for writing.
12058 */
12059void Machine::i_commit()
12060{
12061 AutoCaller autoCaller(this);
12062 AssertComRCReturnVoid(autoCaller.rc());
12063
12064 AutoCaller peerCaller(mPeer);
12065 AssertComRCReturnVoid(peerCaller.rc());
12066
12067 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12068
12069 /*
12070 * use safe commit to ensure Snapshot machines (that share mUserData)
12071 * will still refer to a valid memory location
12072 */
12073 mUserData.commitCopy();
12074
12075 mHWData.commit();
12076
12077 if (mMediumAttachments.isBackedUp())
12078 i_commitMedia(Global::IsOnline(mData->mMachineState));
12079
12080 mBIOSSettings->i_commit();
12081 mVRDEServer->i_commit();
12082 mAudioAdapter->i_commit();
12083 mUSBDeviceFilters->i_commit();
12084 mBandwidthControl->i_commit();
12085
12086 /* Since mNetworkAdapters is a list which might have been changed (resized)
12087 * without using the Backupable<> template we need to handle the copying
12088 * of the list entries manually, including the creation of peers for the
12089 * new objects. */
12090 bool commitNetworkAdapters = false;
12091 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12092 if (mPeer)
12093 {
12094 /* commit everything, even the ones which will go away */
12095 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12096 mNetworkAdapters[slot]->i_commit();
12097 /* copy over the new entries, creating a peer and uninit the original */
12098 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12099 for (size_t slot = 0; slot < newSize; slot++)
12100 {
12101 /* look if this adapter has a peer device */
12102 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12103 if (!peer)
12104 {
12105 /* no peer means the adapter is a newly created one;
12106 * create a peer owning data this data share it with */
12107 peer.createObject();
12108 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12109 }
12110 mPeer->mNetworkAdapters[slot] = peer;
12111 }
12112 /* uninit any no longer needed network adapters */
12113 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12114 mNetworkAdapters[slot]->uninit();
12115 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12116 {
12117 if (mPeer->mNetworkAdapters[slot])
12118 mPeer->mNetworkAdapters[slot]->uninit();
12119 }
12120 /* Keep the original network adapter count until this point, so that
12121 * discarding a chipset type change will not lose settings. */
12122 mNetworkAdapters.resize(newSize);
12123 mPeer->mNetworkAdapters.resize(newSize);
12124 }
12125 else
12126 {
12127 /* we have no peer (our parent is the newly created machine);
12128 * just commit changes to the network adapters */
12129 commitNetworkAdapters = true;
12130 }
12131 if (commitNetworkAdapters)
12132 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12133 mNetworkAdapters[slot]->i_commit();
12134
12135 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12136 mSerialPorts[slot]->i_commit();
12137 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12138 mParallelPorts[slot]->i_commit();
12139
12140 bool commitStorageControllers = false;
12141
12142 if (mStorageControllers.isBackedUp())
12143 {
12144 mStorageControllers.commit();
12145
12146 if (mPeer)
12147 {
12148 /* Commit all changes to new controllers (this will reshare data with
12149 * peers for those who have peers) */
12150 StorageControllerList *newList = new StorageControllerList();
12151 for (StorageControllerList::const_iterator
12152 it = mStorageControllers->begin();
12153 it != mStorageControllers->end();
12154 ++it)
12155 {
12156 (*it)->i_commit();
12157
12158 /* look if this controller has a peer device */
12159 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12160 if (!peer)
12161 {
12162 /* no peer means the device is a newly created one;
12163 * create a peer owning data this device share it with */
12164 peer.createObject();
12165 peer->init(mPeer, *it, true /* aReshare */);
12166 }
12167 else
12168 {
12169 /* remove peer from the old list */
12170 mPeer->mStorageControllers->remove(peer);
12171 }
12172 /* and add it to the new list */
12173 newList->push_back(peer);
12174 }
12175
12176 /* uninit old peer's controllers that are left */
12177 for (StorageControllerList::const_iterator
12178 it = mPeer->mStorageControllers->begin();
12179 it != mPeer->mStorageControllers->end();
12180 ++it)
12181 {
12182 (*it)->uninit();
12183 }
12184
12185 /* attach new list of controllers to our peer */
12186 mPeer->mStorageControllers.attach(newList);
12187 }
12188 else
12189 {
12190 /* we have no peer (our parent is the newly created machine);
12191 * just commit changes to devices */
12192 commitStorageControllers = true;
12193 }
12194 }
12195 else
12196 {
12197 /* the list of controllers itself is not changed,
12198 * just commit changes to controllers themselves */
12199 commitStorageControllers = true;
12200 }
12201
12202 if (commitStorageControllers)
12203 {
12204 for (StorageControllerList::const_iterator
12205 it = mStorageControllers->begin();
12206 it != mStorageControllers->end();
12207 ++it)
12208 {
12209 (*it)->i_commit();
12210 }
12211 }
12212
12213 bool commitUSBControllers = false;
12214
12215 if (mUSBControllers.isBackedUp())
12216 {
12217 mUSBControllers.commit();
12218
12219 if (mPeer)
12220 {
12221 /* Commit all changes to new controllers (this will reshare data with
12222 * peers for those who have peers) */
12223 USBControllerList *newList = new USBControllerList();
12224 for (USBControllerList::const_iterator
12225 it = mUSBControllers->begin();
12226 it != mUSBControllers->end();
12227 ++it)
12228 {
12229 (*it)->i_commit();
12230
12231 /* look if this controller has a peer device */
12232 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12233 if (!peer)
12234 {
12235 /* no peer means the device is a newly created one;
12236 * create a peer owning data this device share it with */
12237 peer.createObject();
12238 peer->init(mPeer, *it, true /* aReshare */);
12239 }
12240 else
12241 {
12242 /* remove peer from the old list */
12243 mPeer->mUSBControllers->remove(peer);
12244 }
12245 /* and add it to the new list */
12246 newList->push_back(peer);
12247 }
12248
12249 /* uninit old peer's controllers that are left */
12250 for (USBControllerList::const_iterator
12251 it = mPeer->mUSBControllers->begin();
12252 it != mPeer->mUSBControllers->end();
12253 ++it)
12254 {
12255 (*it)->uninit();
12256 }
12257
12258 /* attach new list of controllers to our peer */
12259 mPeer->mUSBControllers.attach(newList);
12260 }
12261 else
12262 {
12263 /* we have no peer (our parent is the newly created machine);
12264 * just commit changes to devices */
12265 commitUSBControllers = true;
12266 }
12267 }
12268 else
12269 {
12270 /* the list of controllers itself is not changed,
12271 * just commit changes to controllers themselves */
12272 commitUSBControllers = true;
12273 }
12274
12275 if (commitUSBControllers)
12276 {
12277 for (USBControllerList::const_iterator
12278 it = mUSBControllers->begin();
12279 it != mUSBControllers->end();
12280 ++it)
12281 {
12282 (*it)->i_commit();
12283 }
12284 }
12285
12286 if (i_isSessionMachine())
12287 {
12288 /* attach new data to the primary machine and reshare it */
12289 mPeer->mUserData.attach(mUserData);
12290 mPeer->mHWData.attach(mHWData);
12291 /* mmMediumAttachments is reshared by fixupMedia */
12292 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12293 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12294 }
12295}
12296
12297/**
12298 * Copies all the hardware data from the given machine.
12299 *
12300 * Currently, only called when the VM is being restored from a snapshot. In
12301 * particular, this implies that the VM is not running during this method's
12302 * call.
12303 *
12304 * @note This method must be called from under this object's lock.
12305 *
12306 * @note This method doesn't call #i_commit(), so all data remains backed up and
12307 * unsaved.
12308 */
12309void Machine::i_copyFrom(Machine *aThat)
12310{
12311 AssertReturnVoid(!i_isSnapshotMachine());
12312 AssertReturnVoid(aThat->i_isSnapshotMachine());
12313
12314 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12315
12316 mHWData.assignCopy(aThat->mHWData);
12317
12318 // create copies of all shared folders (mHWData after attaching a copy
12319 // contains just references to original objects)
12320 for (HWData::SharedFolderList::iterator
12321 it = mHWData->mSharedFolders.begin();
12322 it != mHWData->mSharedFolders.end();
12323 ++it)
12324 {
12325 ComObjPtr<SharedFolder> folder;
12326 folder.createObject();
12327 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12328 AssertComRC(rc);
12329 *it = folder;
12330 }
12331
12332 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12333 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12334 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12335 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12336 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12337
12338 /* create private copies of all controllers */
12339 mStorageControllers.backup();
12340 mStorageControllers->clear();
12341 for (StorageControllerList::const_iterator
12342 it = aThat->mStorageControllers->begin();
12343 it != aThat->mStorageControllers->end();
12344 ++it)
12345 {
12346 ComObjPtr<StorageController> ctrl;
12347 ctrl.createObject();
12348 ctrl->initCopy(this, *it);
12349 mStorageControllers->push_back(ctrl);
12350 }
12351
12352 /* create private copies of all USB controllers */
12353 mUSBControllers.backup();
12354 mUSBControllers->clear();
12355 for (USBControllerList::const_iterator
12356 it = aThat->mUSBControllers->begin();
12357 it != aThat->mUSBControllers->end();
12358 ++it)
12359 {
12360 ComObjPtr<USBController> ctrl;
12361 ctrl.createObject();
12362 ctrl->initCopy(this, *it);
12363 mUSBControllers->push_back(ctrl);
12364 }
12365
12366 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12367 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12368 {
12369 if (mNetworkAdapters[slot].isNotNull())
12370 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12371 else
12372 {
12373 unconst(mNetworkAdapters[slot]).createObject();
12374 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12375 }
12376 }
12377 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12378 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12379 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12380 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12381}
12382
12383/**
12384 * Returns whether the given storage controller is hotplug capable.
12385 *
12386 * @returns true if the controller supports hotplugging
12387 * false otherwise.
12388 * @param enmCtrlType The controller type to check for.
12389 */
12390bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12391{
12392 ComPtr<ISystemProperties> systemProperties;
12393 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12394 if (FAILED(rc))
12395 return false;
12396
12397 BOOL aHotplugCapable = FALSE;
12398 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12399
12400 return RT_BOOL(aHotplugCapable);
12401}
12402
12403#ifdef VBOX_WITH_RESOURCE_USAGE_API
12404
12405void Machine::i_getDiskList(MediaList &list)
12406{
12407 for (MediumAttachmentList::const_iterator
12408 it = mMediumAttachments->begin();
12409 it != mMediumAttachments->end();
12410 ++it)
12411 {
12412 MediumAttachment *pAttach = *it;
12413 /* just in case */
12414 AssertContinue(pAttach);
12415
12416 AutoCaller localAutoCallerA(pAttach);
12417 if (FAILED(localAutoCallerA.rc())) continue;
12418
12419 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12420
12421 if (pAttach->i_getType() == DeviceType_HardDisk)
12422 list.push_back(pAttach->i_getMedium());
12423 }
12424}
12425
12426void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12427{
12428 AssertReturnVoid(isWriteLockOnCurrentThread());
12429 AssertPtrReturnVoid(aCollector);
12430
12431 pm::CollectorHAL *hal = aCollector->getHAL();
12432 /* Create sub metrics */
12433 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12434 "Percentage of processor time spent in user mode by the VM process.");
12435 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12436 "Percentage of processor time spent in kernel mode by the VM process.");
12437 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12438 "Size of resident portion of VM process in memory.");
12439 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12440 "Actual size of all VM disks combined.");
12441 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12442 "Network receive rate.");
12443 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12444 "Network transmit rate.");
12445 /* Create and register base metrics */
12446 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12447 cpuLoadUser, cpuLoadKernel);
12448 aCollector->registerBaseMetric(cpuLoad);
12449 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12450 ramUsageUsed);
12451 aCollector->registerBaseMetric(ramUsage);
12452 MediaList disks;
12453 i_getDiskList(disks);
12454 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12455 diskUsageUsed);
12456 aCollector->registerBaseMetric(diskUsage);
12457
12458 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12459 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12460 new pm::AggregateAvg()));
12461 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12462 new pm::AggregateMin()));
12463 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12464 new pm::AggregateMax()));
12465 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12466 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12467 new pm::AggregateAvg()));
12468 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12469 new pm::AggregateMin()));
12470 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12471 new pm::AggregateMax()));
12472
12473 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12474 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12475 new pm::AggregateAvg()));
12476 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12477 new pm::AggregateMin()));
12478 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12479 new pm::AggregateMax()));
12480
12481 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12482 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12483 new pm::AggregateAvg()));
12484 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12485 new pm::AggregateMin()));
12486 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12487 new pm::AggregateMax()));
12488
12489
12490 /* Guest metrics collector */
12491 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12492 aCollector->registerGuest(mCollectorGuest);
12493 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12494
12495 /* Create sub metrics */
12496 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12497 "Percentage of processor time spent in user mode as seen by the guest.");
12498 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12499 "Percentage of processor time spent in kernel mode as seen by the guest.");
12500 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12501 "Percentage of processor time spent idling as seen by the guest.");
12502
12503 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12504 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12505 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12506 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12507 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12508 pm::SubMetric *guestMemCache = new pm::SubMetric(
12509 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12510
12511 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12512 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12513
12514 /* Create and register base metrics */
12515 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12516 machineNetRx, machineNetTx);
12517 aCollector->registerBaseMetric(machineNetRate);
12518
12519 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12520 guestLoadUser, guestLoadKernel, guestLoadIdle);
12521 aCollector->registerBaseMetric(guestCpuLoad);
12522
12523 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12524 guestMemTotal, guestMemFree,
12525 guestMemBalloon, guestMemShared,
12526 guestMemCache, guestPagedTotal);
12527 aCollector->registerBaseMetric(guestCpuMem);
12528
12529 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12530 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12531 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12532 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12533
12534 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12535 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12536 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12537 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12538
12539 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12540 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12541 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12542 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12543
12544 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12545 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12546 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12547 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12548
12549 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12550 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12551 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12552 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12553
12554 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12555 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12556 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12557 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12558
12559 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12560 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12561 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12562 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12563
12564 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12565 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12566 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12567 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12568
12569 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12570 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12571 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12572 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12573
12574 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12575 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12576 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12577 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12578
12579 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12580 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12583}
12584
12585void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12586{
12587 AssertReturnVoid(isWriteLockOnCurrentThread());
12588
12589 if (aCollector)
12590 {
12591 aCollector->unregisterMetricsFor(aMachine);
12592 aCollector->unregisterBaseMetricsFor(aMachine);
12593 }
12594}
12595
12596#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12597
12598
12599////////////////////////////////////////////////////////////////////////////////
12600
12601DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12602
12603HRESULT SessionMachine::FinalConstruct()
12604{
12605 LogFlowThisFunc(("\n"));
12606
12607 mClientToken = NULL;
12608
12609 return BaseFinalConstruct();
12610}
12611
12612void SessionMachine::FinalRelease()
12613{
12614 LogFlowThisFunc(("\n"));
12615
12616 Assert(!mClientToken);
12617 /* paranoia, should not hang around any more */
12618 if (mClientToken)
12619 {
12620 delete mClientToken;
12621 mClientToken = NULL;
12622 }
12623
12624 uninit(Uninit::Unexpected);
12625
12626 BaseFinalRelease();
12627}
12628
12629/**
12630 * @note Must be called only by Machine::LockMachine() from its own write lock.
12631 */
12632HRESULT SessionMachine::init(Machine *aMachine)
12633{
12634 LogFlowThisFuncEnter();
12635 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12636
12637 AssertReturn(aMachine, E_INVALIDARG);
12638
12639 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12640
12641 /* Enclose the state transition NotReady->InInit->Ready */
12642 AutoInitSpan autoInitSpan(this);
12643 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12644
12645 HRESULT rc = S_OK;
12646
12647 RT_ZERO(mAuthLibCtx);
12648
12649 /* create the machine client token */
12650 try
12651 {
12652 mClientToken = new ClientToken(aMachine, this);
12653 if (!mClientToken->isReady())
12654 {
12655 delete mClientToken;
12656 mClientToken = NULL;
12657 rc = E_FAIL;
12658 }
12659 }
12660 catch (std::bad_alloc &)
12661 {
12662 rc = E_OUTOFMEMORY;
12663 }
12664 if (FAILED(rc))
12665 return rc;
12666
12667 /* memorize the peer Machine */
12668 unconst(mPeer) = aMachine;
12669 /* share the parent pointer */
12670 unconst(mParent) = aMachine->mParent;
12671
12672 /* take the pointers to data to share */
12673 mData.share(aMachine->mData);
12674 mSSData.share(aMachine->mSSData);
12675
12676 mUserData.share(aMachine->mUserData);
12677 mHWData.share(aMachine->mHWData);
12678 mMediumAttachments.share(aMachine->mMediumAttachments);
12679
12680 mStorageControllers.allocate();
12681 for (StorageControllerList::const_iterator
12682 it = aMachine->mStorageControllers->begin();
12683 it != aMachine->mStorageControllers->end();
12684 ++it)
12685 {
12686 ComObjPtr<StorageController> ctl;
12687 ctl.createObject();
12688 ctl->init(this, *it);
12689 mStorageControllers->push_back(ctl);
12690 }
12691
12692 mUSBControllers.allocate();
12693 for (USBControllerList::const_iterator
12694 it = aMachine->mUSBControllers->begin();
12695 it != aMachine->mUSBControllers->end();
12696 ++it)
12697 {
12698 ComObjPtr<USBController> ctl;
12699 ctl.createObject();
12700 ctl->init(this, *it);
12701 mUSBControllers->push_back(ctl);
12702 }
12703
12704 unconst(mBIOSSettings).createObject();
12705 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12706 /* create another VRDEServer object that will be mutable */
12707 unconst(mVRDEServer).createObject();
12708 mVRDEServer->init(this, aMachine->mVRDEServer);
12709 /* create another audio adapter object that will be mutable */
12710 unconst(mAudioAdapter).createObject();
12711 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12712 /* create a list of serial ports that will be mutable */
12713 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12714 {
12715 unconst(mSerialPorts[slot]).createObject();
12716 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12717 }
12718 /* create a list of parallel ports that will be mutable */
12719 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12720 {
12721 unconst(mParallelPorts[slot]).createObject();
12722 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12723 }
12724
12725 /* create another USB device filters object that will be mutable */
12726 unconst(mUSBDeviceFilters).createObject();
12727 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12728
12729 /* create a list of network adapters that will be mutable */
12730 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12731 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12732 {
12733 unconst(mNetworkAdapters[slot]).createObject();
12734 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12735 }
12736
12737 /* create another bandwidth control object that will be mutable */
12738 unconst(mBandwidthControl).createObject();
12739 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12740
12741 /* default is to delete saved state on Saved -> PoweredOff transition */
12742 mRemoveSavedState = true;
12743
12744 /* Confirm a successful initialization when it's the case */
12745 autoInitSpan.setSucceeded();
12746
12747 miNATNetworksStarted = 0;
12748
12749 LogFlowThisFuncLeave();
12750 return rc;
12751}
12752
12753/**
12754 * Uninitializes this session object. If the reason is other than
12755 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12756 * or the client watcher code.
12757 *
12758 * @param aReason uninitialization reason
12759 *
12760 * @note Locks mParent + this object for writing.
12761 */
12762void SessionMachine::uninit(Uninit::Reason aReason)
12763{
12764 LogFlowThisFuncEnter();
12765 LogFlowThisFunc(("reason=%d\n", aReason));
12766
12767 /*
12768 * Strongly reference ourselves to prevent this object deletion after
12769 * mData->mSession.mMachine.setNull() below (which can release the last
12770 * reference and call the destructor). Important: this must be done before
12771 * accessing any members (and before AutoUninitSpan that does it as well).
12772 * This self reference will be released as the very last step on return.
12773 */
12774 ComObjPtr<SessionMachine> selfRef;
12775 if (aReason != Uninit::Unexpected)
12776 selfRef = this;
12777
12778 /* Enclose the state transition Ready->InUninit->NotReady */
12779 AutoUninitSpan autoUninitSpan(this);
12780 if (autoUninitSpan.uninitDone())
12781 {
12782 LogFlowThisFunc(("Already uninitialized\n"));
12783 LogFlowThisFuncLeave();
12784 return;
12785 }
12786
12787 if (autoUninitSpan.initFailed())
12788 {
12789 /* We've been called by init() because it's failed. It's not really
12790 * necessary (nor it's safe) to perform the regular uninit sequence
12791 * below, the following is enough.
12792 */
12793 LogFlowThisFunc(("Initialization failed.\n"));
12794 /* destroy the machine client token */
12795 if (mClientToken)
12796 {
12797 delete mClientToken;
12798 mClientToken = NULL;
12799 }
12800 uninitDataAndChildObjects();
12801 mData.free();
12802 unconst(mParent) = NULL;
12803 unconst(mPeer) = NULL;
12804 LogFlowThisFuncLeave();
12805 return;
12806 }
12807
12808 MachineState_T lastState;
12809 {
12810 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12811 lastState = mData->mMachineState;
12812 }
12813 NOREF(lastState);
12814
12815#ifdef VBOX_WITH_USB
12816 // release all captured USB devices, but do this before requesting the locks below
12817 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12818 {
12819 /* Console::captureUSBDevices() is called in the VM process only after
12820 * setting the machine state to Starting or Restoring.
12821 * Console::detachAllUSBDevices() will be called upon successful
12822 * termination. So, we need to release USB devices only if there was
12823 * an abnormal termination of a running VM.
12824 *
12825 * This is identical to SessionMachine::DetachAllUSBDevices except
12826 * for the aAbnormal argument. */
12827 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12828 AssertComRC(rc);
12829 NOREF(rc);
12830
12831 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12832 if (service)
12833 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12834 }
12835#endif /* VBOX_WITH_USB */
12836
12837 // we need to lock this object in uninit() because the lock is shared
12838 // with mPeer (as well as data we modify below). mParent lock is needed
12839 // by several calls to it.
12840 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12841
12842#ifdef VBOX_WITH_RESOURCE_USAGE_API
12843 /*
12844 * It is safe to call Machine::i_unregisterMetrics() here because
12845 * PerformanceCollector::samplerCallback no longer accesses guest methods
12846 * holding the lock.
12847 */
12848 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12849 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12850 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12851 if (mCollectorGuest)
12852 {
12853 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12854 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12855 mCollectorGuest = NULL;
12856 }
12857#endif
12858
12859 if (aReason == Uninit::Abnormal)
12860 {
12861 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12862
12863 /* reset the state to Aborted */
12864 if (mData->mMachineState != MachineState_Aborted)
12865 i_setMachineState(MachineState_Aborted);
12866 }
12867
12868 // any machine settings modified?
12869 if (mData->flModifications)
12870 {
12871 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12872 i_rollback(false /* aNotify */);
12873 }
12874
12875 mData->mSession.mPID = NIL_RTPROCESS;
12876
12877 if (aReason == Uninit::Unexpected)
12878 {
12879 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12880 * client watcher thread to update the set of machines that have open
12881 * sessions. */
12882 mParent->i_updateClientWatcher();
12883 }
12884
12885 /* uninitialize all remote controls */
12886 if (mData->mSession.mRemoteControls.size())
12887 {
12888 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12889 mData->mSession.mRemoteControls.size()));
12890
12891 /* Always restart a the beginning, since the iterator is invalidated
12892 * by using erase(). */
12893 for (Data::Session::RemoteControlList::iterator
12894 it = mData->mSession.mRemoteControls.begin();
12895 it != mData->mSession.mRemoteControls.end();
12896 it = mData->mSession.mRemoteControls.begin())
12897 {
12898 ComPtr<IInternalSessionControl> pControl = *it;
12899 mData->mSession.mRemoteControls.erase(it);
12900 multilock.release();
12901 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12902 HRESULT rc = pControl->Uninitialize();
12903 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12904 if (FAILED(rc))
12905 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12906 multilock.acquire();
12907 }
12908 mData->mSession.mRemoteControls.clear();
12909 }
12910
12911 /* Remove all references to the NAT network service. The service will stop
12912 * if all references (also from other VMs) are removed. */
12913 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12914 {
12915 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12916 {
12917 BOOL enabled;
12918 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12919 if ( FAILED(hrc)
12920 || !enabled)
12921 continue;
12922
12923 NetworkAttachmentType_T type;
12924 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12925 if ( SUCCEEDED(hrc)
12926 && type == NetworkAttachmentType_NATNetwork)
12927 {
12928 Bstr name;
12929 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12930 if (SUCCEEDED(hrc))
12931 {
12932 multilock.release();
12933 Utf8Str strName(name);
12934 LogRel(("VM '%s' stops using NAT network '%s'\n",
12935 mUserData->s.strName.c_str(), strName.c_str()));
12936 mParent->i_natNetworkRefDec(strName);
12937 multilock.acquire();
12938 }
12939 }
12940 }
12941 }
12942
12943 /*
12944 * An expected uninitialization can come only from #i_checkForDeath().
12945 * Otherwise it means that something's gone really wrong (for example,
12946 * the Session implementation has released the VirtualBox reference
12947 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12948 * etc). However, it's also possible, that the client releases the IPC
12949 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12950 * but the VirtualBox release event comes first to the server process.
12951 * This case is practically possible, so we should not assert on an
12952 * unexpected uninit, just log a warning.
12953 */
12954
12955 if (aReason == Uninit::Unexpected)
12956 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12957
12958 if (aReason != Uninit::Normal)
12959 {
12960 mData->mSession.mDirectControl.setNull();
12961 }
12962 else
12963 {
12964 /* this must be null here (see #OnSessionEnd()) */
12965 Assert(mData->mSession.mDirectControl.isNull());
12966 Assert(mData->mSession.mState == SessionState_Unlocking);
12967 Assert(!mData->mSession.mProgress.isNull());
12968 }
12969 if (mData->mSession.mProgress)
12970 {
12971 if (aReason == Uninit::Normal)
12972 mData->mSession.mProgress->i_notifyComplete(S_OK);
12973 else
12974 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12975 COM_IIDOF(ISession),
12976 getComponentName(),
12977 tr("The VM session was aborted"));
12978 mData->mSession.mProgress.setNull();
12979 }
12980
12981 if (mConsoleTaskData.mProgress)
12982 {
12983 Assert(aReason == Uninit::Abnormal);
12984 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12985 COM_IIDOF(ISession),
12986 getComponentName(),
12987 tr("The VM session was aborted"));
12988 mConsoleTaskData.mProgress.setNull();
12989 }
12990
12991 /* remove the association between the peer machine and this session machine */
12992 Assert( (SessionMachine*)mData->mSession.mMachine == this
12993 || aReason == Uninit::Unexpected);
12994
12995 /* reset the rest of session data */
12996 mData->mSession.mLockType = LockType_Null;
12997 mData->mSession.mMachine.setNull();
12998 mData->mSession.mState = SessionState_Unlocked;
12999 mData->mSession.mName.setNull();
13000
13001 /* destroy the machine client token before leaving the exclusive lock */
13002 if (mClientToken)
13003 {
13004 delete mClientToken;
13005 mClientToken = NULL;
13006 }
13007
13008 /* fire an event */
13009 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13010
13011 uninitDataAndChildObjects();
13012
13013 /* free the essential data structure last */
13014 mData.free();
13015
13016 /* release the exclusive lock before setting the below two to NULL */
13017 multilock.release();
13018
13019 unconst(mParent) = NULL;
13020 unconst(mPeer) = NULL;
13021
13022 AuthLibUnload(&mAuthLibCtx);
13023
13024 LogFlowThisFuncLeave();
13025}
13026
13027// util::Lockable interface
13028////////////////////////////////////////////////////////////////////////////////
13029
13030/**
13031 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13032 * with the primary Machine instance (mPeer).
13033 */
13034RWLockHandle *SessionMachine::lockHandle() const
13035{
13036 AssertReturn(mPeer != NULL, NULL);
13037 return mPeer->lockHandle();
13038}
13039
13040// IInternalMachineControl methods
13041////////////////////////////////////////////////////////////////////////////////
13042
13043/**
13044 * Passes collected guest statistics to performance collector object
13045 */
13046HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13047 ULONG aCpuKernel, ULONG aCpuIdle,
13048 ULONG aMemTotal, ULONG aMemFree,
13049 ULONG aMemBalloon, ULONG aMemShared,
13050 ULONG aMemCache, ULONG aPageTotal,
13051 ULONG aAllocVMM, ULONG aFreeVMM,
13052 ULONG aBalloonedVMM, ULONG aSharedVMM,
13053 ULONG aVmNetRx, ULONG aVmNetTx)
13054{
13055#ifdef VBOX_WITH_RESOURCE_USAGE_API
13056 if (mCollectorGuest)
13057 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13058 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13059 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13060 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13061
13062 return S_OK;
13063#else
13064 NOREF(aValidStats);
13065 NOREF(aCpuUser);
13066 NOREF(aCpuKernel);
13067 NOREF(aCpuIdle);
13068 NOREF(aMemTotal);
13069 NOREF(aMemFree);
13070 NOREF(aMemBalloon);
13071 NOREF(aMemShared);
13072 NOREF(aMemCache);
13073 NOREF(aPageTotal);
13074 NOREF(aAllocVMM);
13075 NOREF(aFreeVMM);
13076 NOREF(aBalloonedVMM);
13077 NOREF(aSharedVMM);
13078 NOREF(aVmNetRx);
13079 NOREF(aVmNetTx);
13080 return E_NOTIMPL;
13081#endif
13082}
13083
13084////////////////////////////////////////////////////////////////////////////////
13085//
13086// SessionMachine task records
13087//
13088////////////////////////////////////////////////////////////////////////////////
13089
13090/**
13091 * Task record for saving the machine state.
13092 */
13093class SessionMachine::SaveStateTask
13094 : public Machine::Task
13095{
13096public:
13097 SaveStateTask(SessionMachine *m,
13098 Progress *p,
13099 const Utf8Str &t,
13100 Reason_T enmReason,
13101 const Utf8Str &strStateFilePath)
13102 : Task(m, p, t),
13103 m_enmReason(enmReason),
13104 m_strStateFilePath(strStateFilePath)
13105 {}
13106
13107private:
13108 void handler()
13109 {
13110 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13111 }
13112
13113 Reason_T m_enmReason;
13114 Utf8Str m_strStateFilePath;
13115
13116 friend class SessionMachine;
13117};
13118
13119/**
13120 * Task thread implementation for SessionMachine::SaveState(), called from
13121 * SessionMachine::taskHandler().
13122 *
13123 * @note Locks this object for writing.
13124 *
13125 * @param task
13126 * @return
13127 */
13128void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13129{
13130 LogFlowThisFuncEnter();
13131
13132 AutoCaller autoCaller(this);
13133 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13134 if (FAILED(autoCaller.rc()))
13135 {
13136 /* we might have been uninitialized because the session was accidentally
13137 * closed by the client, so don't assert */
13138 HRESULT rc = setError(E_FAIL,
13139 tr("The session has been accidentally closed"));
13140 task.m_pProgress->i_notifyComplete(rc);
13141 LogFlowThisFuncLeave();
13142 return;
13143 }
13144
13145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13146
13147 HRESULT rc = S_OK;
13148
13149 try
13150 {
13151 ComPtr<IInternalSessionControl> directControl;
13152 if (mData->mSession.mLockType == LockType_VM)
13153 directControl = mData->mSession.mDirectControl;
13154 if (directControl.isNull())
13155 throw setError(VBOX_E_INVALID_VM_STATE,
13156 tr("Trying to save state without a running VM"));
13157 alock.release();
13158 BOOL fSuspendedBySave;
13159 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13160 Assert(!fSuspendedBySave);
13161 alock.acquire();
13162
13163 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13164 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13165 throw E_FAIL);
13166
13167 if (SUCCEEDED(rc))
13168 {
13169 mSSData->strStateFilePath = task.m_strStateFilePath;
13170
13171 /* save all VM settings */
13172 rc = i_saveSettings(NULL);
13173 // no need to check whether VirtualBox.xml needs saving also since
13174 // we can't have a name change pending at this point
13175 }
13176 else
13177 {
13178 // On failure, set the state to the state we had at the beginning.
13179 i_setMachineState(task.m_machineStateBackup);
13180 i_updateMachineStateOnClient();
13181
13182 // Delete the saved state file (might have been already created).
13183 // No need to check whether this is shared with a snapshot here
13184 // because we certainly created a fresh saved state file here.
13185 RTFileDelete(task.m_strStateFilePath.c_str());
13186 }
13187 }
13188 catch (HRESULT aRC) { rc = aRC; }
13189
13190 task.m_pProgress->i_notifyComplete(rc);
13191
13192 LogFlowThisFuncLeave();
13193}
13194
13195/**
13196 * @note Locks this object for writing.
13197 */
13198HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13199{
13200 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13201}
13202
13203HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13204{
13205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13206
13207 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13208 if (FAILED(rc)) return rc;
13209
13210 if ( mData->mMachineState != MachineState_Running
13211 && mData->mMachineState != MachineState_Paused
13212 )
13213 return setError(VBOX_E_INVALID_VM_STATE,
13214 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13215 Global::stringifyMachineState(mData->mMachineState));
13216
13217 ComObjPtr<Progress> pProgress;
13218 pProgress.createObject();
13219 rc = pProgress->init(i_getVirtualBox(),
13220 static_cast<IMachine *>(this) /* aInitiator */,
13221 tr("Saving the execution state of the virtual machine"),
13222 FALSE /* aCancelable */);
13223 if (FAILED(rc))
13224 return rc;
13225
13226 Utf8Str strStateFilePath;
13227 i_composeSavedStateFilename(strStateFilePath);
13228
13229 /* create and start the task on a separate thread (note that it will not
13230 * start working until we release alock) */
13231 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13232 rc = pTask->createThread();
13233 if (FAILED(rc))
13234 return rc;
13235
13236 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13237 i_setMachineState(MachineState_Saving);
13238 i_updateMachineStateOnClient();
13239
13240 pProgress.queryInterfaceTo(aProgress.asOutParam());
13241
13242 return S_OK;
13243}
13244
13245/**
13246 * @note Locks this object for writing.
13247 */
13248HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13249{
13250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13251
13252 HRESULT rc = i_checkStateDependency(MutableStateDep);
13253 if (FAILED(rc)) return rc;
13254
13255 if ( mData->mMachineState != MachineState_PoweredOff
13256 && mData->mMachineState != MachineState_Teleported
13257 && mData->mMachineState != MachineState_Aborted
13258 )
13259 return setError(VBOX_E_INVALID_VM_STATE,
13260 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13261 Global::stringifyMachineState(mData->mMachineState));
13262
13263 com::Utf8Str stateFilePathFull;
13264 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13265 if (RT_FAILURE(vrc))
13266 return setError(VBOX_E_FILE_ERROR,
13267 tr("Invalid saved state file path '%s' (%Rrc)"),
13268 aSavedStateFile.c_str(),
13269 vrc);
13270
13271 mSSData->strStateFilePath = stateFilePathFull;
13272
13273 /* The below i_setMachineState() will detect the state transition and will
13274 * update the settings file */
13275
13276 return i_setMachineState(MachineState_Saved);
13277}
13278
13279/**
13280 * @note Locks this object for writing.
13281 */
13282HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13283{
13284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13285
13286 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13287 if (FAILED(rc)) return rc;
13288
13289 if (mData->mMachineState != MachineState_Saved)
13290 return setError(VBOX_E_INVALID_VM_STATE,
13291 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13292 Global::stringifyMachineState(mData->mMachineState));
13293
13294 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13295
13296 /*
13297 * Saved -> PoweredOff transition will be detected in the SessionMachine
13298 * and properly handled.
13299 */
13300 rc = i_setMachineState(MachineState_PoweredOff);
13301 return rc;
13302}
13303
13304
13305/**
13306 * @note Locks the same as #i_setMachineState() does.
13307 */
13308HRESULT SessionMachine::updateState(MachineState_T aState)
13309{
13310 return i_setMachineState(aState);
13311}
13312
13313/**
13314 * @note Locks this object for writing.
13315 */
13316HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13317{
13318 IProgress *pProgress(aProgress);
13319
13320 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13321
13322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13323
13324 if (mData->mSession.mState != SessionState_Locked)
13325 return VBOX_E_INVALID_OBJECT_STATE;
13326
13327 if (!mData->mSession.mProgress.isNull())
13328 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13329
13330 /* If we didn't reference the NAT network service yet, add a reference to
13331 * force a start */
13332 if (miNATNetworksStarted < 1)
13333 {
13334 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13335 {
13336 BOOL enabled;
13337 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13338 if ( FAILED(hrc)
13339 || !enabled)
13340 continue;
13341
13342 NetworkAttachmentType_T type;
13343 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13344 if ( SUCCEEDED(hrc)
13345 && type == NetworkAttachmentType_NATNetwork)
13346 {
13347 Bstr name;
13348 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13349 if (SUCCEEDED(hrc))
13350 {
13351 Utf8Str strName(name);
13352 LogRel(("VM '%s' starts using NAT network '%s'\n",
13353 mUserData->s.strName.c_str(), strName.c_str()));
13354 mPeer->lockHandle()->unlockWrite();
13355 mParent->i_natNetworkRefInc(strName);
13356#ifdef RT_LOCK_STRICT
13357 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13358#else
13359 mPeer->lockHandle()->lockWrite();
13360#endif
13361 }
13362 }
13363 }
13364 miNATNetworksStarted++;
13365 }
13366
13367 LogFlowThisFunc(("returns S_OK.\n"));
13368 return S_OK;
13369}
13370
13371/**
13372 * @note Locks this object for writing.
13373 */
13374HRESULT SessionMachine::endPowerUp(LONG aResult)
13375{
13376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13377
13378 if (mData->mSession.mState != SessionState_Locked)
13379 return VBOX_E_INVALID_OBJECT_STATE;
13380
13381 /* Finalize the LaunchVMProcess progress object. */
13382 if (mData->mSession.mProgress)
13383 {
13384 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13385 mData->mSession.mProgress.setNull();
13386 }
13387
13388 if (SUCCEEDED((HRESULT)aResult))
13389 {
13390#ifdef VBOX_WITH_RESOURCE_USAGE_API
13391 /* The VM has been powered up successfully, so it makes sense
13392 * now to offer the performance metrics for a running machine
13393 * object. Doing it earlier wouldn't be safe. */
13394 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13395 mData->mSession.mPID);
13396#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13397 }
13398
13399 return S_OK;
13400}
13401
13402/**
13403 * @note Locks this object for writing.
13404 */
13405HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13406{
13407 LogFlowThisFuncEnter();
13408
13409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13410
13411 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13412 E_FAIL);
13413
13414 /* create a progress object to track operation completion */
13415 ComObjPtr<Progress> pProgress;
13416 pProgress.createObject();
13417 pProgress->init(i_getVirtualBox(),
13418 static_cast<IMachine *>(this) /* aInitiator */,
13419 tr("Stopping the virtual machine"),
13420 FALSE /* aCancelable */);
13421
13422 /* fill in the console task data */
13423 mConsoleTaskData.mLastState = mData->mMachineState;
13424 mConsoleTaskData.mProgress = pProgress;
13425
13426 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13427 i_setMachineState(MachineState_Stopping);
13428
13429 pProgress.queryInterfaceTo(aProgress.asOutParam());
13430
13431 return S_OK;
13432}
13433
13434/**
13435 * @note Locks this object for writing.
13436 */
13437HRESULT SessionMachine::endPoweringDown(LONG aResult,
13438 const com::Utf8Str &aErrMsg)
13439{
13440 LogFlowThisFuncEnter();
13441
13442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13443
13444 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13445 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13446 && mConsoleTaskData.mLastState != MachineState_Null,
13447 E_FAIL);
13448
13449 /*
13450 * On failure, set the state to the state we had when BeginPoweringDown()
13451 * was called (this is expected by Console::PowerDown() and the associated
13452 * task). On success the VM process already changed the state to
13453 * MachineState_PoweredOff, so no need to do anything.
13454 */
13455 if (FAILED(aResult))
13456 i_setMachineState(mConsoleTaskData.mLastState);
13457
13458 /* notify the progress object about operation completion */
13459 Assert(mConsoleTaskData.mProgress);
13460 if (SUCCEEDED(aResult))
13461 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13462 else
13463 {
13464 if (aErrMsg.length())
13465 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13466 COM_IIDOF(ISession),
13467 getComponentName(),
13468 aErrMsg.c_str());
13469 else
13470 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13471 }
13472
13473 /* clear out the temporary saved state data */
13474 mConsoleTaskData.mLastState = MachineState_Null;
13475 mConsoleTaskData.mProgress.setNull();
13476
13477 LogFlowThisFuncLeave();
13478 return S_OK;
13479}
13480
13481
13482/**
13483 * Goes through the USB filters of the given machine to see if the given
13484 * device matches any filter or not.
13485 *
13486 * @note Locks the same as USBController::hasMatchingFilter() does.
13487 */
13488HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13489 BOOL *aMatched,
13490 ULONG *aMaskedInterfaces)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494#ifdef VBOX_WITH_USB
13495 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13496#else
13497 NOREF(aDevice);
13498 NOREF(aMaskedInterfaces);
13499 *aMatched = FALSE;
13500#endif
13501
13502 return S_OK;
13503}
13504
13505/**
13506 * @note Locks the same as Host::captureUSBDevice() does.
13507 */
13508HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512#ifdef VBOX_WITH_USB
13513 /* if captureDeviceForVM() fails, it must have set extended error info */
13514 clearError();
13515 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13516 if (FAILED(rc)) return rc;
13517
13518 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13519 AssertReturn(service, E_FAIL);
13520 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13521#else
13522 NOREF(aId);
13523 return E_NOTIMPL;
13524#endif
13525}
13526
13527/**
13528 * @note Locks the same as Host::detachUSBDevice() does.
13529 */
13530HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13531 BOOL aDone)
13532{
13533 LogFlowThisFunc(("\n"));
13534
13535#ifdef VBOX_WITH_USB
13536 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13537 AssertReturn(service, E_FAIL);
13538 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13539#else
13540 NOREF(aId);
13541 NOREF(aDone);
13542 return E_NOTIMPL;
13543#endif
13544}
13545
13546/**
13547 * Inserts all machine filters to the USB proxy service and then calls
13548 * Host::autoCaptureUSBDevices().
13549 *
13550 * Called by Console from the VM process upon VM startup.
13551 *
13552 * @note Locks what called methods lock.
13553 */
13554HRESULT SessionMachine::autoCaptureUSBDevices()
13555{
13556 LogFlowThisFunc(("\n"));
13557
13558#ifdef VBOX_WITH_USB
13559 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13560 AssertComRC(rc);
13561 NOREF(rc);
13562
13563 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13564 AssertReturn(service, E_FAIL);
13565 return service->autoCaptureDevicesForVM(this);
13566#else
13567 return S_OK;
13568#endif
13569}
13570
13571/**
13572 * Removes all machine filters from the USB proxy service and then calls
13573 * Host::detachAllUSBDevices().
13574 *
13575 * Called by Console from the VM process upon normal VM termination or by
13576 * SessionMachine::uninit() upon abnormal VM termination (from under the
13577 * Machine/SessionMachine lock).
13578 *
13579 * @note Locks what called methods lock.
13580 */
13581HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13582{
13583 LogFlowThisFunc(("\n"));
13584
13585#ifdef VBOX_WITH_USB
13586 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13587 AssertComRC(rc);
13588 NOREF(rc);
13589
13590 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13591 AssertReturn(service, E_FAIL);
13592 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13593#else
13594 NOREF(aDone);
13595 return S_OK;
13596#endif
13597}
13598
13599/**
13600 * @note Locks this object for writing.
13601 */
13602HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13603 ComPtr<IProgress> &aProgress)
13604{
13605 LogFlowThisFuncEnter();
13606
13607 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13608 /*
13609 * We don't assert below because it might happen that a non-direct session
13610 * informs us it is closed right after we've been uninitialized -- it's ok.
13611 */
13612
13613 /* get IInternalSessionControl interface */
13614 ComPtr<IInternalSessionControl> control(aSession);
13615
13616 ComAssertRet(!control.isNull(), E_INVALIDARG);
13617
13618 /* Creating a Progress object requires the VirtualBox lock, and
13619 * thus locking it here is required by the lock order rules. */
13620 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13621
13622 if (control == mData->mSession.mDirectControl)
13623 {
13624 /* The direct session is being normally closed by the client process
13625 * ----------------------------------------------------------------- */
13626
13627 /* go to the closing state (essential for all open*Session() calls and
13628 * for #i_checkForDeath()) */
13629 Assert(mData->mSession.mState == SessionState_Locked);
13630 mData->mSession.mState = SessionState_Unlocking;
13631
13632 /* set direct control to NULL to release the remote instance */
13633 mData->mSession.mDirectControl.setNull();
13634 LogFlowThisFunc(("Direct control is set to NULL\n"));
13635
13636 if (mData->mSession.mProgress)
13637 {
13638 /* finalize the progress, someone might wait if a frontend
13639 * closes the session before powering on the VM. */
13640 mData->mSession.mProgress->notifyComplete(E_FAIL,
13641 COM_IIDOF(ISession),
13642 getComponentName(),
13643 tr("The VM session was closed before any attempt to power it on"));
13644 mData->mSession.mProgress.setNull();
13645 }
13646
13647 /* Create the progress object the client will use to wait until
13648 * #i_checkForDeath() is called to uninitialize this session object after
13649 * it releases the IPC semaphore.
13650 * Note! Because we're "reusing" mProgress here, this must be a proxy
13651 * object just like for LaunchVMProcess. */
13652 Assert(mData->mSession.mProgress.isNull());
13653 ComObjPtr<ProgressProxy> progress;
13654 progress.createObject();
13655 ComPtr<IUnknown> pPeer(mPeer);
13656 progress->init(mParent, pPeer,
13657 Bstr(tr("Closing session")).raw(),
13658 FALSE /* aCancelable */);
13659 progress.queryInterfaceTo(aProgress.asOutParam());
13660 mData->mSession.mProgress = progress;
13661 }
13662 else
13663 {
13664 /* the remote session is being normally closed */
13665 bool found = false;
13666 for (Data::Session::RemoteControlList::iterator
13667 it = mData->mSession.mRemoteControls.begin();
13668 it != mData->mSession.mRemoteControls.end();
13669 ++it)
13670 {
13671 if (control == *it)
13672 {
13673 found = true;
13674 // This MUST be erase(it), not remove(*it) as the latter
13675 // triggers a very nasty use after free due to the place where
13676 // the value "lives".
13677 mData->mSession.mRemoteControls.erase(it);
13678 break;
13679 }
13680 }
13681 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13682 E_INVALIDARG);
13683 }
13684
13685 /* signal the client watcher thread, because the client is going away */
13686 mParent->i_updateClientWatcher();
13687
13688 LogFlowThisFuncLeave();
13689 return S_OK;
13690}
13691
13692HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13693 std::vector<com::Utf8Str> &aValues,
13694 std::vector<LONG64> &aTimestamps,
13695 std::vector<com::Utf8Str> &aFlags)
13696{
13697 LogFlowThisFunc(("\n"));
13698
13699#ifdef VBOX_WITH_GUEST_PROPS
13700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13701
13702 size_t cEntries = mHWData->mGuestProperties.size();
13703 aNames.resize(cEntries);
13704 aValues.resize(cEntries);
13705 aTimestamps.resize(cEntries);
13706 aFlags.resize(cEntries);
13707
13708 size_t i = 0;
13709 for (HWData::GuestPropertyMap::const_iterator
13710 it = mHWData->mGuestProperties.begin();
13711 it != mHWData->mGuestProperties.end();
13712 ++it, ++i)
13713 {
13714 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13715 aNames[i] = it->first;
13716 aValues[i] = it->second.strValue;
13717 aTimestamps[i] = it->second.mTimestamp;
13718
13719 /* If it is NULL, keep it NULL. */
13720 if (it->second.mFlags)
13721 {
13722 GuestPropWriteFlags(it->second.mFlags, szFlags);
13723 aFlags[i] = szFlags;
13724 }
13725 else
13726 aFlags[i] = "";
13727 }
13728 return S_OK;
13729#else
13730 ReturnComNotImplemented();
13731#endif
13732}
13733
13734HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13735 const com::Utf8Str &aValue,
13736 LONG64 aTimestamp,
13737 const com::Utf8Str &aFlags)
13738{
13739 LogFlowThisFunc(("\n"));
13740
13741#ifdef VBOX_WITH_GUEST_PROPS
13742 try
13743 {
13744 /*
13745 * Convert input up front.
13746 */
13747 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13748 if (aFlags.length())
13749 {
13750 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13751 AssertRCReturn(vrc, E_INVALIDARG);
13752 }
13753
13754 /*
13755 * Now grab the object lock, validate the state and do the update.
13756 */
13757
13758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13759
13760 if (!Global::IsOnline(mData->mMachineState))
13761 {
13762 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13763 VBOX_E_INVALID_VM_STATE);
13764 }
13765
13766 i_setModified(IsModified_MachineData);
13767 mHWData.backup();
13768
13769 bool fDelete = !aValue.length();
13770 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13771 if (it != mHWData->mGuestProperties.end())
13772 {
13773 if (!fDelete)
13774 {
13775 it->second.strValue = aValue;
13776 it->second.mTimestamp = aTimestamp;
13777 it->second.mFlags = fFlags;
13778 }
13779 else
13780 mHWData->mGuestProperties.erase(it);
13781
13782 mData->mGuestPropertiesModified = TRUE;
13783 }
13784 else if (!fDelete)
13785 {
13786 HWData::GuestProperty prop;
13787 prop.strValue = aValue;
13788 prop.mTimestamp = aTimestamp;
13789 prop.mFlags = fFlags;
13790
13791 mHWData->mGuestProperties[aName] = prop;
13792 mData->mGuestPropertiesModified = TRUE;
13793 }
13794
13795 alock.release();
13796
13797 mParent->i_onGuestPropertyChange(mData->mUuid,
13798 Bstr(aName).raw(),
13799 Bstr(aValue).raw(),
13800 Bstr(aFlags).raw());
13801 }
13802 catch (...)
13803 {
13804 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13805 }
13806 return S_OK;
13807#else
13808 ReturnComNotImplemented();
13809#endif
13810}
13811
13812
13813HRESULT SessionMachine::lockMedia()
13814{
13815 AutoMultiWriteLock2 alock(this->lockHandle(),
13816 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13817
13818 AssertReturn( mData->mMachineState == MachineState_Starting
13819 || mData->mMachineState == MachineState_Restoring
13820 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13821
13822 clearError();
13823 alock.release();
13824 return i_lockMedia();
13825}
13826
13827HRESULT SessionMachine::unlockMedia()
13828{
13829 HRESULT hrc = i_unlockMedia();
13830 return hrc;
13831}
13832
13833HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13834 ComPtr<IMediumAttachment> &aNewAttachment)
13835{
13836 // request the host lock first, since might be calling Host methods for getting host drives;
13837 // next, protect the media tree all the while we're in here, as well as our member variables
13838 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13839 this->lockHandle(),
13840 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13841
13842 IMediumAttachment *iAttach = aAttachment;
13843 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13844
13845 Utf8Str ctrlName;
13846 LONG lPort;
13847 LONG lDevice;
13848 bool fTempEject;
13849 {
13850 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13851
13852 /* Need to query the details first, as the IMediumAttachment reference
13853 * might be to the original settings, which we are going to change. */
13854 ctrlName = pAttach->i_getControllerName();
13855 lPort = pAttach->i_getPort();
13856 lDevice = pAttach->i_getDevice();
13857 fTempEject = pAttach->i_getTempEject();
13858 }
13859
13860 if (!fTempEject)
13861 {
13862 /* Remember previously mounted medium. The medium before taking the
13863 * backup is not necessarily the same thing. */
13864 ComObjPtr<Medium> oldmedium;
13865 oldmedium = pAttach->i_getMedium();
13866
13867 i_setModified(IsModified_Storage);
13868 mMediumAttachments.backup();
13869
13870 // The backup operation makes the pAttach reference point to the
13871 // old settings. Re-get the correct reference.
13872 pAttach = i_findAttachment(*mMediumAttachments.data(),
13873 ctrlName,
13874 lPort,
13875 lDevice);
13876
13877 {
13878 AutoCaller autoAttachCaller(this);
13879 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13880
13881 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13882 if (!oldmedium.isNull())
13883 oldmedium->i_removeBackReference(mData->mUuid);
13884
13885 pAttach->i_updateMedium(NULL);
13886 pAttach->i_updateEjected();
13887 }
13888
13889 i_setModified(IsModified_Storage);
13890 }
13891 else
13892 {
13893 {
13894 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13895 pAttach->i_updateEjected();
13896 }
13897 }
13898
13899 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13900
13901 return S_OK;
13902}
13903
13904HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13905 com::Utf8Str &aResult)
13906{
13907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13908
13909 HRESULT hr = S_OK;
13910
13911 if (!mAuthLibCtx.hAuthLibrary)
13912 {
13913 /* Load the external authentication library. */
13914 Bstr authLibrary;
13915 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13916
13917 Utf8Str filename = authLibrary;
13918
13919 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13920 if (RT_FAILURE(rc))
13921 {
13922 hr = setError(E_FAIL,
13923 tr("Could not load the external authentication library '%s' (%Rrc)"),
13924 filename.c_str(), rc);
13925 }
13926 }
13927
13928 /* The auth library might need the machine lock. */
13929 alock.release();
13930
13931 if (FAILED(hr))
13932 return hr;
13933
13934 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13935 {
13936 enum VRDEAuthParams
13937 {
13938 parmUuid = 1,
13939 parmGuestJudgement,
13940 parmUser,
13941 parmPassword,
13942 parmDomain,
13943 parmClientId
13944 };
13945
13946 AuthResult result = AuthResultAccessDenied;
13947
13948 Guid uuid(aAuthParams[parmUuid]);
13949 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13950 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13951
13952 result = AuthLibAuthenticate(&mAuthLibCtx,
13953 uuid.raw(), guestJudgement,
13954 aAuthParams[parmUser].c_str(),
13955 aAuthParams[parmPassword].c_str(),
13956 aAuthParams[parmDomain].c_str(),
13957 u32ClientId);
13958
13959 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13960 size_t cbPassword = aAuthParams[parmPassword].length();
13961 if (cbPassword)
13962 {
13963 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13964 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13965 }
13966
13967 if (result == AuthResultAccessGranted)
13968 aResult = "granted";
13969 else
13970 aResult = "denied";
13971
13972 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13973 aAuthParams[parmUser].c_str(), aResult.c_str()));
13974 }
13975 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13976 {
13977 enum VRDEAuthDisconnectParams
13978 {
13979 parmUuid = 1,
13980 parmClientId
13981 };
13982
13983 Guid uuid(aAuthParams[parmUuid]);
13984 uint32_t u32ClientId = 0;
13985 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13986 }
13987 else
13988 {
13989 hr = E_INVALIDARG;
13990 }
13991
13992 return hr;
13993}
13994
13995// public methods only for internal purposes
13996/////////////////////////////////////////////////////////////////////////////
13997
13998#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13999/**
14000 * Called from the client watcher thread to check for expected or unexpected
14001 * death of the client process that has a direct session to this machine.
14002 *
14003 * On Win32 and on OS/2, this method is called only when we've got the
14004 * mutex (i.e. the client has either died or terminated normally) so it always
14005 * returns @c true (the client is terminated, the session machine is
14006 * uninitialized).
14007 *
14008 * On other platforms, the method returns @c true if the client process has
14009 * terminated normally or abnormally and the session machine was uninitialized,
14010 * and @c false if the client process is still alive.
14011 *
14012 * @note Locks this object for writing.
14013 */
14014bool SessionMachine::i_checkForDeath()
14015{
14016 Uninit::Reason reason;
14017 bool terminated = false;
14018
14019 /* Enclose autoCaller with a block because calling uninit() from under it
14020 * will deadlock. */
14021 {
14022 AutoCaller autoCaller(this);
14023 if (!autoCaller.isOk())
14024 {
14025 /* return true if not ready, to cause the client watcher to exclude
14026 * the corresponding session from watching */
14027 LogFlowThisFunc(("Already uninitialized!\n"));
14028 return true;
14029 }
14030
14031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14032
14033 /* Determine the reason of death: if the session state is Closing here,
14034 * everything is fine. Otherwise it means that the client did not call
14035 * OnSessionEnd() before it released the IPC semaphore. This may happen
14036 * either because the client process has abnormally terminated, or
14037 * because it simply forgot to call ISession::Close() before exiting. We
14038 * threat the latter also as an abnormal termination (see
14039 * Session::uninit() for details). */
14040 reason = mData->mSession.mState == SessionState_Unlocking ?
14041 Uninit::Normal :
14042 Uninit::Abnormal;
14043
14044 if (mClientToken)
14045 terminated = mClientToken->release();
14046 } /* AutoCaller block */
14047
14048 if (terminated)
14049 uninit(reason);
14050
14051 return terminated;
14052}
14053
14054void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14055{
14056 LogFlowThisFunc(("\n"));
14057
14058 strTokenId.setNull();
14059
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturnVoid(autoCaller.rc());
14062
14063 Assert(mClientToken);
14064 if (mClientToken)
14065 mClientToken->getId(strTokenId);
14066}
14067#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14068IToken *SessionMachine::i_getToken()
14069{
14070 LogFlowThisFunc(("\n"));
14071
14072 AutoCaller autoCaller(this);
14073 AssertComRCReturn(autoCaller.rc(), NULL);
14074
14075 Assert(mClientToken);
14076 if (mClientToken)
14077 return mClientToken->getToken();
14078 else
14079 return NULL;
14080}
14081#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14082
14083Machine::ClientToken *SessionMachine::i_getClientToken()
14084{
14085 LogFlowThisFunc(("\n"));
14086
14087 AutoCaller autoCaller(this);
14088 AssertComRCReturn(autoCaller.rc(), NULL);
14089
14090 return mClientToken;
14091}
14092
14093
14094/**
14095 * @note Locks this object for reading.
14096 */
14097HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14098{
14099 LogFlowThisFunc(("\n"));
14100
14101 AutoCaller autoCaller(this);
14102 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14103
14104 ComPtr<IInternalSessionControl> directControl;
14105 {
14106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14107 if (mData->mSession.mLockType == LockType_VM)
14108 directControl = mData->mSession.mDirectControl;
14109 }
14110
14111 /* ignore notifications sent after #OnSessionEnd() is called */
14112 if (!directControl)
14113 return S_OK;
14114
14115 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14116}
14117
14118/**
14119 * @note Locks this object for reading.
14120 */
14121HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14122 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14123 IN_BSTR aGuestIp, LONG aGuestPort)
14124{
14125 LogFlowThisFunc(("\n"));
14126
14127 AutoCaller autoCaller(this);
14128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14129
14130 ComPtr<IInternalSessionControl> directControl;
14131 {
14132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14133 if (mData->mSession.mLockType == LockType_VM)
14134 directControl = mData->mSession.mDirectControl;
14135 }
14136
14137 /* ignore notifications sent after #OnSessionEnd() is called */
14138 if (!directControl)
14139 return S_OK;
14140 /*
14141 * instead acting like callback we ask IVirtualBox deliver corresponding event
14142 */
14143
14144 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14145 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14146 return S_OK;
14147}
14148
14149/**
14150 * @note Locks this object for reading.
14151 */
14152HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14153{
14154 LogFlowThisFunc(("\n"));
14155
14156 AutoCaller autoCaller(this);
14157 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14158
14159 ComPtr<IInternalSessionControl> directControl;
14160 {
14161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14162 if (mData->mSession.mLockType == LockType_VM)
14163 directControl = mData->mSession.mDirectControl;
14164 }
14165
14166 /* ignore notifications sent after #OnSessionEnd() is called */
14167 if (!directControl)
14168 return S_OK;
14169
14170 return directControl->OnAudioAdapterChange(audioAdapter);
14171}
14172
14173/**
14174 * @note Locks this object for reading.
14175 */
14176HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14177{
14178 LogFlowThisFunc(("\n"));
14179
14180 AutoCaller autoCaller(this);
14181 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14182
14183 ComPtr<IInternalSessionControl> directControl;
14184 {
14185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14186 if (mData->mSession.mLockType == LockType_VM)
14187 directControl = mData->mSession.mDirectControl;
14188 }
14189
14190 /* ignore notifications sent after #OnSessionEnd() is called */
14191 if (!directControl)
14192 return S_OK;
14193
14194 return directControl->OnSerialPortChange(serialPort);
14195}
14196
14197/**
14198 * @note Locks this object for reading.
14199 */
14200HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14201{
14202 LogFlowThisFunc(("\n"));
14203
14204 AutoCaller autoCaller(this);
14205 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14206
14207 ComPtr<IInternalSessionControl> directControl;
14208 {
14209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14210 if (mData->mSession.mLockType == LockType_VM)
14211 directControl = mData->mSession.mDirectControl;
14212 }
14213
14214 /* ignore notifications sent after #OnSessionEnd() is called */
14215 if (!directControl)
14216 return S_OK;
14217
14218 return directControl->OnParallelPortChange(parallelPort);
14219}
14220
14221/**
14222 * @note Locks this object for reading.
14223 */
14224HRESULT SessionMachine::i_onStorageControllerChange()
14225{
14226 LogFlowThisFunc(("\n"));
14227
14228 AutoCaller autoCaller(this);
14229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14230
14231 ComPtr<IInternalSessionControl> directControl;
14232 {
14233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14234 if (mData->mSession.mLockType == LockType_VM)
14235 directControl = mData->mSession.mDirectControl;
14236 }
14237
14238 /* ignore notifications sent after #OnSessionEnd() is called */
14239 if (!directControl)
14240 return S_OK;
14241
14242 return directControl->OnStorageControllerChange();
14243}
14244
14245/**
14246 * @note Locks this object for reading.
14247 */
14248HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14249{
14250 LogFlowThisFunc(("\n"));
14251
14252 AutoCaller autoCaller(this);
14253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14254
14255 ComPtr<IInternalSessionControl> directControl;
14256 {
14257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14258 if (mData->mSession.mLockType == LockType_VM)
14259 directControl = mData->mSession.mDirectControl;
14260 }
14261
14262 /* ignore notifications sent after #OnSessionEnd() is called */
14263 if (!directControl)
14264 return S_OK;
14265
14266 return directControl->OnMediumChange(aAttachment, aForce);
14267}
14268
14269/**
14270 * @note Locks this object for reading.
14271 */
14272HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14273{
14274 LogFlowThisFunc(("\n"));
14275
14276 AutoCaller autoCaller(this);
14277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14278
14279 ComPtr<IInternalSessionControl> directControl;
14280 {
14281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14282 if (mData->mSession.mLockType == LockType_VM)
14283 directControl = mData->mSession.mDirectControl;
14284 }
14285
14286 /* ignore notifications sent after #OnSessionEnd() is called */
14287 if (!directControl)
14288 return S_OK;
14289
14290 return directControl->OnCPUChange(aCPU, aRemove);
14291}
14292
14293HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14294{
14295 LogFlowThisFunc(("\n"));
14296
14297 AutoCaller autoCaller(this);
14298 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14299
14300 ComPtr<IInternalSessionControl> directControl;
14301 {
14302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14303 if (mData->mSession.mLockType == LockType_VM)
14304 directControl = mData->mSession.mDirectControl;
14305 }
14306
14307 /* ignore notifications sent after #OnSessionEnd() is called */
14308 if (!directControl)
14309 return S_OK;
14310
14311 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14312}
14313
14314/**
14315 * @note Locks this object for reading.
14316 */
14317HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14318{
14319 LogFlowThisFunc(("\n"));
14320
14321 AutoCaller autoCaller(this);
14322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14323
14324 ComPtr<IInternalSessionControl> directControl;
14325 {
14326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14327 if (mData->mSession.mLockType == LockType_VM)
14328 directControl = mData->mSession.mDirectControl;
14329 }
14330
14331 /* ignore notifications sent after #OnSessionEnd() is called */
14332 if (!directControl)
14333 return S_OK;
14334
14335 return directControl->OnVRDEServerChange(aRestart);
14336}
14337
14338/**
14339 * @note Locks this object for reading.
14340 */
14341HRESULT SessionMachine::i_onVideoCaptureChange()
14342{
14343 LogFlowThisFunc(("\n"));
14344
14345 AutoCaller autoCaller(this);
14346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14347
14348 ComPtr<IInternalSessionControl> directControl;
14349 {
14350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14351 if (mData->mSession.mLockType == LockType_VM)
14352 directControl = mData->mSession.mDirectControl;
14353 }
14354
14355 /* ignore notifications sent after #OnSessionEnd() is called */
14356 if (!directControl)
14357 return S_OK;
14358
14359 return directControl->OnVideoCaptureChange();
14360}
14361
14362/**
14363 * @note Locks this object for reading.
14364 */
14365HRESULT SessionMachine::i_onUSBControllerChange()
14366{
14367 LogFlowThisFunc(("\n"));
14368
14369 AutoCaller autoCaller(this);
14370 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14371
14372 ComPtr<IInternalSessionControl> directControl;
14373 {
14374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14375 if (mData->mSession.mLockType == LockType_VM)
14376 directControl = mData->mSession.mDirectControl;
14377 }
14378
14379 /* ignore notifications sent after #OnSessionEnd() is called */
14380 if (!directControl)
14381 return S_OK;
14382
14383 return directControl->OnUSBControllerChange();
14384}
14385
14386/**
14387 * @note Locks this object for reading.
14388 */
14389HRESULT SessionMachine::i_onSharedFolderChange()
14390{
14391 LogFlowThisFunc(("\n"));
14392
14393 AutoCaller autoCaller(this);
14394 AssertComRCReturnRC(autoCaller.rc());
14395
14396 ComPtr<IInternalSessionControl> directControl;
14397 {
14398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14399 if (mData->mSession.mLockType == LockType_VM)
14400 directControl = mData->mSession.mDirectControl;
14401 }
14402
14403 /* ignore notifications sent after #OnSessionEnd() is called */
14404 if (!directControl)
14405 return S_OK;
14406
14407 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14408}
14409
14410/**
14411 * @note Locks this object for reading.
14412 */
14413HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14414{
14415 LogFlowThisFunc(("\n"));
14416
14417 AutoCaller autoCaller(this);
14418 AssertComRCReturnRC(autoCaller.rc());
14419
14420 ComPtr<IInternalSessionControl> directControl;
14421 {
14422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14423 if (mData->mSession.mLockType == LockType_VM)
14424 directControl = mData->mSession.mDirectControl;
14425 }
14426
14427 /* ignore notifications sent after #OnSessionEnd() is called */
14428 if (!directControl)
14429 return S_OK;
14430
14431 return directControl->OnClipboardModeChange(aClipboardMode);
14432}
14433
14434/**
14435 * @note Locks this object for reading.
14436 */
14437HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14438{
14439 LogFlowThisFunc(("\n"));
14440
14441 AutoCaller autoCaller(this);
14442 AssertComRCReturnRC(autoCaller.rc());
14443
14444 ComPtr<IInternalSessionControl> directControl;
14445 {
14446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14447 if (mData->mSession.mLockType == LockType_VM)
14448 directControl = mData->mSession.mDirectControl;
14449 }
14450
14451 /* ignore notifications sent after #OnSessionEnd() is called */
14452 if (!directControl)
14453 return S_OK;
14454
14455 return directControl->OnDnDModeChange(aDnDMode);
14456}
14457
14458/**
14459 * @note Locks this object for reading.
14460 */
14461HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 AutoCaller autoCaller(this);
14466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14467
14468 ComPtr<IInternalSessionControl> directControl;
14469 {
14470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14471 if (mData->mSession.mLockType == LockType_VM)
14472 directControl = mData->mSession.mDirectControl;
14473 }
14474
14475 /* ignore notifications sent after #OnSessionEnd() is called */
14476 if (!directControl)
14477 return S_OK;
14478
14479 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14480}
14481
14482/**
14483 * @note Locks this object for reading.
14484 */
14485HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14486{
14487 LogFlowThisFunc(("\n"));
14488
14489 AutoCaller autoCaller(this);
14490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14491
14492 ComPtr<IInternalSessionControl> directControl;
14493 {
14494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14495 if (mData->mSession.mLockType == LockType_VM)
14496 directControl = mData->mSession.mDirectControl;
14497 }
14498
14499 /* ignore notifications sent after #OnSessionEnd() is called */
14500 if (!directControl)
14501 return S_OK;
14502
14503 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14504}
14505
14506/**
14507 * Returns @c true if this machine's USB controller reports it has a matching
14508 * filter for the given USB device and @c false otherwise.
14509 *
14510 * @note locks this object for reading.
14511 */
14512bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14513{
14514 AutoCaller autoCaller(this);
14515 /* silently return if not ready -- this method may be called after the
14516 * direct machine session has been called */
14517 if (!autoCaller.isOk())
14518 return false;
14519
14520#ifdef VBOX_WITH_USB
14521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14522
14523 switch (mData->mMachineState)
14524 {
14525 case MachineState_Starting:
14526 case MachineState_Restoring:
14527 case MachineState_TeleportingIn:
14528 case MachineState_Paused:
14529 case MachineState_Running:
14530 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14531 * elsewhere... */
14532 alock.release();
14533 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14534 default: break;
14535 }
14536#else
14537 NOREF(aDevice);
14538 NOREF(aMaskedIfs);
14539#endif
14540 return false;
14541}
14542
14543/**
14544 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14545 */
14546HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14547 IVirtualBoxErrorInfo *aError,
14548 ULONG aMaskedIfs,
14549 const com::Utf8Str &aCaptureFilename)
14550{
14551 LogFlowThisFunc(("\n"));
14552
14553 AutoCaller autoCaller(this);
14554
14555 /* This notification may happen after the machine object has been
14556 * uninitialized (the session was closed), so don't assert. */
14557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14558
14559 ComPtr<IInternalSessionControl> directControl;
14560 {
14561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14562 if (mData->mSession.mLockType == LockType_VM)
14563 directControl = mData->mSession.mDirectControl;
14564 }
14565
14566 /* fail on notifications sent after #OnSessionEnd() is called, it is
14567 * expected by the caller */
14568 if (!directControl)
14569 return E_FAIL;
14570
14571 /* No locks should be held at this point. */
14572 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14573 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14574
14575 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14576}
14577
14578/**
14579 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14580 */
14581HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14582 IVirtualBoxErrorInfo *aError)
14583{
14584 LogFlowThisFunc(("\n"));
14585
14586 AutoCaller autoCaller(this);
14587
14588 /* This notification may happen after the machine object has been
14589 * uninitialized (the session was closed), so don't assert. */
14590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14591
14592 ComPtr<IInternalSessionControl> directControl;
14593 {
14594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14595 if (mData->mSession.mLockType == LockType_VM)
14596 directControl = mData->mSession.mDirectControl;
14597 }
14598
14599 /* fail on notifications sent after #OnSessionEnd() is called, it is
14600 * expected by the caller */
14601 if (!directControl)
14602 return E_FAIL;
14603
14604 /* No locks should be held at this point. */
14605 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14606 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14607
14608 return directControl->OnUSBDeviceDetach(aId, aError);
14609}
14610
14611// protected methods
14612/////////////////////////////////////////////////////////////////////////////
14613
14614/**
14615 * Deletes the given file if it is no longer in use by either the current machine state
14616 * (if the machine is "saved") or any of the machine's snapshots.
14617 *
14618 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14619 * but is different for each SnapshotMachine. When calling this, the order of calling this
14620 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14621 * is therefore critical. I know, it's all rather messy.
14622 *
14623 * @param strStateFile
14624 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14625 * the test for whether the saved state file is in use.
14626 */
14627void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14628 Snapshot *pSnapshotToIgnore)
14629{
14630 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14631 if ( (strStateFile.isNotEmpty())
14632 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14633 )
14634 // ... and it must also not be shared with other snapshots
14635 if ( !mData->mFirstSnapshot
14636 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14637 // this checks the SnapshotMachine's state file paths
14638 )
14639 RTFileDelete(strStateFile.c_str());
14640}
14641
14642/**
14643 * Locks the attached media.
14644 *
14645 * All attached hard disks are locked for writing and DVD/floppy are locked for
14646 * reading. Parents of attached hard disks (if any) are locked for reading.
14647 *
14648 * This method also performs accessibility check of all media it locks: if some
14649 * media is inaccessible, the method will return a failure and a bunch of
14650 * extended error info objects per each inaccessible medium.
14651 *
14652 * Note that this method is atomic: if it returns a success, all media are
14653 * locked as described above; on failure no media is locked at all (all
14654 * succeeded individual locks will be undone).
14655 *
14656 * The caller is responsible for doing the necessary state sanity checks.
14657 *
14658 * The locks made by this method must be undone by calling #unlockMedia() when
14659 * no more needed.
14660 */
14661HRESULT SessionMachine::i_lockMedia()
14662{
14663 AutoCaller autoCaller(this);
14664 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14665
14666 AutoMultiWriteLock2 alock(this->lockHandle(),
14667 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14668
14669 /* bail out if trying to lock things with already set up locking */
14670 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14671
14672 MultiResult mrc(S_OK);
14673
14674 /* Collect locking information for all medium objects attached to the VM. */
14675 for (MediumAttachmentList::const_iterator
14676 it = mMediumAttachments->begin();
14677 it != mMediumAttachments->end();
14678 ++it)
14679 {
14680 MediumAttachment *pAtt = *it;
14681 DeviceType_T devType = pAtt->i_getType();
14682 Medium *pMedium = pAtt->i_getMedium();
14683
14684 MediumLockList *pMediumLockList(new MediumLockList());
14685 // There can be attachments without a medium (floppy/dvd), and thus
14686 // it's impossible to create a medium lock list. It still makes sense
14687 // to have the empty medium lock list in the map in case a medium is
14688 // attached later.
14689 if (pMedium != NULL)
14690 {
14691 MediumType_T mediumType = pMedium->i_getType();
14692 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14693 || mediumType == MediumType_Shareable;
14694 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14695
14696 alock.release();
14697 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14698 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14699 false /* fMediumLockWriteAll */,
14700 NULL,
14701 *pMediumLockList);
14702 alock.acquire();
14703 if (FAILED(mrc))
14704 {
14705 delete pMediumLockList;
14706 mData->mSession.mLockedMedia.Clear();
14707 break;
14708 }
14709 }
14710
14711 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14712 if (FAILED(rc))
14713 {
14714 mData->mSession.mLockedMedia.Clear();
14715 mrc = setError(rc,
14716 tr("Collecting locking information for all attached media failed"));
14717 break;
14718 }
14719 }
14720
14721 if (SUCCEEDED(mrc))
14722 {
14723 /* Now lock all media. If this fails, nothing is locked. */
14724 alock.release();
14725 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14726 alock.acquire();
14727 if (FAILED(rc))
14728 {
14729 mrc = setError(rc,
14730 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14731 }
14732 }
14733
14734 return mrc;
14735}
14736
14737/**
14738 * Undoes the locks made by by #lockMedia().
14739 */
14740HRESULT SessionMachine::i_unlockMedia()
14741{
14742 AutoCaller autoCaller(this);
14743 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14744
14745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14746
14747 /* we may be holding important error info on the current thread;
14748 * preserve it */
14749 ErrorInfoKeeper eik;
14750
14751 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14752 AssertComRC(rc);
14753 return rc;
14754}
14755
14756/**
14757 * Helper to change the machine state (reimplementation).
14758 *
14759 * @note Locks this object for writing.
14760 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14761 * it can cause crashes in random places due to unexpectedly committing
14762 * the current settings. The caller is responsible for that. The call
14763 * to saveStateSettings is fine, because this method does not commit.
14764 */
14765HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14766{
14767 LogFlowThisFuncEnter();
14768 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14769
14770 AutoCaller autoCaller(this);
14771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14772
14773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14774
14775 MachineState_T oldMachineState = mData->mMachineState;
14776
14777 AssertMsgReturn(oldMachineState != aMachineState,
14778 ("oldMachineState=%s, aMachineState=%s\n",
14779 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14780 E_FAIL);
14781
14782 HRESULT rc = S_OK;
14783
14784 int stsFlags = 0;
14785 bool deleteSavedState = false;
14786
14787 /* detect some state transitions */
14788
14789 if ( ( oldMachineState == MachineState_Saved
14790 && aMachineState == MachineState_Restoring)
14791 || ( ( oldMachineState == MachineState_PoweredOff
14792 || oldMachineState == MachineState_Teleported
14793 || oldMachineState == MachineState_Aborted
14794 )
14795 && ( aMachineState == MachineState_TeleportingIn
14796 || aMachineState == MachineState_Starting
14797 )
14798 )
14799 )
14800 {
14801 /* The EMT thread is about to start */
14802
14803 /* Nothing to do here for now... */
14804
14805 /// @todo NEWMEDIA don't let mDVDDrive and other children
14806 /// change anything when in the Starting/Restoring state
14807 }
14808 else if ( ( oldMachineState == MachineState_Running
14809 || oldMachineState == MachineState_Paused
14810 || oldMachineState == MachineState_Teleporting
14811 || oldMachineState == MachineState_OnlineSnapshotting
14812 || oldMachineState == MachineState_LiveSnapshotting
14813 || oldMachineState == MachineState_Stuck
14814 || oldMachineState == MachineState_Starting
14815 || oldMachineState == MachineState_Stopping
14816 || oldMachineState == MachineState_Saving
14817 || oldMachineState == MachineState_Restoring
14818 || oldMachineState == MachineState_TeleportingPausedVM
14819 || oldMachineState == MachineState_TeleportingIn
14820 )
14821 && ( aMachineState == MachineState_PoweredOff
14822 || aMachineState == MachineState_Saved
14823 || aMachineState == MachineState_Teleported
14824 || aMachineState == MachineState_Aborted
14825 )
14826 )
14827 {
14828 /* The EMT thread has just stopped, unlock attached media. Note that as
14829 * opposed to locking that is done from Console, we do unlocking here
14830 * because the VM process may have aborted before having a chance to
14831 * properly unlock all media it locked. */
14832
14833 unlockMedia();
14834 }
14835
14836 if (oldMachineState == MachineState_Restoring)
14837 {
14838 if (aMachineState != MachineState_Saved)
14839 {
14840 /*
14841 * delete the saved state file once the machine has finished
14842 * restoring from it (note that Console sets the state from
14843 * Restoring to Saved if the VM couldn't restore successfully,
14844 * to give the user an ability to fix an error and retry --
14845 * we keep the saved state file in this case)
14846 */
14847 deleteSavedState = true;
14848 }
14849 }
14850 else if ( oldMachineState == MachineState_Saved
14851 && ( aMachineState == MachineState_PoweredOff
14852 || aMachineState == MachineState_Aborted
14853 || aMachineState == MachineState_Teleported
14854 )
14855 )
14856 {
14857 /*
14858 * delete the saved state after SessionMachine::ForgetSavedState() is called
14859 * or if the VM process (owning a direct VM session) crashed while the
14860 * VM was Saved
14861 */
14862
14863 /// @todo (dmik)
14864 // Not sure that deleting the saved state file just because of the
14865 // client death before it attempted to restore the VM is a good
14866 // thing. But when it crashes we need to go to the Aborted state
14867 // which cannot have the saved state file associated... The only
14868 // way to fix this is to make the Aborted condition not a VM state
14869 // but a bool flag: i.e., when a crash occurs, set it to true and
14870 // change the state to PoweredOff or Saved depending on the
14871 // saved state presence.
14872
14873 deleteSavedState = true;
14874 mData->mCurrentStateModified = TRUE;
14875 stsFlags |= SaveSTS_CurStateModified;
14876 }
14877
14878 if ( aMachineState == MachineState_Starting
14879 || aMachineState == MachineState_Restoring
14880 || aMachineState == MachineState_TeleportingIn
14881 )
14882 {
14883 /* set the current state modified flag to indicate that the current
14884 * state is no more identical to the state in the
14885 * current snapshot */
14886 if (!mData->mCurrentSnapshot.isNull())
14887 {
14888 mData->mCurrentStateModified = TRUE;
14889 stsFlags |= SaveSTS_CurStateModified;
14890 }
14891 }
14892
14893 if (deleteSavedState)
14894 {
14895 if (mRemoveSavedState)
14896 {
14897 Assert(!mSSData->strStateFilePath.isEmpty());
14898
14899 // it is safe to delete the saved state file if ...
14900 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14901 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14902 // ... none of the snapshots share the saved state file
14903 )
14904 RTFileDelete(mSSData->strStateFilePath.c_str());
14905 }
14906
14907 mSSData->strStateFilePath.setNull();
14908 stsFlags |= SaveSTS_StateFilePath;
14909 }
14910
14911 /* redirect to the underlying peer machine */
14912 mPeer->i_setMachineState(aMachineState);
14913
14914 if ( oldMachineState != MachineState_RestoringSnapshot
14915 && ( aMachineState == MachineState_PoweredOff
14916 || aMachineState == MachineState_Teleported
14917 || aMachineState == MachineState_Aborted
14918 || aMachineState == MachineState_Saved))
14919 {
14920 /* the machine has stopped execution
14921 * (or the saved state file was adopted) */
14922 stsFlags |= SaveSTS_StateTimeStamp;
14923 }
14924
14925 if ( ( oldMachineState == MachineState_PoweredOff
14926 || oldMachineState == MachineState_Aborted
14927 || oldMachineState == MachineState_Teleported
14928 )
14929 && aMachineState == MachineState_Saved)
14930 {
14931 /* the saved state file was adopted */
14932 Assert(!mSSData->strStateFilePath.isEmpty());
14933 stsFlags |= SaveSTS_StateFilePath;
14934 }
14935
14936#ifdef VBOX_WITH_GUEST_PROPS
14937 if ( aMachineState == MachineState_PoweredOff
14938 || aMachineState == MachineState_Aborted
14939 || aMachineState == MachineState_Teleported)
14940 {
14941 /* Make sure any transient guest properties get removed from the
14942 * property store on shutdown. */
14943 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14944
14945 /* remove it from the settings representation */
14946 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14947 for (settings::GuestPropertiesList::iterator
14948 it = llGuestProperties.begin();
14949 it != llGuestProperties.end();
14950 /*nothing*/)
14951 {
14952 const settings::GuestProperty &prop = *it;
14953 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14954 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14955 {
14956 it = llGuestProperties.erase(it);
14957 fNeedsSaving = true;
14958 }
14959 else
14960 {
14961 ++it;
14962 }
14963 }
14964
14965 /* Additionally remove it from the HWData representation. Required to
14966 * keep everything in sync, as this is what the API keeps using. */
14967 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14968 for (HWData::GuestPropertyMap::iterator
14969 it = llHWGuestProperties.begin();
14970 it != llHWGuestProperties.end();
14971 /*nothing*/)
14972 {
14973 uint32_t fFlags = it->second.mFlags;
14974 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14975 {
14976 /* iterator where we need to continue after the erase call
14977 * (C++03 is a fact still, and it doesn't return the iterator
14978 * which would allow continuing) */
14979 HWData::GuestPropertyMap::iterator it2 = it;
14980 ++it2;
14981 llHWGuestProperties.erase(it);
14982 it = it2;
14983 fNeedsSaving = true;
14984 }
14985 else
14986 {
14987 ++it;
14988 }
14989 }
14990
14991 if (fNeedsSaving)
14992 {
14993 mData->mCurrentStateModified = TRUE;
14994 stsFlags |= SaveSTS_CurStateModified;
14995 }
14996 }
14997#endif /* VBOX_WITH_GUEST_PROPS */
14998
14999 rc = i_saveStateSettings(stsFlags);
15000
15001 if ( ( oldMachineState != MachineState_PoweredOff
15002 && oldMachineState != MachineState_Aborted
15003 && oldMachineState != MachineState_Teleported
15004 )
15005 && ( aMachineState == MachineState_PoweredOff
15006 || aMachineState == MachineState_Aborted
15007 || aMachineState == MachineState_Teleported
15008 )
15009 )
15010 {
15011 /* we've been shut down for any reason */
15012 /* no special action so far */
15013 }
15014
15015 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15016 LogFlowThisFuncLeave();
15017 return rc;
15018}
15019
15020/**
15021 * Sends the current machine state value to the VM process.
15022 *
15023 * @note Locks this object for reading, then calls a client process.
15024 */
15025HRESULT SessionMachine::i_updateMachineStateOnClient()
15026{
15027 AutoCaller autoCaller(this);
15028 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15029
15030 ComPtr<IInternalSessionControl> directControl;
15031 {
15032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15033 AssertReturn(!!mData, E_FAIL);
15034 if (mData->mSession.mLockType == LockType_VM)
15035 directControl = mData->mSession.mDirectControl;
15036
15037 /* directControl may be already set to NULL here in #OnSessionEnd()
15038 * called too early by the direct session process while there is still
15039 * some operation (like deleting the snapshot) in progress. The client
15040 * process in this case is waiting inside Session::close() for the
15041 * "end session" process object to complete, while #uninit() called by
15042 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15043 * operation to complete. For now, we accept this inconsistent behavior
15044 * and simply do nothing here. */
15045
15046 if (mData->mSession.mState == SessionState_Unlocking)
15047 return S_OK;
15048 }
15049
15050 /* ignore notifications sent after #OnSessionEnd() is called */
15051 if (!directControl)
15052 return S_OK;
15053
15054 return directControl->UpdateMachineState(mData->mMachineState);
15055}
15056
15057
15058/*static*/
15059HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15060{
15061 va_list args;
15062 va_start(args, pcszMsg);
15063 HRESULT rc = setErrorInternal(aResultCode,
15064 getStaticClassIID(),
15065 getStaticComponentName(),
15066 Utf8Str(pcszMsg, args),
15067 false /* aWarning */,
15068 true /* aLogIt */);
15069 va_end(args);
15070 return rc;
15071}
15072
15073
15074HRESULT Machine::updateState(MachineState_T aState)
15075{
15076 NOREF(aState);
15077 ReturnComNotImplemented();
15078}
15079
15080HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15081{
15082 NOREF(aProgress);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::endPowerUp(LONG aResult)
15087{
15088 NOREF(aResult);
15089 ReturnComNotImplemented();
15090}
15091
15092HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15093{
15094 NOREF(aProgress);
15095 ReturnComNotImplemented();
15096}
15097
15098HRESULT Machine::endPoweringDown(LONG aResult,
15099 const com::Utf8Str &aErrMsg)
15100{
15101 NOREF(aResult);
15102 NOREF(aErrMsg);
15103 ReturnComNotImplemented();
15104}
15105
15106HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15107 BOOL *aMatched,
15108 ULONG *aMaskedInterfaces)
15109{
15110 NOREF(aDevice);
15111 NOREF(aMatched);
15112 NOREF(aMaskedInterfaces);
15113 ReturnComNotImplemented();
15114
15115}
15116
15117HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15118{
15119 NOREF(aId); NOREF(aCaptureFilename);
15120 ReturnComNotImplemented();
15121}
15122
15123HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15124 BOOL aDone)
15125{
15126 NOREF(aId);
15127 NOREF(aDone);
15128 ReturnComNotImplemented();
15129}
15130
15131HRESULT Machine::autoCaptureUSBDevices()
15132{
15133 ReturnComNotImplemented();
15134}
15135
15136HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15137{
15138 NOREF(aDone);
15139 ReturnComNotImplemented();
15140}
15141
15142HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15143 ComPtr<IProgress> &aProgress)
15144{
15145 NOREF(aSession);
15146 NOREF(aProgress);
15147 ReturnComNotImplemented();
15148}
15149
15150HRESULT Machine::finishOnlineMergeMedium()
15151{
15152 ReturnComNotImplemented();
15153}
15154
15155HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15156 std::vector<com::Utf8Str> &aValues,
15157 std::vector<LONG64> &aTimestamps,
15158 std::vector<com::Utf8Str> &aFlags)
15159{
15160 NOREF(aNames);
15161 NOREF(aValues);
15162 NOREF(aTimestamps);
15163 NOREF(aFlags);
15164 ReturnComNotImplemented();
15165}
15166
15167HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15168 const com::Utf8Str &aValue,
15169 LONG64 aTimestamp,
15170 const com::Utf8Str &aFlags)
15171{
15172 NOREF(aName);
15173 NOREF(aValue);
15174 NOREF(aTimestamp);
15175 NOREF(aFlags);
15176 ReturnComNotImplemented();
15177}
15178
15179HRESULT Machine::lockMedia()
15180{
15181 ReturnComNotImplemented();
15182}
15183
15184HRESULT Machine::unlockMedia()
15185{
15186 ReturnComNotImplemented();
15187}
15188
15189HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15190 ComPtr<IMediumAttachment> &aNewAttachment)
15191{
15192 NOREF(aAttachment);
15193 NOREF(aNewAttachment);
15194 ReturnComNotImplemented();
15195}
15196
15197HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15198 ULONG aCpuUser,
15199 ULONG aCpuKernel,
15200 ULONG aCpuIdle,
15201 ULONG aMemTotal,
15202 ULONG aMemFree,
15203 ULONG aMemBalloon,
15204 ULONG aMemShared,
15205 ULONG aMemCache,
15206 ULONG aPagedTotal,
15207 ULONG aMemAllocTotal,
15208 ULONG aMemFreeTotal,
15209 ULONG aMemBalloonTotal,
15210 ULONG aMemSharedTotal,
15211 ULONG aVmNetRx,
15212 ULONG aVmNetTx)
15213{
15214 NOREF(aValidStats);
15215 NOREF(aCpuUser);
15216 NOREF(aCpuKernel);
15217 NOREF(aCpuIdle);
15218 NOREF(aMemTotal);
15219 NOREF(aMemFree);
15220 NOREF(aMemBalloon);
15221 NOREF(aMemShared);
15222 NOREF(aMemCache);
15223 NOREF(aPagedTotal);
15224 NOREF(aMemAllocTotal);
15225 NOREF(aMemFreeTotal);
15226 NOREF(aMemBalloonTotal);
15227 NOREF(aMemSharedTotal);
15228 NOREF(aVmNetRx);
15229 NOREF(aVmNetTx);
15230 ReturnComNotImplemented();
15231}
15232
15233HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15234 com::Utf8Str &aResult)
15235{
15236 NOREF(aAuthParams);
15237 NOREF(aResult);
15238 ReturnComNotImplemented();
15239}
15240
15241HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15242{
15243 NOREF(aFlags);
15244 ReturnComNotImplemented();
15245}
15246
15247/* This isn't handled entirely by the wrapper generator yet. */
15248#ifdef VBOX_WITH_XPCOM
15249NS_DECL_CLASSINFO(SessionMachine)
15250NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15251
15252NS_DECL_CLASSINFO(SnapshotMachine)
15253NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15254#endif
Note: See TracBrowser for help on using the repository browser.

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