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