2802 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2802 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			C
		
	
	
	
 | 
						||
/*
 | 
						||
 | 
						||
Copyright 1995, 1998  The Open Group
 | 
						||
 | 
						||
Permission to use, copy, modify, distribute, and sell this software and its
 | 
						||
documentation for any purpose is hereby granted without fee, provided that
 | 
						||
the above copyright notice appear in all copies and that both that
 | 
						||
copyright notice and this permission notice appear in supporting
 | 
						||
documentation.
 | 
						||
 | 
						||
The above copyright notice and this permission notice shall be
 | 
						||
included in all copies or substantial portions of the Software.
 | 
						||
 | 
						||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
						||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
						||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
						||
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | 
						||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | 
						||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | 
						||
OTHER DEALINGS IN THE SOFTWARE.
 | 
						||
 | 
						||
Except as contained in this notice, the name of The Open Group shall
 | 
						||
not be used in advertising or otherwise to promote the sale, use or
 | 
						||
other dealings in this Software without prior written authorization
 | 
						||
from The Open Group.
 | 
						||
 | 
						||
Author: David P. Wiggins, The Open Group
 | 
						||
 | 
						||
This work benefited from earlier work done by Martha Zimet of NCD
 | 
						||
and Jim Haggerty of Metheus.
 | 
						||
 | 
						||
*/
 | 
						||
 | 
						||
#include <dix-config.h>
 | 
						||
 | 
						||
#include "dix/cursor_priv.h"
 | 
						||
#include "dix/eventconvert.h"
 | 
						||
 | 
						||
#include "dixstruct.h"
 | 
						||
#include "extnsionst.h"
 | 
						||
#include "extinit_priv.h"
 | 
						||
#include <X11/extensions/recordproto.h>
 | 
						||
#include "set.h"
 | 
						||
#include "swaprep.h"
 | 
						||
#include "inputstr.h"
 | 
						||
#include "scrnintstr.h"
 | 
						||
 | 
						||
#include <stdio.h>
 | 
						||
#include <assert.h>
 | 
						||
 | 
						||
#ifdef XINERAMA
 | 
						||
#include "globals.h"
 | 
						||
#include "panoramiX.h"
 | 
						||
#include "panoramiXsrv.h"
 | 
						||
#include "cursor.h"
 | 
						||
#endif /* XINERAMA */
 | 
						||
 | 
						||
#include "protocol-versions.h"
 | 
						||
 | 
						||
static RESTYPE RTContext;       /* internal resource type for Record contexts */
 | 
						||
 | 
						||
/* How many bytes of protocol data to buffer in a context. Don't set to less
 | 
						||
 * than 32.
 | 
						||
 */
 | 
						||
#define REPLY_BUF_SIZE 1024
 | 
						||
 | 
						||
/* Record Context structure */
 | 
						||
 | 
						||
typedef struct {
 | 
						||
    XID id;                     /* resource id of context */
 | 
						||
    ClientPtr pRecordingClient; /* client that has context enabled */
 | 
						||
    struct _RecordClientsAndProtocolRec *pListOfRCAP;   /* all registered info */
 | 
						||
    ClientPtr pBufClient;       /* client whose protocol is in replyBuffer */
 | 
						||
    unsigned int continuedReply:1;      /* recording a reply that is split up? */
 | 
						||
    char elemHeaders;           /* element header flags (time/seq no.) */
 | 
						||
    char bufCategory;           /* category of protocol in replyBuffer */
 | 
						||
    int numBufBytes;            /* number of bytes in replyBuffer */
 | 
						||
    char replyBuffer[REPLY_BUF_SIZE];   /* buffered recorded protocol */
 | 
						||
    int inFlush;                /*  are we inside RecordFlushReplyBuffer */
 | 
						||
} RecordContextRec, *RecordContextPtr;
 | 
						||
 | 
						||
/*  RecordMinorOpRec - to hold minor opcode selections for extension requests
 | 
						||
 *  and replies
 | 
						||
 */
 | 
						||
 | 
						||
typedef union {
 | 
						||
    int count;                  /* first element of array: how many "major" structs to follow */
 | 
						||
    struct {                    /* rest of array elements are this */
 | 
						||
        short first;            /* first major opcode */
 | 
						||
        short last;             /* last major opcode */
 | 
						||
        RecordSetPtr pMinOpSet; /*  minor opcode set for above major range */
 | 
						||
    } major;
 | 
						||
} RecordMinorOpRec, *RecordMinorOpPtr;
 | 
						||
 | 
						||
/*  RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
 | 
						||
 *  protocol selections passed in a single CreateContext or RegisterClients.
 | 
						||
 *  Generally, a context will have one of these from the create and an
 | 
						||
 *  additional one for each RegisterClients.  RCAPs are freed when all their
 | 
						||
 *  clients are unregistered.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct _RecordClientsAndProtocolRec {
 | 
						||
    RecordContextPtr pContext;  /* context that owns this RCAP */
 | 
						||
    struct _RecordClientsAndProtocolRec *pNextRCAP;     /* next RCAP on context */
 | 
						||
    RecordSetPtr pRequestMajorOpSet;    /* requests to record */
 | 
						||
    RecordMinorOpPtr pRequestMinOpInfo; /* extension requests to record */
 | 
						||
    RecordSetPtr pReplyMajorOpSet;      /* replies to record */
 | 
						||
    RecordMinorOpPtr pReplyMinOpInfo;   /* extension replies to record */
 | 
						||
    RecordSetPtr pDeviceEventSet;       /* device events to record */
 | 
						||
    RecordSetPtr pDeliveredEventSet;    /* delivered events to record */
 | 
						||
    RecordSetPtr pErrorSet;     /* errors to record */
 | 
						||
    XID *pClientIDs;            /* array of clients to record */
 | 
						||
    short numClients;           /* number of clients in pClientIDs */
 | 
						||
    short sizeClients;          /* size of pClientIDs array */
 | 
						||
    unsigned int clientStarted:1;       /* record new client connections? */
 | 
						||
    unsigned int clientDied:1;  /* record client disconnections? */
 | 
						||
    unsigned int clientIDsSeparatelyAllocated:1;        /* pClientIDs malloced? */
 | 
						||
} RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;
 | 
						||
 | 
						||
/* how much bigger to make pRCAP->pClientIDs when reallocing */
 | 
						||
#define CLIENT_ARRAY_GROWTH_INCREMENT 4
 | 
						||
 | 
						||
/* counts the total number of RCAPs belonging to enabled contexts. */
 | 
						||
static int numEnabledRCAPs;
 | 
						||
 | 
						||
/*  void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
 | 
						||
 *  In the spirit of the VERIFY_* macros in dix.h, this macro fills in
 | 
						||
 *  the context pointer if the given ID is a valid Record Context, else it
 | 
						||
 *  returns an error.
 | 
						||
 */
 | 
						||
#define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
 | 
						||
    int rc = dixLookupResourceByType((void **)&(_pContext), _contextid, \
 | 
						||
                                     RTContext, _client, DixUseAccess); \
 | 
						||
    if (rc != Success) \
 | 
						||
	return rc; \
 | 
						||
}
 | 
						||
 | 
						||
static int RecordDeleteContext(void     *value,
 | 
						||
                               XID      id);
 | 
						||
 | 
						||
/***************************************************************************/
 | 
						||
 | 
						||
/* client private stuff */
 | 
						||
 | 
						||
/*  To make declarations less obfuscated, have a typedef for a pointer to a
 | 
						||
 *  Proc function.
 | 
						||
 */
 | 
						||
typedef int (*ProcFunctionPtr) (ClientPtr       /*pClient */
 | 
						||
    );
 | 
						||
 | 
						||
/* Record client private.  Generally a client only has one of these if
 | 
						||
 * any of its requests are being recorded.
 | 
						||
 */
 | 
						||
typedef struct {
 | 
						||
/* ptr to client's proc vector before Record stuck its nose in */
 | 
						||
    ProcFunctionPtr *originalVector;
 | 
						||
 | 
						||
/* proc vector with pointers for recorded requests redirected to the
 | 
						||
 * function RecordARequest
 | 
						||
 */
 | 
						||
    ProcFunctionPtr recordVector[256];
 | 
						||
} RecordClientPrivateRec, *RecordClientPrivatePtr;
 | 
						||
 | 
						||
static DevPrivateKeyRec RecordClientPrivateKeyRec;
 | 
						||
 | 
						||
#define RecordClientPrivateKey (&RecordClientPrivateKeyRec)
 | 
						||
 | 
						||
/*  RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
 | 
						||
 *  gets the client private of the given client.  Syntactic sugar.
 | 
						||
 */
 | 
						||
#define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
 | 
						||
    dixLookupPrivate(&(_pClient)->devPrivates, RecordClientPrivateKey)
 | 
						||
 | 
						||
/***************************************************************************/
 | 
						||
 | 
						||
/* global list of all contexts */
 | 
						||
 | 
						||
static RecordContextPtr *ppAllContexts;
 | 
						||
 | 
						||
static int numContexts;         /* number of contexts in ppAllContexts */
 | 
						||
 | 
						||
/* number of currently enabled contexts.  All enabled contexts are bunched
 | 
						||
 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
 | 
						||
 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
 | 
						||
 * past disabled contexts.
 | 
						||
 */
 | 
						||
static int numEnabledContexts;
 | 
						||
 | 
						||
/* RecordFindContextOnAllContexts
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context to search for.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	The index into the array ppAllContexts at which pContext is stored.
 | 
						||
 *	If pContext is not found in ppAllContexts, returns -1.
 | 
						||
 *
 | 
						||
 * Side Effects: none.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordFindContextOnAllContexts(RecordContextPtr pContext)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
 | 
						||
    assert(numContexts >= numEnabledContexts);
 | 
						||
    for (i = 0; i < numContexts; i++) {
 | 
						||
        if (ppAllContexts[i] == pContext)
 | 
						||
            return i;
 | 
						||
    }
 | 
						||
    return -1;
 | 
						||
}                               /* RecordFindContextOnAllContexts */
 | 
						||
 | 
						||
/***************************************************************************/
 | 
						||
 | 
						||
/* RecordFlushReplyBuffer
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context to flush.
 | 
						||
 *	data1 is a pointer to additional data, and len1 is its length in bytes.
 | 
						||
 *	data2 is a pointer to additional data, and len2 is its length in bytes.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If the context is enabled, any buffered (recorded) protocol is written
 | 
						||
 *	to the recording client, and the number of buffered bytes is set to
 | 
						||
 *	zero.  If len1 is not zero, data1/len1 are then written to the
 | 
						||
 *	recording client, and similarly for data2/len2 (written after
 | 
						||
 *	data1/len1).
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordFlushReplyBuffer(RecordContextPtr pContext,
 | 
						||
                       void *data1, int len1, void *data2, int len2)
 | 
						||
{
 | 
						||
    if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone ||
 | 
						||
        pContext->inFlush)
 | 
						||
        return;
 | 
						||
    ++pContext->inFlush;
 | 
						||
    if (pContext->numBufBytes)
 | 
						||
        WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
 | 
						||
                      pContext->replyBuffer);
 | 
						||
    pContext->numBufBytes = 0;
 | 
						||
    if (len1)
 | 
						||
        WriteToClient(pContext->pRecordingClient, len1, data1);
 | 
						||
    if (len2)
 | 
						||
        WriteToClient(pContext->pRecordingClient, len2, data2);
 | 
						||
    --pContext->inFlush;
 | 
						||
}                               /* RecordFlushReplyBuffer */
 | 
						||
 | 
						||
/* RecordAProtocolElement
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context that is recording a protocol element.
 | 
						||
 *	pClient is the client whose protocol is being recorded.  For
 | 
						||
 *	  device events and EndOfData, pClient is NULL.
 | 
						||
 *	category is the category of the protocol element, as defined
 | 
						||
 *	  by the RECORD spec.
 | 
						||
 *	data is a pointer to the protocol data, and datalen - padlen
 | 
						||
 *	  is its length in bytes.
 | 
						||
 *	padlen is the number of pad bytes from a zeroed array.
 | 
						||
 *	futurelen is the number of bytes that will be sent in subsequent
 | 
						||
 *	  calls to this function to complete this protocol element.
 | 
						||
 *	  In those subsequent calls, futurelen will be -1 to indicate
 | 
						||
 *	  that the current data is a continuation of the same protocol
 | 
						||
 *	  element.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The context may be flushed.  The new protocol element will be
 | 
						||
 *	added to the context's protocol buffer with appropriate element
 | 
						||
 *	headers prepended (sequence number and timestamp).  If the data
 | 
						||
 *	is continuation data (futurelen == -1), element headers won't
 | 
						||
 *	be added.  If the protocol element and headers won't fit in
 | 
						||
 *	the context's buffer, it is sent directly to the recording
 | 
						||
 *	client (after any buffered data).
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
 | 
						||
                       int category, void *data, int datalen, int padlen,
 | 
						||
                       int futurelen)
 | 
						||
{
 | 
						||
    CARD32 elemHeaderData[2];
 | 
						||
    int numElemHeaders = 0;
 | 
						||
    Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
 | 
						||
    CARD32 serverTime = 0;
 | 
						||
    Bool gotServerTime = FALSE;
 | 
						||
    int replylen;
 | 
						||
 | 
						||
    if (futurelen >= 0) {       /* start of new protocol element */
 | 
						||
        xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
 | 
						||
            pContext->replyBuffer;
 | 
						||
 | 
						||
        if (pContext->pBufClient != pClient ||
 | 
						||
            pContext->bufCategory != category) {
 | 
						||
            RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
 | 
						||
            pContext->pBufClient = pClient;
 | 
						||
            pContext->bufCategory = category;
 | 
						||
        }
 | 
						||
 | 
						||
        if (!pContext->numBufBytes) {
 | 
						||
            serverTime = GetTimeInMillis();
 | 
						||
            gotServerTime = TRUE;
 | 
						||
            pRep->type = X_Reply;
 | 
						||
            pRep->category = category;
 | 
						||
            pRep->sequenceNumber = pContext->pRecordingClient->sequence;
 | 
						||
            pRep->length = 0;
 | 
						||
            pRep->elementHeader = pContext->elemHeaders;
 | 
						||
            pRep->serverTime = serverTime;
 | 
						||
            if (pClient) {
 | 
						||
                pRep->clientSwapped =
 | 
						||
                    (pClient->swapped != recordingClientSwapped);
 | 
						||
                pRep->idBase = pClient->clientAsMask;
 | 
						||
                pRep->recordedSequenceNumber = pClient->sequence;
 | 
						||
            }
 | 
						||
            else {              /* it's a device event, StartOfData, or EndOfData */
 | 
						||
 | 
						||
                pRep->clientSwapped = (category != XRecordFromServer) &&
 | 
						||
                    recordingClientSwapped;
 | 
						||
                pRep->idBase = 0;
 | 
						||
                pRep->recordedSequenceNumber = 0;
 | 
						||
            }
 | 
						||
 | 
						||
            if (recordingClientSwapped) {
 | 
						||
                swaps(&pRep->sequenceNumber);
 | 
						||
                swapl(&pRep->length);
 | 
						||
                swapl(&pRep->idBase);
 | 
						||
                swapl(&pRep->serverTime);
 | 
						||
                swapl(&pRep->recordedSequenceNumber);
 | 
						||
            }
 | 
						||
            pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
 | 
						||
        }
 | 
						||
 | 
						||
        /* generate element headers if needed */
 | 
						||
 | 
						||
        if (((pContext->elemHeaders & XRecordFromClientTime)
 | 
						||
             && category == XRecordFromClient)
 | 
						||
            || ((pContext->elemHeaders & XRecordFromServerTime)
 | 
						||
                && category == XRecordFromServer)) {
 | 
						||
            if (gotServerTime)
 | 
						||
                elemHeaderData[numElemHeaders] = serverTime;
 | 
						||
            else
 | 
						||
                elemHeaderData[numElemHeaders] = GetTimeInMillis();
 | 
						||
            if (recordingClientSwapped)
 | 
						||
                swapl(&elemHeaderData[numElemHeaders]);
 | 
						||
            numElemHeaders++;
 | 
						||
        }
 | 
						||
 | 
						||
        if ((pContext->elemHeaders & XRecordFromClientSequence)
 | 
						||
            && (category == XRecordFromClient || category == XRecordClientDied)) {
 | 
						||
            elemHeaderData[numElemHeaders] = pClient->sequence;
 | 
						||
            if (recordingClientSwapped)
 | 
						||
                swapl(&elemHeaderData[numElemHeaders]);
 | 
						||
            numElemHeaders++;
 | 
						||
        }
 | 
						||
 | 
						||
        /* adjust reply length */
 | 
						||
 | 
						||
        replylen = pRep->length;
 | 
						||
        if (recordingClientSwapped)
 | 
						||
            swapl(&replylen);
 | 
						||
        replylen += numElemHeaders + bytes_to_int32(datalen) +
 | 
						||
            bytes_to_int32(futurelen);
 | 
						||
        if (recordingClientSwapped)
 | 
						||
            swapl(&replylen);
 | 
						||
        pRep->length = replylen;
 | 
						||
    }                           /* end if not continued reply */
 | 
						||
 | 
						||
    numElemHeaders *= 4;
 | 
						||
 | 
						||
    /* if space available >= space needed, buffer the data */
 | 
						||
 | 
						||
    if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders) {
 | 
						||
        if (numElemHeaders) {
 | 
						||
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
 | 
						||
                   elemHeaderData, numElemHeaders);
 | 
						||
            pContext->numBufBytes += numElemHeaders;
 | 
						||
        }
 | 
						||
        if (datalen) {
 | 
						||
            static char padBuffer[3];   /* as in FlushClient */
 | 
						||
 | 
						||
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
 | 
						||
                   data, datalen - padlen);
 | 
						||
            pContext->numBufBytes += datalen - padlen;
 | 
						||
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
 | 
						||
                   padBuffer, padlen);
 | 
						||
            pContext->numBufBytes += padlen;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else {
 | 
						||
        RecordFlushReplyBuffer(pContext, (void *) elemHeaderData,
 | 
						||
                               numElemHeaders, (void *) data,
 | 
						||
                               datalen - padlen);
 | 
						||
    }
 | 
						||
}                               /* RecordAProtocolElement */
 | 
						||
 | 
						||
/* RecordFindClientOnContext
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context to search.
 | 
						||
 *	clientspec is the resource ID mask identifying the client to search
 | 
						||
 *	  for, or XRecordFutureClients.
 | 
						||
 *	pposition is a pointer to an int, or NULL.  See Returns.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	The RCAP on which clientspec was found, or NULL if not found on
 | 
						||
 *	any RCAP on the given context.
 | 
						||
 *	If pposition was not NULL and the returned RCAP is not NULL,
 | 
						||
 *	*pposition will be set to the index into the returned the RCAP's
 | 
						||
 *	pClientIDs array that holds clientspec.
 | 
						||
 *
 | 
						||
 * Side Effects: none.
 | 
						||
 */
 | 
						||
static RecordClientsAndProtocolPtr
 | 
						||
RecordFindClientOnContext(RecordContextPtr pContext,
 | 
						||
                          XID clientspec, int *pposition)
 | 
						||
{
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
 | 
						||
        int i;
 | 
						||
 | 
						||
        for (i = 0; i < pRCAP->numClients; i++) {
 | 
						||
            if (pRCAP->pClientIDs[i] == clientspec) {
 | 
						||
                if (pposition)
 | 
						||
                    *pposition = i;
 | 
						||
                return pRCAP;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
    return NULL;
 | 
						||
}                               /* RecordFindClientOnContext */
 | 
						||
 | 
						||
/* RecordABigRequest
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the recording context.
 | 
						||
 *	client is the client being recorded.
 | 
						||
 *	stuff is a pointer to the big request of client (see the Big Requests
 | 
						||
 *	extension for details.)
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The big request is recorded with the correct length field re-inserted.
 | 
						||
 *	
 | 
						||
 * Note: this function exists mainly to make RecordARequest smaller.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq * stuff)
 | 
						||
{
 | 
						||
    CARD32 bigLength;
 | 
						||
    int bytesLeft;
 | 
						||
 | 
						||
    /* note: client->req_len has been frobbed by ReadRequestFromClient
 | 
						||
     * (os/io.c) to discount the extra 4 bytes taken by the extended length
 | 
						||
     * field in a big request.  The actual request length to record is
 | 
						||
     * client->req_len + 1 (measured in CARD32s).
 | 
						||
     */
 | 
						||
 | 
						||
    /* record the request header */
 | 
						||
    bytesLeft = client->req_len << 2;
 | 
						||
    RecordAProtocolElement(pContext, client, XRecordFromClient,
 | 
						||
                           (void *) stuff, SIZEOF(xReq), 0, bytesLeft);
 | 
						||
 | 
						||
    /* reinsert the extended length field that was squished out */
 | 
						||
    bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
 | 
						||
    if (client->swapped)
 | 
						||
        swapl(&bigLength);
 | 
						||
    RecordAProtocolElement(pContext, client, XRecordFromClient,
 | 
						||
                           (void *) &bigLength, sizeof(bigLength), 0,
 | 
						||
                           /* continuation */ -1);
 | 
						||
    bytesLeft -= sizeof(bigLength);
 | 
						||
 | 
						||
    /* record the rest of the request after the length */
 | 
						||
    RecordAProtocolElement(pContext, client, XRecordFromClient,
 | 
						||
                           (void *) (stuff + 1), bytesLeft, 0,
 | 
						||
                           /* continuation */ -1);
 | 
						||
}                               /* RecordABigRequest */
 | 
						||
 | 
						||
/* RecordARequest
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	client is a client that the server has dispatched a request to by
 | 
						||
 *	calling client->requestVector[request opcode] .
 | 
						||
 *	The request is in client->requestBuffer.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	Whatever is returned by the "real" Proc function for this request.
 | 
						||
 *	The "real" Proc function is the function that was in
 | 
						||
 *	client->requestVector[request opcode]  before it was replaced by
 | 
						||
 *	RecordARequest.  (See the function RecordInstallHooks.)
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The request is recorded by all contexts that have registered this
 | 
						||
 *	request for this client.  The real Proc function is called.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordARequest(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int i;
 | 
						||
    RecordClientPrivatePtr pClientPriv;
 | 
						||
 | 
						||
    REQUEST(xReq);
 | 
						||
    int majorop;
 | 
						||
 | 
						||
    majorop = stuff->reqType;
 | 
						||
    for (i = 0; i < numEnabledContexts; i++) {
 | 
						||
        pContext = ppAllContexts[i];
 | 
						||
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
 | 
						||
        if (pRCAP && pRCAP->pRequestMajorOpSet &&
 | 
						||
            RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop)) {
 | 
						||
            if (majorop <= 127) {       /* core request */
 | 
						||
 | 
						||
                if (stuff->length == 0)
 | 
						||
                    RecordABigRequest(pContext, client, stuff);
 | 
						||
                else
 | 
						||
                    RecordAProtocolElement(pContext, client, XRecordFromClient,
 | 
						||
                                           (void *) stuff,
 | 
						||
                                           client->req_len << 2, 0, 0);
 | 
						||
            }
 | 
						||
            else {              /* extension, check minor opcode */
 | 
						||
 | 
						||
                int minorop = client->minorOp;
 | 
						||
                int numMinOpInfo;
 | 
						||
                RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
 | 
						||
 | 
						||
                assert(pMinorOpInfo);
 | 
						||
                numMinOpInfo = pMinorOpInfo->count;
 | 
						||
                pMinorOpInfo++;
 | 
						||
                assert(numMinOpInfo);
 | 
						||
                for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
 | 
						||
                    if (majorop >= pMinorOpInfo->major.first &&
 | 
						||
                        majorop <= pMinorOpInfo->major.last &&
 | 
						||
                        RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
 | 
						||
                                            minorop)) {
 | 
						||
                        if (stuff->length == 0)
 | 
						||
                            RecordABigRequest(pContext, client, stuff);
 | 
						||
                        else
 | 
						||
                            RecordAProtocolElement(pContext, client,
 | 
						||
                                                   XRecordFromClient,
 | 
						||
                                                   (void *) stuff,
 | 
						||
                                                   client->req_len << 2, 0, 0);
 | 
						||
                        break;
 | 
						||
                    }
 | 
						||
                }               /* end for each minor op info */
 | 
						||
            }                   /* end extension request */
 | 
						||
        }                       /* end this RCAP wants this major opcode */
 | 
						||
    }                           /* end for each context */
 | 
						||
    pClientPriv = RecordClientPrivate(client);
 | 
						||
    assert(pClientPriv);
 | 
						||
    return (*pClientPriv->originalVector[majorop]) (client);
 | 
						||
}                               /* RecordARequest */
 | 
						||
 | 
						||
/* RecordAReply
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pcbl is &ReplyCallback.
 | 
						||
 *	nulldata is NULL.
 | 
						||
 *	calldata is a pointer to a ReplyInfoRec (include/os.h)
 | 
						||
 *	  which provides information about replies that are being sent
 | 
						||
 *	  to clients.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The reply is recorded by all contexts that have registered this
 | 
						||
 *	reply type for this client.  If more data belonging to the same
 | 
						||
 *	reply is expected, and if the reply is being recorded by any
 | 
						||
 *	context, pContext->continuedReply is set to 1.
 | 
						||
 *	If pContext->continuedReply was already 1 and this is the last
 | 
						||
 *	chunk of data belonging to this reply, it is set to 0.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordAReply(CallbackListPtr *pcbl, void *nulldata, void *calldata)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int eci;
 | 
						||
    ReplyInfoRec *pri = (ReplyInfoRec *) calldata;
 | 
						||
    ClientPtr client = pri->client;
 | 
						||
 | 
						||
    for (eci = 0; eci < numEnabledContexts; eci++) {
 | 
						||
        pContext = ppAllContexts[eci];
 | 
						||
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
 | 
						||
        if (pRCAP) {
 | 
						||
            int majorop = client->majorOp;
 | 
						||
 | 
						||
            if (pContext->continuedReply) {
 | 
						||
                RecordAProtocolElement(pContext, client, XRecordFromServer,
 | 
						||
                                       (void *) pri->replyData,
 | 
						||
                                       pri->dataLenBytes, pri->padBytes,
 | 
						||
                                       /* continuation */ -1);
 | 
						||
                if (!pri->bytesRemaining)
 | 
						||
                    pContext->continuedReply = 0;
 | 
						||
            }
 | 
						||
            else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
 | 
						||
                     RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop)) {
 | 
						||
                if (majorop <= 127) {   /* core reply */
 | 
						||
                    RecordAProtocolElement(pContext, client, XRecordFromServer,
 | 
						||
                                           (void *) pri->replyData,
 | 
						||
                                           pri->dataLenBytes, 0,
 | 
						||
                                           pri->bytesRemaining);
 | 
						||
                    if (pri->bytesRemaining)
 | 
						||
                        pContext->continuedReply = 1;
 | 
						||
                }
 | 
						||
                else {          /* extension, check minor opcode */
 | 
						||
 | 
						||
                    int minorop = client->minorOp;
 | 
						||
                    int numMinOpInfo;
 | 
						||
                    RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;
 | 
						||
 | 
						||
                    assert(pMinorOpInfo);
 | 
						||
                    numMinOpInfo = pMinorOpInfo->count;
 | 
						||
                    pMinorOpInfo++;
 | 
						||
                    assert(numMinOpInfo);
 | 
						||
                    for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
 | 
						||
                        if (majorop >= pMinorOpInfo->major.first &&
 | 
						||
                            majorop <= pMinorOpInfo->major.last &&
 | 
						||
                            RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
 | 
						||
                                                minorop)) {
 | 
						||
                            RecordAProtocolElement(pContext, client,
 | 
						||
                                                   XRecordFromServer,
 | 
						||
                                                   (void *) pri->replyData,
 | 
						||
                                                   pri->dataLenBytes, 0,
 | 
						||
                                                   pri->bytesRemaining);
 | 
						||
                            if (pri->bytesRemaining)
 | 
						||
                                pContext->continuedReply = 1;
 | 
						||
                            break;
 | 
						||
                        }
 | 
						||
                    }           /* end for each minor op info */
 | 
						||
                }               /* end extension reply */
 | 
						||
            }                   /* end continued reply vs. start of reply */
 | 
						||
        }                       /* end client is registered on this context */
 | 
						||
    }                           /* end for each context */
 | 
						||
}                               /* RecordAReply */
 | 
						||
 | 
						||
/* RecordADeliveredEventOrError
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pcbl is &EventCallback.
 | 
						||
 *	nulldata is NULL.
 | 
						||
 *	calldata is a pointer to a EventInfoRec (include/dix.h)
 | 
						||
 *	  which provides information about events that are being sent
 | 
						||
 *	  to clients.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The event or error is recorded by all contexts that have registered
 | 
						||
 *	it for this client.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordADeliveredEventOrError(CallbackListPtr *pcbl, void *nulldata,
 | 
						||
                             void *calldata)
 | 
						||
{
 | 
						||
    EventInfoRec *pei = (EventInfoRec *) calldata;
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int eci;                    /* enabled context index */
 | 
						||
    ClientPtr pClient = pei->client;
 | 
						||
 | 
						||
    for (eci = 0; eci < numEnabledContexts; eci++) {
 | 
						||
        pContext = ppAllContexts[eci];
 | 
						||
        pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
 | 
						||
                                          NULL);
 | 
						||
        if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet)) {
 | 
						||
            int ev;             /* event index */
 | 
						||
            xEvent *pev = pei->events;
 | 
						||
 | 
						||
            for (ev = 0; ev < pei->count; ev++, pev++) {
 | 
						||
                int recordit = 0;
 | 
						||
 | 
						||
                if (pRCAP->pErrorSet) {
 | 
						||
                    recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
 | 
						||
                                                   ((xError *) (pev))->
 | 
						||
                                                   errorCode);
 | 
						||
                }
 | 
						||
                else if (pRCAP->pDeliveredEventSet) {
 | 
						||
                    recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
 | 
						||
                                                   pev->u.u.type & 0177);
 | 
						||
                }
 | 
						||
                if (recordit) {
 | 
						||
                    xEvent swappedEvent;
 | 
						||
                    xEvent *pEvToRecord = pev;
 | 
						||
 | 
						||
                    if (pClient->swapped) {
 | 
						||
                        (*EventSwapVector[pev->u.u.type & 0177])
 | 
						||
                            (pev, &swappedEvent);
 | 
						||
                        pEvToRecord = &swappedEvent;
 | 
						||
 | 
						||
                    }
 | 
						||
                    RecordAProtocolElement(pContext, pClient,
 | 
						||
                                           XRecordFromServer, pEvToRecord,
 | 
						||
                                           SIZEOF(xEvent), 0, 0);
 | 
						||
                }
 | 
						||
            }                   /* end for each event */
 | 
						||
        }                       /* end this client is on this context */
 | 
						||
    }                           /* end for each enabled context */
 | 
						||
}                               /* RecordADeliveredEventOrError */
 | 
						||
 | 
						||
static void
 | 
						||
RecordSendProtocolEvents(RecordClientsAndProtocolPtr pRCAP,
 | 
						||
                         RecordContextPtr pContext, xEvent *pev, int count)
 | 
						||
{
 | 
						||
    int ev;                     /* event index */
 | 
						||
 | 
						||
    for (ev = 0; ev < count; ev++, pev++) {
 | 
						||
        if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet, pev->u.u.type & 0177)) {
 | 
						||
            xEvent swappedEvent;
 | 
						||
            xEvent *pEvToRecord = pev;
 | 
						||
 | 
						||
#ifdef XINERAMA
 | 
						||
            xEvent shiftedEvent;
 | 
						||
 | 
						||
            if (!noPanoramiXExtension &&
 | 
						||
                (pev->u.u.type == MotionNotify ||
 | 
						||
                 pev->u.u.type == ButtonPress ||
 | 
						||
                 pev->u.u.type == ButtonRelease ||
 | 
						||
                 pev->u.u.type == KeyPress || pev->u.u.type == KeyRelease)) {
 | 
						||
                int scr = inputInfo.pointer->spriteInfo->sprite->screen->myNum;
 | 
						||
 | 
						||
                memcpy(&shiftedEvent, pev, sizeof(xEvent));
 | 
						||
                shiftedEvent.u.keyButtonPointer.rootX +=
 | 
						||
                    screenInfo.screens[scr]->x - screenInfo.screens[0]->x;
 | 
						||
                shiftedEvent.u.keyButtonPointer.rootY +=
 | 
						||
                    screenInfo.screens[scr]->y - screenInfo.screens[0]->y;
 | 
						||
                pEvToRecord = &shiftedEvent;
 | 
						||
            }
 | 
						||
#endif /* XINERAMA */
 | 
						||
 | 
						||
            if (pContext->pRecordingClient->swapped) {
 | 
						||
                (*EventSwapVector[pEvToRecord->u.u.type & 0177])
 | 
						||
                    (pEvToRecord, &swappedEvent);
 | 
						||
                pEvToRecord = &swappedEvent;
 | 
						||
            }
 | 
						||
 | 
						||
            RecordAProtocolElement(pContext, NULL,
 | 
						||
                                   XRecordFromServer, pEvToRecord,
 | 
						||
                                   SIZEOF(xEvent), 0, 0);
 | 
						||
            /* make sure device events get flushed in the absence
 | 
						||
             * of other client activity
 | 
						||
             */
 | 
						||
            SetCriticalOutputPending();
 | 
						||
        }
 | 
						||
    }                           /* end for each event */
 | 
						||
 | 
						||
}                               /* RecordADeviceEvent */
 | 
						||
 | 
						||
/* RecordADeviceEvent
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pcbl is &DeviceEventCallback.
 | 
						||
 *	nulldata is NULL.
 | 
						||
 *	calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
 | 
						||
 *	  which provides information about device events that occur.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The device event is recorded by all contexts that have registered
 | 
						||
 *	it for this client.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordADeviceEvent(CallbackListPtr *pcbl, void *nulldata, void *calldata)
 | 
						||
{
 | 
						||
    DeviceEventInfoRec *pei = (DeviceEventInfoRec *) calldata;
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int eci;                    /* enabled context index */
 | 
						||
 | 
						||
    for (eci = 0; eci < numEnabledContexts; eci++) {
 | 
						||
        pContext = ppAllContexts[eci];
 | 
						||
        for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
 | 
						||
            if (pRCAP->pDeviceEventSet) {
 | 
						||
                int count;
 | 
						||
                xEvent *xi_events = NULL;
 | 
						||
 | 
						||
                /* TODO check return values */
 | 
						||
                if (IsMaster(pei->device)) {
 | 
						||
                    xEvent *core_events;
 | 
						||
 | 
						||
                    EventToCore(pei->event, &core_events, &count);
 | 
						||
                    RecordSendProtocolEvents(pRCAP, pContext, core_events,
 | 
						||
                                             count);
 | 
						||
                    free(core_events);
 | 
						||
                }
 | 
						||
 | 
						||
                EventToXI(pei->event, &xi_events, &count);
 | 
						||
                RecordSendProtocolEvents(pRCAP, pContext, xi_events, count);
 | 
						||
                free(xi_events);
 | 
						||
            }                   /* end this RCAP selects device events */
 | 
						||
        }                       /* end for each RCAP on this context */
 | 
						||
    }                           /* end for each enabled context */
 | 
						||
}
 | 
						||
 | 
						||
/* RecordFlushAllContexts
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pcbl is &FlushCallback.
 | 
						||
 *	nulldata and calldata are NULL.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	All buffered reply data of all enabled contexts is written to
 | 
						||
 *	the recording clients.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordFlushAllContexts(CallbackListPtr *pcbl,
 | 
						||
                       void *nulldata, void *calldata)
 | 
						||
{
 | 
						||
    int eci;                    /* enabled context index */
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    for (eci = 0; eci < numEnabledContexts; eci++) {
 | 
						||
        pContext = ppAllContexts[eci];
 | 
						||
 | 
						||
        /* In most cases we leave it to RecordFlushReplyBuffer to make
 | 
						||
         * this check, but this function could be called very often, so we
 | 
						||
         * check before calling hoping to save the function call cost
 | 
						||
         * most of the time.
 | 
						||
         */
 | 
						||
        if (pContext->numBufBytes)
 | 
						||
            RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
 | 
						||
    }
 | 
						||
}                               /* RecordFlushAllContexts */
 | 
						||
 | 
						||
/* RecordInstallHooks
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pRCAP is an RCAP on an enabled or being-enabled context.
 | 
						||
 *	oneclient can be zero or the resource ID mask identifying a client.
 | 
						||
 *
 | 
						||
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Recording hooks needed by RCAP are installed.
 | 
						||
 *	If oneclient is zero, recording hooks needed for all clients and
 | 
						||
 *	protocol on the RCAP are installed.  If oneclient is non-zero,
 | 
						||
 *	only those hooks needed for the specified client are installed.
 | 
						||
 *	
 | 
						||
 *	Client requestVectors may be altered.  numEnabledRCAPs will be
 | 
						||
 *	incremented if oneclient == 0.  Callbacks may be added to
 | 
						||
 *	various callback lists.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
 | 
						||
{
 | 
						||
    int i = 0;
 | 
						||
    XID client;
 | 
						||
 | 
						||
    if (oneclient)
 | 
						||
        client = oneclient;
 | 
						||
    else
 | 
						||
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
 | 
						||
 | 
						||
    while (client) {
 | 
						||
        if (client != XRecordFutureClients) {
 | 
						||
            if (pRCAP->pRequestMajorOpSet) {
 | 
						||
                RecordSetIteratePtr pIter = NULL;
 | 
						||
                RecordSetInterval interval;
 | 
						||
                ClientPtr pClient = clients[CLIENT_ID(client)];
 | 
						||
 | 
						||
                if (pClient && !RecordClientPrivate(pClient)) {
 | 
						||
                    RecordClientPrivatePtr pClientPriv;
 | 
						||
 | 
						||
                    /* no Record proc vector; allocate one */
 | 
						||
                    pClientPriv = (RecordClientPrivatePtr)
 | 
						||
                        malloc(sizeof(RecordClientPrivateRec));
 | 
						||
                    if (!pClientPriv)
 | 
						||
                        return BadAlloc;
 | 
						||
                    /* copy old proc vector to new */
 | 
						||
                    memcpy(pClientPriv->recordVector, pClient->requestVector,
 | 
						||
                           sizeof(pClientPriv->recordVector));
 | 
						||
                    pClientPriv->originalVector = pClient->requestVector;
 | 
						||
                    dixSetPrivate(&pClient->devPrivates,
 | 
						||
                                  RecordClientPrivateKey, pClientPriv);
 | 
						||
                    pClient->requestVector = pClientPriv->recordVector;
 | 
						||
                }
 | 
						||
                while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
 | 
						||
                                                 pIter, &interval))) {
 | 
						||
                    unsigned int j;
 | 
						||
 | 
						||
                    for (j = interval.first; j <= interval.last; j++)
 | 
						||
                        pClient->requestVector[j] = RecordARequest;
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
        if (oneclient)
 | 
						||
            client = 0;
 | 
						||
        else
 | 
						||
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
 | 
						||
    }
 | 
						||
 | 
						||
    assert(numEnabledRCAPs >= 0);
 | 
						||
    if (!oneclient && ++numEnabledRCAPs == 1) { /* we're enabling the first context */
 | 
						||
        if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
 | 
						||
            return BadAlloc;
 | 
						||
        if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
 | 
						||
            return BadAlloc;
 | 
						||
        if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
 | 
						||
            return BadAlloc;
 | 
						||
        if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
 | 
						||
            return BadAlloc;
 | 
						||
        /* Alternate context flushing scheme: delete the line above
 | 
						||
         * and call RegisterBlockAndWakeupHandlers here passing
 | 
						||
         * RecordFlushAllContexts.  Is this any better?
 | 
						||
         */
 | 
						||
    }
 | 
						||
    return Success;
 | 
						||
}                               /* RecordInstallHooks */
 | 
						||
 | 
						||
/* RecordUninstallHooks
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pRCAP is an RCAP on an enabled or being-disabled context.
 | 
						||
 *	oneclient can be zero or the resource ID mask identifying a client.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Recording hooks needed by RCAP may be uninstalled.
 | 
						||
 *	If oneclient is zero, recording hooks needed for all clients and
 | 
						||
 *	protocol on the RCAP may be uninstalled.  If oneclient is non-zero,
 | 
						||
 *	only those hooks needed for the specified client may be uninstalled.
 | 
						||
 *	
 | 
						||
 *	Client requestVectors may be altered.  numEnabledRCAPs will be
 | 
						||
 *	decremented if oneclient == 0.  Callbacks may be deleted from
 | 
						||
 *	various callback lists.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
 | 
						||
{
 | 
						||
    int i = 0;
 | 
						||
    XID client;
 | 
						||
 | 
						||
    if (oneclient)
 | 
						||
        client = oneclient;
 | 
						||
    else
 | 
						||
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
 | 
						||
 | 
						||
    while (client) {
 | 
						||
        if (client != XRecordFutureClients) {
 | 
						||
            if (pRCAP->pRequestMajorOpSet) {
 | 
						||
                ClientPtr pClient = clients[CLIENT_ID(client)];
 | 
						||
                int c;
 | 
						||
                Bool otherRCAPwantsProcVector = FALSE;
 | 
						||
                RecordClientPrivatePtr pClientPriv = NULL;
 | 
						||
 | 
						||
                assert(pClient);
 | 
						||
                pClientPriv = RecordClientPrivate(pClient);
 | 
						||
                assert(pClientPriv);
 | 
						||
                memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
 | 
						||
                       sizeof(pClientPriv->recordVector));
 | 
						||
 | 
						||
                for (c = 0; c < numEnabledContexts; c++) {
 | 
						||
                    RecordClientsAndProtocolPtr pOtherRCAP;
 | 
						||
                    RecordContextPtr pContext = ppAllContexts[c];
 | 
						||
 | 
						||
                    if (pContext == pRCAP->pContext)
 | 
						||
                        continue;
 | 
						||
                    pOtherRCAP = RecordFindClientOnContext(pContext, client,
 | 
						||
                                                           NULL);
 | 
						||
                    if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet) {
 | 
						||
                        RecordSetIteratePtr pIter = NULL;
 | 
						||
                        RecordSetInterval interval;
 | 
						||
 | 
						||
                        otherRCAPwantsProcVector = TRUE;
 | 
						||
                        while ((pIter =
 | 
						||
                                RecordIterateSet(pOtherRCAP->pRequestMajorOpSet,
 | 
						||
                                                 pIter, &interval))) {
 | 
						||
                            unsigned int j;
 | 
						||
 | 
						||
                            for (j = interval.first; j <= interval.last; j++)
 | 
						||
                                pClient->requestVector[j] = RecordARequest;
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
                }
 | 
						||
                if (!otherRCAPwantsProcVector) {        /* nobody needs it, so free it */
 | 
						||
                    pClient->requestVector = pClientPriv->originalVector;
 | 
						||
                    dixSetPrivate(&pClient->devPrivates,
 | 
						||
                                  RecordClientPrivateKey, NULL);
 | 
						||
                    free(pClientPriv);
 | 
						||
                }
 | 
						||
            }                   /* end if this RCAP specifies any requests */
 | 
						||
        }                       /* end if not future clients */
 | 
						||
        if (oneclient)
 | 
						||
            client = 0;
 | 
						||
        else
 | 
						||
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
 | 
						||
    }
 | 
						||
 | 
						||
    assert(numEnabledRCAPs >= 1);
 | 
						||
    if (!oneclient && --numEnabledRCAPs == 0) { /* we're disabling the last context */
 | 
						||
        DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
 | 
						||
        DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
 | 
						||
        DeleteCallback(&ReplyCallback, RecordAReply, NULL);
 | 
						||
        DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
 | 
						||
        /* Alternate context flushing scheme: delete the line above
 | 
						||
         * and call RemoveBlockAndWakeupHandlers here passing
 | 
						||
         * RecordFlushAllContexts.  Is this any better?
 | 
						||
         */
 | 
						||
        /* Having deleted the callback, call it one last time. -gildea */
 | 
						||
        RecordFlushAllContexts(&FlushCallback, NULL, NULL);
 | 
						||
    }
 | 
						||
}                               /* RecordUninstallHooks */
 | 
						||
 | 
						||
/* RecordDeleteClientFromRCAP
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pRCAP is an RCAP to delete the client from.
 | 
						||
 *	position is the index into the array pRCAP->pClientIDs of the
 | 
						||
 *	client to delete.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Recording hooks needed by client will be uninstalled if the context
 | 
						||
 *	is enabled.  The designated client will be removed from the
 | 
						||
 *	pRCAP->pClientIDs array.  If it was the only client on the RCAP,
 | 
						||
 *	the RCAP is removed from the context and freed.  (Invariant: RCAPs
 | 
						||
 *	have at least one client.)
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
 | 
						||
{
 | 
						||
    if (pRCAP->pContext->pRecordingClient)
 | 
						||
        RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
 | 
						||
    if (position != pRCAP->numClients - 1)
 | 
						||
        pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
 | 
						||
    if (--pRCAP->numClients == 0) {     /* no more clients; remove RCAP from context's list */
 | 
						||
        RecordContextPtr pContext = pRCAP->pContext;
 | 
						||
 | 
						||
        if (pContext->pRecordingClient)
 | 
						||
            RecordUninstallHooks(pRCAP, 0);
 | 
						||
        if (pContext->pListOfRCAP == pRCAP)
 | 
						||
            pContext->pListOfRCAP = pRCAP->pNextRCAP;
 | 
						||
        else {
 | 
						||
            RecordClientsAndProtocolPtr prevRCAP;
 | 
						||
 | 
						||
            for (prevRCAP = pContext->pListOfRCAP;
 | 
						||
                 prevRCAP->pNextRCAP != pRCAP; prevRCAP = prevRCAP->pNextRCAP);
 | 
						||
            prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
 | 
						||
        }
 | 
						||
        /* free the RCAP */
 | 
						||
        if (pRCAP->clientIDsSeparatelyAllocated)
 | 
						||
            free(pRCAP->pClientIDs);
 | 
						||
        free(pRCAP);
 | 
						||
    }
 | 
						||
}                               /* RecordDeleteClientFromRCAP */
 | 
						||
 | 
						||
/* RecordAddClientToRCAP
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pRCAP is an RCAP to add the client to.
 | 
						||
 *	clientspec is the resource ID mask identifying a client, or
 | 
						||
 *	  XRecordFutureClients.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Recording hooks needed by client will be installed if the context
 | 
						||
 *	is enabled.  The designated client will be added to the
 | 
						||
 *	pRCAP->pClientIDs array, which may be realloced.
 | 
						||
 *	pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
 | 
						||
 *	is no more room to hold clients internal to the RCAP.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
 | 
						||
{
 | 
						||
    if (pRCAP->numClients == pRCAP->sizeClients) {
 | 
						||
        if (pRCAP->clientIDsSeparatelyAllocated) {
 | 
						||
            XID *pNewIDs =
 | 
						||
                reallocarray(pRCAP->pClientIDs,
 | 
						||
                             pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
 | 
						||
                             sizeof(XID));
 | 
						||
            if (!pNewIDs)
 | 
						||
                return;
 | 
						||
            pRCAP->pClientIDs = pNewIDs;
 | 
						||
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
 | 
						||
        }
 | 
						||
        else {
 | 
						||
            XID *pNewIDs =
 | 
						||
                xallocarray(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
 | 
						||
                            sizeof(XID));
 | 
						||
            if (!pNewIDs)
 | 
						||
                return;
 | 
						||
            memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients * sizeof(XID));
 | 
						||
            pRCAP->pClientIDs = pNewIDs;
 | 
						||
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
 | 
						||
            pRCAP->clientIDsSeparatelyAllocated = 1;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
 | 
						||
    if (pRCAP->pContext->pRecordingClient)
 | 
						||
        RecordInstallHooks(pRCAP, clientspec);
 | 
						||
}                               /* RecordDeleteClientFromRCAP */
 | 
						||
 | 
						||
/* RecordDeleteClientFromContext
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context to delete from.
 | 
						||
 *	clientspec is the resource ID mask identifying a client, or
 | 
						||
 *	  XRecordFutureClients.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If clientspec is on any RCAP of the context, it is deleted from that
 | 
						||
 *	RCAP.  (A given clientspec can only be on one RCAP of a context.)
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
 | 
						||
{
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int position;
 | 
						||
 | 
						||
    if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
 | 
						||
        RecordDeleteClientFromRCAP(pRCAP, position);
 | 
						||
}                               /* RecordDeleteClientFromContext */
 | 
						||
 | 
						||
/* RecordSanityCheckClientSpecifiers
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	clientspecs is an array of alleged CLIENTSPECs passed by the client.
 | 
						||
 *	nspecs is the number of elements in clientspecs.
 | 
						||
 *	errorspec, if non-zero, is the resource id base of a client that
 | 
						||
 *	  must not appear in clienspecs.
 | 
						||
 *
 | 
						||
 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects: none.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordSanityCheckClientSpecifiers(ClientPtr client, XID *clientspecs,
 | 
						||
                                  int nspecs, XID errorspec)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
    int clientIndex;
 | 
						||
    int rc;
 | 
						||
    void *value;
 | 
						||
 | 
						||
    for (i = 0; i < nspecs; i++) {
 | 
						||
        if (clientspecs[i] == XRecordCurrentClients ||
 | 
						||
            clientspecs[i] == XRecordFutureClients ||
 | 
						||
            clientspecs[i] == XRecordAllClients)
 | 
						||
            continue;
 | 
						||
        if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec))
 | 
						||
            return BadMatch;
 | 
						||
        clientIndex = CLIENT_ID(clientspecs[i]);
 | 
						||
        if (clientIndex && clients[clientIndex] &&
 | 
						||
            clients[clientIndex]->clientState == ClientStateRunning) {
 | 
						||
            if (clientspecs[i] == clients[clientIndex]->clientAsMask)
 | 
						||
                continue;
 | 
						||
            rc = dixLookupResourceByClass(&value, clientspecs[i], RC_ANY,
 | 
						||
                                          client, DixGetAttrAccess);
 | 
						||
            if (rc != Success)
 | 
						||
                return rc;
 | 
						||
        }
 | 
						||
        else
 | 
						||
            return BadMatch;
 | 
						||
    }
 | 
						||
    return Success;
 | 
						||
}                               /* RecordSanityCheckClientSpecifiers */
 | 
						||
 | 
						||
/* RecordCanonicalizeClientSpecifiers
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pClientspecs is an array of CLIENTSPECs that have been sanity
 | 
						||
 *	  checked.
 | 
						||
 *	pNumClientspecs is a pointer to the number of elements in pClientspecs.
 | 
						||
 *	excludespec, if non-zero, is the resource id base of a client that
 | 
						||
 *	  should not be included in the expansion of XRecordAllClients or
 | 
						||
 *	  XRecordCurrentClients.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	A pointer to an array of CLIENTSPECs that is the same as the
 | 
						||
 *	passed array with the following modifications:
 | 
						||
 *	  - all but the client id bits of resource IDs are stripped off.
 | 
						||
 *	  - duplicates removed.
 | 
						||
 *	  - XRecordAllClients expanded to a list of all currently connected
 | 
						||
 *	    clients + XRecordFutureClients - excludespec (if non-zero)
 | 
						||
 *	  - XRecordCurrentClients expanded to a list of all currently
 | 
						||
 *	    connected clients - excludespec (if non-zero)
 | 
						||
 *	The returned array may be the passed array modified in place, or
 | 
						||
 *	it may be an malloc'ed array.  The caller should keep a pointer to the
 | 
						||
 *	original array and free the returned array if it is different.
 | 
						||
 *
 | 
						||
 *	*pNumClientspecs is set to the number of elements in the returned
 | 
						||
 *	array.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	pClientspecs may be modified in place.
 | 
						||
 */
 | 
						||
static XID *
 | 
						||
RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs,
 | 
						||
                                   XID excludespec)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
    int numClients = *pNumClientspecs;
 | 
						||
 | 
						||
    /*  first pass strips off the resource index bits, leaving just the
 | 
						||
     *  client id bits.  This makes searching for a particular client simpler
 | 
						||
     *  (and faster.)
 | 
						||
     */
 | 
						||
    for (i = 0; i < numClients; i++) {
 | 
						||
        XID cs = pClientspecs[i];
 | 
						||
 | 
						||
        if (cs > XRecordAllClients)
 | 
						||
            pClientspecs[i] = CLIENT_BITS(cs);
 | 
						||
    }
 | 
						||
 | 
						||
    for (i = 0; i < numClients; i++) {
 | 
						||
        if (pClientspecs[i] == XRecordAllClients || pClientspecs[i] == XRecordCurrentClients) { /* expand All/Current */
 | 
						||
            int j, nc;
 | 
						||
            XID *pCanon = xallocarray(currentMaxClients + 1, sizeof(XID));
 | 
						||
 | 
						||
            if (!pCanon)
 | 
						||
                return NULL;
 | 
						||
            for (nc = 0, j = 1; j < currentMaxClients; j++) {
 | 
						||
                ClientPtr client = clients[j];
 | 
						||
 | 
						||
                if (client != NullClient &&
 | 
						||
                    client->clientState == ClientStateRunning &&
 | 
						||
                    client->clientAsMask != excludespec) {
 | 
						||
                    pCanon[nc++] = client->clientAsMask;
 | 
						||
                }
 | 
						||
            }
 | 
						||
            if (pClientspecs[i] == XRecordAllClients)
 | 
						||
                pCanon[nc++] = XRecordFutureClients;
 | 
						||
            *pNumClientspecs = nc;
 | 
						||
            return pCanon;
 | 
						||
        }
 | 
						||
        else {                  /* not All or Current */
 | 
						||
 | 
						||
            int j;
 | 
						||
 | 
						||
            for (j = i + 1; j < numClients;) {
 | 
						||
                if (pClientspecs[i] == pClientspecs[j]) {
 | 
						||
                    pClientspecs[j] = pClientspecs[--numClients];
 | 
						||
                }
 | 
						||
                else
 | 
						||
                    j++;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }                           /* end for each clientspec */
 | 
						||
    *pNumClientspecs = numClients;
 | 
						||
    return pClientspecs;
 | 
						||
}                               /* RecordCanonicalizeClientSpecifiers */
 | 
						||
 | 
						||
/****************************************************************************/
 | 
						||
 | 
						||
/* stuff for RegisterClients */
 | 
						||
 | 
						||
/* RecordPadAlign
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	size is the number of bytes taken by an object.
 | 
						||
 *	align is a byte boundary (e.g. 4, 8)
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	the number of pad bytes to add at the end of an object of the
 | 
						||
 *	given size so that an object placed immediately behind it will
 | 
						||
 *	begin on an <align>-byte boundary.
 | 
						||
 *
 | 
						||
 * Side Effects: none.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordPadAlign(int size, int align)
 | 
						||
{
 | 
						||
    return (align - (size & (align - 1))) & (align - 1);
 | 
						||
}                               /* RecordPadAlign */
 | 
						||
 | 
						||
/* RecordSanityCheckRegisterClients
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context being registered on.
 | 
						||
 *	client is the client that issued a RecordCreateContext or
 | 
						||
 *	  RecordRegisterClients request.
 | 
						||
 *	stuff is a pointer to the request.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	Any one of several possible error values if any of the request
 | 
						||
 *	arguments are invalid.  Success if everything is OK.
 | 
						||
 *
 | 
						||
 * Side Effects: none.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client,
 | 
						||
                                 xRecordRegisterClientsReq * stuff)
 | 
						||
{
 | 
						||
    int err;
 | 
						||
    xRecordRange *pRange;
 | 
						||
    int i;
 | 
						||
    XID recordingClient;
 | 
						||
 | 
						||
    if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
 | 
						||
        4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
 | 
						||
        return BadLength;
 | 
						||
 | 
						||
    if (stuff->elementHeader &
 | 
						||
        ~(XRecordFromClientSequence | XRecordFromClientTime |
 | 
						||
          XRecordFromServerTime)) {
 | 
						||
        client->errorValue = stuff->elementHeader;
 | 
						||
        return BadValue;
 | 
						||
    }
 | 
						||
 | 
						||
    recordingClient = pContext->pRecordingClient ?
 | 
						||
        pContext->pRecordingClient->clientAsMask : 0;
 | 
						||
    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
 | 
						||
                                            stuff->nClients, recordingClient);
 | 
						||
    if (err != Success)
 | 
						||
        return err;
 | 
						||
 | 
						||
    pRange = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
 | 
						||
    for (i = 0; i < stuff->nRanges; i++, pRange++) {
 | 
						||
        if (pRange->coreRequestsFirst > pRange->coreRequestsLast) {
 | 
						||
            client->errorValue = pRange->coreRequestsFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->coreRepliesFirst > pRange->coreRepliesLast) {
 | 
						||
            client->errorValue = pRange->coreRepliesFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
 | 
						||
            (pRange->extRequestsMajorFirst < 128 ||
 | 
						||
             pRange->extRequestsMajorLast < 128 ||
 | 
						||
             pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast)) {
 | 
						||
            client->errorValue = pRange->extRequestsMajorFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast) {
 | 
						||
            client->errorValue = pRange->extRequestsMinorFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
 | 
						||
            (pRange->extRepliesMajorFirst < 128 ||
 | 
						||
             pRange->extRepliesMajorLast < 128 ||
 | 
						||
             pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast)) {
 | 
						||
            client->errorValue = pRange->extRepliesMajorFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast) {
 | 
						||
            client->errorValue = pRange->extRepliesMinorFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
 | 
						||
            (pRange->deliveredEventsFirst < 2 ||
 | 
						||
             pRange->deliveredEventsLast < 2 ||
 | 
						||
             pRange->deliveredEventsFirst > pRange->deliveredEventsLast)) {
 | 
						||
            client->errorValue = pRange->deliveredEventsFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
 | 
						||
            (pRange->deviceEventsFirst < 2 ||
 | 
						||
             pRange->deviceEventsLast < 2 ||
 | 
						||
             pRange->deviceEventsFirst > pRange->deviceEventsLast)) {
 | 
						||
            client->errorValue = pRange->deviceEventsFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->errorsFirst > pRange->errorsLast) {
 | 
						||
            client->errorValue = pRange->errorsFirst;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue) {
 | 
						||
            client->errorValue = pRange->clientStarted;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
        if (pRange->clientDied != xFalse && pRange->clientDied != xTrue) {
 | 
						||
            client->errorValue = pRange->clientDied;
 | 
						||
            return BadValue;
 | 
						||
        }
 | 
						||
    }                           /* end for each range */
 | 
						||
    return Success;
 | 
						||
}                               /* end RecordSanityCheckRegisterClients */
 | 
						||
 | 
						||
/* This is a tactical structure used to gather information about all the sets
 | 
						||
 * (RecordSetPtr) that need to be created for an RCAP in the process of
 | 
						||
 * digesting a list of RECORDRANGEs (converting it to the internal
 | 
						||
 * representation).
 | 
						||
 */
 | 
						||
typedef struct {
 | 
						||
    int nintervals;             /* number of intervals in following array */
 | 
						||
    RecordSetInterval *intervals;       /* array of intervals for this set */
 | 
						||
    int size;                   /* size of intervals array; >= nintervals */
 | 
						||
    int align;                  /* alignment restriction for set */
 | 
						||
    int offset;                 /* where to store set pointer rel. to start of RCAP */
 | 
						||
    short first, last;          /* if for extension, major opcode interval */
 | 
						||
} SetInfoRec, *SetInfoPtr;
 | 
						||
 | 
						||
/* These constant are used to index into an array of SetInfoRec. */
 | 
						||
enum { REQ,                     /* set info for requests */
 | 
						||
    RI_REP,                     /* set info for replies */
 | 
						||
    RI_ERR,                     /* set info for errors */
 | 
						||
    RI_DEV,                     /* set info for device events */
 | 
						||
    RI_DLEV,                    /* set info for delivered events */
 | 
						||
    RI_PREDEFSETS
 | 
						||
};                              /* number of predefined array entries */
 | 
						||
 | 
						||
/* RecordAllocIntervals
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
 | 
						||
 *	nIntervals is the desired size of the intervals array.
 | 
						||
 *
 | 
						||
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If Success is returned, psi->intervals is a pointer to size
 | 
						||
 *	RecordSetIntervals, all zeroed, and psi->size is set to size.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
 | 
						||
{
 | 
						||
    assert(!psi->intervals);
 | 
						||
    psi->intervals = xallocarray(nIntervals, sizeof(RecordSetInterval));
 | 
						||
    if (!psi->intervals)
 | 
						||
        return BadAlloc;
 | 
						||
    memset(psi->intervals, 0, nIntervals * sizeof(RecordSetInterval));
 | 
						||
    psi->size = nIntervals;
 | 
						||
    return Success;
 | 
						||
}                               /* end RecordAllocIntervals */
 | 
						||
 | 
						||
/* RecordConvertRangesToIntervals
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	psi is a pointer to the SetInfoRec we are building.
 | 
						||
 *	pRanges is an array of xRecordRanges.
 | 
						||
 *	nRanges is the number of elements in pRanges.
 | 
						||
 *	byteoffset is the offset from the start of an xRecordRange of the
 | 
						||
 *	  two bytes (1 for first, 1 for last) we are interested in.
 | 
						||
 *	pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
 | 
						||
 *	  above are followed by four bytes (2 for first, 2 for last)
 | 
						||
 *	  representing a minor opcode range, and this information should be
 | 
						||
 *	  stored in one of the SetInfoRecs starting at pExtSetInfo.
 | 
						||
 *	pnExtSetInfo is the number of elements in the pExtSetInfo array.
 | 
						||
 *
 | 
						||
 * Returns:  BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The slice of pRanges indicated by byteoffset is stored in psi.
 | 
						||
 *	If pExtSetInfo is non-NULL, minor opcode intervals are stored
 | 
						||
 *	in an existing SetInfoRec if the major opcode interval matches, else
 | 
						||
 *	they are stored in a new SetInfoRec, and *pnExtSetInfo is
 | 
						||
 *	increased accordingly.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordConvertRangesToIntervals(SetInfoPtr psi,
 | 
						||
                               xRecordRange * pRanges,
 | 
						||
                               int nRanges,
 | 
						||
                               int byteoffset,
 | 
						||
                               SetInfoPtr pExtSetInfo, int *pnExtSetInfo)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
    CARD8 *pCARD8;
 | 
						||
    int first, last;
 | 
						||
    int err;
 | 
						||
 | 
						||
    for (i = 0; i < nRanges; i++, pRanges++) {
 | 
						||
        pCARD8 = ((CARD8 *) pRanges) + byteoffset;
 | 
						||
        first = pCARD8[0];
 | 
						||
        last = pCARD8[1];
 | 
						||
        if (first || last) {
 | 
						||
            if (!psi->intervals) {
 | 
						||
                err = RecordAllocIntervals(psi, 2 * (nRanges - i));
 | 
						||
                if (err != Success)
 | 
						||
                    return err;
 | 
						||
            }
 | 
						||
            psi->intervals[psi->nintervals].first = first;
 | 
						||
            psi->intervals[psi->nintervals].last = last;
 | 
						||
            psi->nintervals++;
 | 
						||
            assert(psi->nintervals <= psi->size);
 | 
						||
            if (pExtSetInfo) {
 | 
						||
                SetInfoPtr pesi = pExtSetInfo;
 | 
						||
                CARD16 *pCARD16 = (CARD16 *) (pCARD8 + 2);
 | 
						||
                int j;
 | 
						||
 | 
						||
                for (j = 0; j < *pnExtSetInfo; j++, pesi++) {
 | 
						||
                    if ((first == pesi->first) && (last == pesi->last))
 | 
						||
                        break;
 | 
						||
                }
 | 
						||
                if (j == *pnExtSetInfo) {
 | 
						||
                    err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
 | 
						||
                    if (err != Success)
 | 
						||
                        return err;
 | 
						||
                    pesi->first = first;
 | 
						||
                    pesi->last = last;
 | 
						||
                    (*pnExtSetInfo)++;
 | 
						||
                }
 | 
						||
                pesi->intervals[pesi->nintervals].first = pCARD16[0];
 | 
						||
                pesi->intervals[pesi->nintervals].last = pCARD16[1];
 | 
						||
                pesi->nintervals++;
 | 
						||
                assert(pesi->nintervals <= pesi->size);
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
    return Success;
 | 
						||
}                               /* end RecordConvertRangesToIntervals */
 | 
						||
 | 
						||
#define offset_of(_structure, _field) \
 | 
						||
    ((char *)(& (_structure . _field)) - (char *)(&_structure))
 | 
						||
 | 
						||
/* RecordRegisterClients
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context on which to register the clients.
 | 
						||
 *	client is the client that issued the RecordCreateContext or
 | 
						||
 *	  RecordRegisterClients request.
 | 
						||
 *	stuff is a pointer to the request.
 | 
						||
 *
 | 
						||
 * Returns:
 | 
						||
 *	Any one of several possible error values defined by the protocol.
 | 
						||
 *	Success if everything is OK.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If different element headers are specified, the context is flushed.
 | 
						||
 *	If any of the specified clients are already registered on the
 | 
						||
 *	context, they are first unregistered.  A new RCAP is created to
 | 
						||
 *	hold the specified protocol and clients, and it is linked onto the
 | 
						||
 *	context.  If the context is enabled, appropriate hooks are installed
 | 
						||
 *	to record the new clients and protocol.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordRegisterClients(RecordContextPtr pContext, ClientPtr client,
 | 
						||
                      xRecordRegisterClientsReq * stuff)
 | 
						||
{
 | 
						||
    int err;
 | 
						||
    int i;
 | 
						||
    SetInfoPtr si;
 | 
						||
    int maxSets;
 | 
						||
    int nExtReqSets = 0;
 | 
						||
    int nExtRepSets = 0;
 | 
						||
    int extReqSetsOffset = 0;
 | 
						||
    int extRepSetsOffset = 0;
 | 
						||
    SetInfoPtr pExtReqSets, pExtRepSets;
 | 
						||
    int clientListOffset;
 | 
						||
    XID *pCanonClients;
 | 
						||
    int clientStarted = 0, clientDied = 0;
 | 
						||
    xRecordRange *pRanges, rr;
 | 
						||
    int nClients;
 | 
						||
    int sizeClients;
 | 
						||
    int totRCAPsize;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int pad;
 | 
						||
    XID recordingClient;
 | 
						||
 | 
						||
    /* do all sanity checking up front */
 | 
						||
 | 
						||
    err = RecordSanityCheckRegisterClients(pContext, client, stuff);
 | 
						||
    if (err != Success)
 | 
						||
        return err;
 | 
						||
 | 
						||
    /* if element headers changed, flush buffer */
 | 
						||
 | 
						||
    if (pContext->elemHeaders != stuff->elementHeader) {
 | 
						||
        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
 | 
						||
        pContext->elemHeaders = stuff->elementHeader;
 | 
						||
    }
 | 
						||
 | 
						||
    nClients = stuff->nClients;
 | 
						||
    if (!nClients)
 | 
						||
        /* if empty clients list, we're done. */
 | 
						||
        return Success;
 | 
						||
 | 
						||
    recordingClient = pContext->pRecordingClient ?
 | 
						||
        pContext->pRecordingClient->clientAsMask : 0;
 | 
						||
    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
 | 
						||
                                                       &nClients,
 | 
						||
                                                       recordingClient);
 | 
						||
    if (!pCanonClients)
 | 
						||
        return BadAlloc;
 | 
						||
 | 
						||
    /* We may have to create as many as one set for each "predefined"
 | 
						||
     * protocol types, plus one per range for extension requests, plus one per
 | 
						||
     * range for extension replies.
 | 
						||
     */
 | 
						||
    maxSets = RI_PREDEFSETS + 2 * stuff->nRanges;
 | 
						||
    si = xallocarray(maxSets, sizeof(SetInfoRec));
 | 
						||
    if (!si) {
 | 
						||
        err = BadAlloc;
 | 
						||
        goto bailout;
 | 
						||
    }
 | 
						||
    memset(si, 0, sizeof(SetInfoRec) * maxSets);
 | 
						||
 | 
						||
    /* theoretically you must do this because NULL may not be all-bits-zero */
 | 
						||
    for (i = 0; i < maxSets; i++)
 | 
						||
        si[i].intervals = NULL;
 | 
						||
 | 
						||
    pExtReqSets = si + RI_PREDEFSETS;
 | 
						||
    pExtRepSets = pExtReqSets + stuff->nRanges;
 | 
						||
 | 
						||
    pRanges = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, coreRequestsFirst), NULL,
 | 
						||
                                         NULL);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, extRequestsMajorFirst),
 | 
						||
                                         pExtReqSets, &nExtReqSets);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[RI_REP], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, coreRepliesFirst), NULL,
 | 
						||
                                         NULL);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[RI_REP], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, extRepliesMajorFirst),
 | 
						||
                                         pExtRepSets, &nExtRepSets);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[RI_ERR], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, errorsFirst), NULL,
 | 
						||
                                         NULL);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[RI_DLEV], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, deliveredEventsFirst),
 | 
						||
                                         NULL, NULL);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    err = RecordConvertRangesToIntervals(&si[RI_DEV], pRanges, stuff->nRanges,
 | 
						||
                                         offset_of(rr, deviceEventsFirst), NULL,
 | 
						||
                                         NULL);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    /* collect client-started and client-died */
 | 
						||
 | 
						||
    for (i = 0; i < stuff->nRanges; i++) {
 | 
						||
        if (pRanges[i].clientStarted)
 | 
						||
            clientStarted = TRUE;
 | 
						||
        if (pRanges[i].clientDied)
 | 
						||
            clientDied = TRUE;
 | 
						||
    }
 | 
						||
 | 
						||
    /*  We now have all the information collected to create all the sets,
 | 
						||
     * and we can compute the total memory required for the RCAP.
 | 
						||
     */
 | 
						||
 | 
						||
    totRCAPsize = sizeof(RecordClientsAndProtocolRec);
 | 
						||
 | 
						||
    /* leave a little room to grow before forcing a separate allocation */
 | 
						||
    sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
 | 
						||
    pad = RecordPadAlign(totRCAPsize, sizeof(XID));
 | 
						||
    clientListOffset = totRCAPsize + pad;
 | 
						||
    totRCAPsize += pad + sizeClients * sizeof(XID);
 | 
						||
 | 
						||
    if (nExtReqSets) {
 | 
						||
        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
 | 
						||
        extReqSetsOffset = totRCAPsize + pad;
 | 
						||
        totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
 | 
						||
    }
 | 
						||
    if (nExtRepSets) {
 | 
						||
        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
 | 
						||
        extRepSetsOffset = totRCAPsize + pad;
 | 
						||
        totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
 | 
						||
    }
 | 
						||
 | 
						||
    for (i = 0; i < maxSets; i++) {
 | 
						||
        if (si[i].nintervals) {
 | 
						||
            si[i].size =
 | 
						||
                RecordSetMemoryRequirements(si[i].intervals, si[i].nintervals,
 | 
						||
                                            &si[i].align);
 | 
						||
            pad = RecordPadAlign(totRCAPsize, si[i].align);
 | 
						||
            si[i].offset = pad + totRCAPsize;
 | 
						||
            totRCAPsize += pad + si[i].size;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    /* allocate memory for the whole RCAP */
 | 
						||
 | 
						||
    pRCAP = (RecordClientsAndProtocolPtr) malloc(totRCAPsize);
 | 
						||
    if (!pRCAP) {
 | 
						||
        err = BadAlloc;
 | 
						||
        goto bailout;
 | 
						||
    }
 | 
						||
 | 
						||
    /* fill in the RCAP */
 | 
						||
 | 
						||
    pRCAP->pContext = pContext;
 | 
						||
    pRCAP->pClientIDs = (XID *) ((char *) pRCAP + clientListOffset);
 | 
						||
    pRCAP->numClients = nClients;
 | 
						||
    pRCAP->sizeClients = sizeClients;
 | 
						||
    pRCAP->clientIDsSeparatelyAllocated = 0;
 | 
						||
    for (i = 0; i < nClients; i++) {
 | 
						||
        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
 | 
						||
        pRCAP->pClientIDs[i] = pCanonClients[i];
 | 
						||
    }
 | 
						||
 | 
						||
    /* create all the sets */
 | 
						||
 | 
						||
    if (si[REQ].intervals) {
 | 
						||
        pRCAP->pRequestMajorOpSet =
 | 
						||
            RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
 | 
						||
                            (RecordSetPtr) ((char *) pRCAP + si[REQ].offset),
 | 
						||
                            si[REQ].size);
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pRequestMajorOpSet = NULL;
 | 
						||
 | 
						||
    if (si[RI_REP].intervals) {
 | 
						||
        pRCAP->pReplyMajorOpSet =
 | 
						||
            RecordCreateSet(si[RI_REP].intervals, si[RI_REP].nintervals,
 | 
						||
                            (RecordSetPtr) ((char *) pRCAP + si[RI_REP].offset),
 | 
						||
                            si[RI_REP].size);
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pReplyMajorOpSet = NULL;
 | 
						||
 | 
						||
    if (si[RI_ERR].intervals) {
 | 
						||
        pRCAP->pErrorSet =
 | 
						||
            RecordCreateSet(si[RI_ERR].intervals, si[RI_ERR].nintervals,
 | 
						||
                            (RecordSetPtr) ((char *) pRCAP + si[RI_ERR].offset),
 | 
						||
                            si[RI_ERR].size);
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pErrorSet = NULL;
 | 
						||
 | 
						||
    if (si[RI_DEV].intervals) {
 | 
						||
        pRCAP->pDeviceEventSet =
 | 
						||
            RecordCreateSet(si[RI_DEV].intervals, si[RI_DEV].nintervals,
 | 
						||
                            (RecordSetPtr) ((char *) pRCAP + si[RI_DEV].offset),
 | 
						||
                            si[RI_DEV].size);
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pDeviceEventSet = NULL;
 | 
						||
 | 
						||
    if (si[RI_DLEV].intervals) {
 | 
						||
        pRCAP->pDeliveredEventSet =
 | 
						||
            RecordCreateSet(si[RI_DLEV].intervals, si[RI_DLEV].nintervals,
 | 
						||
                            (RecordSetPtr) ((char *) pRCAP + si[RI_DLEV].offset),
 | 
						||
                            si[RI_DLEV].size);
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pDeliveredEventSet = NULL;
 | 
						||
 | 
						||
    if (nExtReqSets) {
 | 
						||
        pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
 | 
						||
            ((char *) pRCAP + extReqSetsOffset);
 | 
						||
        pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
 | 
						||
        for (i = 0; i < nExtReqSets; i++, pExtReqSets++) {
 | 
						||
            pRCAP->pRequestMinOpInfo[i + 1].major.first = pExtReqSets->first;
 | 
						||
            pRCAP->pRequestMinOpInfo[i + 1].major.last = pExtReqSets->last;
 | 
						||
            pRCAP->pRequestMinOpInfo[i + 1].major.pMinOpSet =
 | 
						||
                RecordCreateSet(pExtReqSets->intervals,
 | 
						||
                                pExtReqSets->nintervals,
 | 
						||
                                (RecordSetPtr) ((char *) pRCAP +
 | 
						||
                                                pExtReqSets->offset),
 | 
						||
                                pExtReqSets->size);
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pRequestMinOpInfo = NULL;
 | 
						||
 | 
						||
    if (nExtRepSets) {
 | 
						||
        pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
 | 
						||
            ((char *) pRCAP + extRepSetsOffset);
 | 
						||
        pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
 | 
						||
        for (i = 0; i < nExtRepSets; i++, pExtRepSets++) {
 | 
						||
            pRCAP->pReplyMinOpInfo[i + 1].major.first = pExtRepSets->first;
 | 
						||
            pRCAP->pReplyMinOpInfo[i + 1].major.last = pExtRepSets->last;
 | 
						||
            pRCAP->pReplyMinOpInfo[i + 1].major.pMinOpSet =
 | 
						||
                RecordCreateSet(pExtRepSets->intervals,
 | 
						||
                                pExtRepSets->nintervals,
 | 
						||
                                (RecordSetPtr) ((char *) pRCAP +
 | 
						||
                                                pExtRepSets->offset),
 | 
						||
                                pExtRepSets->size);
 | 
						||
        }
 | 
						||
    }
 | 
						||
    else
 | 
						||
        pRCAP->pReplyMinOpInfo = NULL;
 | 
						||
 | 
						||
    pRCAP->clientStarted = clientStarted;
 | 
						||
    pRCAP->clientDied = clientDied;
 | 
						||
 | 
						||
    /* link the RCAP onto the context */
 | 
						||
 | 
						||
    pRCAP->pNextRCAP = pContext->pListOfRCAP;
 | 
						||
    pContext->pListOfRCAP = pRCAP;
 | 
						||
 | 
						||
    if (pContext->pRecordingClient)     /* context enabled */
 | 
						||
        RecordInstallHooks(pRCAP, 0);
 | 
						||
 | 
						||
 bailout:
 | 
						||
    if (si) {
 | 
						||
        for (i = 0; i < maxSets; i++)
 | 
						||
            free(si[i].intervals);
 | 
						||
        free(si);
 | 
						||
    }
 | 
						||
    if (pCanonClients && pCanonClients != (XID *) &stuff[1])
 | 
						||
        free(pCanonClients);
 | 
						||
    return err;
 | 
						||
}                               /* RecordRegisterClients */
 | 
						||
 | 
						||
/* Proc functions all take a client argument, execute the request in
 | 
						||
 * client->requestBuffer, and return a protocol error status.
 | 
						||
 */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordQueryVersion(ClientPtr client)
 | 
						||
{
 | 
						||
    /* REQUEST(xRecordQueryVersionReq); */
 | 
						||
    xRecordQueryVersionReply rep = {
 | 
						||
        .type = X_Reply,
 | 
						||
        .sequenceNumber = client->sequence,
 | 
						||
        .length = 0,
 | 
						||
        .majorVersion = SERVER_RECORD_MAJOR_VERSION,
 | 
						||
        .minorVersion = SERVER_RECORD_MINOR_VERSION
 | 
						||
    };
 | 
						||
 | 
						||
    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
 | 
						||
    if (client->swapped) {
 | 
						||
        swaps(&rep.sequenceNumber);
 | 
						||
        swaps(&rep.majorVersion);
 | 
						||
        swaps(&rep.minorVersion);
 | 
						||
    }
 | 
						||
    WriteToClient(client, sizeof(xRecordQueryVersionReply), &rep);
 | 
						||
    return Success;
 | 
						||
}                               /* ProcRecordQueryVersion */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordCreateContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordCreateContextReq);
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    RecordContextPtr *ppNewAllContexts = NULL;
 | 
						||
    int err = BadAlloc;
 | 
						||
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
 | 
						||
    LEGAL_NEW_RESOURCE(stuff->context, client);
 | 
						||
 | 
						||
    pContext = (RecordContextPtr) malloc(sizeof(RecordContextRec));
 | 
						||
    if (!pContext)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    /* make sure there is room in ppAllContexts to store the new context */
 | 
						||
 | 
						||
    ppNewAllContexts =
 | 
						||
        reallocarray(ppAllContexts, numContexts + 1, sizeof(RecordContextPtr));
 | 
						||
    if (!ppNewAllContexts)
 | 
						||
        goto bailout;
 | 
						||
    ppAllContexts = ppNewAllContexts;
 | 
						||
 | 
						||
    pContext->id = stuff->context;
 | 
						||
    pContext->pRecordingClient = NULL;
 | 
						||
    pContext->pListOfRCAP = NULL;
 | 
						||
    pContext->elemHeaders = 0;
 | 
						||
    pContext->bufCategory = 0;
 | 
						||
    pContext->numBufBytes = 0;
 | 
						||
    pContext->pBufClient = NULL;
 | 
						||
    pContext->continuedReply = 0;
 | 
						||
    pContext->inFlush = 0;
 | 
						||
 | 
						||
    err = RecordRegisterClients(pContext, client,
 | 
						||
                                (xRecordRegisterClientsReq *) stuff);
 | 
						||
    if (err != Success)
 | 
						||
        goto bailout;
 | 
						||
 | 
						||
    if (AddResource(pContext->id, RTContext, pContext)) {
 | 
						||
        ppAllContexts[numContexts++] = pContext;
 | 
						||
        return Success;
 | 
						||
    }
 | 
						||
    else {
 | 
						||
        return BadAlloc;
 | 
						||
    }
 | 
						||
 bailout:
 | 
						||
    free(pContext);
 | 
						||
    return err;
 | 
						||
}                               /* ProcRecordCreateContext */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordRegisterClients(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    REQUEST(xRecordRegisterClientsReq);
 | 
						||
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
 | 
						||
    return RecordRegisterClients(pContext, client, stuff);
 | 
						||
}                               /* ProcRecordRegisterClients */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordUnregisterClients(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
    int err;
 | 
						||
 | 
						||
    REQUEST(xRecordUnregisterClientsReq);
 | 
						||
    XID *pCanonClients;
 | 
						||
    int nClients;
 | 
						||
    int i;
 | 
						||
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
 | 
						||
    if (INT_MAX / 4 < stuff->nClients ||
 | 
						||
        (client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
 | 
						||
        4 * stuff->nClients)
 | 
						||
        return BadLength;
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
 | 
						||
                                            stuff->nClients, 0);
 | 
						||
    if (err != Success)
 | 
						||
        return err;
 | 
						||
 | 
						||
    nClients = stuff->nClients;
 | 
						||
    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
 | 
						||
                                                       &nClients, 0);
 | 
						||
    if (!pCanonClients)
 | 
						||
        return BadAlloc;
 | 
						||
 | 
						||
    for (i = 0; i < nClients; i++) {
 | 
						||
        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
 | 
						||
    }
 | 
						||
    if (pCanonClients != (XID *) &stuff[1])
 | 
						||
        free(pCanonClients);
 | 
						||
    return Success;
 | 
						||
}                               /* ProcRecordUnregisterClients */
 | 
						||
 | 
						||
/****************************************************************************/
 | 
						||
 | 
						||
/* stuff for GetContext */
 | 
						||
 | 
						||
/* This is a tactical structure used to hold the xRecordRanges as they are
 | 
						||
 * being reconstituted from the sets in the RCAPs.
 | 
						||
 */
 | 
						||
 | 
						||
typedef struct {
 | 
						||
    xRecordRange *pRanges;      /* array of xRecordRanges for one RCAP */
 | 
						||
    int size;                   /* number of elements in pRanges, >= nRanges */
 | 
						||
    int nRanges;                /* number of occupied element of pRanges */
 | 
						||
} GetContextRangeInfoRec, *GetContextRangeInfoPtr;
 | 
						||
 | 
						||
/* RecordAllocRanges
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pri is a pointer to a GetContextRangeInfoRec to allocate for.
 | 
						||
 *	nRanges is the number of xRecordRanges desired for pri.
 | 
						||
 *
 | 
						||
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If Success is returned, pri->pRanges points to at least nRanges
 | 
						||
 *	ranges.  pri->nRanges is set to nRanges.  pri->size is the actual
 | 
						||
 *	number of ranges.  Newly allocated ranges are zeroed.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
 | 
						||
{
 | 
						||
    int newsize;
 | 
						||
    xRecordRange *pNewRange;
 | 
						||
 | 
						||
#define SZINCR 8
 | 
						||
 | 
						||
    newsize = max(pri->size + SZINCR, nRanges);
 | 
						||
    pNewRange = reallocarray(pri->pRanges, newsize, sizeof(xRecordRange));
 | 
						||
    if (!pNewRange)
 | 
						||
        return BadAlloc;
 | 
						||
 | 
						||
    pri->pRanges = pNewRange;
 | 
						||
    pri->size = newsize;
 | 
						||
    memset(&pri->pRanges[pri->size - SZINCR], 0, SZINCR * sizeof(xRecordRange));
 | 
						||
    if (pri->nRanges < nRanges)
 | 
						||
        pri->nRanges = nRanges;
 | 
						||
    return Success;
 | 
						||
}                               /* RecordAllocRanges */
 | 
						||
 | 
						||
/* RecordConvertSetToRanges
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pSet is the set to be converted.
 | 
						||
 *	pri is where the result should be stored.
 | 
						||
 *	byteoffset is the offset from the start of an xRecordRange of the
 | 
						||
 *	  two vales (first, last) we are interested in.
 | 
						||
 *	card8 is TRUE if the vales are one byte each and FALSE if two bytes
 | 
						||
 *	  each.
 | 
						||
 *	imax is the largest set value to store in pri->pRanges.
 | 
						||
 *	pStartIndex, if non-NULL, is the index of the first range in
 | 
						||
 *	  pri->pRanges that should be stored to.  If NULL,
 | 
						||
 *	  start at index 0.
 | 
						||
 *
 | 
						||
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If Success is returned, the slice of pri->pRanges indicated by
 | 
						||
 *	byteoffset and card8 is filled in with the intervals from pSet.
 | 
						||
 *	if pStartIndex was non-NULL, *pStartIndex is filled in with one
 | 
						||
 *	more than the index of the last xRecordRange that was touched.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordConvertSetToRanges(RecordSetPtr pSet,
 | 
						||
                         GetContextRangeInfoPtr pri,
 | 
						||
                         int byteoffset,
 | 
						||
                         Bool card8, unsigned int imax, int *pStartIndex)
 | 
						||
{
 | 
						||
    int nRanges;
 | 
						||
    RecordSetIteratePtr pIter = NULL;
 | 
						||
    RecordSetInterval interval;
 | 
						||
    CARD8 *pCARD8;
 | 
						||
    CARD16 *pCARD16;
 | 
						||
    int err;
 | 
						||
 | 
						||
    if (!pSet)
 | 
						||
        return Success;
 | 
						||
 | 
						||
    nRanges = pStartIndex ? *pStartIndex : 0;
 | 
						||
    while ((pIter = RecordIterateSet(pSet, pIter, &interval))) {
 | 
						||
        if (interval.first > imax)
 | 
						||
            break;
 | 
						||
        if (interval.last > imax)
 | 
						||
            interval.last = imax;
 | 
						||
        nRanges++;
 | 
						||
        if (nRanges > pri->size) {
 | 
						||
            err = RecordAllocRanges(pri, nRanges);
 | 
						||
            if (err != Success)
 | 
						||
                return err;
 | 
						||
        }
 | 
						||
        else
 | 
						||
            pri->nRanges = max(pri->nRanges, nRanges);
 | 
						||
        if (card8) {
 | 
						||
            pCARD8 = ((CARD8 *) &pri->pRanges[nRanges - 1]) + byteoffset;
 | 
						||
            *pCARD8++ = interval.first;
 | 
						||
            *pCARD8 = interval.last;
 | 
						||
        }
 | 
						||
        else {
 | 
						||
            pCARD16 = (CARD16 *)
 | 
						||
                (((char *) &pri->pRanges[nRanges - 1]) + byteoffset);
 | 
						||
            *pCARD16++ = interval.first;
 | 
						||
            *pCARD16 = interval.last;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    if (pStartIndex)
 | 
						||
        *pStartIndex = nRanges;
 | 
						||
    return Success;
 | 
						||
}                               /* RecordConvertSetToRanges */
 | 
						||
 | 
						||
/* RecordConvertMinorOpInfoToRanges
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pMinOpInfo is the minor opcode info to convert to xRecordRanges.
 | 
						||
 *	pri is where the result should be stored.
 | 
						||
 *	byteoffset is the offset from the start of an xRecordRange of the
 | 
						||
 *	  four vales (CARD8 major_first, CARD8 major_last,
 | 
						||
 *	  CARD16 minor_first, CARD16 minor_last) we are going to store.
 | 
						||
 *
 | 
						||
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If Success is returned, the slice of pri->pRanges indicated by
 | 
						||
 *	byteoffset is filled in with the information from pMinOpInfo.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordConvertMinorOpInfoToRanges(RecordMinorOpPtr pMinOpInfo,
 | 
						||
                                 GetContextRangeInfoPtr pri, int byteoffset)
 | 
						||
{
 | 
						||
    int nsets;
 | 
						||
    int start;
 | 
						||
    int i;
 | 
						||
    int err;
 | 
						||
 | 
						||
    if (!pMinOpInfo)
 | 
						||
        return Success;
 | 
						||
 | 
						||
    nsets = pMinOpInfo->count;
 | 
						||
    pMinOpInfo++;
 | 
						||
    start = 0;
 | 
						||
    for (i = 0; i < nsets; i++) {
 | 
						||
        int j, s;
 | 
						||
 | 
						||
        s = start;
 | 
						||
        err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
 | 
						||
                                       byteoffset + 2, FALSE, 65535, &start);
 | 
						||
        if (err != Success)
 | 
						||
            return err;
 | 
						||
        for (j = s; j < start; j++) {
 | 
						||
            CARD8 *pCARD8 = ((CARD8 *) &pri->pRanges[j]) + byteoffset;
 | 
						||
 | 
						||
            *pCARD8++ = pMinOpInfo[i].major.first;
 | 
						||
            *pCARD8 = pMinOpInfo[i].major.last;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    return Success;
 | 
						||
}                               /* RecordConvertMinorOpInfoToRanges */
 | 
						||
 | 
						||
/* RecordSwapRanges
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pRanges is an array of xRecordRanges.
 | 
						||
 *	nRanges is the number of elements in pRanges.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The 16 bit fields of each xRecordRange are byte swapped.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordSwapRanges(xRecordRange * pRanges, int nRanges)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
 | 
						||
    for (i = 0; i < nRanges; i++, pRanges++) {
 | 
						||
        swaps(&pRanges->extRequestsMinorFirst);
 | 
						||
        swaps(&pRanges->extRequestsMinorLast);
 | 
						||
        swaps(&pRanges->extRepliesMinorFirst);
 | 
						||
        swaps(&pRanges->extRepliesMinorLast);
 | 
						||
    }
 | 
						||
}                               /* RecordSwapRanges */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordGetContext(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    REQUEST(xRecordGetContextReq);
 | 
						||
    xRecordGetContextReply rep;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int nRCAPs = 0;
 | 
						||
    GetContextRangeInfoPtr pRangeInfo;
 | 
						||
    GetContextRangeInfoPtr pri;
 | 
						||
    int i;
 | 
						||
    int err;
 | 
						||
    CARD32 nClients, length;
 | 
						||
 | 
						||
    REQUEST_SIZE_MATCH(xRecordGetContextReq);
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
 | 
						||
    /* how many RCAPs are there on this context? */
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
 | 
						||
        nRCAPs++;
 | 
						||
 | 
						||
    /* allocate and initialize space for record range info */
 | 
						||
 | 
						||
    pRangeInfo = xallocarray(nRCAPs, sizeof(GetContextRangeInfoRec));
 | 
						||
    if (!pRangeInfo && nRCAPs > 0)
 | 
						||
        return BadAlloc;
 | 
						||
    for (i = 0; i < nRCAPs; i++) {
 | 
						||
        pRangeInfo[i].pRanges = NULL;
 | 
						||
        pRangeInfo[i].size = 0;
 | 
						||
        pRangeInfo[i].nRanges = 0;
 | 
						||
    }
 | 
						||
 | 
						||
    /* convert the RCAP (internal) representation of the recorded protocol
 | 
						||
     * to the wire protocol (external) representation, storing the information
 | 
						||
     * for the ith RCAP in pri[i]
 | 
						||
     */
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
 | 
						||
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
 | 
						||
        xRecordRange rr;
 | 
						||
 | 
						||
        err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
 | 
						||
                                       offset_of(rr, coreRequestsFirst), TRUE,
 | 
						||
                                       127, NULL);
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
 | 
						||
                                       offset_of(rr, coreRepliesFirst), TRUE,
 | 
						||
                                       127, NULL);
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
 | 
						||
                                       offset_of(rr, deliveredEventsFirst),
 | 
						||
                                       TRUE, 255, NULL);
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
 | 
						||
                                       offset_of(rr, deviceEventsFirst), TRUE,
 | 
						||
                                       255, NULL);
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
 | 
						||
                                       offset_of(rr, errorsFirst), TRUE, 255,
 | 
						||
                                       NULL);
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
 | 
						||
                                               pri, offset_of(rr,
 | 
						||
                                                              extRequestsMajorFirst));
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
 | 
						||
                                               pri, offset_of(rr,
 | 
						||
                                                              extRepliesMajorFirst));
 | 
						||
        if (err != Success)
 | 
						||
            goto bailout;
 | 
						||
 | 
						||
        if (pRCAP->clientStarted || pRCAP->clientDied) {
 | 
						||
            if (pri->nRanges == 0)
 | 
						||
                RecordAllocRanges(pri, 1);
 | 
						||
            pri->pRanges[0].clientStarted = pRCAP->clientStarted;
 | 
						||
            pri->pRanges[0].clientDied = pRCAP->clientDied;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    /* calculate number of clients and reply length */
 | 
						||
 | 
						||
    nClients = 0;
 | 
						||
    length = 0;
 | 
						||
    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
 | 
						||
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
 | 
						||
        nClients += pRCAP->numClients;
 | 
						||
        length += pRCAP->numClients *
 | 
						||
            (bytes_to_int32(sizeof(xRecordClientInfo)) +
 | 
						||
             pri->nRanges * bytes_to_int32(sizeof(xRecordRange)));
 | 
						||
    }
 | 
						||
 | 
						||
    /* write the reply header */
 | 
						||
 | 
						||
    rep = (xRecordGetContextReply) {
 | 
						||
        .type = X_Reply,
 | 
						||
        .enabled = pContext->pRecordingClient != NULL,
 | 
						||
        .sequenceNumber = client->sequence,
 | 
						||
        .length = length,
 | 
						||
        .elementHeader = pContext->elemHeaders,
 | 
						||
        .nClients = nClients
 | 
						||
    };
 | 
						||
    if (client->swapped) {
 | 
						||
        swaps(&rep.sequenceNumber);
 | 
						||
        swapl(&rep.length);
 | 
						||
        swapl(&rep.nClients);
 | 
						||
    }
 | 
						||
    WriteToClient(client, sizeof(xRecordGetContextReply), &rep);
 | 
						||
 | 
						||
    /* write all the CLIENT_INFOs */
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
 | 
						||
         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
 | 
						||
        xRecordClientInfo rci;
 | 
						||
 | 
						||
        rci.nRanges = pri->nRanges;
 | 
						||
        if (client->swapped) {
 | 
						||
            swapl(&rci.nRanges);
 | 
						||
            RecordSwapRanges(pri->pRanges, pri->nRanges);
 | 
						||
        }
 | 
						||
        for (i = 0; i < pRCAP->numClients; i++) {
 | 
						||
            rci.clientResource = pRCAP->pClientIDs[i];
 | 
						||
            if (client->swapped)
 | 
						||
                swapl(&rci.clientResource);
 | 
						||
            WriteToClient(client, sizeof(xRecordClientInfo), &rci);
 | 
						||
            WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
 | 
						||
                          pri->pRanges);
 | 
						||
        }
 | 
						||
    }
 | 
						||
    err = Success;
 | 
						||
 | 
						||
 bailout:
 | 
						||
    for (i = 0; i < nRCAPs; i++) {
 | 
						||
        free(pRangeInfo[i].pRanges);
 | 
						||
    }
 | 
						||
    free(pRangeInfo);
 | 
						||
    return err;
 | 
						||
}                               /* ProcRecordGetContext */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordEnableContext(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    REQUEST(xRecordEnableContextReq);
 | 
						||
    int i;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
 | 
						||
    REQUEST_SIZE_MATCH(xRecordGetContextReq);
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
    if (pContext->pRecordingClient)
 | 
						||
        return BadMatch;        /* already enabled */
 | 
						||
 | 
						||
    /* install record hooks for each RCAP */
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
 | 
						||
        int err = RecordInstallHooks(pRCAP, 0);
 | 
						||
 | 
						||
        if (err != Success) {   /* undo the previous installs */
 | 
						||
            RecordClientsAndProtocolPtr pUninstallRCAP;
 | 
						||
 | 
						||
            for (pUninstallRCAP = pContext->pListOfRCAP;
 | 
						||
                 pUninstallRCAP != pRCAP;
 | 
						||
                 pUninstallRCAP = pUninstallRCAP->pNextRCAP) {
 | 
						||
                RecordUninstallHooks(pUninstallRCAP, 0);
 | 
						||
            }
 | 
						||
            return err;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    /* Disallow further request processing on this connection until
 | 
						||
     * the context is disabled.
 | 
						||
     */
 | 
						||
    IgnoreClient(client);
 | 
						||
    pContext->pRecordingClient = client;
 | 
						||
 | 
						||
    /* Don't allow the data connection to record itself; unregister it. */
 | 
						||
    RecordDeleteClientFromContext(pContext,
 | 
						||
                                  pContext->pRecordingClient->clientAsMask);
 | 
						||
 | 
						||
    /* move the newly enabled context to the front part of ppAllContexts,
 | 
						||
     * where all the enabled contexts are
 | 
						||
     */
 | 
						||
    i = RecordFindContextOnAllContexts(pContext);
 | 
						||
    assert(i >= numEnabledContexts);
 | 
						||
    if (i != numEnabledContexts) {
 | 
						||
        ppAllContexts[i] = ppAllContexts[numEnabledContexts];
 | 
						||
        ppAllContexts[numEnabledContexts] = pContext;
 | 
						||
    }
 | 
						||
 | 
						||
    ++numEnabledContexts;
 | 
						||
    assert(numEnabledContexts > 0);
 | 
						||
 | 
						||
    /* send StartOfData */
 | 
						||
    RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0, 0);
 | 
						||
    RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
 | 
						||
    return Success;
 | 
						||
}                               /* ProcRecordEnableContext */
 | 
						||
 | 
						||
/* RecordDisableContext
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is the context to disable.
 | 
						||
 *	nRanges is the number of elements in pRanges.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If the context was enabled, it is disabled.  An EndOfData
 | 
						||
 *	message is sent to the recording client.  Recording hooks for
 | 
						||
 *	this context are uninstalled.  The context is moved to the
 | 
						||
 *	rear part of the ppAllContexts array.  numEnabledContexts is
 | 
						||
 *	decremented.  Request processing for the formerly recording client
 | 
						||
 *	is resumed.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordDisableContext(RecordContextPtr pContext)
 | 
						||
{
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
    int i;
 | 
						||
 | 
						||
    if (!pContext->pRecordingClient)
 | 
						||
        return;
 | 
						||
    if (!pContext->pRecordingClient->clientGone) {
 | 
						||
        RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0, 0);
 | 
						||
        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
 | 
						||
    }
 | 
						||
    /* Re-enable request processing on this connection. */
 | 
						||
    AttendClient(pContext->pRecordingClient);
 | 
						||
 | 
						||
    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
 | 
						||
        RecordUninstallHooks(pRCAP, 0);
 | 
						||
    }
 | 
						||
 | 
						||
    pContext->pRecordingClient = NULL;
 | 
						||
 | 
						||
    /* move the newly disabled context to the rear part of ppAllContexts,
 | 
						||
     * where all the disabled contexts are
 | 
						||
     */
 | 
						||
    i = RecordFindContextOnAllContexts(pContext);
 | 
						||
    assert(i != -1);
 | 
						||
    assert(i < numEnabledContexts);
 | 
						||
    if (i != (numEnabledContexts - 1)) {
 | 
						||
        ppAllContexts[i] = ppAllContexts[numEnabledContexts - 1];
 | 
						||
        ppAllContexts[numEnabledContexts - 1] = pContext;
 | 
						||
    }
 | 
						||
    --numEnabledContexts;
 | 
						||
    assert(numEnabledContexts >= 0);
 | 
						||
}                               /* RecordDisableContext */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordDisableContext(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    REQUEST(xRecordDisableContextReq);
 | 
						||
 | 
						||
    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
    RecordDisableContext(pContext);
 | 
						||
    return Success;
 | 
						||
}                               /* ProcRecordDisableContext */
 | 
						||
 | 
						||
/* RecordDeleteContext
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	value is the context to delete.
 | 
						||
 *	id is its resource ID.
 | 
						||
 *
 | 
						||
 * Returns: Success.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Disables the context, frees all associated memory, and removes
 | 
						||
 *	it from the ppAllContexts array.
 | 
						||
 */
 | 
						||
static int
 | 
						||
RecordDeleteContext(void *value, XID id)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
    RecordContextPtr pContext = (RecordContextPtr) value;
 | 
						||
    RecordClientsAndProtocolPtr pRCAP;
 | 
						||
 | 
						||
    RecordDisableContext(pContext);
 | 
						||
 | 
						||
    /*  Remove all the clients from all the RCAPs.
 | 
						||
     *  As a result, the RCAPs will be freed.
 | 
						||
     */
 | 
						||
 | 
						||
    while ((pRCAP = pContext->pListOfRCAP)) {
 | 
						||
        int numClients = pRCAP->numClients;
 | 
						||
 | 
						||
        /* when the last client is deleted, the RCAP will go away. */
 | 
						||
        while (numClients--) {
 | 
						||
            RecordDeleteClientFromRCAP(pRCAP, numClients);
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    /* remove context from AllContexts list */
 | 
						||
 | 
						||
    if (-1 != (i = RecordFindContextOnAllContexts(pContext))) {
 | 
						||
        ppAllContexts[i] = ppAllContexts[numContexts - 1];
 | 
						||
        if (--numContexts == 0) {
 | 
						||
            free(ppAllContexts);
 | 
						||
            ppAllContexts = NULL;
 | 
						||
        }
 | 
						||
    }
 | 
						||
    free(pContext);
 | 
						||
 | 
						||
    return Success;
 | 
						||
}                               /* RecordDeleteContext */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordFreeContext(ClientPtr client)
 | 
						||
{
 | 
						||
    RecordContextPtr pContext;
 | 
						||
 | 
						||
    REQUEST(xRecordFreeContextReq);
 | 
						||
 | 
						||
    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
 | 
						||
    VERIFY_CONTEXT(pContext, stuff->context, client);
 | 
						||
    FreeResource(stuff->context, X11_RESTYPE_NONE);
 | 
						||
    return Success;
 | 
						||
}                               /* ProcRecordFreeContext */
 | 
						||
 | 
						||
static int
 | 
						||
ProcRecordDispatch(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xReq);
 | 
						||
 | 
						||
    switch (stuff->data) {
 | 
						||
    case X_RecordQueryVersion:
 | 
						||
        return ProcRecordQueryVersion(client);
 | 
						||
    case X_RecordCreateContext:
 | 
						||
        return ProcRecordCreateContext(client);
 | 
						||
    case X_RecordRegisterClients:
 | 
						||
        return ProcRecordRegisterClients(client);
 | 
						||
    case X_RecordUnregisterClients:
 | 
						||
        return ProcRecordUnregisterClients(client);
 | 
						||
    case X_RecordGetContext:
 | 
						||
        return ProcRecordGetContext(client);
 | 
						||
    case X_RecordEnableContext:
 | 
						||
        return ProcRecordEnableContext(client);
 | 
						||
    case X_RecordDisableContext:
 | 
						||
        return ProcRecordDisableContext(client);
 | 
						||
    case X_RecordFreeContext:
 | 
						||
        return ProcRecordFreeContext(client);
 | 
						||
    default:
 | 
						||
        return BadRequest;
 | 
						||
    }
 | 
						||
}                               /* ProcRecordDispatch */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordQueryVersion(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordQueryVersionReq);
 | 
						||
    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
 | 
						||
    swaps(&stuff->majorVersion);
 | 
						||
    swaps(&stuff->minorVersion);
 | 
						||
    return ProcRecordQueryVersion(client);
 | 
						||
}                               /* SProcRecordQueryVersion */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SwapCreateRegister(ClientPtr client, xRecordRegisterClientsReq * stuff)
 | 
						||
{
 | 
						||
    int i;
 | 
						||
    XID *pClientID;
 | 
						||
 | 
						||
    swapl(&stuff->context);
 | 
						||
    swapl(&stuff->nClients);
 | 
						||
    swapl(&stuff->nRanges);
 | 
						||
    pClientID = (XID *) &stuff[1];
 | 
						||
    if (stuff->nClients >
 | 
						||
        client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq))
 | 
						||
        return BadLength;
 | 
						||
    for (i = 0; i < stuff->nClients; i++, pClientID++) {
 | 
						||
        swapl(pClientID);
 | 
						||
    }
 | 
						||
    if (stuff->nRanges >
 | 
						||
        (client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq)
 | 
						||
        - stuff->nClients) / bytes_to_int32(sz_xRecordRange))
 | 
						||
        return BadLength;
 | 
						||
    RecordSwapRanges((xRecordRange *) pClientID, stuff->nRanges);
 | 
						||
    return Success;
 | 
						||
}                               /* SwapCreateRegister */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordCreateContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordCreateContextReq);
 | 
						||
    int status;
 | 
						||
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
 | 
						||
    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
 | 
						||
        return status;
 | 
						||
    return ProcRecordCreateContext(client);
 | 
						||
}                               /* SProcRecordCreateContext */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordRegisterClients(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordRegisterClientsReq);
 | 
						||
    int status;
 | 
						||
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
 | 
						||
    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
 | 
						||
        return status;
 | 
						||
    return ProcRecordRegisterClients(client);
 | 
						||
}                               /* SProcRecordRegisterClients */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordUnregisterClients(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordUnregisterClientsReq);
 | 
						||
    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
 | 
						||
    swapl(&stuff->context);
 | 
						||
    swapl(&stuff->nClients);
 | 
						||
    SwapRestL(stuff);
 | 
						||
    return ProcRecordUnregisterClients(client);
 | 
						||
}                               /* SProcRecordUnregisterClients */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordGetContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordGetContextReq);
 | 
						||
    REQUEST_SIZE_MATCH(xRecordGetContextReq);
 | 
						||
    swapl(&stuff->context);
 | 
						||
    return ProcRecordGetContext(client);
 | 
						||
}                               /* SProcRecordGetContext */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordEnableContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordEnableContextReq);
 | 
						||
    REQUEST_SIZE_MATCH(xRecordEnableContextReq);
 | 
						||
    swapl(&stuff->context);
 | 
						||
    return ProcRecordEnableContext(client);
 | 
						||
}                               /* SProcRecordEnableContext */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordDisableContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordDisableContextReq);
 | 
						||
    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
 | 
						||
    swapl(&stuff->context);
 | 
						||
    return ProcRecordDisableContext(client);
 | 
						||
}                               /* SProcRecordDisableContext */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordFreeContext(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xRecordFreeContextReq);
 | 
						||
    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
 | 
						||
    swapl(&stuff->context);
 | 
						||
    return ProcRecordFreeContext(client);
 | 
						||
}                               /* SProcRecordFreeContext */
 | 
						||
 | 
						||
static int _X_COLD
 | 
						||
SProcRecordDispatch(ClientPtr client)
 | 
						||
{
 | 
						||
    REQUEST(xReq);
 | 
						||
 | 
						||
    switch (stuff->data) {
 | 
						||
    case X_RecordQueryVersion:
 | 
						||
        return SProcRecordQueryVersion(client);
 | 
						||
    case X_RecordCreateContext:
 | 
						||
        return SProcRecordCreateContext(client);
 | 
						||
    case X_RecordRegisterClients:
 | 
						||
        return SProcRecordRegisterClients(client);
 | 
						||
    case X_RecordUnregisterClients:
 | 
						||
        return SProcRecordUnregisterClients(client);
 | 
						||
    case X_RecordGetContext:
 | 
						||
        return SProcRecordGetContext(client);
 | 
						||
    case X_RecordEnableContext:
 | 
						||
        return SProcRecordEnableContext(client);
 | 
						||
    case X_RecordDisableContext:
 | 
						||
        return SProcRecordDisableContext(client);
 | 
						||
    case X_RecordFreeContext:
 | 
						||
        return SProcRecordFreeContext(client);
 | 
						||
    default:
 | 
						||
        return BadRequest;
 | 
						||
    }
 | 
						||
}                               /* SProcRecordDispatch */
 | 
						||
 | 
						||
/* RecordConnectionSetupInfo
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pContext is an enabled context that specifies recording of
 | 
						||
 *	  connection setup info.
 | 
						||
 *	pci holds the connection setup info.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	The connection setup info is sent to the recording client.
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec * pci)
 | 
						||
{
 | 
						||
    int prefixsize = SIZEOF(xConnSetupPrefix);
 | 
						||
    int restsize = pci->prefix->length * 4;
 | 
						||
 | 
						||
    if (pci->client->swapped) {
 | 
						||
        char *pConnSetup = (char *) malloc(prefixsize + restsize);
 | 
						||
 | 
						||
        if (!pConnSetup)
 | 
						||
            return;
 | 
						||
        SwapConnSetupPrefix(pci->prefix, (xConnSetupPrefix *) pConnSetup);
 | 
						||
        SwapConnSetupInfo((char *) pci->setup,
 | 
						||
                          (char *) (pConnSetup + prefixsize));
 | 
						||
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
 | 
						||
                               (void *) pConnSetup, prefixsize + restsize, 0,
 | 
						||
                               0);
 | 
						||
        free(pConnSetup);
 | 
						||
    }
 | 
						||
    else {
 | 
						||
        /* don't alloc and copy as in the swapped case; just send the
 | 
						||
         * data in two pieces
 | 
						||
         */
 | 
						||
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
 | 
						||
                               (void *) pci->prefix, prefixsize, 0, restsize);
 | 
						||
        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
 | 
						||
                               (void *) pci->setup, restsize, 0,
 | 
						||
                               /* continuation */ -1);
 | 
						||
    }
 | 
						||
}                               /* RecordConnectionSetupInfo */
 | 
						||
 | 
						||
/* RecordDeleteContext
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	pcbl is &ClientStateCallback.
 | 
						||
 *	nullata is NULL.
 | 
						||
 *	calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
 | 
						||
 *	which contains information about client state changes.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	If a new client has connected and any contexts have specified
 | 
						||
 *	XRecordFutureClients, the new client is registered on those contexts.
 | 
						||
 *	If any of those contexts specify recording of the connection setup
 | 
						||
 *	info, it is recorded.
 | 
						||
 *
 | 
						||
 *	If an existing client has disconnected, it is deleted from any
 | 
						||
 *	contexts that it was registered on.  If any of those contexts
 | 
						||
 *	specified XRecordClientDied, they record a ClientDied protocol element.
 | 
						||
 *	If the disconnectiong client happened to be the data connection of an
 | 
						||
 *	enabled context, the context is disabled.
 | 
						||
 */
 | 
						||
 | 
						||
static void
 | 
						||
RecordAClientStateChange(CallbackListPtr *pcbl, void *nulldata,
 | 
						||
                         void *calldata)
 | 
						||
{
 | 
						||
    NewClientInfoRec *pci = (NewClientInfoRec *) calldata;
 | 
						||
    int i;
 | 
						||
    ClientPtr pClient = pci->client;
 | 
						||
    RecordContextPtr *ppAllContextsCopy = NULL;
 | 
						||
    int numContextsCopy = 0;
 | 
						||
 | 
						||
    switch (pClient->clientState) {
 | 
						||
    case ClientStateRunning:   /* new client */
 | 
						||
        for (i = 0; i < numContexts; i++) {
 | 
						||
            RecordClientsAndProtocolPtr pRCAP;
 | 
						||
            RecordContextPtr pContext = ppAllContexts[i];
 | 
						||
 | 
						||
            if ((pRCAP = RecordFindClientOnContext(pContext,
 | 
						||
                                                   XRecordFutureClients, NULL)))
 | 
						||
            {
 | 
						||
                RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
 | 
						||
                if (pContext->pRecordingClient && pRCAP->clientStarted)
 | 
						||
                    RecordConnectionSetupInfo(pContext, pci);
 | 
						||
            }
 | 
						||
        }
 | 
						||
        break;
 | 
						||
 | 
						||
    case ClientStateGone:
 | 
						||
    case ClientStateRetained:  /* client disconnected */
 | 
						||
 | 
						||
        /* RecordDisableContext modifies contents of ppAllContexts. */
 | 
						||
        if (!(numContextsCopy = numContexts))
 | 
						||
            break;
 | 
						||
        ppAllContextsCopy = xallocarray(numContextsCopy,
 | 
						||
                                        sizeof(RecordContextPtr));
 | 
						||
        assert(ppAllContextsCopy);
 | 
						||
        memcpy(ppAllContextsCopy, ppAllContexts,
 | 
						||
               numContextsCopy * sizeof(RecordContextPtr));
 | 
						||
 | 
						||
        for (i = 0; i < numContextsCopy; i++) {
 | 
						||
            RecordClientsAndProtocolPtr pRCAP;
 | 
						||
            RecordContextPtr pContext = ppAllContextsCopy[i];
 | 
						||
            int pos;
 | 
						||
 | 
						||
            if (pContext->pRecordingClient == pClient)
 | 
						||
                RecordDisableContext(pContext);
 | 
						||
            if ((pRCAP = RecordFindClientOnContext(pContext,
 | 
						||
                                                   pClient->clientAsMask,
 | 
						||
                                                   &pos))) {
 | 
						||
                if (pContext->pRecordingClient && pRCAP->clientDied)
 | 
						||
                    RecordAProtocolElement(pContext, pClient,
 | 
						||
                                           XRecordClientDied, NULL, 0, 0, 0);
 | 
						||
                RecordDeleteClientFromRCAP(pRCAP, pos);
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        free(ppAllContextsCopy);
 | 
						||
        break;
 | 
						||
 | 
						||
    default:
 | 
						||
        break;
 | 
						||
    }                           /* end switch on client state */
 | 
						||
}                               /* RecordAClientStateChange */
 | 
						||
 | 
						||
/* RecordCloseDown
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	extEntry is the extension information for RECORD.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Performs any cleanup needed by RECORD at server shutdown time.
 | 
						||
 *	
 | 
						||
 */
 | 
						||
static void
 | 
						||
RecordCloseDown(ExtensionEntry * extEntry)
 | 
						||
{
 | 
						||
    DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
 | 
						||
}                               /* RecordCloseDown */
 | 
						||
 | 
						||
/* RecordExtensionInit
 | 
						||
 *
 | 
						||
 * Arguments: none.
 | 
						||
 *
 | 
						||
 * Returns: nothing.
 | 
						||
 *
 | 
						||
 * Side Effects:
 | 
						||
 *	Enables the RECORD extension if possible.
 | 
						||
 */
 | 
						||
void
 | 
						||
RecordExtensionInit(void)
 | 
						||
{
 | 
						||
    ExtensionEntry *extentry;
 | 
						||
 | 
						||
    RTContext = CreateNewResourceType(RecordDeleteContext, "RecordContext");
 | 
						||
    if (!RTContext)
 | 
						||
        return;
 | 
						||
 | 
						||
    if (!dixRegisterPrivateKey(RecordClientPrivateKey, PRIVATE_CLIENT, 0))
 | 
						||
        return;
 | 
						||
 | 
						||
    ppAllContexts = NULL;
 | 
						||
    numContexts = numEnabledContexts = numEnabledRCAPs = 0;
 | 
						||
 | 
						||
    if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
 | 
						||
        return;
 | 
						||
 | 
						||
    extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
 | 
						||
                            ProcRecordDispatch, SProcRecordDispatch,
 | 
						||
                            RecordCloseDown, StandardMinorOpcode);
 | 
						||
    if (!extentry) {
 | 
						||
        DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    SetResourceTypeErrorValue(RTContext,
 | 
						||
                              extentry->errorBase + XRecordBadContext);
 | 
						||
 | 
						||
}                               /* RecordExtensionInit */
 |