2803 lines
		
	
	
		
			98 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2803 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.
 | ||
| 
 | ||
| */
 | ||
| 
 | ||
| #ifdef HAVE_DIX_CONFIG_H
 | ||
| #include <dix-config.h>
 | ||
| #endif
 | ||
| 
 | ||
| #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 PANORAMIX
 | ||
| #include "globals.h"
 | ||
| #include "panoramiX.h"
 | ||
| #include "panoramiXsrv.h"
 | ||
| #include "cursor.h"
 | ||
| #endif
 | ||
| 
 | ||
| #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 PANORAMIX
 | ||
|             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 = XineramaGetCursorScreen(inputInfo.pointer);
 | ||
| 
 | ||
|                 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                          /* PANORAMIX */
 | ||
| 
 | ||
|             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 */
 |