service.cpp revision 2ad9f8a731c73f6ac74044d42d47bbaf6f44a566
/* $Id$ */
/** @file
* Drag and Drop Service.
*/
/*
* Copyright (C) 2011-2014 Oracle Corporation
*
* 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.
*/
/** @page pg_svc_guest_control Guest Control HGCM Service
*
* This service acts as a proxy for handling and buffering host command requests
* and clients on the guest. It tries to be as transparent as possible to let
* the guest (client) and host side do their protocol handling as desired.
*
* The following terms are used:
* - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
* which wants to control something on the guest.
* - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
* new host commands to perform. There can be multiple clients connected
* to a service. A client is represented by its HGCM client ID.
* - Context ID: An (almost) unique ID automatically generated on the host (Main API)
* to not only distinguish clients but individual requests. Because
* the host does not know anything about connected clients it needs
* an indicator which it can refer to later. This context ID gets
* internally bound by the service to a client which actually processes
* the command in order to have a relationship between client<->context ID(s).
*
* The host can trigger commands which get buffered by the service (with full HGCM
* parameter info). As soon as a client connects (or is ready to do some new work)
* it gets a buffered host command to process it. This command then will be immediately
* removed from the command list. If there are ready clients but no new commands to be
* processed, these clients will be set into a deferred state (that is being blocked
* to return until a new command is available).
*
* If a client needs to inform the host that something happened, it can send a
* message to a low level HGCM callback registered in Main. This callback contains
* the actual data as well as the context ID to let the host do the next necessary
* steps for this context. This context ID makes it possible to wait for an event
* inside the host's Main API function (like starting a process on the guest and
* wait for getting its PID returned by the client) as well as cancelling blocking
* host calls in order the client terminated/crashed (HGCM detects disconnected
* clients and reports it to this service's callback).
*/
/******************************************************************************
* Header Files *
******************************************************************************/
#ifdef LOG_GROUP
#endif
#define LOG_GROUP LOG_GROUP_GUEST_DND
#include "dndmanager.h"
/******************************************************************************
* Service class declaration *
******************************************************************************/
/**
* Specialized drag & drop service class.
*/
{
public:
, m_pManager(0)
, m_cClients(0)
{}
protected:
/* HGCM service implementation */
int uninit();
void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
static DECLCALLBACK(int) progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser);
};
/******************************************************************************
* Service class implementation *
******************************************************************************/
{
/* Register functions. */
/* Drag'n drop mode is disabled by default. */
return VINF_SUCCESS;
}
int DragAndDropService::uninit(void)
{
delete m_pManager;
return VINF_SUCCESS;
}
{
if (m_cClients < UINT32_MAX)
m_cClients++;
else
AssertMsgFailed(("Maximum number of clients reached\n"));
/*
* Clear the message queue as soon as a new clients connect
* to ensure that every client has the same state.
*/
if (m_pManager)
m_pManager->clear();
return VINF_SUCCESS;
}
{
/* Remove all waiters with this u32ClientID. */
{
{
if (m_pHelpers)
delete pClient;
}
else
i++;
}
return VINF_SUCCESS;
}
{
/** @todo Validate mode. */
switch (u32Mode)
{
break;
default:
break;
}
return VINF_SUCCESS;
}
{
LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
/* Check if we've the right mode set. */
bool fIgnoreRequest = true; /* Play safe. */
switch (u32Function)
{
if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
{
fIgnoreRequest = false;
}
else
LogFlowFunc(("Drag'n drop disabled, ignoring request\n"));
break;
if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
{
fIgnoreRequest = false;
}
else
LogFlowFunc(("Host -> guest DnD mode disabled, ignoring request\n"));
break;
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
{
fIgnoreRequest = false;
}
else
LogFlowFunc(("Guest -> host DnD mode disabled, ignoring request\n"));
break;
#endif
default:
/* Reach through to DnD manager. */
fIgnoreRequest = false;
break;
}
int rc;
if (!fIgnoreRequest)
{
switch (u32Function)
{
/* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
* used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
* HOST_DND_GH_REQ_PENDING, which led this service returning
* VERR_INVALID_PARAMETER when the guest wanted to actually
* handle HOST_DND_GH_REQ_PENDING. */
{
LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
if ( cParms != 3
else
{
if ( RT_FAILURE(rc)
{
}
}
break;
}
{
LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
if ( cParms != 1
else
{
if (m_pfnHostCallback)
// m_pHelpers->pfnCallComplete(callHandle, rc);
}
break;
}
{
LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
if ( cParms != 1
else
{
if (m_pfnHostCallback)
// m_pHelpers->pfnCallComplete(callHandle, rc);
// if (data.pszFormat)
// RTMemFree(data.pszFormat);
// if (data.pszTmpPath)
// RTMemFree(data.pszTmpPath);
}
break;
}
#ifdef VBOX_WITH_DRAG_AND_DROP_GH
{
LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
if ( cParms != 3
else
{
if (m_pfnHostCallback)
}
break;
}
{
LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
if ( cParms != 2
else
{
if (m_pfnHostCallback)
}
break;
}
{
LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
if ( cParms != 3
else
{
#ifdef DEBUG_andy
LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n",
#endif
if (m_pfnHostCallback)
}
break;
}
{
LogFlowFunc(("GUEST_DND_GH_SND_FILE\n"));
if ( cParms != 5
else
{
/* paParms[3] is cbData. */
#ifdef DEBUG_andy
LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
#endif
if (m_pfnHostCallback)
}
break;
}
{
LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
if ( cParms != 1
else
{
if (m_pfnHostCallback)
}
break;
}
#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
default:
{
/* All other messages are handled by the DnD manager. */
break;
}
}
}
else
/* If async execute is requested, we didn't notify the guest about
* completion. The client is queued into the waiters list and will be
* notified as soon as a new event is available. */
if ( rc != VINF_HGCM_ASYNC_EXECUTE
&& m_pHelpers)
{
}
}
{
int rc;
{
if (cParms != 1)
else
}
else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
{
{
if (RT_SUCCESS(rc))
{
/* Check if this was a request for getting the next host
* message. If so, return the message id and the parameter
* count. The message itself has to be queued. */
{
if (RT_SUCCESS(rc))
{
if (m_pHelpers)
delete pClient;
}
else
}
else
AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
}
else
AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
u32Function, rc));
}
else
{
/* Tell the host that the guest does not support drag'n drop.
* This might happen due to not installed Guest Additions or
* not running VBoxTray/VBoxClient. */
}
}
else
{
/* Tell the host that a wrong drag'n drop mode is set. */
}
return rc;
}
DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser)
{
if (pSelf->m_pfnHostCallback)
{
LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uPercentage=%RU32, uState=%RU32, rc=%Rrc\n",
}
return VINF_SUCCESS;
}
/**
* @copydoc VBOXHGCMSVCLOAD
*/
{
}