2935 lines
85 KiB
C
2935 lines
85 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 "dixstruct.h"
|
||
#include "extnsionst.h"
|
||
#include <X11/extensions/recordproto.h>
|
||
#include "set.h"
|
||
#include "swaprep.h"
|
||
#include "inputstr.h"
|
||
#include "eventconvert.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((pointer *)&(_pContext), _contextid, \
|
||
RTContext, _client, DixUseAccess); \
|
||
if (rc != Success) \
|
||
return rc; \
|
||
}
|
||
|
||
static int RecordDeleteContext(
|
||
pointer /*value*/,
|
||
XID /*id*/
|
||
);
|
||
|
||
void RecordExtensionInit(void);
|
||
|
||
/***************************************************************************/
|
||
|
||
/* 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,
|
||
pointer data1,
|
||
int len1,
|
||
pointer data2,
|
||
int len2
|
||
)
|
||
{
|
||
if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone || pContext->inFlush)
|
||
return;
|
||
++pContext->inFlush;
|
||
if (pContext->numBufBytes)
|
||
WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
|
||
(char *)pContext->replyBuffer);
|
||
pContext->numBufBytes = 0;
|
||
if (len1)
|
||
WriteToClient(pContext->pRecordingClient, len1, (char *)data1);
|
||
if (len2)
|
||
WriteToClient(pContext->pRecordingClient, len2, (char *)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 is its length
|
||
* in bytes.
|
||
* 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, pointer data, int datalen, int futurelen)
|
||
{
|
||
CARD32 elemHeaderData[2];
|
||
int numElemHeaders = 0;
|
||
Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
|
||
int n;
|
||
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, n);
|
||
swapl(&pRep->length, n);
|
||
swapl(&pRep->idBase, n);
|
||
swapl(&pRep->serverTime, n);
|
||
swapl(&pRep->recordedSequenceNumber, n);
|
||
}
|
||
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], n);
|
||
numElemHeaders++;
|
||
}
|
||
|
||
if ( (pContext->elemHeaders & XRecordFromClientSequence)
|
||
&&
|
||
(category == XRecordFromClient || category == XRecordClientDied))
|
||
{
|
||
elemHeaderData[numElemHeaders] = pClient->sequence;
|
||
if (recordingClientSwapped)
|
||
swapl(&elemHeaderData[numElemHeaders], n);
|
||
numElemHeaders++;
|
||
}
|
||
|
||
/* adjust reply length */
|
||
|
||
replylen = pRep->length;
|
||
if (recordingClientSwapped) swapl(&replylen, n);
|
||
replylen += numElemHeaders + bytes_to_int32(datalen) +
|
||
bytes_to_int32(futurelen);
|
||
if (recordingClientSwapped) swapl(&replylen, n);
|
||
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)
|
||
{
|
||
memcpy(pContext->replyBuffer + pContext->numBufBytes,
|
||
data, datalen);
|
||
pContext->numBufBytes += datalen;
|
||
}
|
||
}
|
||
else
|
||
RecordFlushReplyBuffer(pContext, (pointer)elemHeaderData,
|
||
numElemHeaders, (pointer)data, datalen);
|
||
|
||
} /* 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;
|
||
char n;
|
||
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,
|
||
(pointer)stuff, SIZEOF(xReq), bytesLeft);
|
||
|
||
/* reinsert the extended length field that was squished out */
|
||
bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
|
||
if (client->swapped)
|
||
swapl(&bigLength, n);
|
||
RecordAProtocolElement(pContext, client, XRecordFromClient,
|
||
(pointer)&bigLength, sizeof(bigLength), /* continuation */ -1);
|
||
bytesLeft -= sizeof(bigLength);
|
||
|
||
/* record the rest of the request after the length */
|
||
RecordAProtocolElement(pContext, client, XRecordFromClient,
|
||
(pointer)(stuff + 1), bytesLeft, /* 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,
|
||
(pointer)stuff, client->req_len << 2, 0);
|
||
}
|
||
else /* extension, check minor opcode */
|
||
{
|
||
int minorop = MinorOpcodeOfRequest(client);
|
||
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, (pointer)stuff,
|
||
client->req_len << 2, 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, pointer nulldata, pointer calldata)
|
||
{
|
||
RecordContextPtr pContext;
|
||
RecordClientsAndProtocolPtr pRCAP;
|
||
int eci;
|
||
int majorop;
|
||
ReplyInfoRec *pri = (ReplyInfoRec *)calldata;
|
||
ClientPtr client = pri->client;
|
||
REQUEST(xReq);
|
||
|
||
majorop = stuff->reqType;
|
||
for (eci = 0; eci < numEnabledContexts; eci++)
|
||
{
|
||
pContext = ppAllContexts[eci];
|
||
pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
|
||
NULL);
|
||
if (pRCAP)
|
||
{
|
||
if (pContext->continuedReply)
|
||
{
|
||
RecordAProtocolElement(pContext, client, XRecordFromServer,
|
||
(pointer)pri->replyData, pri->dataLenBytes, /* 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,
|
||
(pointer)pri->replyData, pri->dataLenBytes, pri->bytesRemaining);
|
||
if (pri->bytesRemaining)
|
||
pContext->continuedReply = 1;
|
||
}
|
||
else /* extension, check minor opcode */
|
||
{
|
||
int minorop = MinorOpcodeOfRequest(client);
|
||
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, (pointer)pri->replyData,
|
||
pri->dataLenBytes, 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, pointer nulldata, pointer 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);
|
||
}
|
||
} /* 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);
|
||
/* 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, pointer nulldata, pointer 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 xE;
|
||
EventToCore(pei->event, &xE);
|
||
RecordSendProtocolEvents(pRCAP, pContext, &xE, 1);
|
||
}
|
||
|
||
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,
|
||
pointer nulldata,
|
||
pointer 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 = (XID *)realloc(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 = (XID *)malloc((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;
|
||
pointer 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 = (XID *)malloc(sizeof(XID) * (currentMaxClients + 1));
|
||
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 intevals 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 */
|
||
REP, /* set info for replies */
|
||
ERR, /* set info for errors */
|
||
DEV, /* set info for device events */
|
||
DLEV, /* set info for delivered events */
|
||
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 = (RecordSetInterval *)
|
||
malloc(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 reuests, plus one per
|
||
* range for extension replies.
|
||
*/
|
||
maxSets = PREDEFSETS + 2 * stuff->nRanges;
|
||
si = (SetInfoPtr)malloc(sizeof(SetInfoRec) * maxSets);
|
||
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 + 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[REP], pRanges, stuff->nRanges,
|
||
offset_of(rr, coreRepliesFirst), NULL, NULL);
|
||
if (err != Success) goto bailout;
|
||
|
||
err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
|
||
offset_of(rr, extRepliesMajorFirst), pExtRepSets, &nExtRepSets);
|
||
if (err != Success) goto bailout;
|
||
|
||
err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
|
||
offset_of(rr, errorsFirst), NULL, NULL);
|
||
if (err != Success) goto bailout;
|
||
|
||
err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
|
||
offset_of(rr, deliveredEventsFirst), NULL, NULL);
|
||
if (err != Success) goto bailout;
|
||
|
||
err = RecordConvertRangesToIntervals(&si[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[REP].intervals)
|
||
{
|
||
pRCAP->pReplyMajorOpSet =
|
||
RecordCreateSet(si[REP].intervals, si[REP].nintervals,
|
||
(RecordSetPtr)((char *)pRCAP + si[REP].offset), si[REP].size);
|
||
}
|
||
else pRCAP->pReplyMajorOpSet = NULL;
|
||
|
||
if (si[ERR].intervals)
|
||
{
|
||
pRCAP->pErrorSet =
|
||
RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
|
||
(RecordSetPtr)((char *)pRCAP + si[ERR].offset), si[ERR].size);
|
||
}
|
||
else pRCAP->pErrorSet = NULL;
|
||
|
||
if (si[DEV].intervals)
|
||
{
|
||
pRCAP->pDeviceEventSet =
|
||
RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
|
||
(RecordSetPtr)((char *)pRCAP + si[DEV].offset), si[DEV].size);
|
||
}
|
||
else pRCAP->pDeviceEventSet = NULL;
|
||
|
||
if (si[DLEV].intervals)
|
||
{
|
||
pRCAP->pDeliveredEventSet =
|
||
RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
|
||
(RecordSetPtr)((char *)pRCAP + si[DLEV].offset), si[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;
|
||
int n;
|
||
|
||
REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
|
||
rep.type = X_Reply;
|
||
rep.sequenceNumber = client->sequence;
|
||
rep.length = 0;
|
||
rep.majorVersion = SERVER_RECORD_MAJOR_VERSION;
|
||
rep.minorVersion = SERVER_RECORD_MINOR_VERSION;
|
||
if(client->swapped)
|
||
{
|
||
swaps(&rep.sequenceNumber, n);
|
||
swaps(&rep.majorVersion, n);
|
||
swaps(&rep.minorVersion, n);
|
||
}
|
||
(void)WriteToClient(client, sizeof(xRecordQueryVersionReply),
|
||
(char *)&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 = (RecordContextPtr *)
|
||
realloc(ppAllContexts, sizeof(RecordContextPtr) * (numContexts + 1));
|
||
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
|
||
{
|
||
RecordDeleteContext((pointer)pContext, pContext->id);
|
||
err = 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 ((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 = (xRecordRange *)realloc(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;
|
||
register char n;
|
||
for (i = 0; i < nRanges; i++, pRanges++)
|
||
{
|
||
swaps(&pRanges->extRequestsMinorFirst, n);
|
||
swaps(&pRanges->extRequestsMinorLast, n);
|
||
swaps(&pRanges->extRepliesMinorFirst, n);
|
||
swaps(&pRanges->extRepliesMinorLast, n);
|
||
}
|
||
} /* RecordSwapRanges */
|
||
|
||
|
||
static int
|
||
ProcRecordGetContext(ClientPtr client)
|
||
{
|
||
RecordContextPtr pContext;
|
||
REQUEST(xRecordGetContextReq);
|
||
xRecordGetContextReply rep;
|
||
int n;
|
||
RecordClientsAndProtocolPtr pRCAP;
|
||
int nRCAPs = 0;
|
||
GetContextRangeInfoPtr pRangeInfo;
|
||
GetContextRangeInfoPtr pri;
|
||
int i;
|
||
int err;
|
||
|
||
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 = (GetContextRangeInfoPtr)malloc(
|
||
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 */
|
||
|
||
rep.nClients = 0;
|
||
rep.length = 0;
|
||
for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
|
||
pRCAP;
|
||
pRCAP = pRCAP->pNextRCAP, pri++)
|
||
{
|
||
rep.nClients += pRCAP->numClients;
|
||
rep.length += pRCAP->numClients *
|
||
( bytes_to_int32(sizeof(xRecordClientInfo)) +
|
||
pri->nRanges * bytes_to_int32(sizeof(xRecordRange)));
|
||
}
|
||
|
||
/* write the reply header */
|
||
|
||
rep.type = X_Reply;
|
||
rep.sequenceNumber = client->sequence;
|
||
rep.enabled = pContext->pRecordingClient != NULL;
|
||
rep.elementHeader = pContext->elemHeaders;
|
||
if(client->swapped)
|
||
{
|
||
swaps(&rep.sequenceNumber, n);
|
||
swapl(&rep.length, n);
|
||
swapl(&rep.nClients, n);
|
||
}
|
||
(void)WriteToClient(client, sizeof(xRecordGetContextReply),
|
||
(char *)&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, n);
|
||
RecordSwapRanges(pri->pRanges, pri->nRanges);
|
||
}
|
||
for (i = 0; i < pRCAP->numClients; i++)
|
||
{
|
||
rci.clientResource = pRCAP->pClientIDs[i];
|
||
if (client->swapped) swapl(&rci.clientResource, n);
|
||
WriteToClient(client, sizeof(xRecordClientInfo), (char *)&rci);
|
||
WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
|
||
(char *)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);
|
||
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);
|
||
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) && (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(pointer 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, RT_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
|
||
SProcRecordQueryVersion(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordQueryVersionReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
|
||
swaps(&stuff->majorVersion, n);
|
||
swaps(&stuff->minorVersion,n);
|
||
return ProcRecordQueryVersion(client);
|
||
} /* SProcRecordQueryVersion */
|
||
|
||
|
||
static int
|
||
SwapCreateRegister(xRecordRegisterClientsReq *stuff)
|
||
{
|
||
register char n;
|
||
int i;
|
||
XID *pClientID;
|
||
|
||
swapl(&stuff->context, n);
|
||
swapl(&stuff->nClients, n);
|
||
swapl(&stuff->nRanges, n);
|
||
pClientID = (XID *)&stuff[1];
|
||
if (stuff->nClients > stuff->length - bytes_to_int32(sz_xRecordRegisterClientsReq))
|
||
return BadLength;
|
||
for (i = 0; i < stuff->nClients; i++, pClientID++)
|
||
{
|
||
swapl(pClientID, n);
|
||
}
|
||
if (stuff->nRanges > stuff->length - bytes_to_int32(sz_xRecordRegisterClientsReq)
|
||
- stuff->nClients)
|
||
return BadLength;
|
||
RecordSwapRanges((xRecordRange *)pClientID, stuff->nRanges);
|
||
return Success;
|
||
} /* SwapCreateRegister */
|
||
|
||
|
||
static int
|
||
SProcRecordCreateContext(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordCreateContextReq);
|
||
int status;
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
|
||
if ((status = SwapCreateRegister((pointer)stuff)) != Success)
|
||
return status;
|
||
return ProcRecordCreateContext(client);
|
||
} /* SProcRecordCreateContext */
|
||
|
||
|
||
static int
|
||
SProcRecordRegisterClients(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordRegisterClientsReq);
|
||
int status;
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
|
||
if ((status = SwapCreateRegister((pointer)stuff)) != Success)
|
||
return status;
|
||
return ProcRecordRegisterClients(client);
|
||
} /* SProcRecordRegisterClients */
|
||
|
||
|
||
static int
|
||
SProcRecordUnregisterClients(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordUnregisterClientsReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
|
||
swapl(&stuff->context, n);
|
||
swapl(&stuff->nClients, n);
|
||
SwapRestL(stuff);
|
||
return ProcRecordUnregisterClients(client);
|
||
} /* SProcRecordUnregisterClients */
|
||
|
||
|
||
static int
|
||
SProcRecordGetContext(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordGetContextReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_SIZE_MATCH(xRecordGetContextReq);
|
||
swapl(&stuff->context, n);
|
||
return ProcRecordGetContext(client);
|
||
} /* SProcRecordGetContext */
|
||
|
||
static int
|
||
SProcRecordEnableContext(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordEnableContextReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_SIZE_MATCH(xRecordEnableContextReq);
|
||
swapl(&stuff->context, n);
|
||
return ProcRecordEnableContext(client);
|
||
} /* SProcRecordEnableContext */
|
||
|
||
|
||
static int
|
||
SProcRecordDisableContext(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordDisableContextReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_SIZE_MATCH(xRecordDisableContextReq);
|
||
swapl(&stuff->context, n);
|
||
return ProcRecordDisableContext(client);
|
||
} /* SProcRecordDisableContext */
|
||
|
||
|
||
static int
|
||
SProcRecordFreeContext(ClientPtr client)
|
||
{
|
||
REQUEST(xRecordFreeContextReq);
|
||
register char n;
|
||
|
||
swaps(&stuff->length, n);
|
||
REQUEST_SIZE_MATCH(xRecordFreeContextReq);
|
||
swapl(&stuff->context, n);
|
||
return ProcRecordFreeContext(client);
|
||
} /* SProcRecordFreeContext */
|
||
|
||
|
||
static int
|
||
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,
|
||
(pointer)pConnSetup, prefixsize + restsize, 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,
|
||
(pointer)pci->prefix, prefixsize, restsize);
|
||
RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
|
||
(pointer)pci->setup, restsize, /* 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, pointer nulldata, pointer 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. */
|
||
numContextsCopy = numContexts;
|
||
ppAllContextsCopy = malloc(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);
|
||
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 */
|
||
|