ConsoleImpl.cpp revision 4ffa88e3c640c58346df49580a7d02f5b389f75f
/* $Id$ */
/** @file
*
* VBox Console COM Class implementation
*/
/*
* Copyright (C) 2006-2009 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/** @todo Move the TAP mess back into the driver! */
#if defined(RT_OS_WINDOWS)
#elif defined(RT_OS_LINUX)
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
#elif defined(RT_OS_FREEBSD)
# include <errno.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
#endif
#include "ConsoleImpl.h"
#include "Global.h"
#include "GuestImpl.h"
#include "KeyboardImpl.h"
#include "MouseImpl.h"
#include "DisplayImpl.h"
#include "MachineDebuggerImpl.h"
#include "USBDeviceImpl.h"
#include "RemoteUSBDeviceImpl.h"
#include "SharedFolderImpl.h"
#include "AudioSnifferInterface.h"
#include "ConsoleVRDPServer.h"
#include "VMMDev.h"
#include "package-generated.h"
// generated header
#include "SchemaDefs.h"
#include "Logging.h"
#include <iprt/buildconfig.h>
#include <iprt/cpputils.h>
#ifdef VBOX_WITH_USB
#endif
#ifdef VBOX_WITH_GUEST_PROPS
#endif
#include <set>
#include <algorithm>
#include <memory> // for auto_ptr
#include <vector>
// VMTask and friends
////////////////////////////////////////////////////////////////////////////////
/**
* Task structure for asynchronous VM operations.
*
* Once created, the task structure adds itself as a Console caller. This means:
*
* 1. The user must check for #rc() before using the created structure
* (e.g. passing it as a thread function argument). If #rc() returns a
* failure, the Console object may not be used by the task (see
* Console::addCaller() for more details).
* 2. On successful initialization, the structure keeps the Console caller
* until destruction (to ensure Console remains in the Ready state and won't
* be accidentally uninitialized). Forgetting to delete the created task
* will lead to Console::uninit() stuck waiting for releasing all added
* callers.
*
* If \a aUsesVMPtr parameter is true, the task structure will also add itself
* as a Console::mpVM caller with the same meaning as above. See
* Console::addVMCaller() for more info.
*/
struct VMTask
{
mCallerAdded(false),
mVMCallerAdded(false)
{
{
mCallerAdded = true;
if (aUsesVMPtr)
{
mVMCallerAdded = true;
}
}
}
~VMTask()
{
if (mVMCallerAdded)
if (mCallerAdded)
}
/** Releases the Console caller before destruction. Not normally necessary. */
void releaseCaller()
{
mCallerAdded = false;
}
/** Releases the VM caller before destruction. Not normally necessary. */
void releaseVMCaller()
{
mVMCallerAdded = false;
}
private:
bool mCallerAdded : 1;
bool mVMCallerAdded : 1;
};
struct VMProgressTask : public VMTask
{
bool aUsesVMPtr)
{}
};
struct VMTakeSnapshotTask : public VMProgressTask
{
{}
bool fTakingSnapshotOnline;
};
struct VMPowerUpTask : public VMProgressTask
{
mStartPaused(false),
{}
bool mStartPaused;
/* array of progress objects for hard disk reset operations */
};
struct VMSaveTask : public VMProgressTask
{
{}
};
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
: mSavedStateDataLoaded(false)
, mVMCallers(0)
, mVMDestroying(false)
, mVMPoweredOff(false)
, mVMStateChangeCallbackDisabled(false)
{
}
{}
{
LogFlowThisFunc(("\n"));
return S_OK;
}
void Console::FinalRelease()
{
LogFlowThisFunc(("\n"));
uninit();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
/* Cache essential properties and objects */
#ifdef VBOX_WITH_VRDP
#endif
/* Create associated child COM objects */
/* Grab global and machine shared folder lists */
/* Create other child objects */
mcAudioRefs = 0;
mcVRDPClients = 0;
/* Confirm a successful initialization when it's the case */
return S_OK;
}
/**
* Uninitializes the Console object.
*/
{
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
{
LogFlowThisFunc(("Already uninitialized.\n"));
return;
}
/*
* Uninit all children that use addDependentChild()/removeDependentChild()
* in their init()/uninit() methods.
*/
/* power down the VM if necessary */
if (mpVM)
{
powerDown();
}
if (mVMZeroCallersSem != NIL_RTSEMEVENT)
{
}
if (mAudioSniffer)
{
delete mAudioSniffer;
}
if (mVMMDev)
{
delete mVMMDev;
}
mUSBDevices.clear();
if (mRemoteDisplayInfo)
{
}
if (mDebugger)
{
}
if (mDisplay)
{
}
if (mMouse)
{
}
if (mKeyboard)
{
}
if (mGuest)
{
}
if (mConsoleVRDPServer)
{
delete mConsoleVRDPServer;
}
#ifdef VBOX_WITH_VRDP
#endif
/* Release all callbacks. Do this after uninitializing the components,
* as some of them are well-behaved and unregister their callbacks.
* These would trigger error messages complaining about trying to
* unregister a non-registered callback. */
mCallbacks.clear();
/* dynamically allocated members of mCallbackData are uninitialized
* at the end of powerDown() */
}
#ifdef VBOX_WITH_GUEST_PROPS
bool Console::enabledGuestPropertiesVRDP(void)
{
HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/EnableGuestPropertiesVRDP"), value.asOutParam());
{
if (value == "1")
{
return true;
}
}
return false;
}
void Console::updateGuestPropertiesVRDPLogon(uint32_t u32ClientId, const char *pszUser, const char *pszDomain)
{
if (!enabledGuestPropertiesVRDP())
{
return;
}
int rc;
char *pszPropertyName;
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
{
}
char *pszClientId;
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastConnectedClient"), Bstr(pszClientId), Bstr("RDONLYGUEST"));
}
return;
}
{
if (!enabledGuestPropertiesVRDP())
{
return;
}
int rc;
char *pszPropertyName;
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
{
}
if (RT_SUCCESS(rc))
{
}
char *pszClientId;
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastDisconnectedClient"), Bstr(pszClientId), Bstr("RDONLYGUEST"));
}
return;
}
#endif /* VBOX_WITH_GUEST_PROPS */
int Console::VRDPClientLogon(uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)
{
AutoCaller autoCaller(this);
if (!autoCaller.isOk())
{
/* Console has been already uninitialized, deny request */
LogRel(("VRDPAUTH: Access denied (Console uninitialized).\n"));
return VERR_ACCESS_DENIED;
}
ULONG authTimeout = 0;
LogRel(("VRDPAUTH: User: [%s]. Domain: [%s]. Authentication type: [%s]\n",
"Null":
"External":
"Guest":
"INVALID"
)
)
));
switch (authType)
{
case VRDPAuthType_Null:
{
break;
}
case VRDPAuthType_External:
{
/* Call the external library. */
result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
if (result != VRDPAuthDelegateToGuest)
{
break;
}
LogRel(("VRDPAUTH: Delegated to guest.\n"));
LogFlowFunc(("External auth asked for guest judgement\n"));
} /* pass through */
case VRDPAuthType_Guest:
{
if (mVMMDev)
{
/* Issue the request to guest. Assume that the call does not require EMT. It should not. */
/* Ask the guest to judge these credentials. */
if (RT_SUCCESS(rc))
{
/* Wait for guest. */
if (RT_SUCCESS(rc))
{
switch (u32GuestFlags & (VMMDEV_CREDENTIALS_JUDGE_OK | VMMDEV_CREDENTIALS_JUDGE_DENY | VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT))
{
default:
}
}
else
{
}
}
else
{
}
}
if (authType == VRDPAuthType_External)
{
result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
}
else
{
switch (guestJudgement)
{
break;
default:
break;
}
}
} break;
default:
AssertFailed();
}
if (result != VRDPAuthAccessGranted)
{
/* Reject. */
LogRel(("VRDPAUTH: Access denied.\n"));
return VERR_ACCESS_DENIED;
}
LogRel(("VRDPAUTH: Access granted.\n"));
/* Multiconnection check must be made after authentication, so bad clients would not interfere with a good one. */
LogFlowFunc(("allowMultiConnection %d, reuseSingleConnection = %d, mcVRDPClients = %d, mu32SingleRDPClientId = %d\n", allowMultiConnection, reuseSingleConnection, mcVRDPClients, mu32SingleRDPClientId));
if (allowMultiConnection == FALSE)
{
/* Note: the 'mcVRDPClients' variable is incremented in ClientConnect callback, which is called when the client
* is successfully connected, that is after the ClientLogon callback. Therefore the mcVRDPClients
* value is 0 for first client.
*/
if (mcVRDPClients != 0)
{
/* There is a client already.
* If required drop the existing client connection and let the connecting one in.
*/
{
LogRel(("VRDPAUTH: Multiple connections are not enabled. Disconnecting existing client.\n"));
}
else
{
/* Reject. */
LogRel(("VRDPAUTH: Multiple connections are not enabled. Access denied.\n"));
return VERR_ACCESS_DENIED;
}
}
/* Save the connected client id. From now on it will be necessary to disconnect this one. */
}
#ifdef VBOX_WITH_GUEST_PROPS
#endif /* VBOX_WITH_GUEST_PROPS */
return VINF_SUCCESS;
}
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_VRDP
if (u32Clients == 1)
{
getVMMDev()->getVMMDevPort()->
true, VRDP_EXPERIENCE_LEVEL_FULL); // @todo configurable
}
mDisplay->VideoAccelVRDP(true);
#endif /* VBOX_WITH_VRDP */
return;
}
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_VRDP
if (u32Clients == 0)
{
getVMMDev()->getVMMDevPort()->
false, 0);
}
mDisplay->VideoAccelVRDP(false);
#endif /* VBOX_WITH_VRDP */
{
}
#ifdef VBOX_WITH_VRDP
{
}
{
mcAudioRefs--;
if (mcAudioRefs <= 0)
{
if (mAudioSniffer)
{
if (port)
{
}
}
}
}
#endif /* VBOX_WITH_VRDP */
if (authType == VRDPAuthType_External)
#ifdef VBOX_WITH_GUEST_PROPS
#endif /* VBOX_WITH_GUEST_PROPS */
return;
}
{
AutoCaller autoCaller(this);
LogFlowFunc(("mAudioSniffer %p, u32ClientId %d.\n",
#ifdef VBOX_WITH_VRDP
++mcAudioRefs;
if (mcAudioRefs == 1)
{
if (mAudioSniffer)
{
if (port)
{
}
}
}
#endif
return;
}
{
AutoCaller autoCaller(this);
return;
}
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_VRDP
#endif /* VBOX_WITH_VRDP */
return;
}
//static
//static
/**
* Loads various console data stored in the saved state file.
* This method does validation of the state file and returns an error info
* when appropriate.
*
* The method does nothing if the machine is not in the Saved file or if
* console data from it has already been loaded.
*
* @note The caller must lock this object for writing.
*/
{
return S_OK;
return rc;
if (RT_SUCCESS(vrc))
{
{
if (RT_SUCCESS(vrc))
else if (vrc == VERR_SSM_UNIT_NOT_FOUND)
vrc = VINF_SUCCESS;
}
else
}
if (RT_FAILURE(vrc))
tr("The saved state file '%ls' is invalid (%Rrc). Discard the saved state and try again"),
mSavedStateDataLoaded = true;
return rc;
}
/**
* Callback handler to save various console data to the state file,
* called when the user saves the VM state.
*
* @param pvUser pointer to Console
*
* @note Locks the Console object for reading.
*/
//static
DECLCALLBACK(void)
{
LogFlowFunc(("\n"));
++ it)
{
// don't lock the folder because methods we access are const
}
return;
}
/**
* Callback handler to load various console data from the state file.
* Called when the VM is being restored from the saved state.
*
* @param pvUser pointer to Console
* @param uVersion Console unit version.
* Should match sSSMConsoleVer.
* @param uPass The data pass.
*
* @note Should locks the Console object for writing, if necessary.
*/
//static
DECLCALLBACK(int)
{
LogFlowFunc(("\n"));
return VERR_VERSION_MISMATCH;
/* Currently, nothing to do when we've been called from VMR3Load*. */
return SSMR3SkipToEndOfUnit(pSSM);
}
/**
* Method to load various console data from the state file.
* Called from #loadDataFromSavedState.
*
* @param pvUser pointer to Console
* @param u32Version Console unit version.
* Should match sSSMConsoleVer.
*
* @note Locks the Console object for writing.
*/
int
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
{
bool writable = true;
delete[] buf;
delete[] buf;
if (u32Version > 0x00010000)
}
return VINF_SUCCESS;
}
#ifdef VBOX_WITH_GUEST_PROPS
// static
DECLCALLBACK(int)
{
using namespace guestProp;
// LogFlowFunc(("pvExtension=%p, pvParms=%p, cbParms=%u\n", pvExtension, pvParms, cbParms));
int rc = VINF_SUCCESS;
/* No locking, as this is purely a notification which does not make any
* changes to the object state. */
// LogFlowFunc(("pCBData->pcszName=%s, pCBData->pcszValue=%s, pCBData->pcszFlags=%s\n", pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
)
rc = VERR_NO_MEMORY;
else
{
flags);
{
// LogFunc(("pConsole->mControl->PushGuestProperty failed, hrc=0x%x\n", hrc));
// LogFunc(("pCBData->pcszName=%s\n", pCBData->pcszName));
// LogFunc(("pCBData->pcszValue=%s\n", pCBData->pcszValue));
// LogFunc(("pCBData->pcszFlags=%s\n", pCBData->pcszFlags));
}
}
// LogFlowFunc(("rc=%Rrc\n", rc));
return rc;
}
{
using namespace guestProp;
/*
* Now things get slightly complicated. Due to a race with the guest adding
* properties, there is no good way to know how much to enlarge a buffer for
* the service to enumerate into. We choose a decent starting size and loop a
* few times, each time retrying with the size suggested by the service plus
* one Kb.
*/
int vrc = VERR_BUFFER_OVERFLOW;
{
try
{
}
catch(...)
{
return E_OUTOFMEMORY;
}
&parm[0]);
}
if (VERR_BUFFER_OVERFLOW == vrc)
return setError(E_UNEXPECTED,
tr("Temporary failure due to guest activity, please retry"));
/*
* Finally we have to unpack the data returned by the service into the safe
* arrays supplied by the caller. We start by counting the number of entries.
*/
const char *pszBuf
unsigned cEntries = 0;
/* The list is terminated by a zero-length string at the end of a set
* of four strings. */
{
/* We are counting sets of four strings. */
for (unsigned j = 0; j < 4; ++j)
++cEntries;
}
/*
* And now we create the COM safe arrays and fill them in.
*/
/* Rely on the service to have formated the data correctly. */
for (unsigned i = 0; i < cEntries; ++i)
{
}
return S_OK;
}
#endif
// IConsole properties
/////////////////////////////////////////////////////////////////////////////
{
AutoCaller autoCaller(this);
/* mMachine is constant during life time, no need to lock */
/* callers expect to get a valid reference, better fail than crash them */
return E_FAIL;
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
/* we return our local state (since it's always the same as on the server) */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* mGuest is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* mKeyboard is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* mMouse is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* mDisplay is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* we need a write lock because of the lazy mDebugger initialization*/
AutoWriteLock alock(this);
/* check if we have to create the debugger object */
if (!mDebugger)
{
}
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(RemoteUSBDevices)(ComSafeArrayOut(IHostUSBDevice *, aRemoteUSBDevices))
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
/* mDisplay is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
/* loadDataFromSavedState() needs a write lock */
AutoWriteLock alock(this);
/* Read console data stored in the saved state file (if not yet done) */
return S_OK;
}
// IConsole methods
/////////////////////////////////////////////////////////////////////////////
{
}
{
}
{
return E_POINTER;
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/** @todo Live Migration: Support powering down while teleporting. Maybe also
* while taking a live snapshot. (In case they never finish and you
* or some other operator wish to shut down the VM.) */
switch (mMachineState)
{
case MachineState_Running:
case MachineState_Paused:
case MachineState_Stuck:
break;
/* extra nice error message for a common case */
case MachineState_Saved:
case MachineState_Stopping:
default:
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
}
LogFlowThisFunc(("Initiating SHUTDOWN request...\n"));
/* create an IProgress object to track progress of this operation */
FALSE /* aCancelable */);
/* setup task object and thread to carry out the operation asynchronously */
"VMPowerDown");
/* task is now owned by powerDownThread(), so release it */
/* go to Stopping state to forbid state-dependant operations */
/* pass the progress to the caller */
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
/** @todo r=bird: This should be allowed on paused VMs as well. Later. */
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* leave the lock before a VMR3* call (EMT will call us back)! */
tr("Could not reset the machine (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
LogFlowThisFunc(("Sending PAUSE request...\n"));
/* leave the lock before a VMR3* call (EMT will call us back)! */
tr("Could not suspend the machine execution (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if (mMachineState != MachineState_Paused)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot resume the machine as it is not paused (machine state: %s)"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
LogFlowThisFunc(("Sending RESUME request...\n"));
/* leave the lock before a VMR3* call (EMT will call us back)! */
int vrc;
else
tr("Could not resume the machine execution (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (RT_SUCCESS(vrc))
{
}
tr("Controlled power off failed (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
bool handled = false;
if (RT_SUCCESS(vrc))
{
}
tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
bool entered = false;
if (RT_SUCCESS(vrc))
{
}
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s)"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (RT_SUCCESS(vrc))
{
}
tr("Sending sleep button event failed (%Rrc)"),
vrc);
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused)
{
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
}
/* memorize the current machine state */
if (mMachineState == MachineState_Running)
{
}
/* create a progress object to track operation completion */
FALSE /* aCancelable */);
bool fBeganSavingState = false;
bool fTaskCreationFailed = false;
do
{
/* create a task object early to ensure mpVM protection is successful */
/*
* If we fail here it means a PowerDown() call happened on another
* thread while we were doing Pause() (which leaves the Console lock).
* We assign PowerDown() a higher precedence than SaveState(),
* therefore just return the error to the caller.
*/
{
fTaskCreationFailed = true;
break;
}
/*
* request a saved state file path from the server
* (this will set the machine state to Saving on the server to block
* others from accessing this machine)
*/
fBeganSavingState = true;
/* sync the state with the server */
/* ensure the directory for the saved state file exists */
{
dir.stripFilename();
{
if (RT_FAILURE(vrc))
{
tr("Could not create a directory '%s' to save the state to (%Rrc)"),
break;
}
}
}
/* setup task object and thread to carry out the operation asynchronously */
/* set the state the operation thread will restore when it is finished */
/* create a thread to wait until the VM state is saved */
0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
/* task is now owned by saveStateThread(), so release it */
/* return the progress to the caller */
}
while (0);
{
/* preserve existing error info */
if (fBeganSavingState)
{
/*
* cancel the requested save state procedure.
* This will reset the machine state to the state it had right
* before calling mControl->BeginSavingState().
*/
}
if (lastMachineState == MachineState_Running)
{
/* restore the paused state if appropriate */
/* restore the running state if appropriate */
Resume();
}
else
}
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_PoweredOff
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if (mMachineState != MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot discard the machine state as the machine is not in the saved state (machine state: %s)"),
/*
* Saved -> PoweredOff transition will be detected in the SessionMachine
* and properly handled.
*/
return rc;
}
/** read the value of a LEd. */
{
if (!pLed)
return 0;
return u32;
}
{
AutoCaller autoCaller(this);
/*
* Note: we don't lock the console object here because
* readAndClearLed() should be thread safe.
*/
/* Get LED array to read */
PDMLEDCORE SumLed = {0};
switch (aDeviceType)
{
case DeviceType_Floppy:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapFDLeds); ++i)
break;
}
case DeviceType_DVD:
{
break;
}
case DeviceType_HardDisk:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapSATALeds); ++i)
for (unsigned i = 0; i < RT_ELEMENTS(mapSCSILeds); ++i)
break;
}
case DeviceType_Network:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
break;
}
case DeviceType_USB:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
break;
}
case DeviceType_SharedFolder:
{
break;
}
default:
return setError(E_INVALIDARG,
tr("Invalid device type: %d"),
}
/* Compose the result */
{
case 0:
break;
case PDMLED_READING:
break;
case PDMLED_WRITING:
case PDMLED_READING | PDMLED_WRITING:
break;
}
return S_OK;
}
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* Don't proceed unless we've found the usb controller. */
if (RT_FAILURE(vrc))
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
/* leave the lock because the USB Proxy service may call us back
* (via onUSBDeviceAttach()) */
/* Request the device capture */
return rc;
#else /* !VBOX_WITH_USB */
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
#endif /* !VBOX_WITH_USB */
}
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Find it. */
{
{
break;
}
++ it;
}
if (!device)
return setError(E_INVALIDARG,
tr("USB device with UUID {%RTuuid} is not attached to this machine"),
/*
* Inform the USB device and USB proxy about what's cooking.
*/
return rc2;
/* Request the PDM to detach the USB device. */
{
/* leave the lock since we don't need it any more (note though that
* the USB Proxy service must not call us back here) */
/* Request the device release. Even if it fails, the device will
* remain as held by proxy, which is OK for us (the VM process). */
}
return rc;
#else /* !VBOX_WITH_USB */
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
#endif /* !VBOX_WITH_USB */
}
{
#ifdef VBOX_WITH_USB
{
{
}
}
return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
tr("Could not find a USB device with address '%ls'"),
aAddress);
#else /* !VBOX_WITH_USB */
return E_NOTIMPL;
#endif /* !VBOX_WITH_USB */
}
{
#ifdef VBOX_WITH_USB
{
{
}
}
return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
tr("Could not find a USB device with uuid {%RTuuid}"),
#else /* !VBOX_WITH_USB */
return E_NOTIMPL;
#endif /* !VBOX_WITH_USB */
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/// @todo see @todo in AttachUSBDevice() about the Paused state
if (mMachineState == MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot create a transient shared folder on the machine in the saved state"));
if ( mMachineState != MachineState_PoweredOff
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
return setError(VBOX_E_FILE_ERROR,
tr("Shared folder named '%ls' already exists"),
aName);
/* protect mpVM (if not NULL) */
{
/* If the VM is online and supports shared folders, share this folder
* under the specified name. */
/* first, remove the machine or the global folder if there is any */
{
}
/* second, create the given folder */
}
/* notify console callbacks after the folder is added to the list */
{
}
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/// @todo see @todo in AttachUSBDevice() about the Paused state
if (mMachineState == MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot remove a transient shared folder from the machine in the saved state"));
if ( mMachineState != MachineState_PoweredOff
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
/* protect mpVM (if not NULL) */
{
/* if the VM is online and supports shared folders, UNshare this
* folder. */
/* first, remove the given folder */
/* first, remove the machine or the global folder if there is any */
{
/* don't check rc here because we need to remove the console
* folder from the collection even on failure */
}
}
/* notify console callbacks after the folder is removed to the list */
{
}
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
/* prepare the progress object:
a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
for (size_t i = 0;
i < aMediumAttachments.size();
++i)
{
if (type == DeviceType_HardDisk)
{
++cOperations;
// assume that creating a diff image takes as long as saving a 1 MB state
// (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
ulTotalOperationsWeight += 1;
}
}
// b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
bool fTakingSnapshotOnline = ((mMachineState == MachineState_Running) || (mMachineState == MachineState_Paused));
LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
|| (mMachineState == MachineState_Saved)
)
{
++cOperations;
}
// finally, create the progress object
FALSE /* aCancelable */,
1); // ulFirstOperationWeight
return VERR_NO_MEMORY;
try
{
/*
* If we fail here it means a PowerDown() call happened on another
* thread while we were doing Pause() (which leaves the Console lock).
* We assign PowerDown() a higher precedence than TakeSnapshot(),
* therefore just return the error to the caller.
*/
/* memorize the current machine state */
(void*)pTask,
0,
0,
"ConsoleTakeSnap");
tr("Could not create VMTakeSnap thread (%Rrc)"),
vrc);
}
{
delete pTask;
}
return rc;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot discard a snapshot of the running machine (machine state: %s)"),
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot discard the current state of the running machine (machine state: %s)"),
return S_OK;
}
{
AutoCaller autoCaller(this);
#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
void *dummy;
return hrc;
#endif
AutoWriteLock alock(this);
/* Inform the callback about the current status (for example, the new
* callback must know the current mouse capabilities and the pointer
* shape in order to properly integrate the mouse pointer). */
/* Note: we don't call OnStateChange for new callbacks because the
* machine state is a) not actually changed on callback registration
* and b) can be always queried from Console. */
return S_OK;
}
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
mCallbacks.end(),
return setError(E_INVALIDARG,
tr("The given callback handler is not registered"));
return S_OK;
}
// Non-interface public methods
/////////////////////////////////////////////////////////////////////////////
{
switch (enmCtrlType)
{
return "lsilogicscsi";
return "buslogic";
return "ahci";
return "piix3ide";
return "i82078";
default:
return NULL;
}
}
HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
{
switch (enmBus)
{
case StorageBus_IDE:
case StorageBus_Floppy:
{
return S_OK;
}
case StorageBus_SATA:
case StorageBus_SCSI:
{
return S_OK;
}
default:
uLun = 0;
}
}
/**
* Process a medium change.
*
* @param aMediumAttachment The medium attachment with the new medium state.
*
* @note Locks this object for writing.
*/
{
AutoCaller autoCaller(this);
/* We will need to release the write lock before calling EMT */
AutoWriteLock alock(this);
unsigned uInstance = 0;
unsigned uLun = 0;
/** @todo support multiple instances of a controller */
uInstance = 0;
{
}
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
/* leave the lock before waiting for a result (EMT will call us back!) */
{
if (RT_SUCCESS(vrc))
}
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
return S_OK;
}
vrc);
}
/**
* Performs the medium change in EMT.
*
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param fHostDrive True if this is a host drive attachment.
* @param pszPath The path to the media / drive which is now being mounted / captured.
* If NULL no media or drive is attached and the LUN will be configured with
* the default block driver with no media. This will also be the state if
* mounting / capturing the specified media / drive fails.
* @param pszFormat Medium format string, usually "RAW".
* @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
*
* @thread EMT
* @note Locks the Console object for writing.
* @todo the error handling in this method needs to be improved seriously - what if mounting fails...
*/
DECLCALLBACK(int) Console::changeDrive(Console *pThis, const char *pszDevice, unsigned uInstance, unsigned uLun,
{
/// @todo change this to use the same code as in ConsoleImpl2.cpp
LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u fHostDrive=%d pszPath=%p:{%s} pszFormat=%p:{%s} fPassthrough=%d\n",
pThis, pszDevice, pszDevice, uInstance, uLun, fHostDrive, pszPath, pszPath, pszFormat, pszFormat, fPassthrough));
/* protect mpVM */
/*
* Suspend the VM first.
*
* The VM must not be running since it might have pending I/O to
* the drive which is being changed.
*/
bool fResume;
switch (enmVMState)
{
case VMSTATE_RESETTING:
case VMSTATE_RUNNING:
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
pThis->mVMStateChangeCallbackDisabled = false;
fResume = true;
break;
}
case VMSTATE_SUSPENDED:
case VMSTATE_CREATED:
case VMSTATE_OFF:
fResume = false;
break;
case VMSTATE_RUNNING_LS:
default:
}
int rc = VINF_SUCCESS;
int rcRet = VINF_SUCCESS;
/*
In general locking the object before doing VMR3* calls is quite safe
here, since we're on EMT. Anyway we lock for write after eventually
suspending the vm. The reason is that in the vmstateChangeCallback the
var mVMStateChangeCallbackDisabled is checked under a lock also, which
can lead to an dead lock. The write lock is necessary because we
indirectly modify the meDVDState/meFloppyState members (pointed to by
peState).
*/
do
{
/*
* Unmount existing media / detach host drive.
*/
if (RT_FAILURE(rc))
{
rc = VINF_SUCCESS;
}
else
{
/*
* Unmount the media.
*/
if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
rc = VINF_SUCCESS;
}
}
if (RT_FAILURE(rc))
{
break;
}
/** @todo this does a very thorough job. usually it's too much,
* as a simple medium change (without changing between host attachment
* and image) could be done with a lot less effort, by just using the
* pfnUnmount and pfnMount interfaces. Later. */
/*
* Construct a new driver configuration.
*/
/* nuke anything which might have been left behind. */
if (fHostDrive)
{
rc = CFGMR3InsertString(pLunL0, "Driver", !strcmp(pszDevice, "i82078") ? "HostFloppy" : "HostDVD"); RC_CHECK();
{
}
}
else
{
/* create a new block driver config */
rc = CFGMR3InsertString(pCfg, "Type", !strcmp(pszDevice, "i82078") ? "Floppy 1.44" : "DVD"); RC_CHECK();
}
/*
* Attach the driver.
*/
rc = PDMR3DeviceAttach(pVM, pszDevice, uInstance, uLun, PDM_TACH_FLAGS_NOT_HOT_PLUG, &pBase); RC_CHECK();
{
{
}
/** @todo later pass full VDConfig information and parent images */
}
/* Dump the new controller configuration. */
{
if (!pIMount)
{
AssertFailed();
return rc;
}
}
}
while (0);
/*
Unlock before resuming because the vmstateChangeCallback problem
described above.
*/
/*
* Resume the VM if necessary.
*/
if (fResume)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
pThis->mVMStateChangeCallbackDisabled = false;
if (RT_FAILURE(rc))
{
/* too bad, we failed. try to sync the console state with the VMM state */
}
/// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
// error (if any) will be hidden from the caller. For proper reporting
// of such multiple errors to the caller we need to enhance the
// IVirtualBoxError interface. For now, give the first error the higher
// priority.
if (RT_SUCCESS(rcRet))
}
return rcRet;
}
/**
* Called by IInternalSessionControl::OnNetworkAdapterChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Don't do anything if the VM isn't running */
if (!mpVM)
return S_OK;
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* Get the properties we need from the adapter */
{
}
{
{
/*
* Find the pcnet instance, get the config interface and update
* the link state.
*/
const char *pszAdapterName = NULL;
switch (adapterType)
{
pszAdapterName = "e1000";
break;
#ifdef VBOX_WITH_E1000
pszAdapterName = "e1000";
break;
#endif
#ifdef VBOX_WITH_VIRTIO
pszAdapterName = "virtio";
break;
#endif
default:
AssertFailed();
pszAdapterName = "unknown";
break;
}
if (RT_SUCCESS(vrc))
{
if (pINetCfg)
{
Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
}
#ifdef VBOX_DYNAMIC_NET_ATTACH
{
if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbit or deal correctly with the _LS variants */
|| enmVMState == VMSTATE_SUSPENDED)
{
{
}
{
}
}
}
#endif /* VBOX_DYNAMIC_NET_ATTACH */
}
if (RT_FAILURE(vrc))
}
}
/* notify console callbacks on success */
{
}
return rc;
}
#ifdef VBOX_DYNAMIC_NET_ATTACH
/**
* Process a network adaptor change.
*
* @returns COM status code.
*
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param aNetworkAdapter The network adapter whose attachment needs to be changed
*
* @note Locks this object for writing.
*/
unsigned uInstance,
unsigned uLun,
{
LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
AutoCaller autoCaller(this);
/* We will need to release the write lock before calling EMT */
AutoWriteLock alock(this);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
/* leave the lock before waiting for a result (EMT will call us back!) */
{
if (RT_SUCCESS(vrc))
}
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
return S_OK;
}
tr("Could not change the network adaptor attachement type (%Rrc)"),
vrc);
}
/**
* Performs the Network Adaptor change in EMT.
*
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param aNetworkAdapter The network adapter whose attachment needs to be changed
*
* @thread EMT
* @note Locks the Console object for writing.
*/
const char *pszDevice,
unsigned uInstance,
unsigned uLun,
{
LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
AssertMsg( (!strcmp(pszDevice, "pcnet") && uLun == 0 && uInstance < SchemaDefs::NetworkAdapterCount)
/* protect mpVM */
/*
* Suspend the VM first.
*
* The VM must not be running since it might have pending I/O to
* the drive which is being changed.
*/
bool fResume;
switch (enmVMState)
{
case VMSTATE_RESETTING:
case VMSTATE_RUNNING:
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
pThis->mVMStateChangeCallbackDisabled = false;
fResume = true;
break;
}
case VMSTATE_SUSPENDED:
case VMSTATE_CREATED:
case VMSTATE_OFF:
fResume = false;
break;
default:
}
int rc = VINF_SUCCESS;
int rcRet = VINF_SUCCESS;
rcRet = configNetwork(pThis, pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
/*
* Resume the VM if necessary.
*/
if (fResume)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
pThis->mVMStateChangeCallbackDisabled = false;
if (RT_FAILURE(rc))
{
/* too bad, we failed. try to sync the console state with the VMM state */
}
/// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
// error (if any) will be hidden from the caller. For proper reporting
// of such multiple errors to the caller we need to enhance the
// IVirtualBoxError interface. For now, give the first error the higher
// priority.
if (RT_SUCCESS(rcRet))
}
return rcRet;
}
#endif /* VBOX_DYNAMIC_NET_ATTACH */
/**
* Called by IInternalSessionControl::OnSerialPortChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Don't do anything if the VM isn't running */
if (!mpVM)
return S_OK;
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* nothing to do so far */
/* notify console callbacks on success */
{
}
return rc;
}
/**
* Called by IInternalSessionControl::OnParallelPortChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Don't do anything if the VM isn't running */
if (!mpVM)
return S_OK;
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* nothing to do so far */
/* notify console callbacks on success */
{
}
return rc;
}
/**
* Called by IInternalSessionControl::OnStorageControllerChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Don't do anything if the VM isn't running */
if (!mpVM)
return S_OK;
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* nothing to do so far */
/* notify console callbacks on success */
{
(*it++)->OnStorageControllerChange();
}
return rc;
}
/**
* Called by IInternalSessionControl::OnMediumChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Don't do anything if the VM isn't running */
if (!mpVM)
return S_OK;
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* notify console callbacks on success */
{
}
return rc;
}
/**
* Called by IInternalSessionControl::OnVRDPServerChange().
*
* @note Locks this object for writing.
*/
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if ( mVRDPServer
&& ( mMachineState == MachineState_Running
)
)
{
/* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
if (vrdpEnabled)
{
// If there was no VRDP server started the 'stop' will do nothing.
// However if a server was started and this notification was called,
// we have to restart the server.
{
}
else
{
}
}
else
{
}
}
/* notify console callbacks on success */
{
(*it++)->OnVRDPServerChange();
}
return rc;
}
/**
* @note Locks this object for reading.
*/
void Console::onRemoteDisplayInfoChange()
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
(*it++)->OnRemoteDisplayInfoChange();
}
/**
* Called by IInternalSessionControl::OnUSBControllerChange().
*
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Ignore if no VM is running yet. */
if (!mpVM)
return S_OK;
/// @todo (dmik)
// check for the Enabled state and disable virtual USB controller??
// Anyway, if we want to query the machine's USB Controller we need to cache
// it to mUSBController in #init() (as it is done with mDVDDrive).
//
// bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)
//
// /* protect mpVM */
// AutoVMCaller autoVMCaller(this);
// CheckComRCReturnRC(autoVMCaller.rc());
/* notify console callbacks on success */
{
(*it++)->OnUSBControllerChange();
}
return rc;
}
/**
* Called by IInternalSessionControl::OnSharedFolderChange().
*
* @note Locks this object for writing.
*/
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* notify console callbacks on success */
{
: (Scope_T)Scope_Machine);
}
return rc;
}
/**
* Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
* processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
* returns TRUE for a given remote USB device.
*
* @return S_OK if the device was attached to the VM.
* @return failure if not attached.
*
* @param aDevice
* The device in question.
* @param aMaskedIfs
* The interfaces to hide from the guest.
*
* @note Locks this object for writing.
*/
HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* protect mpVM (we don't need error info, since it's a callback) */
AutoVMCallerQuiet autoVMCaller(this);
{
/* The VM may be no more operational when this message arrives
* (e.g. it may be Saving or Stopping or just PoweredOff) --
* autoVMCaller.rc() will return a failure in this case. */
LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
return autoVMCaller.rc();
}
{
/* notify callbacks about the error */
return S_OK;
}
/* Don't proceed unless there's at least one USB hub. */
if (!PDMR3USBHasHub(mpVM))
{
LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
return E_FAIL;
}
{
/* take the current error info */
/* the error must be a VirtualBoxErrorInfo instance */
{
/* notify callbacks about the error */
}
}
return rc;
#else /* !VBOX_WITH_USB */
return E_FAIL;
#endif /* !VBOX_WITH_USB */
}
/**
* Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
* processRemoteUSBDevices().
*
* @note Locks this object for writing.
*/
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Find the device. */
{
{
break;
}
++ it;
}
{
LogFlowThisFunc(("USB device not found.\n"));
/* The VM may be no more operational when this message arrives
* (e.g. it may be Saving or Stopping or just PoweredOff). Use
* AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
* failure in this case. */
AutoVMCallerQuiet autoVMCaller(this);
{
LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
return autoVMCaller.rc();
}
/* the device must be in the list otherwise */
}
{
/* notify callback about an error */
return S_OK;
}
{
/* take the current error info */
/* the error must be a VirtualBoxErrorInfo instance */
{
/* notify callbacks about the error */
}
}
return rc;
#else /* !VBOX_WITH_USB */
return E_FAIL;
#endif /* !VBOX_WITH_USB */
}
/**
* @note Temporarily locks this object for writing.
*/
{
#ifndef VBOX_WITH_GUEST_PROPS
#else
return E_INVALIDARG;
return E_POINTER;
return E_POINTER;
return E_POINTER;
AutoCaller autoCaller(this);
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
using namespace guestProp;
try
{
/* The + 1 is the null terminator */
4, &parm[0]);
/* The returned string should never be able to be greater than our buffer */
{
if (vrc != VERR_NOT_FOUND)
{
}
else
}
else
tr("The service call failed with the error %Rrc"),
vrc);
}
{
rc = E_OUTOFMEMORY;
}
return rc;
#endif /* VBOX_WITH_GUEST_PROPS */
}
/**
* @note Temporarily locks this object for writing.
*/
{
#ifndef VBOX_WITH_GUEST_PROPS
#else
return E_INVALIDARG;
return E_INVALIDARG;
return E_INVALIDARG;
AutoCaller autoCaller(this);
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
using namespace guestProp;
int vrc = VINF_SUCCESS;
/* The + 1 is the null terminator */
{
/* The + 1 is the null terminator */
}
{
/* The + 1 is the null terminator */
}
3, &parm[0]);
2, &parm[0]);
else
1, &parm[0]);
if (RT_SUCCESS(vrc))
else
tr("The service call failed with the error %Rrc"),
vrc);
return rc;
#endif /* VBOX_WITH_GUEST_PROPS */
}
/**
* @note Temporarily locks this object for writing.
*/
{
#ifndef VBOX_WITH_GUEST_PROPS
#else
return E_POINTER;
if (ComSafeArrayOutIsNull(aNames))
return E_POINTER;
if (ComSafeArrayOutIsNull(aValues))
return E_POINTER;
return E_POINTER;
if (ComSafeArrayOutIsNull(aFlags))
return E_POINTER;
AutoCaller autoCaller(this);
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
#endif /* VBOX_WITH_GUEST_PROPS */
}
/**
* Gets called by Session::UpdateMachineState()
* (IInternalSessionControl::updateMachineState()).
*
* Must be called only in certain cases (see the implementation).
*
* @note Locks this object for writing.
*/
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
, E_FAIL);
return setMachineStateLocally(aMachineState);
}
/**
* @note Locks this object for writing.
*/
void *pShape)
{
#if 0
LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
#endif
AutoCaller autoCaller(this);
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this);
/* Save the callback arguments */
/* start with not valid */
{
/* try to reuse the old shape buffer if the size is the same */
if (!wasValid)
else
{
}
{
}
}
else
{
}
#if 0
#endif
}
/**
* @note Locks this object for writing.
*/
{
LogFlowThisFunc(("supportsAbsolute=%d needsHostCursor=%d\n",
AutoCaller autoCaller(this);
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this);
/* save the callback arguments */
{
}
}
/**
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
}
/**
* @note Locks this object for reading.
*/
void Console::onAdditionsStateChange()
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
(*it++)->OnAdditionsStateChange();
}
/**
* @note Locks this object for reading.
*/
void Console::onAdditionsOutdated()
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
/** @todo Use the On-Screen Display feature to report the fact.
* The user should be told to install additions that are
* provided with the current VBox build:
* VBOX_VERSION_MAJOR.VBOX_VERSION_MINOR.VBOX_VERSION_BUILD
*/
}
/**
* @note Locks this object for writing.
*/
{
AutoCaller autoCaller(this);
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this);
/* save the callback arguments */
}
/**
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
}
/**
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
AutoReadLock alock(this);
}
/**
* @note Locks this object for reading.
*/
{
*aWinId = 0;
AutoCaller autoCaller(this);
AutoReadLock alock(this);
if (aCheck)
{
{
return rc;
}
}
else
{
{
return rc;
/* only one callback may return non-null winId */
if (*aWinId == 0)
}
}
return S_OK;
}
// private methods
////////////////////////////////////////////////////////////////////////////////
/**
* Increases the usage counter of the mpVM pointer. Guarantees that
* VMR3Destroy() will not be called on it at least until releaseVMCaller()
* is called.
*
* If this method returns a failure, the caller is not allowed to use mpVM
* and may return the failed result code to the upper level. This method sets
* the extended error info on failure if \a aQuiet is false.
*
* Setting \a aQuiet to true is useful for methods that don't want to return
* the failed result code to the caller when this method fails (e.g. need to
* silently check for the mpVM availability).
*
* When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
* returned instead of asserting. Having it false is intended as a sanity check
* for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
*
* @param aQuiet true to suppress setting error info
* @param aAllowNullVM true to accept mpVM being NULL and return a failure
* (otherwise this method will assert if mpVM is NULL)
*
* @note Locks this object for writing.
*/
bool aAllowNullVM /* = false */)
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if (mVMDestroying)
{
/* powerDown() is waiting for all callers to finish */
tr("Virtual machine is being powered down"));
}
{
Assert(aAllowNullVM == true);
/* The machine is not powered up */
tr("Virtual machine is not powered up"));
}
++ mVMCallers;
return S_OK;
}
/**
* Decreases the usage counter of the mpVM pointer. Must always complete
* the addVMCaller() call after the mpVM pointer is no more necessary.
*
* @note Locks this object for writing.
*/
void Console::releaseVMCaller()
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
Assert(mVMCallers > 0);
--mVMCallers;
if (mVMCallers == 0 && mVMDestroying)
{
/* inform powerDown() there are no more callers */
}
}
/**
* Initialize the release logging facility. In case something
* goes wrong, there will be no release logging. Maybe in the future
* we can add some logic to use different file names in this case.
* Note that the logic must be in sync with Machine::DeleteSettings().
*/
{
/* make sure the Logs folder exists */
/*
* Age the old log files
* Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
* Overwrite target files in case they exist.
*/
if (uLogHistoryCount)
{
for (int i = uLogHistoryCount-1; i >= 0; i--)
{
for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
{
if (i > 0)
else
/* If the old file doesn't exist, delete the new file (if it
* exists) to provide correct rotation even if the sequence is
* broken */
}
}
}
static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
#endif
if (RT_SUCCESS(vrc))
{
/* some introductory information */
char szTmp[256];
RTLogRelLogger(loggerRelease, 0, ~0U,
"VirtualBox %s r%u %s (%s %s) release log\n"
#ifdef VBOX_BLEEDING_EDGE
#endif
"Log opened %s\n",
/* the package type is interesting for Linux distributions */
char szExecName[RTPATH_MAX];
RTLogRelLogger(loggerRelease, 0, ~0U,
"Executable: %s\n"
"Process ID: %u\n"
"Package type: %s"
#ifdef VBOX_OSE
" (OSE)"
#endif
"\n",
RTProcSelf(),
/* register this logger as the release logger */
}
else
tr("Failed to open release log (%s, %Rrc)"),
return hrc;
}
/**
* Common worker for PowerUp and PowerUpPaused.
*
* @returns COM status code.
*
* @param aProgress Where to return the progress object.
* @param aPaused true if PowerUpPaused called.
*
* @todo move down to powerDown();
*/
{
return E_POINTER;
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
return setError(VBOX_E_INVALID_VM_STATE,
tr("Virtual machine is already running or busy (machine state: %s)"),
/* the network cards will undergo a quick consistency check */
{
if (!enabled)
continue;
switch (netattach)
{
{
#ifdef RT_OS_WINDOWS
/* a valid host interface must have been set */
if (!hostif)
{
return setError(VBOX_E_HOST_ERROR,
tr("VM cannot start because host interface networking requires a host interface name to be set"));
}
{
return setError(VBOX_E_HOST_ERROR,
tr("VM cannot start because the host interface '%ls' does not exist"),
}
#endif /* RT_OS_WINDOWS */
break;
}
default:
break;
}
}
/* Read console data stored in the saved state file (if not yet done) */
rc = loadDataFromSavedState();
/* Check all types of shared folders and compose a single list */
{
/* first, insert global folders */
/* second, insert machine folders */
/* third, insert console folders */
}
/*
* Saved VMs will have to prove that their saved states seem kosher.
*/
if (mMachineState == MachineState_Saved)
{
if (RT_FAILURE(vrc))
return setError(VBOX_E_FILE_ERROR,
tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Discard the saved state prior to starting the VM"),
}
/* test and clear the TeleporterEnabled property */
if (fTeleporterEnabled)
{
}
/* create a progress object to track progress of this operation */
if (mMachineState == MachineState_Saved)
else if (fTeleporterEnabled)
else
fTeleporterEnabled /* aCancelable */);
/* setup task object and thread to carry out the operation
* asynchronously */
if (mMachineState == MachineState_Saved)
/* Reset differencing hard disks for which autoReset is true */
{
{
/** @todo later applies to floppies as well */
if (devType == DeviceType_HardDisk)
{
/* save for later use on the powerup thread */
/* needs autoreset? */
if (autoReset)
{
/* save for later use on the powerup thread */
}
}
}
}
/* pass the progress object to the caller if requested */
if (aProgress)
{
{
/* there are no other operations to track, return the powerup
* progress only */
}
else
{
/* create a combined progress object */
progresses.end());
}
}
0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
E_FAIL);
/* task is now owned by powerUpThread(), so release it */
/* finally, set the state: no right to fail in this method afterwards
* since we've already started the thread and it is now responsible for
* any error reporting and appropriate state change! */
if (mMachineState == MachineState_Saved)
else if (fTeleporterEnabled)
else
return S_OK;
}
/**
* Internal power off worker routine.
*
* This method may be called only at certain places with the following meaning
* as shown below:
*
* - if the machine state is either Running or Paused, a normal
* Console-initiated powerdown takes place (e.g. PowerDown());
* - if the machine state is Saving, saveStateThread() has successfully done its
* job;
* - if the machine state is Starting or Restoring, powerUpThread() has failed
* - if the machine state is Stopping, the VM has powered itself off (i.e. not
* as a result of the powerDown() call).
*
* Calling it in situations other than the above will cause unexpected behavior.
*
* Note that this method should be the only one that destroys mpVM and sets it
* to NULL.
*
* @param aProgress Progress object to run (may be NULL).
*
* @note Locks this object for writing.
*
* @note Never call this method from a thread that called addVMCaller() or
* instantiated an AutoVMCaller object; first call releaseVMCaller() or
* release(). Otherwise it will deadlock.
*/
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
/* Total # of steps for the progress object. Must correspond to the
* number of "advance percent count" comments in this method! */
enum { StepCount = 7 };
/* current step */
int vrc = VINF_SUCCESS;
/* sanity */
Assert(mVMDestroying == false);
LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%d, InUninit=%d)\n",
/* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
* VM has already powered itself off in vmstateChangeCallback() and is just
* notifying Console about that. In case of Starting or Restoring,
* powerUpThread() is calling us on failure, so the VM is already off at
* that point. */
if ( !mVMPoweredOff
&& ( mMachineState == MachineState_Starting
)
mVMPoweredOff = true;
/* go to Stopping state if not already there. Note that we don't go from
* set the state to Saved on VMSTATE_TERMINATED. In terms of protecting from
* inappropriate operations while leaving the lock below, Saving or
* Restoring should be fine too. Ditto for Teleporting* -> Teleported. */
if ( mMachineState != MachineState_Saving
&& mMachineState != MachineState_Teleporting /** @todo Live Migration: what should really happen here? */
&& mMachineState != MachineState_LiveSnapshotting /** @todo Live Migration: what should really happen here? */
)
/* ----------------------------------------------------------------------
* DONE with necessary state changes, perform the power down actions (it's
* safe to leave the object lock now if needed)
* ---------------------------------------------------------------------- */
/* Stop the VRDP server to prevent new clients connection while VM is being
* powered off. */
if (mConsoleVRDPServer)
{
LogFlowThisFunc(("Stopping VRDP server...\n"));
/* Leave the lock since EMT will call us back as addVMCaller()
* in updateDisplayData(). */
}
/* advance percent count */
if (aProgress)
#ifdef VBOX_WITH_HGCM
# ifdef VBOX_WITH_GUEST_PROPS
/* Save all guest property store entries to the machine XML file */
rc = E_OUTOFMEMORY;
else
{
try
{
{
)
{
}
}
|| timestampsIn.isNull()
)
/* PushGuestProperties() calls DiscardSettings(), which calls us back */
}
{
rc = E_OUTOFMEMORY;
}
}
/* advance percent count */
if (aProgress)
# endif /* VBOX_WITH_GUEST_PROPS defined */
/* Shutdown HGCM services before stopping the guest, because they might
* need a cleanup. */
if (mVMMDev)
{
LogFlowThisFunc(("Shutdown HGCM...\n"));
/* Leave the lock since EMT will call us back as addVMCaller() */
mVMMDev->hgcmShutdown();
}
/* advance percent count */
if (aProgress)
#endif /* VBOX_WITH_HGCM */
/* ----------------------------------------------------------------------
* Now, wait for all mpVM callers to finish their work if there are still
* some on other threads. NO methods that need mpVM (or initiate other calls
* that need it) may be called after this point
* ---------------------------------------------------------------------- */
if (mVMCallers > 0)
{
/* go to the destroying state to prevent from adding new callers */
mVMDestroying = true;
/* lazy creation */
if (mVMZeroCallersSem == NIL_RTSEMEVENT)
LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
mVMCallers));
}
/* advance percent count */
if (aProgress)
vrc = VINF_SUCCESS;
/* Power off the VM if not already done that */
if (!mVMPoweredOff)
{
LogFlowThisFunc(("Powering off the VM...\n"));
/* Leave the lock since EMT will call us back on VMR3PowerOff() */
/* Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
* VM-(guest-)initiated power off happened in parallel a ms before this
* call. So far, we let this error pop up on the user's side. */
}
else
{
/* reset the flag for further re-use */
mVMPoweredOff = false;
}
/* advance percent count */
if (aProgress)
LogFlowThisFunc(("Ready for VM destruction.\n"));
/* If we are called from Console::uninit(), then try to destroy the VM even
* on failure (this will most likely fail too, but what to do?..) */
{
/* If the machine has an USB comtroller, release all USB devices
* (symmetric to the code in captureUSBDevices()) */
bool fHasUSBController = false;
{
if (RT_SUCCESS(vrc))
{
fHasUSBController = true;
detachAllUSBDevices(false /* aDone */);
}
}
/* Now we've got to destroy the VM as well. (mpVM is not valid beyond
* this point). We leave the lock before calling VMR3Destroy() because
* it will result into calling destructors of drivers associated with
* Console children which may in turn try to lock Console (e.g. by
* instantiating SafeVMPtr to access mpVM). It's safe here because
* mVMDestroying is set which should prevent any activity. */
/* Set mpVM to NULL early just in case if some old code is not using
* addVMCaller()/releaseVMCaller(). */
LogFlowThisFunc(("Destroying the VM...\n"));
/* take the lock again */
/* advance percent count */
if (aProgress)
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
/* Note: the Console-level machine state change happens on the
* VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
* powerDown() is called from EMT (i.e. from vmstateChangeCallback()
* on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
* occurred yet. This is okay, because mMachineState is already
* Stopping in this case, so any other attempt to call PowerDown()
* will be rejected. */
}
else
{
/* bad bad bad, but what to do? */
tr("Could not destroy the machine. (Error: %Rrc)"),
vrc);
}
/* Complete the detaching of the USB devices. */
if (fHasUSBController)
detachAllUSBDevices(true /* aDone */);
/* advance percent count */
if (aProgress)
}
else
{
tr("Could not power off the machine. (Error: %Rrc)"),
vrc);
}
/* Finished with destruction. Note that if something impossible happened and
* we've failed to destroy the VM, mVMDestroying will remain true and
* mMachineState will be something like Stopping, so most Console methods
* will return an error to the caller. */
mVMDestroying = false;
{
/* uninit dynamically allocated members of mCallbackData */
{
}
}
/* complete the progress */
if (aProgress)
return rc;
}
/**
* @note Locks this object for writing.
*/
bool aUpdateServer /* = true */)
{
AutoCaller autoCaller(this);
AutoWriteLock alock(this);
if (mMachineState != aMachineState)
{
/// @todo (dmik)
// possibly, we need to redo onStateChange() using the dedicated
// Event thread, like it is done in VirtualBox. This will make it
// much safer (no deadlocks possible if someone tries to use the
// console from the callback), however, listeners will lose the
// ability to synchronously react to state changes (is it really
// necessary??)
LogFlowThisFunc(("Doing onStateChange()...\n"));
LogFlowThisFunc(("Done onStateChange()\n"));
if (aUpdateServer)
{
/* Server notification MUST be done from under the lock; otherwise
* the machine state here and on the server might go out of sync
* which can lead to various unexpected results (like the machine
* state being >= MachineState_Running on the server, while the
* session state is already SessionState_Closed at the same time
* there).
*
* Cross-lock conditions should be carefully watched out: calling
* UpdateState we will require Machine and SessionMachine locks
* (remember that here we're holding the Console lock here, and also
* all locks that have been entered by the thread before calling
* this method).
*/
LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
}
}
return rc;
}
/**
* Searches for a shared folder with the given logical name
* in the collection of shared folders.
*
* @param aName logical name of the shared folder
* @param aSharedFolder where to return the found object
* @param aSetError whether to set the error info if the folder is
* not found
* @return
* S_OK when found or E_INVALIDARG when not found
*
* @note The caller must lock this object for writing.
*/
bool aSetError /* = false */)
{
/* sanity check */
{
return S_OK;
}
if (aSetError)
tr("Could not find a shared folder named '%ls'."),
aName);
return VBOX_E_FILE_ERROR;
}
/**
* Fetches the list of global or machine shared folders from the server.
*
* @param aGlobal true to fetch global folders.
*
* @note The caller must lock this object for writing.
*/
{
/* sanity check */
/* protect mpVM (if not NULL) */
if (aGlobal)
{
/// @todo grab & process global folders when they are done
}
else
{
if (online)
{
/* send changes to HGCM if the VM is running */
/// @todo report errors as runtime warnings through VMSetError
if (online)
{
{
/* a new machine folder is added or
* the existing machine folder is changed */
; /* the console folder exists, nothing to do */
else
{
/* remove the old machine folder (when changed)
* or the global folder if any (when new) */
/* create the new machine folder */
}
}
/* forget the processed (or identical) folder */
}
}
/* process outdated (removed) folders */
/// @todo report errors as runtime warnings through VMSetError
if (online)
{
{
; /* the console folder exists, nothing to do */
else
{
/* remove the outdated machine folder */
/* create the global folder if there is any */
}
}
}
}
return rc;
}
/**
* Searches for a shared folder with the given name in the list of machine
* shared folders and then in the list of the global shared folders.
*
* @param aName Name of the folder to search for.
* @param aIt Where to store the pointer to the found folder.
* @return @c true if the folder was found and @c false otherwise.
*
* @note The caller must lock this object for reading.
*/
{
/* sanity check */
AssertReturn(isWriteLockOnCurrentThread(), false);
/* first, search machine folders */
return true;
/* second, search machine folders */
return true;
return false;
}
/**
* Calls the HGCM service to add a shared folder definition.
*
* @param aName Shared folder name.
* @param aHostPath Shared folder path.
*
* @note Must be called from under AutoVMCaller and when mpVM != NULL!
* @note Doesn't lock anything.
*/
{
/* sanity checks */
if (cbString >= UINT16_MAX)
if (cbString >= UINT16_MAX)
{
}
SHFL_CPARMS_ADD_MAPPING, &parms[0]);
if (RT_FAILURE(vrc))
tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
return S_OK;
}
/**
* Calls the HGCM service to remove the shared folder definition.
*
* @param aName Shared folder name.
*
* @note Must be called from under AutoVMCaller and when mpVM != NULL!
* @note Doesn't lock anything.
*/
{
/* sanity checks */
if (cbString >= UINT16_MAX)
1, &parms);
if (RT_FAILURE(vrc))
tr("Could not remove the shared folder '%ls' (%Rrc)"),
return S_OK;
}
/**
* VM state callback function. Called by the VMM
* using its state machine states.
*
* Primarily used to handle VM initiated power off, suspend and state saving,
* but also for doing termination completed work (VMSTATE_TERMINATE).
*
* In general this function is called in the context of the EMT.
*
* @param aVM The VM handle.
* @param aState The new state.
* @param aOldState The old state.
* @param aUser The user argument (pointer to the Console object).
*
* @note Locks the Console object for writing.
*/
void *aUser)
{
LogFlowFunc(("Changing state from %d to %d (aVM=%p)\n",
/* Note that we must let this method proceed even if Console::uninit() has
* been already called. In such case this VMSTATE change is a result of:
* 1) powerDown() called from uninit() itself, or
* 2) VM-(guest-)initiated power off. */
switch (aState)
{
/*
* The VM has terminated
*/
case VMSTATE_OFF:
{
break;
/* Do we still think that it is running? It may happen if this is a
*/
)
{
LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
/* prevent powerDown() from calling VMR3PowerOff() again */
that->mVMPoweredOff = true;
/* we are stopping now */
/* Setup task object and thread to carry out the operation
* asynchronously (if we call powerDown() right here but there
* is one or more mpVM callers (added with addVMCaller()) we'll
* deadlock).
*/
true /* aUsesVMPtr */));
/* If creating a task is falied, this can currently mean one of
* two: either Console::uninit() has been called just a ms
* before (so a powerDown() call is already on the way), or
* powerDown() itself is being already executed. Just do
* nothing.
*/
{
LogFlowFunc(("Console is already being uninitialized.\n"));
break;
}
"VMPowerDown");
/* task is now owned by powerDownThread(), so release it */
}
break;
}
/* The VM has been completely destroyed.
*
* Note: This state change can happen at two points:
* 1) At the end of VMR3Destroy() if it was not called from EMT.
* 2) At the end of vmR3EmulationThread if VMR3Destroy() was
* called by EMT.
*/
case VMSTATE_TERMINATED:
{
break;
/* Terminate host interface networking. If aVM is NULL, we've been
* manually called from powerUpThread() either before calling
* VMR3Create() or after VMR3Create() failed, so no need to touch
* networking.
*/
if (aVM)
/* From now on the machine is officially powered down or remains in
* the Saved state.
*/
switch (that->mMachineState)
{
default:
AssertFailed();
/* fall through */
case MachineState_Stopping:
/* successfully powered down */
break;
case MachineState_Saving:
/* successfully saved (note that the machine is already in
* the Saved state on the server due to EndSavingState()
* called from saveStateThread(), so only change the local
* state) */
break;
case MachineState_Starting:
/* failed to start, but be patient: set back to PoweredOff
* (for similarity with the below) */
break;
case MachineState_Restoring:
/* failed to load the saved state file, but be patient: set
* back to Saved (to preserve the saved state file) */
break;
/* Teleportation failed or was cancelled. Back to powered off. */
break;
case MachineState_Teleporting:
/* Successfully teleported the VM. */
break;
}
break;
}
case VMSTATE_SUSPENDED:
{
if (aOldState == VMSTATE_SUSPENDING)
{
break;
/* Change the machine state from Running to Paused. */
}
break;
}
case VMSTATE_RUNNING:
{
if ( aOldState == VMSTATE_POWERING_ON
|| aOldState == VMSTATE_RESUMING)
{
break;
/* Change the machine state from Starting, Restoring or Paused
* to Running */
&& aOldState == VMSTATE_POWERING_ON)
&& aOldState == VMSTATE_RESUMING));
}
break;
}
case VMSTATE_FATAL_ERROR:
{
break;
/* Fatal errors are only for running VMs. */
/* Note! 'Pause' is used here in want of something better. There
* are currently only two places where fatal errors might be
* raised, so it is not worth adding a new externally
* visible state for this yet. */
break;
}
case VMSTATE_GURU_MEDITATION:
{
break;
/* Guru are only for running VMs */
break;
}
default: /* shut up gcc */
break;
}
}
#ifdef VBOX_WITH_USB
/**
* Sends a request to VMM to attach the given host device.
* After this method succeeds, the attached device will appear in the
* mUSBDevices collection.
*
* @param aHostDevice device to attach
*
* @note Synchronously calls EMT.
* @note Must be called from under this object's lock.
*/
{
/* still want a lock object because we need to leave it */
AutoWriteLock alock(this);
/*
* Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
* method in EMT (using usbAttachCallback()).
*/
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
/* leave the lock before a VMR3* call (EMT will call us back)! */
/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
/* restore the lock */
/* hrc is S_OK here */
if (RT_FAILURE(vrc))
{
LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
switch (vrc)
{
case VERR_VUSB_NO_PORTS:
tr("Failed to attach the USB device. (No available ports on the USB controller)."));
break;
tr("Not permitted to open the USB device, check usbfs options"));
break;
default:
tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
vrc);
break;
}
}
return hrc;
}
/**
* USB device attach callback used by AttachUSBDevice().
* Note that AttachUSBDevice() doesn't return until this callback is executed,
* so we don't use AutoCaller and don't care about reference counters of
* interface pointers passed in.
*
* @thread EMT
* @note Locks the console object for writing.
*/
//static
DECLCALLBACK(int)
Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
{
void *pvRemoteBackend = NULL;
if (aRemote)
{
pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
if (!pvRemoteBackend)
return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
}
if (RT_SUCCESS(vrc))
{
/* Create a OUSBDevice and add it to the device list */
/* notify callbacks */
}
return vrc;
}
/**
* Sends a request to VMM to detach the given host device. After this method
* succeeds, the detached device will disappear from the mUSBDevices
* collection.
*
* @param aIt Iterator pointing to the device to detach.
*
* @note Synchronously calls EMT.
* @note Must be called from under this object's lock.
*/
{
/* still want a lock object because we need to leave it */
AutoWriteLock alock(this);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
/* if the device is attached, then there must at least one USB hub. */
LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
/* leave the lock before a VMR3* call (EMT will call us back)! */
/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
return S_OK;
}
/**
* USB device detach callback used by DetachUSBDevice().
* Note that DetachUSBDevice() doesn't return until this callback is executed,
* so we don't use AutoCaller and don't care about reference counters of
* interface pointers passed in.
*
* @thread EMT
* @note Locks the console object for writing.
*/
//static
DECLCALLBACK(int)
{
/*
* If that was a remote device, release the backend pointer.
* The pointer was requested in usbAttachCallback.
*/
if (fRemote)
{
}
if (RT_SUCCESS(vrc))
{
/* Remove the device from the collection */
/* notify callbacks */
}
return vrc;
}
#endif /* VBOX_WITH_USB */
/**
* Helper function to handle host interface device creation and attachment.
*
* @param networkAdapter the network adapter which attachment should be reset
* @return COM status code
*
* @note The caller must lock this object for writing.
*
* @todo Move this back into the driver!
*/
{
LogFlowThisFunc(("\n"));
/* sanity check */
# ifdef VBOX_STRICT
/* paranoia */
# endif /* VBOX_STRICT */
# ifdef RT_OS_LINUX
/*
* Allocate a host interface device
*/
if (RT_SUCCESS(rcVBox))
{
/*
*/
/* The name of the TAP interface we are using */
if (tapDeviceName.isEmpty())
{
LogRel(("No TAP device name was supplied.\n"));
}
{
/* If we are using a static TAP device then try to open it. */
else
memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
if (rcVBox != 0)
{
tr("Failed to open the host network interface %ls"),
tapDeviceName.raw());
}
}
{
/*
* Make it pollable.
*/
{
/*
* Here is the right place to communicate the TAP file descriptor and
* necessary.
*/
}
else
{
LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
tr("could not set up the host networking device for non blocking access: %s"),
}
}
}
else
{
switch (rcVBox)
{
case VERR_ACCESS_DENIED:
/* will be handled by our caller */
break;
default:
tr("Could not set up the host networking device: %Rrc"),
rcVBox);
break;
}
}
# elif defined(RT_OS_FREEBSD)
/*
*/
/* The name of the TAP interface we are using */
if (tapDeviceName.isEmpty())
{
LogRel(("No TAP device name was supplied.\n"));
}
/* If we are using a static TAP device then try to open it. */
else
memcpy(szTapdev + strlen(szTapdev), str.raw(), sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
if (RT_SUCCESS(rcVBox))
else
{
switch (rcVBox)
{
case VERR_ACCESS_DENIED:
/* will be handled by our caller */
break;
default:
tr("Failed to open the host network interface %ls"),
tapDeviceName.raw());
break;
}
}
# else
# error "huh?"
# endif
/* in case of failure, cleanup. */
{
LogRel(("General failure attaching to host interface\n"));
tr("General failure attaching to host interface"));
}
return rc;
}
/**
* Helper function to handle detachment from a host interface
*
* @param networkAdapter the network adapter which attachment should be reset
* @return COM status code
*
* @note The caller must lock this object for writing.
*
* @todo Move this back into the driver!
*/
{
/* sanity check */
LogFlowThisFunc(("\n"));
# ifdef VBOX_STRICT
/* paranoia */
# endif /* VBOX_STRICT */
/* is there an open TAP device? */
{
/*
* Close the file handle.
*/
bool isStatic = true;
{
/* If the name is empty, this is a dynamic TAP device, so close it now,
so that the termination script can remove the interface. Otherwise we still
need the FD to pass to the termination script. */
isStatic = false;
}
if (isStatic)
{
/* If we are using a static TAP device, we close it now, after having called the
termination script. */
}
/* the TAP device name and handle are no longer valid */
}
return rc;
}
#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
/**
* Called at power down to terminate host interface networking.
*
* @note The caller must lock this object for writing.
*/
{
LogFlowThisFunc(("\n"));
/* sanity check */
/*
* host interface termination handling
*/
{
if (!enabled)
continue;
{
#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
#endif
}
}
return rc;
}
/**
* Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
* and VMR3Teleport.
*
* @param pVM The VM handle.
* @param uPercent Completetion precentage (0-100).
* @param pvUser Pointer to the VMProgressTask structure.
* @return VINF_SUCCESS.
*/
/*static*/
{
/* update the progress object */
return VINF_SUCCESS;
}
/**
* VM error callback function. Called by the various VM components.
*
* @param pVM VM handle. Can be NULL if an error occurred before
* successfully creating a VM.
* @param pvUser Pointer to the VMProgressTask structure.
* @param rc VBox status code.
* @param pszFormat Printf-like error message.
* @param args Various number of arguments for the error message.
*
* @thread EMT, VMPowerUp...
*
* @note The VMProgressTask structure modified by this callback is not thread
* safe.
*/
/* static */ DECLCALLBACK(void)
{
/* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */
/* append to the existing error message if any */
else
}
/**
* VM runtime error callback function.
* See VMSetRuntimeError for the detailed description of parameters.
*
* @param pVM The VM handle.
* @param pvUser The user argument.
* @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
* @param pszErrorId Error ID string.
* @param pszFormat Error message format string.
* @param va Error message arguments.
* @thread EMT.
*/
/* static */ DECLCALLBACK(void)
const char *pszErrorId,
{
LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
}
/**
* Captures USB devices that match filters of the VM.
* Called at VM startup.
*
* @param pVM The VM handle.
*
* @note The caller must lock this object for writing.
*/
{
LogFlowThisFunc(("\n"));
/* sanity check */
/* If the machine has an USB controller, ask the USB proxy service to
* capture devices */
if (RT_SUCCESS(vrc))
{
/* leave the lock before calling Host in VBoxSVC since Host may call
* us back from under its lock (e.g. onUSBDeviceAttach()) which would
* produce an inter-process dead-lock otherwise. */
AutoWriteLock alock(this);
}
else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
vrc = VINF_SUCCESS;
else
}
/**
* Detach all USB device which are attached to the VM for the
* purpose of clean up and such like.
*
* @note The caller must lock this object for writing.
*/
{
/* sanity check */
mUSBDevices.clear();
/* leave the lock before calling Host in VBoxSVC since Host may call
* us back from under its lock (e.g. onUSBDeviceAttach()) which would
* produce an inter-process dead-lock otherwise. */
AutoWriteLock alock(this);
}
/**
* @note Locks this object for writing.
*/
void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
{
LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
AutoCaller autoCaller(this);
if (!autoCaller.isOk())
{
/* Console has been already uninitialized, deny request */
AssertMsgFailed(("Console is already uninitialized\n"));
LogFlowThisFunc(("Console is already uninitialized\n"));
return;
}
AutoWriteLock alock(this);
/*
* Mark all existing remote USB devices as dirty.
*/
{
++ it;
}
/*
* Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
*/
/** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
VRDPUSBDEVICEDESC *e = pDevList;
/* The cbDevList condition must be checked first, because the function can
* receive pDevList = NULL and cbDevList = 0 on client disconnect.
*/
{
LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
bool fNewDevice = true;
{
{
/* The device is already in the list. */
fNewDevice = false;
break;
}
++ it;
}
if (fNewDevice)
{
LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
/* Create the device object and add the new device to list. */
/* Check if the device is ok for current USB filters. */
ULONG fMaskedIfs = 0;
if (fMatched)
{
/// @todo (r=dmik) warning reporting subsystem
{
LogFlowThisFunc(("Device attached\n"));
}
}
}
{
LogWarningThisFunc(("cbDevList %d > oNext %d\n",
break;
}
}
/*
* Remove dirty devices, that is those which are not reported by the server anymore.
*/
for (;;)
{
{
{
break;
}
++ it;
}
if (!device)
{
break;
}
LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
/* Detach the device from VM. */
{
}
/* And remove it from the list. */
}
}
/**
* Thread function which starts the VM (also from saved state) and
* track progress.
*
* @param Thread The thread id.
* @param pvUser Pointer to a VMPowerUpTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
{
#if defined(RT_OS_WINDOWS)
{
/* initialize COM */
}
#endif
int vrc = VINF_SUCCESS;
/* Set up a build identifier so that it can be seen from core dumps what
* exact build was used to produce the core. */
static char saBuildID[40];
/* Note: no need to use addCaller() because VMPowerUpTask does that */
/* The lock is also used as a signal from the task initiator (which
* releases it only after RTThreadCreate()) that we can start the job */
/* sanity */
try
{
/* wait for auto reset ops to complete so that we can successfully lock
* the attached hard disks by calling LockMedia() below */
{
}
/* lock attached media. This method will also check their
* accessibility. Note that the media will be unlocked automatically
* by SessionMachine::setMachineState() when the VM is powered down. */
#ifdef VBOX_WITH_VRDP
/* Create the VRDP server. In case of headless operation, this will
* also create the framebuffer, required at VM creation.
*/
/* Does VRDP server call Console from the other thread?
* Not sure (and can change), so leave the lock just in case.
*/
if (vrc == VERR_NET_ADDRESS_IN_USE)
{
LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
}
else if (RT_FAILURE(vrc))
{
switch (vrc)
{
case VERR_FILE_NOT_FOUND:
{
break;
}
default:
vrc);
}
LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
}
#endif /* VBOX_WITH_VRDP */
/*
* Create the VM
*/
/*
* leave the lock since EMT will call Console. It's safe because
* mMachineState is either Starting or Restoring state here.
*/
&pVM);
#ifdef VBOX_WITH_VRDP
/* Enable client connections to the server. */
#endif /* VBOX_WITH_VRDP */
if (RT_SUCCESS(vrc))
{
do
{
/*
*/
if (RT_FAILURE(vrc))
break;
/*
* Synchronize debugger settings
*/
if (machineDebugger)
{
}
/*
* Shared Folders
*/
{
/* Does the code below call Console from the other thread?
* Not sure, so leave the lock just in case. */
++ it)
{
}
/* enter the lock again */
}
/*
* Capture USB devices.
*/
/* leave the lock before a lengthy operation */
/* Load saved state? */
{
LogFlowFunc(("Restoring saved state from '%s'...\n",
if (RT_SUCCESS(vrc))
{
if (task->mStartPaused)
/* done */
else
{
}
}
/* Power off in case we failed loading or resuming the VM */
if (RT_FAILURE(vrc))
{
}
}
else if (task->mTeleporterEnabled)
{
/* -> ConsoleImplTeleporter.cpp */
if (RT_FAILURE(vrc))
}
else if (task->mStartPaused)
/* done */
else
{
/* Power on the VM (i.e. start executing) */
}
/* enter the lock again */
}
while (0);
/* On failure, destroy the VM */
{
/* preserve existing error info */
/* powerDown() will call VMR3Destroy() and do all necessary
* cleanup (VRDP, USB devices) */
}
else
{
/*
* Deregister the VMSetError callback. This is necessary as the
* pfnVMAtError() function passed to VMR3Create() is supposed to
* be sticky but our error callback isn't.
*/
/** @todo register another VMSetError callback? */
}
}
else
{
/*
* If VMR3Create() failed it has released the VM memory.
*/
}
{
/* If VMR3Create() or one of the other calls in this function fail,
* an appropriate error message has been set in task->mErrorMsg.
* However since that happens via a callback, the rc status code in
* this function is not updated.
*/
{
/* If the error message is not set but we've got a failure,
* convert the VBox status code into a meaningful error message.
* This becomes unused once all the sources of errors set the
* appropriate error message themselves.
*/
vrc);
}
/* Set the error message as the COM error.
* Progress::notifyComplete() will pick it up later. */
}
}
)
{
*
* 1) we failed before VMR3Create() was called;
* 2) VMR3Create() failed.
*
* In both cases, there is no need to call powerDown(), but we still
* need to go back to the PoweredOff/Saved state. Reuse
* vmstateChangeCallback() for that purpose.
*/
/* preserve existing error info */
console);
}
/*
* Evaluate the final result. Note that the appropriate mMachineState value
* is already set by vmstateChangeCallback() in all cases.
*/
/* leave the lock, don't need it any more */
{
/* Notify the progress object of the success */
}
else
{
/* The progress object will fetch the current error info */
}
#if defined(RT_OS_WINDOWS)
/* uninitialize COM */
#endif
return VINF_SUCCESS;
}
/**
* Reconfigures a VDI.
*
* @param pVM The VM handle.
* @param lInstance The instance of the controller.
* @param enmController The type of the controller.
* @param hda The harddisk attachment.
* @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
* @return VBox status code.
*/
{
int rc;
#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
/*
* Figure out which IDE device this is.
*/
int iLUN;
const char *pcszDevice = NULL;
bool fSCSI = false;
switch (enmController)
{
{
{
return VERR_GENERAL_FAILURE;
}
{
return VERR_GENERAL_FAILURE;
}
pcszDevice = "piix3ide";
break;
}
{
pcszDevice = "ahci";
break;
}
{
pcszDevice = "buslogic";
fSCSI = true;
break;
}
{
pcszDevice = "lsilogicscsi";
fSCSI = true;
break;
}
default:
{
return VERR_GENERAL_FAILURE;
}
}
/** @todo this should be unified with the relevant part of
* Console::configConstructor to avoid inconsistencies. */
/*
* Is there an existing LUN? If not create it.
* We ASSUME that this will NEVER collide with the DVD.
*/
/* SCSI has an extra driver between the device and the block driver. */
if (fSCSI)
pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/AttachedDriver/", pcszDevice, lInstance, iLUN);
else
pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/LUN#%d/AttachedDriver/", pcszDevice, lInstance, iLUN);
if (!pLunL1)
{
if (fSCSI)
{
}
}
else
{
#ifdef VBOX_STRICT
char *pszDriver;
#endif
/* Here used to be a lot of code checking if things have changed,
* but that's not really worth it, as with snapshots there is always
* some change, so the code was just logging useless information in
* a hard to analyze form. */
/*
* Detach the driver and replace the config node.
*/
}
/*
* Create the driver configuration.
*/
/* Pass all custom parameters. */
bool fHostIP = true;
ComSafeArrayAsOutParam(values)); H();
{
{
if (values[i])
{
fHostIP = false;
}
}
}
/* Create an inversed tree of parents. */
{
break;
/* Pass all custom parameters. */
ComSafeArrayAsOutParam(values)); H();
{
{
if (values[i])
{
fHostIP = false;
}
}
}
/* Custom code: put marker to not use host IP stack to driver
* configuration node. Simplifies life of DrvVD a bit. */
if (!fHostIP)
{
}
/* next */
}
/*
* Attach the new driver.
*/
rc = PDMR3DeviceAttach(pVM, pcszDevice, 0, iLUN, PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/); RC_CHECK();
LogFlowFunc(("Returns success\n"));
return rc;
}
/**
* Worker thread created by Console::TakeSnapshot.
* @param Thread The current thread (ignored).
* @param pvUser The task.
* @return VINF_SUCCESS (ignored).
*/
/*static*/
{
// taking a snapshot consists of the following:
// 1) creating a diff image for each virtual hard disk, into which write operations go after
// the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
// 2) creating a Snapshot object with the state of the machine (hardware + storage,
// done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
// 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
bool fBeganTakingSnapshot = false;
try
{
/* STEP 1 + 2:
* request creating the diff images on the server and create the snapshot object
* (this will set the machine state to Saving on the server to block
* others from accessing this machine)
*/
fBeganTakingSnapshot = true;
/*
* state file is non-null only when the VM is paused
* (i.e. creating a snapshot online)
*/
/* sync the state with the server */
// STEP 3: save the VM state (if online)
if (pTask->fTakingSnapshotOnline)
{
true /*fContinueAfterwards*/,
(void*)pTask);
if (RT_FAILURE(vrc))
tr("Failed to save the machine state to '%s' (%Rrc)"),
// STEP 4: reattach hard disks
LogFlowFunc(("Reattaching new differencing hard disks...\n"));
1); // operation weight, same as computed when setting up progress object
for (size_t i = 0;
++i)
{
/*
* We can't pass a storage controller object directly
* (g++ complains about not being able to pass non POD types through '...')
* so we have to query needed values here and pass them.
*/
/*
* don't leave the lock since reconfigureHardDisks isn't going
* to access Console.
*/
5,
atts[i],
&rc);
if (RT_FAILURE(vrc))
break;
break;
}
}
/*
* finalize the requested snapshot object.
* This will reset the machine state to the state it had right
* before calling mControl->BeginTakingSnapshot().
*/
// do not throw rc here because we can't call EndTakingSnapshot() twice
}
{
/* preserve existing error info */
if (fBeganTakingSnapshot)
}
delete pTask;
{
/* restore the paused state if appropriate */
/* restore the running state if appropriate */
}
else
return VINF_SUCCESS;
}
/**
* Thread for executing the saved state operation.
*
* @param Thread The thread handle.
* @param pvUser Pointer to a VMSaveTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
{
false, /*fContinueAfterwards*/
if (RT_FAILURE(vrc))
{
}
/* lock the console once we're going to access it */
/*
* finalize the requested save state procedure.
* In case of success, the server will set the machine state to Saved;
* in case of failure it will reset the it to the state it had right
* before calling mControl->BeginSavingState().
*/
/* synchronize the state with the server */
{
/*
* The machine has been successfully saved, so power it down
* (vmstateChangeCallback() will set state to Saved on success).
* Note: we release the task's VM caller, otherwise it will
* deadlock.
*/
task->releaseVMCaller();
}
/* notify the progress object about operation completion */
else
{
else
}
return VINF_SUCCESS;
}
/**
* Thread for powering down the Console.
*
* @param Thread The thread handle.
* @param pvUser Pointer to the VMTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
{
/* Note: no need to use addCaller() to protect Console because VMTask does
* that */
/* wait until the method tat started us returns */
/* release VM caller to avoid the powerDown() deadlock */
task->releaseVMCaller();
return VINF_SUCCESS;
}
/**
* The Main status driver instance data.
*/
typedef struct DRVMAINSTATUS
{
/** The LED connectors. */
/** Pointer to the LED ports interface above us. */
/** Pointer to the array of LED pointers. */
/** The unit number corresponding to the first entry in the LED array. */
/** The unit number corresponding to the last entry in the LED array.
* (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
/**
* Notification about a unit which have been changed.
*
* The driver must discard any pointers to data owned by
* the unit and requery it.
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param iLUN The unit number.
*/
{
{
if (RT_FAILURE(rc))
}
}
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the driver.
* @param pInterface Pointer to this interface structure.
* @param enmInterface The requested interface identification.
*/
DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pDrv->ILedConnectors;
default:
return NULL;
}
}
/**
* Destruct a status driver instance.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
*/
{
{
while (iLed-- > 0)
}
}
/**
* Construct a status driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
{
/*
* Validate configuration.
*/
("Configuration error: Not possible to attach anything to this driver!\n"),
/*
* Data.
*/
/*
* Read config.
*/
if (RT_FAILURE(rc))
{
return rc;
}
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (RT_FAILURE(rc))
{
return rc;
}
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (RT_FAILURE(rc))
{
return rc;
}
{
AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
return VERR_GENERAL_FAILURE;
}
/*
* query the LEDs we want.
*/
pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS);
{
AssertMsgFailed(("Configuration error: No led ports interface above!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
}
return VINF_SUCCESS;
}
/**
* Keyboard driver registration record.
*/
{
/* u32Version */
/* szDriverName */
"MainStatus",
/* pszDescription */
"Main status driver (Main as in the API).",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVMAINSTATUS),
/* pfnConstruct */
/* pfnDestruct */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
};
/* vi: set tabstop=4 shiftwidth=4 expandtab: */