/* $Id: service.cpp $ */
/* @file
 * Host Channel: Host service entry points.
 */

/*
 * Copyright (C) 2012 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * 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.
 */


/*
 * The HostChannel host service provides a generic proxy between a host's
 * channel provider and a client running in the guest.
 *
 * Host providers must register via a HostCall.
 *
 * A guest client can connect to a host provider and send/receive data.
 *
 * GuestCalls:
 *    * Attach      - attach to a host channel
 *    * Detach      - completely detach from a channel
 *    * Send        - send data from the guest to the channel
 *    * Recv        - non blocking read of available data from the channel
 *    * Control     - generic channel specific command exchange
 *    * EventWait   - wait for a host event
 *    * EventCancel - make the blocking EventWait call to return
 * HostCalls:
 *    * Register    - register a host channel
 *    * Unregister  - unregister it
 *
 * The guest HGCM client connects to the service. The client can attach multiple channels.
 *
 */

#include <iprt/alloc.h>
#include <iprt/string.h>
#include <iprt/assert.h>
#include <iprt/critsect.h>
#include <VBox/vmm/ssm.h>

#include "HostChannel.h"


static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
{
    pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
    pParm->u.uint32 = u32;
}

static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
{
    if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
    {
        *pu32 = pParm->u.uint32;
        return VINF_SUCCESS;
    }

    AssertFailed();
    return VERR_INVALID_PARAMETER;
}

static void VBoxHGCMParmPtrSet(VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
{
    pParm->type             = VBOX_HGCM_SVC_PARM_PTR;
    pParm->u.pointer.size   = cb;
    pParm->u.pointer.addr   = pv;
}

static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
{
    if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
    {
        *ppv = pParm->u.pointer.addr;
        *pcb = pParm->u.pointer.size;
        return VINF_SUCCESS;
    }

    AssertFailed();
    return VERR_INVALID_PARAMETER;
}


static PVBOXHGCMSVCHELPERS g_pHelpers = NULL;

static RTCRITSECT g_critsect;

/*
 * Helpers.
 */

int vboxHostChannelLock(void)
{
    return RTCritSectEnter(&g_critsect);
}

void vboxHostChannelUnlock(void)
{
    RTCritSectLeave(&g_critsect);
}

void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms,
                                  uint32_t u32ChannelHandle,
                                  uint32_t u32Id,
                                  const void *pvEvent,
                                  uint32_t cbEvent)
{
    if (cbEvent > 0)
    {
        void *pvParm = NULL;
        uint32_t cbParm = 0;

        VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm);

        uint32_t cbToCopy = RT_MIN(cbParm, cbEvent);
        if (cbToCopy > 0)
        {
            Assert(pvParm);
            memcpy(pvParm, pvEvent, cbToCopy);
        }
    }

    VBoxHGCMParmUInt32Set(&paParms[0], u32ChannelHandle);
    VBoxHGCMParmUInt32Set(&paParms[1], u32Id);
    VBoxHGCMParmUInt32Set(&paParms[3], cbEvent);
}

/* This is called under the lock. */
void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
                                uint32_t u32ChannelHandle,
                                uint32_t u32Id,
                                const void *pvEvent,
                                uint32_t cbEvent)
{
    Assert(RTCritSectIsOwner(&g_critsect));

    vboxHostChannelEventParmsSet(pClient->async.paParms,
                                 u32ChannelHandle,
                                 u32Id,
                                 pvEvent,
                                 cbEvent);

    LogRelFlow(("svcCall: CallComplete for pending\n"));

    g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS);
}


/*
 *  Service entry points.
 */

static DECLCALLBACK(int) svcUnload(void *pvService)
{
    NOREF(pvService);
    vboxHostChannelDestroy();
    RTCritSectDelete(&g_critsect);
    return VINF_SUCCESS;
}

static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient)
{
    NOREF(pvService);

    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;

    vboxHostChannelClientDisconnect(pClient);

    memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));

    return VINF_SUCCESS;
}

static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient)
{
    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;

    int rc = VINF_SUCCESS;

    /* Register the client. */
    memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));

    pClient->u32ClientID = u32ClientID;

    rc = vboxHostChannelClientConnect(pClient);

    LogRel2(("svcConnect: rc = %Rrc\n", rc));

    return rc;
}

static DECLCALLBACK(void) svcCall(void *pvService,
                                  VBOXHGCMCALLHANDLE callHandle,
                                  uint32_t u32ClientID,
                                  void *pvClient,
                                  uint32_t u32Function,
                                  uint32_t cParms,
                                  VBOXHGCMSVCPARM paParms[])
{
    NOREF(pvService);

    int rc = VINF_SUCCESS;

    LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
             u32ClientID, u32Function, cParms, paParms));

    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;

    bool fAsynchronousProcessing = false;

#ifdef DEBUG
    uint32_t i;

    for (i = 0; i < cParms; i++)
    {
        /** @todo parameters other than 32 bit */
        LogRel2(("    pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
    }
#endif

    switch (u32Function)
    {
        case VBOX_HOST_CHANNEL_FN_ATTACH:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_ATTACH\n"));

            if (cParms != 3)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR     /* name */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* flags */
                     || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                uint32_t u32Flags;
                void *pvName;
                uint32_t cbName;

                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);

                if (RT_SUCCESS(rc))
                {
                    rc = VBoxHGCMParmUInt32Get(&paParms[1], &u32Flags);

                    if (RT_SUCCESS(rc))
                    {
                        uint32_t u32Handle = 0;

                        /* @todo make sure that pvName is a nul terminated */
                        rc = vboxHostChannelAttach(pClient, &u32Handle, (const char *)pvName, u32Flags);

                        if (RT_SUCCESS(rc))
                        {
                            VBoxHGCMParmUInt32Set(&paParms[2], u32Handle);
                        }
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_DETACH:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_DETACH\n"));

            if (cParms != 1)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                uint32_t u32Handle;

                rc = VBoxHGCMParmUInt32Get(&paParms[0], &u32Handle);

                if (RT_SUCCESS(rc))
                {
                    rc = vboxHostChannelDetach(pClient, u32Handle);
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_SEND:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_SEND\n"));

            if (cParms != 2)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                uint32_t u32Handle;
                void *pvData;
                uint32_t cbData;

                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);

                if (RT_SUCCESS (rc))
                {
                    rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);

                    if (RT_SUCCESS (rc))
                    {
                        rc = vboxHostChannelSend(pClient, u32Handle, pvData, cbData);
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_RECV:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_RECV\n"));

            if (cParms != 4)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
                     || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeReceived */
                     || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeRemaining */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                uint32_t u32Handle;
                void *pvData;
                uint32_t cbData;

                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);

                if (RT_SUCCESS (rc))
                {
                    rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);

                    if (RT_SUCCESS (rc))
                    {
                        uint32_t u32SizeReceived = 0;
                        uint32_t u32SizeRemaining = 0;

                        rc = vboxHostChannelRecv(pClient, u32Handle,
                                                 pvData, cbData,
                                                 &u32SizeReceived, &u32SizeRemaining);

                        if (RT_SUCCESS(rc))
                        {
                            VBoxHGCMParmUInt32Set(&paParms[2], u32SizeReceived);
                            VBoxHGCMParmUInt32Set(&paParms[3], u32SizeRemaining);
                        }
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_CONTROL:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_CONTROL\n"));

            if (cParms != 5)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* code */
                     || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR     /* parm */
                     || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
                     || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeDataReturned */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                uint32_t u32Handle;
                uint32_t u32Code;
                void *pvParm;
                uint32_t cbParm;
                void *pvData;
                uint32_t cbData;

                rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);

                if (RT_SUCCESS (rc))
                {
                    rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);

                    if (RT_SUCCESS (rc))
                    {
                        rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);

                        if (RT_SUCCESS (rc))
                        {
                            rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);

                            if (RT_SUCCESS (rc))
                            {
                                uint32_t u32SizeDataReturned = 0;

                                rc = vboxHostChannelControl(pClient, u32Handle, u32Code,
                                                            pvParm, cbParm,
                                                            pvData, cbData, &u32SizeDataReturned);
                                if (RT_SUCCESS(rc))
                                {
                                    VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
                                }
                            }
                        }
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_EVENT_WAIT:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_WAIT\n"));

            if (cParms != 4)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT   /* handle */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* id */
                     || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR     /* parm */
                     || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeReturned */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                bool fEvent = false;

                rc = vboxHostChannelEventWait(pClient, &fEvent, callHandle, paParms);

                if (RT_SUCCESS(rc))
                {
                    if (!fEvent)
                    {
                        /* No event available at the time. Process asynchronously. */
                        fAsynchronousProcessing = true;

                        LogRel2(("svcCall: async.\n"));
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_EVENT_CANCEL:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_CANCEL\n"));

            if (cParms != 0)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                rc = vboxHostChannelEventCancel(pClient);
            }
        } break;

        case VBOX_HOST_CHANNEL_FN_QUERY:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_QUERY\n"));

            if (cParms != 5)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR     /* channel name */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT   /* code */
                     || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR     /* parm */
                     || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR     /* data */
                     || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT   /* sizeDataReturned */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                void *pvName;
                uint32_t cbName;
                uint32_t u32Code;
                void *pvParm;
                uint32_t cbParm;
                void *pvData;
                uint32_t cbData;

                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);

                if (RT_SUCCESS (rc))
                {
                    rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);

                    if (RT_SUCCESS (rc))
                    {
                        rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);

                        if (RT_SUCCESS (rc))
                        {
                            rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);

                            if (RT_SUCCESS (rc))
                            {
                                uint32_t u32SizeDataReturned = 0;

                                /* @todo make sure that pvName is a nul terminated */
                                rc = vboxHostChannelQuery(pClient, (const char *)pvName, u32Code,
                                                          pvParm, cbParm,
                                                          pvData, cbData, &u32SizeDataReturned);
                                if (RT_SUCCESS(rc))
                                {
                                    VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
                                }
                            }
                        }
                    }
                }
            }
        } break;

        default:
        {
            rc = VERR_NOT_IMPLEMENTED;
        }
    }

    LogRelFlow(("svcCall: rc = %Rrc, async %d\n", rc, fAsynchronousProcessing));

    if (!fAsynchronousProcessing)
    {
        g_pHelpers->pfnCallComplete(callHandle, rc);
    }
}

static DECLCALLBACK(int) svcHostCall(void *pvService,
                                     uint32_t u32Function,
                                     uint32_t cParms,
                                     VBOXHGCMSVCPARM paParms[])
{
    NOREF(pvService);

    int rc = VINF_SUCCESS;

    LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
             u32Function, cParms, paParms));

    switch (u32Function)
    {
        case VBOX_HOST_CHANNEL_HOST_FN_REGISTER:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_REGISTER\n"));

            if (cParms != 2)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
                     || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* iface */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                void *pvName;
                uint32_t cbName;
                void *pvInterface;
                uint32_t cbInterface;

                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);

                if (RT_SUCCESS(rc))
                {
                    rc = VBoxHGCMParmPtrGet(&paParms[1], &pvInterface, &cbInterface);

                    if (RT_SUCCESS(rc))
                    {
                        rc = vboxHostChannelRegister((const char *)pvName,
                                                     (VBOXHOSTCHANNELINTERFACE *)pvInterface, cbInterface);
                    }
                }
            }
        } break;

        case VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER:
        {
            LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER\n"));

            if (cParms != 1)
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else if (   paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
                    )
            {
                rc = VERR_INVALID_PARAMETER;
            }
            else
            {
                void *pvName;
                uint32_t cbName;

                rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);

                if (RT_SUCCESS(rc))
                {
                    rc = vboxHostChannelUnregister((const char *)pvName);
                }
            }
        } break;

        default:
            break;
    }

    LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
    return rc;
}

#if 0
/** If the client in the guest is waiting for a read operation to complete
 * then complete it, otherwise return.  See the protocol description in the
 * shared clipboard module description. */
void vboxSvcClipboardCompleteReadData(VBOXHOSTCHCLIENT *pClient, int rc, uint32_t cbActual)
{
    VBOXHGCMCALLHANDLE callHandle = NULL;
    VBOXHGCMSVCPARM *paParms = NULL;
    bool fReadPending = false;
    if (vboxSvcClipboardLock())  /* if not can we do anything useful? */
    {
        callHandle   = pClient->asyncRead.callHandle;
        paParms      = pClient->asyncRead.paParms;
        fReadPending = pClient->fReadPending;
        pClient->fReadPending = false;
        vboxSvcClipboardUnlock();
    }
    if (fReadPending)
    {
        VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
        g_pHelpers->pfnCallComplete (callHandle, rc);
    }
}

/**
 * SSM descriptor table for the VBOXHOSTCHCLIENT structure.
 */
static SSMFIELD const g_aClipboardClientDataFields[] =
{
    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32ClientID),  /* for validation purposes */
    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgQuit),
    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgReadData),
    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgFormats),
    SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32RequestedFormat),
    SSMFIELD_ENTRY_TERM()
};

static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
{
    NOREF(pvService);

    /* If there are any pending requests, they must be completed here. Since
     * the service is single threaded, there could be only requests
     * which the service itself has postponed.
     *
     * HGCM knows that the state is being saved and that the pfnComplete
     * calls are just clean ups. These requests are saved by the VMMDev.
     *
     * When the state will be restored, these requests will be reissued
     * by VMMDev. The service therefore must save state as if there were no
     * pending request.
     */
    LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));

    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;

    /* This field used to be the length. We're using it as a version field
       with the high bit set. */
    SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
    int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
    AssertRCReturn (rc, rc);

    if (pClient->fAsync)
    {
        g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
        pClient->fAsync = false;
    }

    vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0);

    return VINF_SUCCESS;
}

/**
 * This structure corresponds to the original layout of the
 * VBOXHOSTCHCLIENT structure.  As the structure was saved as a whole
 * when saving state, we need to remember it forever in order to preserve
 * compatibility.
 *
 * (Starting with 3.1 this is no longer used.)
 *
 * @remarks Putting this outside svcLoadState to avoid visibility warning caused
 *          by -Wattributes.
 */
typedef struct CLIPSAVEDSTATEDATA
{
    struct CLIPSAVEDSTATEDATA *pNext;
    struct CLIPSAVEDSTATEDATA *pPrev;

    VBOXCLIPBOARDCONTEXT *pCtx;

    uint32_t u32ClientID;

    bool fAsync: 1; /* Guest is waiting for a message. */

    bool fMsgQuit: 1;
    bool fMsgReadData: 1;
    bool fMsgFormats: 1;

    struct {
        VBOXHGCMCALLHANDLE callHandle;
        VBOXHGCMSVCPARM *paParms;
    } async;

    struct {
         void *pv;
         uint32_t cb;
         uint32_t u32Format;
    } data;

    uint32_t u32AvailableFormats;
    uint32_t u32RequestedFormat;

} CLIPSAVEDSTATEDATA;

static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
{
    LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));

    VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;

    /* Existing client can not be in async state yet. */
    Assert (!pClient->fAsync);

    /* Save the client ID for data validation. */
    /** @todo isn't this the same as u32ClientID? Playing safe for now... */
    uint32_t const u32ClientIDOld = pClient->u32ClientID;

    /* Restore the client data. */
    uint32_t lenOrVer;
    int rc = SSMR3GetU32 (pSSM, &lenOrVer);
    AssertRCReturn (rc, rc);
    if (lenOrVer == UINT32_C (0x80000002))
    {
        rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
        AssertRCReturn (rc, rc);
    }
    else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48))
    {
        /**
         * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
         */
        static SSMFIELD const s_aClipSavedStateDataFields30[] =
        {
            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pNext),
            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pPrev),
            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, pCtx),
            SSMFIELD_ENTRY(                 CLIPSAVEDSTATEDATA, u32ClientID),
            SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, async.callHandle),
            SSMFIELD_ENTRY_IGN_HCPTR(       CLIPSAVEDSTATEDATA, async.paParms),
            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.pv),
            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.cb),
            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, data.u32Format),
            SSMFIELD_ENTRY_IGNORE(          CLIPSAVEDSTATEDATA, u32AvailableFormats),
            SSMFIELD_ENTRY(                 CLIPSAVEDSTATEDATA, u32RequestedFormat),
            SSMFIELD_ENTRY_TERM()
        };

        CLIPSAVEDSTATEDATA savedState;
        RT_ZERO (savedState);
        rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
                               &s_aClipSavedStateDataFields30[0], NULL);
        AssertRCReturn (rc, rc);

        pClient->fMsgQuit           = savedState.fMsgQuit;
        pClient->fMsgReadData       = savedState.fMsgReadData;
        pClient->fMsgFormats        = savedState.fMsgFormats;
        pClient->u32RequestedFormat = savedState.u32RequestedFormat;
    }
    else
    {
        LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
    }

    /* Verify the client ID. */
    if (pClient->u32ClientID != u32ClientIDOld)
    {
        LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
        pClient->u32ClientID = u32ClientIDOld;
        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
    }

    /* Actual host data are to be reported to guest (SYNC). */
    vboxClipboardSync (pClient);

    return VINF_SUCCESS;
}
#endif

static int svcInit(void)
{
    int rc = RTCritSectInit(&g_critsect);

    if (RT_SUCCESS (rc))
    {
        rc = vboxHostChannelInit();

        /* Clean up on failure, because 'svnUnload' will not be called
         * if the 'svcInit' returns an error.
         */
        if (RT_FAILURE(rc))
        {
            RTCritSectDelete(&g_critsect);
        }
    }

    return rc;
}

extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
{
    int rc = VINF_SUCCESS;

    LogRelFlowFunc(("pTable = %p\n", pTable));

    if (!pTable)
    {
        rc = VERR_INVALID_PARAMETER;
    }
    else
    {
        LogRel2(("VBoxHGCMSvcLoad: pTable->cbSize = %d, pTable->u32Version = 0x%08X\n",
                  pTable->cbSize, pTable->u32Version));

        if (   pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
            || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
        {
            rc = VERR_INVALID_PARAMETER;
        }
        else
        {
            g_pHelpers = pTable->pHelpers;

            pTable->cbClient = sizeof(VBOXHOSTCHCLIENT);

            pTable->pfnUnload     = svcUnload;
            pTable->pfnConnect    = svcConnect;
            pTable->pfnDisconnect = svcDisconnect;
            pTable->pfnCall       = svcCall;
            pTable->pfnHostCall   = svcHostCall;
            pTable->pfnSaveState  = NULL; // svcSaveState;
            pTable->pfnLoadState  = NULL; // svcLoadState;
            pTable->pfnRegisterExtension  = NULL;
            pTable->pvService     = NULL;

            /* Service specific initialization. */
            rc = svcInit();
        }
    }

    return rc;
}
