1
2/*
3
4Copyright 1995, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall
24not be used in advertising or otherwise to promote the sale, use or
25other dealings in this Software without prior written authorization
26from The Open Group.
27
28Author: David P. Wiggins, The Open Group
29
30This work benefited from earlier work done by Martha Zimet of NCD
31and Jim Haggerty of Metheus.
32
33*/
34
35#ifdef HAVE_DIX_CONFIG_H
36#include <dix-config.h>
37#endif
38
39#include "dixstruct.h"
40#include "extnsionst.h"
41#include "extinit.h"
42#include <X11/extensions/recordproto.h>
43#include "set.h"
44#include "swaprep.h"
45#include "inputstr.h"
46#include "eventconvert.h"
47#include "scrnintstr.h"
48#include "opaque.h"
49
50#include <stdio.h>
51#include <assert.h>
52
53#ifdef PANORAMIX
54#include "globals.h"
55#include "panoramiX.h"
56#include "panoramiXsrv.h"
57#include "cursor.h"
58#endif
59
60#include "protocol-versions.h"
61
62static RESTYPE RTContext;       /* internal resource type for Record contexts */
63
64/* How many bytes of protocol data to buffer in a context. Don't set to less
65 * than 32.
66 */
67#define REPLY_BUF_SIZE 1024
68
69/* Record Context structure */
70
71typedef struct {
72    XID id;                     /* resource id of context */
73    ClientPtr pRecordingClient; /* client that has context enabled */
74    struct _RecordClientsAndProtocolRec *pListOfRCAP;   /* all registered info */
75    ClientPtr pBufClient;       /* client whose protocol is in replyBuffer */
76    unsigned int continuedReply:1;      /* recording a reply that is split up? */
77    char elemHeaders;           /* element header flags (time/seq no.) */
78    char bufCategory;           /* category of protocol in replyBuffer */
79    int numBufBytes;            /* number of bytes in replyBuffer */
80    char replyBuffer[REPLY_BUF_SIZE];   /* buffered recorded protocol */
81    int inFlush;                /*  are we inside RecordFlushReplyBuffer */
82} RecordContextRec, *RecordContextPtr;
83
84/*  RecordMinorOpRec - to hold minor opcode selections for extension requests
85 *  and replies
86 */
87
88typedef union {
89    int count;                  /* first element of array: how many "major" structs to follow */
90    struct {                    /* rest of array elements are this */
91        short first;            /* first major opcode */
92        short last;             /* last major opcode */
93        RecordSetPtr pMinOpSet; /*  minor opcode set for above major range */
94    } major;
95} RecordMinorOpRec, *RecordMinorOpPtr;
96
97/*  RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
98 *  protocol selections passed in a single CreateContext or RegisterClients.
99 *  Generally, a context will have one of these from the create and an
100 *  additional one for each RegisterClients.  RCAPs are freed when all their
101 *  clients are unregistered.
102 */
103
104typedef struct _RecordClientsAndProtocolRec {
105    RecordContextPtr pContext;  /* context that owns this RCAP */
106    struct _RecordClientsAndProtocolRec *pNextRCAP;     /* next RCAP on context */
107    RecordSetPtr pRequestMajorOpSet;    /* requests to record */
108    RecordMinorOpPtr pRequestMinOpInfo; /* extension requests to record */
109    RecordSetPtr pReplyMajorOpSet;      /* replies to record */
110    RecordMinorOpPtr pReplyMinOpInfo;   /* extension replies to record */
111    RecordSetPtr pDeviceEventSet;       /* device events to record */
112    RecordSetPtr pDeliveredEventSet;    /* delivered events to record */
113    RecordSetPtr pErrorSet;     /* errors to record */
114    XID *pClientIDs;            /* array of clients to record */
115    short numClients;           /* number of clients in pClientIDs */
116    short sizeClients;          /* size of pClientIDs array */
117    unsigned int clientStarted:1;       /* record new client connections? */
118    unsigned int clientDied:1;  /* record client disconnections? */
119    unsigned int clientIDsSeparatelyAllocated:1;        /* pClientIDs malloced? */
120} RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;
121
122/* how much bigger to make pRCAP->pClientIDs when reallocing */
123#define CLIENT_ARRAY_GROWTH_INCREMENT 4
124
125/* counts the total number of RCAPs belonging to enabled contexts. */
126static int numEnabledRCAPs;
127
128/*  void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
129 *  In the spirit of the VERIFY_* macros in dix.h, this macro fills in
130 *  the context pointer if the given ID is a valid Record Context, else it
131 *  returns an error.
132 */
133#define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
134    int rc = dixLookupResourceByType((void **)&(_pContext), _contextid, \
135                                     RTContext, _client, DixUseAccess); \
136    if (rc != Success) \
137	return rc; \
138}
139
140static int RecordDeleteContext(void     *value,
141                               XID      id);
142
143/***************************************************************************/
144
145/* client private stuff */
146
147/*  To make declarations less obfuscated, have a typedef for a pointer to a
148 *  Proc function.
149 */
150typedef int (*ProcFunctionPtr) (ClientPtr       /*pClient */
151    );
152
153/* Record client private.  Generally a client only has one of these if
154 * any of its requests are being recorded.
155 */
156typedef struct {
157/* ptr to client's proc vector before Record stuck its nose in */
158    ProcFunctionPtr *originalVector;
159
160/* proc vector with pointers for recorded requests redirected to the
161 * function RecordARequest
162 */
163    ProcFunctionPtr recordVector[256];
164} RecordClientPrivateRec, *RecordClientPrivatePtr;
165
166static DevPrivateKeyRec RecordClientPrivateKeyRec;
167
168#define RecordClientPrivateKey (&RecordClientPrivateKeyRec)
169
170/*  RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
171 *  gets the client private of the given client.  Syntactic sugar.
172 */
173#define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
174    dixLookupPrivate(&(_pClient)->devPrivates, RecordClientPrivateKey)
175
176/***************************************************************************/
177
178/* global list of all contexts */
179
180static RecordContextPtr *ppAllContexts;
181
182static int numContexts;         /* number of contexts in ppAllContexts */
183
184/* number of currently enabled contexts.  All enabled contexts are bunched
185 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
186 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
187 * past disabled contexts.
188 */
189static int numEnabledContexts;
190
191/* RecordFindContextOnAllContexts
192 *
193 * Arguments:
194 *	pContext is the context to search for.
195 *
196 * Returns:
197 *	The index into the array ppAllContexts at which pContext is stored.
198 *	If pContext is not found in ppAllContexts, returns -1.
199 *
200 * Side Effects: none.
201 */
202static int
203RecordFindContextOnAllContexts(RecordContextPtr pContext)
204{
205    int i;
206
207    assert(numContexts >= numEnabledContexts);
208    for (i = 0; i < numContexts; i++) {
209        if (ppAllContexts[i] == pContext)
210            return i;
211    }
212    return -1;
213}                               /* RecordFindContextOnAllContexts */
214
215/***************************************************************************/
216
217/* RecordFlushReplyBuffer
218 *
219 * Arguments:
220 *	pContext is the context to flush.
221 *	data1 is a pointer to additional data, and len1 is its length in bytes.
222 *	data2 is a pointer to additional data, and len2 is its length in bytes.
223 *
224 * Returns: nothing.
225 *
226 * Side Effects:
227 *	If the context is enabled, any buffered (recorded) protocol is written
228 *	to the recording client, and the number of buffered bytes is set to
229 *	zero.  If len1 is not zero, data1/len1 are then written to the
230 *	recording client, and similarly for data2/len2 (written after
231 *	data1/len1).
232 */
233static void
234RecordFlushReplyBuffer(RecordContextPtr pContext,
235                       void *data1, int len1, void *data2, int len2)
236{
237    if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone ||
238        pContext->inFlush)
239        return;
240    ++pContext->inFlush;
241    if (pContext->numBufBytes)
242        WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
243                      pContext->replyBuffer);
244    pContext->numBufBytes = 0;
245    if (len1)
246        WriteToClient(pContext->pRecordingClient, len1, data1);
247    if (len2)
248        WriteToClient(pContext->pRecordingClient, len2, data2);
249    --pContext->inFlush;
250}                               /* RecordFlushReplyBuffer */
251
252/* RecordAProtocolElement
253 *
254 * Arguments:
255 *	pContext is the context that is recording a protocol element.
256 *	pClient is the client whose protocol is being recorded.  For
257 *	  device events and EndOfData, pClient is NULL.
258 *	category is the category of the protocol element, as defined
259 *	  by the RECORD spec.
260 *	data is a pointer to the protocol data, and datalen - padlen
261 *	  is its length in bytes.
262 *	padlen is the number of pad bytes from a zeroed array.
263 *	futurelen is the number of bytes that will be sent in subsequent
264 *	  calls to this function to complete this protocol element.
265 *	  In those subsequent calls, futurelen will be -1 to indicate
266 *	  that the current data is a continuation of the same protocol
267 *	  element.
268 *
269 * Returns: nothing.
270 *
271 * Side Effects:
272 *	The context may be flushed.  The new protocol element will be
273 *	added to the context's protocol buffer with appropriate element
274 *	headers prepended (sequence number and timestamp).  If the data
275 *	is continuation data (futurelen == -1), element headers won't
276 *	be added.  If the protocol element and headers won't fit in
277 *	the context's buffer, it is sent directly to the recording
278 *	client (after any buffered data).
279 */
280static void
281RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
282                       int category, void *data, int datalen, int padlen,
283                       int futurelen)
284{
285    CARD32 elemHeaderData[2];
286    int numElemHeaders = 0;
287    Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
288    CARD32 serverTime = 0;
289    Bool gotServerTime = FALSE;
290    int replylen;
291
292    if (futurelen >= 0) {       /* start of new protocol element */
293        xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
294            pContext->replyBuffer;
295
296        if (pContext->pBufClient != pClient ||
297            pContext->bufCategory != category) {
298            RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
299            pContext->pBufClient = pClient;
300            pContext->bufCategory = category;
301        }
302
303        if (!pContext->numBufBytes) {
304            serverTime = GetTimeInMillis();
305            gotServerTime = TRUE;
306            pRep->type = X_Reply;
307            pRep->category = category;
308            pRep->sequenceNumber = pContext->pRecordingClient->sequence;
309            pRep->length = 0;
310            pRep->elementHeader = pContext->elemHeaders;
311            pRep->serverTime = serverTime;
312            if (pClient) {
313                pRep->clientSwapped =
314                    (pClient->swapped != recordingClientSwapped);
315                pRep->idBase = pClient->clientAsMask;
316                pRep->recordedSequenceNumber = pClient->sequence;
317            }
318            else {              /* it's a device event, StartOfData, or EndOfData */
319
320                pRep->clientSwapped = (category != XRecordFromServer) &&
321                    recordingClientSwapped;
322                pRep->idBase = 0;
323                pRep->recordedSequenceNumber = 0;
324            }
325
326            if (recordingClientSwapped) {
327                swaps(&pRep->sequenceNumber);
328                swapl(&pRep->length);
329                swapl(&pRep->idBase);
330                swapl(&pRep->serverTime);
331                swapl(&pRep->recordedSequenceNumber);
332            }
333            pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
334        }
335
336        /* generate element headers if needed */
337
338        if (((pContext->elemHeaders & XRecordFromClientTime)
339             && category == XRecordFromClient)
340            || ((pContext->elemHeaders & XRecordFromServerTime)
341                && category == XRecordFromServer)) {
342            if (gotServerTime)
343                elemHeaderData[numElemHeaders] = serverTime;
344            else
345                elemHeaderData[numElemHeaders] = GetTimeInMillis();
346            if (recordingClientSwapped)
347                swapl(&elemHeaderData[numElemHeaders]);
348            numElemHeaders++;
349        }
350
351        if ((pContext->elemHeaders & XRecordFromClientSequence)
352            && (category == XRecordFromClient || category == XRecordClientDied)) {
353            elemHeaderData[numElemHeaders] = pClient->sequence;
354            if (recordingClientSwapped)
355                swapl(&elemHeaderData[numElemHeaders]);
356            numElemHeaders++;
357        }
358
359        /* adjust reply length */
360
361        replylen = pRep->length;
362        if (recordingClientSwapped)
363            swapl(&replylen);
364        replylen += numElemHeaders + bytes_to_int32(datalen) +
365            bytes_to_int32(futurelen);
366        if (recordingClientSwapped)
367            swapl(&replylen);
368        pRep->length = replylen;
369    }                           /* end if not continued reply */
370
371    numElemHeaders *= 4;
372
373    /* if space available >= space needed, buffer the data */
374
375    if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders) {
376        if (numElemHeaders) {
377            memcpy(pContext->replyBuffer + pContext->numBufBytes,
378                   elemHeaderData, numElemHeaders);
379            pContext->numBufBytes += numElemHeaders;
380        }
381        if (datalen) {
382            static char padBuffer[3];   /* as in FlushClient */
383
384            memcpy(pContext->replyBuffer + pContext->numBufBytes,
385                   data, datalen - padlen);
386            pContext->numBufBytes += datalen - padlen;
387            memcpy(pContext->replyBuffer + pContext->numBufBytes,
388                   padBuffer, padlen);
389            pContext->numBufBytes += padlen;
390        }
391    }
392    else {
393        RecordFlushReplyBuffer(pContext, (void *) elemHeaderData,
394                               numElemHeaders, (void *) data,
395                               datalen - padlen);
396    }
397}                               /* RecordAProtocolElement */
398
399/* RecordFindClientOnContext
400 *
401 * Arguments:
402 *	pContext is the context to search.
403 *	clientspec is the resource ID mask identifying the client to search
404 *	  for, or XRecordFutureClients.
405 *	pposition is a pointer to an int, or NULL.  See Returns.
406 *
407 * Returns:
408 *	The RCAP on which clientspec was found, or NULL if not found on
409 *	any RCAP on the given context.
410 *	If pposition was not NULL and the returned RCAP is not NULL,
411 *	*pposition will be set to the index into the returned the RCAP's
412 *	pClientIDs array that holds clientspec.
413 *
414 * Side Effects: none.
415 */
416static RecordClientsAndProtocolPtr
417RecordFindClientOnContext(RecordContextPtr pContext,
418                          XID clientspec, int *pposition)
419{
420    RecordClientsAndProtocolPtr pRCAP;
421
422    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
423        int i;
424
425        for (i = 0; i < pRCAP->numClients; i++) {
426            if (pRCAP->pClientIDs[i] == clientspec) {
427                if (pposition)
428                    *pposition = i;
429                return pRCAP;
430            }
431        }
432    }
433    return NULL;
434}                               /* RecordFindClientOnContext */
435
436/* RecordABigRequest
437 *
438 * Arguments:
439 *	pContext is the recording context.
440 *	client is the client being recorded.
441 *	stuff is a pointer to the big request of client (see the Big Requests
442 *	extension for details.)
443 *
444 * Returns: nothing.
445 *
446 * Side Effects:
447 *	The big request is recorded with the correct length field re-inserted.
448 *
449 * Note: this function exists mainly to make RecordARequest smaller.
450 */
451static void
452RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq * stuff)
453{
454    CARD32 bigLength;
455    int bytesLeft;
456
457    /* note: client->req_len has been frobbed by ReadRequestFromClient
458     * (os/io.c) to discount the extra 4 bytes taken by the extended length
459     * field in a big request.  The actual request length to record is
460     * client->req_len + 1 (measured in CARD32s).
461     */
462
463    /* record the request header */
464    bytesLeft = client->req_len << 2;
465    RecordAProtocolElement(pContext, client, XRecordFromClient,
466                           (void *) stuff, SIZEOF(xReq), 0, bytesLeft);
467
468    /* reinsert the extended length field that was squished out */
469    bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
470    if (client->swapped)
471        swapl(&bigLength);
472    RecordAProtocolElement(pContext, client, XRecordFromClient,
473                           (void *) &bigLength, sizeof(bigLength), 0,
474                           /* continuation */ -1);
475    bytesLeft -= sizeof(bigLength);
476
477    /* record the rest of the request after the length */
478    RecordAProtocolElement(pContext, client, XRecordFromClient,
479                           (void *) (stuff + 1), bytesLeft, 0,
480                           /* continuation */ -1);
481}                               /* RecordABigRequest */
482
483/* RecordARequest
484 *
485 * Arguments:
486 *	client is a client that the server has dispatched a request to by
487 *	calling client->requestVector[request opcode] .
488 *	The request is in client->requestBuffer.
489 *
490 * Returns:
491 *	Whatever is returned by the "real" Proc function for this request.
492 *	The "real" Proc function is the function that was in
493 *	client->requestVector[request opcode]  before it was replaced by
494 *	RecordARequest.  (See the function RecordInstallHooks.)
495 *
496 * Side Effects:
497 *	The request is recorded by all contexts that have registered this
498 *	request for this client.  The real Proc function is called.
499 */
500static int
501RecordARequest(ClientPtr client)
502{
503    RecordContextPtr pContext;
504    RecordClientsAndProtocolPtr pRCAP;
505    int i;
506    RecordClientPrivatePtr pClientPriv;
507
508    REQUEST(xReq);
509    int majorop;
510
511    majorop = stuff->reqType;
512    for (i = 0; i < numEnabledContexts; i++) {
513        pContext = ppAllContexts[i];
514        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
515        if (pRCAP && pRCAP->pRequestMajorOpSet &&
516            RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop)) {
517            if (majorop <= 127) {       /* core request */
518
519                if (stuff->length == 0)
520                    RecordABigRequest(pContext, client, stuff);
521                else
522                    RecordAProtocolElement(pContext, client, XRecordFromClient,
523                                           (void *) stuff,
524                                           client->req_len << 2, 0, 0);
525            }
526            else {              /* extension, check minor opcode */
527
528                int minorop = client->minorOp;
529                int numMinOpInfo;
530                RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
531
532                assert(pMinorOpInfo);
533                numMinOpInfo = pMinorOpInfo->count;
534                pMinorOpInfo++;
535                assert(numMinOpInfo);
536                for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
537                    if (majorop >= pMinorOpInfo->major.first &&
538                        majorop <= pMinorOpInfo->major.last &&
539                        RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
540                                            minorop)) {
541                        if (stuff->length == 0)
542                            RecordABigRequest(pContext, client, stuff);
543                        else
544                            RecordAProtocolElement(pContext, client,
545                                                   XRecordFromClient,
546                                                   (void *) stuff,
547                                                   client->req_len << 2, 0, 0);
548                        break;
549                    }
550                }               /* end for each minor op info */
551            }                   /* end extension request */
552        }                       /* end this RCAP wants this major opcode */
553    }                           /* end for each context */
554    pClientPriv = RecordClientPrivate(client);
555    assert(pClientPriv);
556    return (*pClientPriv->originalVector[majorop]) (client);
557}                               /* RecordARequest */
558
559/* RecordAReply
560 *
561 * Arguments:
562 *	pcbl is &ReplyCallback.
563 *	nulldata is NULL.
564 *	calldata is a pointer to a ReplyInfoRec (include/os.h)
565 *	  which provides information about replies that are being sent
566 *	  to clients.
567 *
568 * Returns: nothing.
569 *
570 * Side Effects:
571 *	The reply is recorded by all contexts that have registered this
572 *	reply type for this client.  If more data belonging to the same
573 *	reply is expected, and if the reply is being recorded by any
574 *	context, pContext->continuedReply is set to 1.
575 *	If pContext->continuedReply was already 1 and this is the last
576 *	chunk of data belonging to this reply, it is set to 0.
577 */
578static void
579RecordAReply(CallbackListPtr *pcbl, void *nulldata, void *calldata)
580{
581    RecordContextPtr pContext;
582    RecordClientsAndProtocolPtr pRCAP;
583    int eci;
584    ReplyInfoRec *pri = (ReplyInfoRec *) calldata;
585    ClientPtr client = pri->client;
586
587    for (eci = 0; eci < numEnabledContexts; eci++) {
588        pContext = ppAllContexts[eci];
589        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
590        if (pRCAP) {
591            int majorop = client->majorOp;
592
593            if (pContext->continuedReply) {
594                RecordAProtocolElement(pContext, client, XRecordFromServer,
595                                       (void *) pri->replyData,
596                                       pri->dataLenBytes, pri->padBytes,
597                                       /* continuation */ -1);
598                if (!pri->bytesRemaining)
599                    pContext->continuedReply = 0;
600            }
601            else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
602                     RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop)) {
603                if (majorop <= 127) {   /* core reply */
604                    RecordAProtocolElement(pContext, client, XRecordFromServer,
605                                           (void *) pri->replyData,
606                                           pri->dataLenBytes, 0,
607                                           pri->bytesRemaining);
608                    if (pri->bytesRemaining)
609                        pContext->continuedReply = 1;
610                }
611                else {          /* extension, check minor opcode */
612
613                    int minorop = client->minorOp;
614                    int numMinOpInfo;
615                    RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;
616
617                    assert(pMinorOpInfo);
618                    numMinOpInfo = pMinorOpInfo->count;
619                    pMinorOpInfo++;
620                    assert(numMinOpInfo);
621                    for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
622                        if (majorop >= pMinorOpInfo->major.first &&
623                            majorop <= pMinorOpInfo->major.last &&
624                            RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
625                                                minorop)) {
626                            RecordAProtocolElement(pContext, client,
627                                                   XRecordFromServer,
628                                                   (void *) pri->replyData,
629                                                   pri->dataLenBytes, 0,
630                                                   pri->bytesRemaining);
631                            if (pri->bytesRemaining)
632                                pContext->continuedReply = 1;
633                            break;
634                        }
635                    }           /* end for each minor op info */
636                }               /* end extension reply */
637            }                   /* end continued reply vs. start of reply */
638        }                       /* end client is registered on this context */
639    }                           /* end for each context */
640}                               /* RecordAReply */
641
642/* RecordADeliveredEventOrError
643 *
644 * Arguments:
645 *	pcbl is &EventCallback.
646 *	nulldata is NULL.
647 *	calldata is a pointer to a EventInfoRec (include/dix.h)
648 *	  which provides information about events that are being sent
649 *	  to clients.
650 *
651 * Returns: nothing.
652 *
653 * Side Effects:
654 *	The event or error is recorded by all contexts that have registered
655 *	it for this client.
656 */
657static void
658RecordADeliveredEventOrError(CallbackListPtr *pcbl, void *nulldata,
659                             void *calldata)
660{
661    EventInfoRec *pei = (EventInfoRec *) calldata;
662    RecordContextPtr pContext;
663    RecordClientsAndProtocolPtr pRCAP;
664    int eci;                    /* enabled context index */
665    ClientPtr pClient = pei->client;
666
667    for (eci = 0; eci < numEnabledContexts; eci++) {
668        pContext = ppAllContexts[eci];
669        pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
670                                          NULL);
671        if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet)) {
672            int ev;             /* event index */
673            xEvent *pev = pei->events;
674
675            for (ev = 0; ev < pei->count; ev++, pev++) {
676                int recordit = 0;
677
678                if (pRCAP->pErrorSet) {
679                    recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
680                                                   ((xError *) (pev))->
681                                                   errorCode);
682                }
683                else if (pRCAP->pDeliveredEventSet) {
684                    recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
685                                                   pev->u.u.type & 0177);
686                }
687                if (recordit) {
688                    xEvent swappedEvent;
689                    xEvent *pEvToRecord = pev;
690
691                    if (pClient->swapped) {
692                        (*EventSwapVector[pev->u.u.type & 0177])
693                            (pev, &swappedEvent);
694                        pEvToRecord = &swappedEvent;
695
696                    }
697                    RecordAProtocolElement(pContext, pClient,
698                                           XRecordFromServer, pEvToRecord,
699                                           SIZEOF(xEvent), 0, 0);
700                }
701            }                   /* end for each event */
702        }                       /* end this client is on this context */
703    }                           /* end for each enabled context */
704}                               /* RecordADeliveredEventOrError */
705
706static void
707RecordSendProtocolEvents(RecordClientsAndProtocolPtr pRCAP,
708                         RecordContextPtr pContext, xEvent *pev, int count)
709{
710    int ev;                     /* event index */
711
712    for (ev = 0; ev < count; ev++, pev++) {
713        if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet, pev->u.u.type & 0177)) {
714            xEvent swappedEvent;
715            xEvent *pEvToRecord = pev;
716
717#ifdef PANORAMIX
718            xEvent shiftedEvent;
719
720            if (!noPanoramiXExtension &&
721                (pev->u.u.type == MotionNotify ||
722                 pev->u.u.type == ButtonPress ||
723                 pev->u.u.type == ButtonRelease ||
724                 pev->u.u.type == KeyPress || pev->u.u.type == KeyRelease)) {
725                int scr = XineramaGetCursorScreen(inputInfo.pointer);
726
727                memcpy(&shiftedEvent, pev, sizeof(xEvent));
728                shiftedEvent.u.keyButtonPointer.rootX +=
729                    screenInfo.screens[scr]->x - screenInfo.screens[0]->x;
730                shiftedEvent.u.keyButtonPointer.rootY +=
731                    screenInfo.screens[scr]->y - screenInfo.screens[0]->y;
732                pEvToRecord = &shiftedEvent;
733            }
734#endif                          /* PANORAMIX */
735
736            if (pContext->pRecordingClient->swapped) {
737                (*EventSwapVector[pEvToRecord->u.u.type & 0177])
738                    (pEvToRecord, &swappedEvent);
739                pEvToRecord = &swappedEvent;
740            }
741
742            RecordAProtocolElement(pContext, NULL,
743                                   XRecordFromServer, pEvToRecord,
744                                   SIZEOF(xEvent), 0, 0);
745            /* make sure device events get flushed in the absence
746             * of other client activity
747             */
748            SetCriticalOutputPending();
749        }
750    }                           /* end for each event */
751
752}                               /* RecordADeviceEvent */
753
754/* RecordADeviceEvent
755 *
756 * Arguments:
757 *	pcbl is &DeviceEventCallback.
758 *	nulldata is NULL.
759 *	calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
760 *	  which provides information about device events that occur.
761 *
762 * Returns: nothing.
763 *
764 * Side Effects:
765 *	The device event is recorded by all contexts that have registered
766 *	it for this client.
767 */
768static void
769RecordADeviceEvent(CallbackListPtr *pcbl, void *nulldata, void *calldata)
770{
771    DeviceEventInfoRec *pei = (DeviceEventInfoRec *) calldata;
772    RecordContextPtr pContext;
773    RecordClientsAndProtocolPtr pRCAP;
774    int eci;                    /* enabled context index */
775
776    for (eci = 0; eci < numEnabledContexts; eci++) {
777        pContext = ppAllContexts[eci];
778        for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
779            if (pRCAP->pDeviceEventSet) {
780                int count;
781                xEvent *xi_events = NULL;
782
783                /* TODO check return values */
784                if (IsMaster(pei->device)) {
785                    xEvent *core_events;
786
787                    EventToCore(pei->event, &core_events, &count);
788                    RecordSendProtocolEvents(pRCAP, pContext, core_events,
789                                             count);
790                    free(core_events);
791                }
792
793                EventToXI(pei->event, &xi_events, &count);
794                RecordSendProtocolEvents(pRCAP, pContext, xi_events, count);
795                free(xi_events);
796            }                   /* end this RCAP selects device events */
797        }                       /* end for each RCAP on this context */
798    }                           /* end for each enabled context */
799}
800
801/* RecordFlushAllContexts
802 *
803 * Arguments:
804 *	pcbl is &FlushCallback.
805 *	nulldata and calldata are NULL.
806 *
807 * Returns: nothing.
808 *
809 * Side Effects:
810 *	All buffered reply data of all enabled contexts is written to
811 *	the recording clients.
812 */
813static void
814RecordFlushAllContexts(CallbackListPtr *pcbl,
815                       void *nulldata, void *calldata)
816{
817    int eci;                    /* enabled context index */
818    RecordContextPtr pContext;
819
820    for (eci = 0; eci < numEnabledContexts; eci++) {
821        pContext = ppAllContexts[eci];
822
823        /* In most cases we leave it to RecordFlushReplyBuffer to make
824         * this check, but this function could be called very often, so we
825         * check before calling hoping to save the function call cost
826         * most of the time.
827         */
828        if (pContext->numBufBytes)
829            RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
830    }
831}                               /* RecordFlushAllContexts */
832
833/* RecordInstallHooks
834 *
835 * Arguments:
836 *	pRCAP is an RCAP on an enabled or being-enabled context.
837 *	oneclient can be zero or the resource ID mask identifying a client.
838 *
839 * Returns: BadAlloc if a memory allocation error occurred, else Success.
840 *
841 * Side Effects:
842 *	Recording hooks needed by RCAP are installed.
843 *	If oneclient is zero, recording hooks needed for all clients and
844 *	protocol on the RCAP are installed.  If oneclient is non-zero,
845 *	only those hooks needed for the specified client are installed.
846 *
847 *	Client requestVectors may be altered.  numEnabledRCAPs will be
848 *	incremented if oneclient == 0.  Callbacks may be added to
849 *	various callback lists.
850 */
851static int
852RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
853{
854    int i = 0;
855    XID client;
856
857    if (oneclient)
858        client = oneclient;
859    else
860        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
861
862    while (client) {
863        if (client != XRecordFutureClients) {
864            if (pRCAP->pRequestMajorOpSet) {
865                RecordSetIteratePtr pIter = NULL;
866                RecordSetInterval interval;
867                ClientPtr pClient = clients[CLIENT_ID(client)];
868
869                if (pClient && !RecordClientPrivate(pClient)) {
870                    RecordClientPrivatePtr pClientPriv;
871
872                    /* no Record proc vector; allocate one */
873                    pClientPriv = (RecordClientPrivatePtr)
874                        malloc(sizeof(RecordClientPrivateRec));
875                    if (!pClientPriv)
876                        return BadAlloc;
877                    /* copy old proc vector to new */
878                    memcpy(pClientPriv->recordVector, pClient->requestVector,
879                           sizeof(pClientPriv->recordVector));
880                    pClientPriv->originalVector = pClient->requestVector;
881                    dixSetPrivate(&pClient->devPrivates,
882                                  RecordClientPrivateKey, pClientPriv);
883                    pClient->requestVector = pClientPriv->recordVector;
884                }
885                while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
886                                                 pIter, &interval))) {
887                    unsigned int j;
888
889                    for (j = interval.first; j <= interval.last; j++)
890                        pClient->requestVector[j] = RecordARequest;
891                }
892            }
893        }
894        if (oneclient)
895            client = 0;
896        else
897            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
898    }
899
900    assert(numEnabledRCAPs >= 0);
901    if (!oneclient && ++numEnabledRCAPs == 1) { /* we're enabling the first context */
902        if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
903            return BadAlloc;
904        if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
905            return BadAlloc;
906        if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
907            return BadAlloc;
908        if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
909            return BadAlloc;
910        /* Alternate context flushing scheme: delete the line above
911         * and call RegisterBlockAndWakeupHandlers here passing
912         * RecordFlushAllContexts.  Is this any better?
913         */
914    }
915    return Success;
916}                               /* RecordInstallHooks */
917
918/* RecordUninstallHooks
919 *
920 * Arguments:
921 *	pRCAP is an RCAP on an enabled or being-disabled context.
922 *	oneclient can be zero or the resource ID mask identifying a client.
923 *
924 * Returns: nothing.
925 *
926 * Side Effects:
927 *	Recording hooks needed by RCAP may be uninstalled.
928 *	If oneclient is zero, recording hooks needed for all clients and
929 *	protocol on the RCAP may be uninstalled.  If oneclient is non-zero,
930 *	only those hooks needed for the specified client may be uninstalled.
931 *
932 *	Client requestVectors may be altered.  numEnabledRCAPs will be
933 *	decremented if oneclient == 0.  Callbacks may be deleted from
934 *	various callback lists.
935 */
936static void
937RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
938{
939    int i = 0;
940    XID client;
941
942    if (oneclient)
943        client = oneclient;
944    else
945        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
946
947    while (client) {
948        if (client != XRecordFutureClients) {
949            if (pRCAP->pRequestMajorOpSet) {
950                ClientPtr pClient = clients[CLIENT_ID(client)];
951                int c;
952                Bool otherRCAPwantsProcVector = FALSE;
953                RecordClientPrivatePtr pClientPriv = NULL;
954
955                assert(pClient);
956                pClientPriv = RecordClientPrivate(pClient);
957                assert(pClientPriv);
958                memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
959                       sizeof(pClientPriv->recordVector));
960
961                for (c = 0; c < numEnabledContexts; c++) {
962                    RecordClientsAndProtocolPtr pOtherRCAP;
963                    RecordContextPtr pContext = ppAllContexts[c];
964
965                    if (pContext == pRCAP->pContext)
966                        continue;
967                    pOtherRCAP = RecordFindClientOnContext(pContext, client,
968                                                           NULL);
969                    if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet) {
970                        RecordSetIteratePtr pIter = NULL;
971                        RecordSetInterval interval;
972
973                        otherRCAPwantsProcVector = TRUE;
974                        while ((pIter =
975                                RecordIterateSet(pOtherRCAP->pRequestMajorOpSet,
976                                                 pIter, &interval))) {
977                            unsigned int j;
978
979                            for (j = interval.first; j <= interval.last; j++)
980                                pClient->requestVector[j] = RecordARequest;
981                        }
982                    }
983                }
984                if (!otherRCAPwantsProcVector) {        /* nobody needs it, so free it */
985                    pClient->requestVector = pClientPriv->originalVector;
986                    dixSetPrivate(&pClient->devPrivates,
987                                  RecordClientPrivateKey, NULL);
988                    free(pClientPriv);
989                }
990            }                   /* end if this RCAP specifies any requests */
991        }                       /* end if not future clients */
992        if (oneclient)
993            client = 0;
994        else
995            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
996    }
997
998    assert(numEnabledRCAPs >= 1);
999    if (!oneclient && --numEnabledRCAPs == 0) { /* we're disabling the last context */
1000        DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
1001        DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
1002        DeleteCallback(&ReplyCallback, RecordAReply, NULL);
1003        DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
1004        /* Alternate context flushing scheme: delete the line above
1005         * and call RemoveBlockAndWakeupHandlers here passing
1006         * RecordFlushAllContexts.  Is this any better?
1007         */
1008        /* Having deleted the callback, call it one last time. -gildea */
1009        RecordFlushAllContexts(&FlushCallback, NULL, NULL);
1010    }
1011}                               /* RecordUninstallHooks */
1012
1013/* RecordDeleteClientFromRCAP
1014 *
1015 * Arguments:
1016 *	pRCAP is an RCAP to delete the client from.
1017 *	position is the index into the array pRCAP->pClientIDs of the
1018 *	client to delete.
1019 *
1020 * Returns: nothing.
1021 *
1022 * Side Effects:
1023 *	Recording hooks needed by client will be uninstalled if the context
1024 *	is enabled.  The designated client will be removed from the
1025 *	pRCAP->pClientIDs array.  If it was the only client on the RCAP,
1026 *	the RCAP is removed from the context and freed.  (Invariant: RCAPs
1027 *	have at least one client.)
1028 */
1029static void
1030RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
1031{
1032    if (pRCAP->pContext->pRecordingClient)
1033        RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
1034    if (position != pRCAP->numClients - 1)
1035        pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
1036    if (--pRCAP->numClients == 0) {     /* no more clients; remove RCAP from context's list */
1037        RecordContextPtr pContext = pRCAP->pContext;
1038
1039        if (pContext->pRecordingClient)
1040            RecordUninstallHooks(pRCAP, 0);
1041        if (pContext->pListOfRCAP == pRCAP)
1042            pContext->pListOfRCAP = pRCAP->pNextRCAP;
1043        else {
1044            RecordClientsAndProtocolPtr prevRCAP;
1045
1046            for (prevRCAP = pContext->pListOfRCAP;
1047                 prevRCAP->pNextRCAP != pRCAP; prevRCAP = prevRCAP->pNextRCAP);
1048            prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
1049        }
1050        /* free the RCAP */
1051        if (pRCAP->clientIDsSeparatelyAllocated)
1052            free(pRCAP->pClientIDs);
1053        free(pRCAP);
1054    }
1055}                               /* RecordDeleteClientFromRCAP */
1056
1057/* RecordAddClientToRCAP
1058 *
1059 * Arguments:
1060 *	pRCAP is an RCAP to add the client to.
1061 *	clientspec is the resource ID mask identifying a client, or
1062 *	  XRecordFutureClients.
1063 *
1064 * Returns: nothing.
1065 *
1066 * Side Effects:
1067 *	Recording hooks needed by client will be installed if the context
1068 *	is enabled.  The designated client will be added to the
1069 *	pRCAP->pClientIDs array, which may be realloced.
1070 *	pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
1071 *	is no more room to hold clients internal to the RCAP.
1072 */
1073static void
1074RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
1075{
1076    if (pRCAP->numClients == pRCAP->sizeClients) {
1077        if (pRCAP->clientIDsSeparatelyAllocated) {
1078            XID *pNewIDs =
1079                reallocarray(pRCAP->pClientIDs,
1080                             pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
1081                             sizeof(XID));
1082            if (!pNewIDs)
1083                return;
1084            pRCAP->pClientIDs = pNewIDs;
1085            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1086        }
1087        else {
1088            XID *pNewIDs =
1089                xallocarray(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
1090                            sizeof(XID));
1091            if (!pNewIDs)
1092                return;
1093            memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients * sizeof(XID));
1094            pRCAP->pClientIDs = pNewIDs;
1095            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1096            pRCAP->clientIDsSeparatelyAllocated = 1;
1097        }
1098    }
1099    pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
1100    if (pRCAP->pContext->pRecordingClient)
1101        RecordInstallHooks(pRCAP, clientspec);
1102}                               /* RecordDeleteClientFromRCAP */
1103
1104/* RecordDeleteClientFromContext
1105 *
1106 * Arguments:
1107 *	pContext is the context to delete from.
1108 *	clientspec is the resource ID mask identifying a client, or
1109 *	  XRecordFutureClients.
1110 *
1111 * Returns: nothing.
1112 *
1113 * Side Effects:
1114 *	If clientspec is on any RCAP of the context, it is deleted from that
1115 *	RCAP.  (A given clientspec can only be on one RCAP of a context.)
1116 */
1117static void
1118RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
1119{
1120    RecordClientsAndProtocolPtr pRCAP;
1121    int position;
1122
1123    if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
1124        RecordDeleteClientFromRCAP(pRCAP, position);
1125}                               /* RecordDeleteClientFromContext */
1126
1127/* RecordSanityCheckClientSpecifiers
1128 *
1129 * Arguments:
1130 *	clientspecs is an array of alleged CLIENTSPECs passed by the client.
1131 *	nspecs is the number of elements in clientspecs.
1132 *	errorspec, if non-zero, is the resource id base of a client that
1133 *	  must not appear in clienspecs.
1134 *
1135 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
1136 *
1137 * Side Effects: none.
1138 */
1139static int
1140RecordSanityCheckClientSpecifiers(ClientPtr client, XID *clientspecs,
1141                                  int nspecs, XID errorspec)
1142{
1143    int i;
1144    int clientIndex;
1145    int rc;
1146    void *value;
1147
1148    for (i = 0; i < nspecs; i++) {
1149        if (clientspecs[i] == XRecordCurrentClients ||
1150            clientspecs[i] == XRecordFutureClients ||
1151            clientspecs[i] == XRecordAllClients)
1152            continue;
1153        if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec))
1154            return BadMatch;
1155        clientIndex = CLIENT_ID(clientspecs[i]);
1156        if (clientIndex && clients[clientIndex] &&
1157            clients[clientIndex]->clientState == ClientStateRunning) {
1158            if (clientspecs[i] == clients[clientIndex]->clientAsMask)
1159                continue;
1160            rc = dixLookupResourceByClass(&value, clientspecs[i], RC_ANY,
1161                                          client, DixGetAttrAccess);
1162            if (rc != Success)
1163                return rc;
1164        }
1165        else
1166            return BadMatch;
1167    }
1168    return Success;
1169}                               /* RecordSanityCheckClientSpecifiers */
1170
1171/* RecordCanonicalizeClientSpecifiers
1172 *
1173 * Arguments:
1174 *	pClientspecs is an array of CLIENTSPECs that have been sanity
1175 *	  checked.
1176 *	pNumClientspecs is a pointer to the number of elements in pClientspecs.
1177 *	excludespec, if non-zero, is the resource id base of a client that
1178 *	  should not be included in the expansion of XRecordAllClients or
1179 *	  XRecordCurrentClients.
1180 *
1181 * Returns:
1182 *	A pointer to an array of CLIENTSPECs that is the same as the
1183 *	passed array with the following modifications:
1184 *	  - all but the client id bits of resource IDs are stripped off.
1185 *	  - duplicates removed.
1186 *	  - XRecordAllClients expanded to a list of all currently connected
1187 *	    clients + XRecordFutureClients - excludespec (if non-zero)
1188 *	  - XRecordCurrentClients expanded to a list of all currently
1189 *	    connected clients - excludespec (if non-zero)
1190 *	The returned array may be the passed array modified in place, or
1191 *	it may be an malloc'ed array.  The caller should keep a pointer to the
1192 *	original array and free the returned array if it is different.
1193 *
1194 *	*pNumClientspecs is set to the number of elements in the returned
1195 *	array.
1196 *
1197 * Side Effects:
1198 *	pClientspecs may be modified in place.
1199 */
1200static XID *
1201RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs,
1202                                   XID excludespec)
1203{
1204    int i;
1205    int numClients = *pNumClientspecs;
1206
1207    /*  first pass strips off the resource index bits, leaving just the
1208     *  client id bits.  This makes searching for a particular client simpler
1209     *  (and faster.)
1210     */
1211    for (i = 0; i < numClients; i++) {
1212        XID cs = pClientspecs[i];
1213
1214        if (cs > XRecordAllClients)
1215            pClientspecs[i] = CLIENT_BITS(cs);
1216    }
1217
1218    for (i = 0; i < numClients; i++) {
1219        if (pClientspecs[i] == XRecordAllClients || pClientspecs[i] == XRecordCurrentClients) { /* expand All/Current */
1220            int j, nc;
1221            XID *pCanon = xallocarray(currentMaxClients + 1, sizeof(XID));
1222
1223            if (!pCanon)
1224                return NULL;
1225            for (nc = 0, j = 1; j < currentMaxClients; j++) {
1226                ClientPtr client = clients[j];
1227
1228                if (client != NullClient &&
1229                    client->clientState == ClientStateRunning &&
1230                    client->clientAsMask != excludespec) {
1231                    pCanon[nc++] = client->clientAsMask;
1232                }
1233            }
1234            if (pClientspecs[i] == XRecordAllClients)
1235                pCanon[nc++] = XRecordFutureClients;
1236            *pNumClientspecs = nc;
1237            return pCanon;
1238        }
1239        else {                  /* not All or Current */
1240
1241            int j;
1242
1243            for (j = i + 1; j < numClients;) {
1244                if (pClientspecs[i] == pClientspecs[j]) {
1245                    pClientspecs[j] = pClientspecs[--numClients];
1246                }
1247                else
1248                    j++;
1249            }
1250        }
1251    }                           /* end for each clientspec */
1252    *pNumClientspecs = numClients;
1253    return pClientspecs;
1254}                               /* RecordCanonicalizeClientSpecifiers */
1255
1256/****************************************************************************/
1257
1258/* stuff for RegisterClients */
1259
1260/* RecordPadAlign
1261 *
1262 * Arguments:
1263 *	size is the number of bytes taken by an object.
1264 *	align is a byte boundary (e.g. 4, 8)
1265 *
1266 * Returns:
1267 *	the number of pad bytes to add at the end of an object of the
1268 *	given size so that an object placed immediately behind it will
1269 *	begin on an <align>-byte boundary.
1270 *
1271 * Side Effects: none.
1272 */
1273static int
1274RecordPadAlign(int size, int align)
1275{
1276    return (align - (size & (align - 1))) & (align - 1);
1277}                               /* RecordPadAlign */
1278
1279/* RecordSanityCheckRegisterClients
1280 *
1281 * Arguments:
1282 *	pContext is the context being registered on.
1283 *	client is the client that issued a RecordCreateContext or
1284 *	  RecordRegisterClients request.
1285 *	stuff is a pointer to the request.
1286 *
1287 * Returns:
1288 *	Any one of several possible error values if any of the request
1289 *	arguments are invalid.  Success if everything is OK.
1290 *
1291 * Side Effects: none.
1292 */
1293static int
1294RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client,
1295                                 xRecordRegisterClientsReq * stuff)
1296{
1297    int err;
1298    xRecordRange *pRange;
1299    int i;
1300    XID recordingClient;
1301
1302    /* LimitClients is 2048 at max, way less that MAXINT */
1303    if (stuff->nClients > LimitClients)
1304        return BadValue;
1305
1306    if (stuff->nRanges > (MAXINT - 4 * stuff->nClients) / SIZEOF(xRecordRange))
1307        return BadValue;
1308
1309    if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
1310        4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
1311        return BadLength;
1312
1313    if (stuff->elementHeader &
1314        ~(XRecordFromClientSequence | XRecordFromClientTime |
1315          XRecordFromServerTime)) {
1316        client->errorValue = stuff->elementHeader;
1317        return BadValue;
1318    }
1319
1320    recordingClient = pContext->pRecordingClient ?
1321        pContext->pRecordingClient->clientAsMask : 0;
1322    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
1323                                            stuff->nClients, recordingClient);
1324    if (err != Success)
1325        return err;
1326
1327    pRange = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
1328    for (i = 0; i < stuff->nRanges; i++, pRange++) {
1329        if (pRange->coreRequestsFirst > pRange->coreRequestsLast) {
1330            client->errorValue = pRange->coreRequestsFirst;
1331            return BadValue;
1332        }
1333        if (pRange->coreRepliesFirst > pRange->coreRepliesLast) {
1334            client->errorValue = pRange->coreRepliesFirst;
1335            return BadValue;
1336        }
1337        if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
1338            (pRange->extRequestsMajorFirst < 128 ||
1339             pRange->extRequestsMajorLast < 128 ||
1340             pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast)) {
1341            client->errorValue = pRange->extRequestsMajorFirst;
1342            return BadValue;
1343        }
1344        if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast) {
1345            client->errorValue = pRange->extRequestsMinorFirst;
1346            return BadValue;
1347        }
1348        if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
1349            (pRange->extRepliesMajorFirst < 128 ||
1350             pRange->extRepliesMajorLast < 128 ||
1351             pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast)) {
1352            client->errorValue = pRange->extRepliesMajorFirst;
1353            return BadValue;
1354        }
1355        if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast) {
1356            client->errorValue = pRange->extRepliesMinorFirst;
1357            return BadValue;
1358        }
1359        if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
1360            (pRange->deliveredEventsFirst < 2 ||
1361             pRange->deliveredEventsLast < 2 ||
1362             pRange->deliveredEventsFirst > pRange->deliveredEventsLast)) {
1363            client->errorValue = pRange->deliveredEventsFirst;
1364            return BadValue;
1365        }
1366        if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
1367            (pRange->deviceEventsFirst < 2 ||
1368             pRange->deviceEventsLast < 2 ||
1369             pRange->deviceEventsFirst > pRange->deviceEventsLast)) {
1370            client->errorValue = pRange->deviceEventsFirst;
1371            return BadValue;
1372        }
1373        if (pRange->errorsFirst > pRange->errorsLast) {
1374            client->errorValue = pRange->errorsFirst;
1375            return BadValue;
1376        }
1377        if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue) {
1378            client->errorValue = pRange->clientStarted;
1379            return BadValue;
1380        }
1381        if (pRange->clientDied != xFalse && pRange->clientDied != xTrue) {
1382            client->errorValue = pRange->clientDied;
1383            return BadValue;
1384        }
1385    }                           /* end for each range */
1386    return Success;
1387}                               /* end RecordSanityCheckRegisterClients */
1388
1389/* This is a tactical structure used to gather information about all the sets
1390 * (RecordSetPtr) that need to be created for an RCAP in the process of
1391 * digesting a list of RECORDRANGEs (converting it to the internal
1392 * representation).
1393 */
1394typedef struct {
1395    int nintervals;             /* number of intervals in following array */
1396    RecordSetInterval *intervals;       /* array of intervals for this set */
1397    int size;                   /* size of intervals array; >= nintervals */
1398    int align;                  /* alignment restriction for set */
1399    int offset;                 /* where to store set pointer rel. to start of RCAP */
1400    short first, last;          /* if for extension, major opcode interval */
1401} SetInfoRec, *SetInfoPtr;
1402
1403#if defined(ERR) && defined(__sun)
1404#undef ERR /* Avoid conflict with Solaris <sys/regset.h> */
1405#endif
1406
1407/* These constant are used to index into an array of SetInfoRec. */
1408enum { REQ,                     /* set info for requests */
1409    REP,                        /* set info for replies */
1410    ERR,                        /* set info for errors */
1411    DEV,                        /* set info for device events */
1412    DLEV,                       /* set info for delivered events */
1413    PREDEFSETS
1414};                              /* number of predefined array entries */
1415
1416/* RecordAllocIntervals
1417 *
1418 * Arguments:
1419 *	psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
1420 *	nIntervals is the desired size of the intervals array.
1421 *
1422 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1423 *
1424 * Side Effects:
1425 *	If Success is returned, psi->intervals is a pointer to size
1426 *	RecordSetIntervals, all zeroed, and psi->size is set to size.
1427 */
1428static int
1429RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
1430{
1431    assert(!psi->intervals);
1432    psi->intervals = xallocarray(nIntervals, sizeof(RecordSetInterval));
1433    if (!psi->intervals)
1434        return BadAlloc;
1435    memset(psi->intervals, 0, nIntervals * sizeof(RecordSetInterval));
1436    psi->size = nIntervals;
1437    return Success;
1438}                               /* end RecordAllocIntervals */
1439
1440/* RecordConvertRangesToIntervals
1441 *
1442 * Arguments:
1443 *	psi is a pointer to the SetInfoRec we are building.
1444 *	pRanges is an array of xRecordRanges.
1445 *	nRanges is the number of elements in pRanges.
1446 *	byteoffset is the offset from the start of an xRecordRange of the
1447 *	  two bytes (1 for first, 1 for last) we are interested in.
1448 *	pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
1449 *	  above are followed by four bytes (2 for first, 2 for last)
1450 *	  representing a minor opcode range, and this information should be
1451 *	  stored in one of the SetInfoRecs starting at pExtSetInfo.
1452 *	pnExtSetInfo is the number of elements in the pExtSetInfo array.
1453 *
1454 * Returns:  BadAlloc if a memory allocation error occurred, else Success.
1455 *
1456 * Side Effects:
1457 *	The slice of pRanges indicated by byteoffset is stored in psi.
1458 *	If pExtSetInfo is non-NULL, minor opcode intervals are stored
1459 *	in an existing SetInfoRec if the major opcode interval matches, else
1460 *	they are stored in a new SetInfoRec, and *pnExtSetInfo is
1461 *	increased accordingly.
1462 */
1463static int
1464RecordConvertRangesToIntervals(SetInfoPtr psi,
1465                               xRecordRange * pRanges,
1466                               int nRanges,
1467                               int byteoffset,
1468                               SetInfoPtr pExtSetInfo, int *pnExtSetInfo)
1469{
1470    int i;
1471    CARD8 *pCARD8;
1472    int first, last;
1473    int err;
1474
1475    for (i = 0; i < nRanges; i++, pRanges++) {
1476        pCARD8 = ((CARD8 *) pRanges) + byteoffset;
1477        first = pCARD8[0];
1478        last = pCARD8[1];
1479        if (first || last) {
1480            if (!psi->intervals) {
1481                err = RecordAllocIntervals(psi, 2 * (nRanges - i));
1482                if (err != Success)
1483                    return err;
1484            }
1485            psi->intervals[psi->nintervals].first = first;
1486            psi->intervals[psi->nintervals].last = last;
1487            psi->nintervals++;
1488            assert(psi->nintervals <= psi->size);
1489            if (pExtSetInfo) {
1490                SetInfoPtr pesi = pExtSetInfo;
1491                CARD16 *pCARD16 = (CARD16 *) (pCARD8 + 2);
1492                int j;
1493
1494                for (j = 0; j < *pnExtSetInfo; j++, pesi++) {
1495                    if ((first == pesi->first) && (last == pesi->last))
1496                        break;
1497                }
1498                if (j == *pnExtSetInfo) {
1499                    err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
1500                    if (err != Success)
1501                        return err;
1502                    pesi->first = first;
1503                    pesi->last = last;
1504                    (*pnExtSetInfo)++;
1505                }
1506                pesi->intervals[pesi->nintervals].first = pCARD16[0];
1507                pesi->intervals[pesi->nintervals].last = pCARD16[1];
1508                pesi->nintervals++;
1509                assert(pesi->nintervals <= pesi->size);
1510            }
1511        }
1512    }
1513    return Success;
1514}                               /* end RecordConvertRangesToIntervals */
1515
1516#define offset_of(_structure, _field) \
1517    ((char *)(& (_structure . _field)) - (char *)(&_structure))
1518
1519/* RecordRegisterClients
1520 *
1521 * Arguments:
1522 *	pContext is the context on which to register the clients.
1523 *	client is the client that issued the RecordCreateContext or
1524 *	  RecordRegisterClients request.
1525 *	stuff is a pointer to the request.
1526 *
1527 * Returns:
1528 *	Any one of several possible error values defined by the protocol.
1529 *	Success if everything is OK.
1530 *
1531 * Side Effects:
1532 *	If different element headers are specified, the context is flushed.
1533 *	If any of the specified clients are already registered on the
1534 *	context, they are first unregistered.  A new RCAP is created to
1535 *	hold the specified protocol and clients, and it is linked onto the
1536 *	context.  If the context is enabled, appropriate hooks are installed
1537 *	to record the new clients and protocol.
1538 */
1539static int
1540RecordRegisterClients(RecordContextPtr pContext, ClientPtr client,
1541                      xRecordRegisterClientsReq * stuff)
1542{
1543    int err;
1544    int i;
1545    SetInfoPtr si;
1546    int maxSets;
1547    int nExtReqSets = 0;
1548    int nExtRepSets = 0;
1549    int extReqSetsOffset = 0;
1550    int extRepSetsOffset = 0;
1551    SetInfoPtr pExtReqSets, pExtRepSets;
1552    int clientListOffset;
1553    XID *pCanonClients;
1554    int clientStarted = 0, clientDied = 0;
1555    xRecordRange *pRanges, rr;
1556    int nClients;
1557    int sizeClients;
1558    int totRCAPsize;
1559    RecordClientsAndProtocolPtr pRCAP;
1560    int pad;
1561    XID recordingClient;
1562
1563    /* do all sanity checking up front */
1564
1565    err = RecordSanityCheckRegisterClients(pContext, client, stuff);
1566    if (err != Success)
1567        return err;
1568
1569    /* if element headers changed, flush buffer */
1570
1571    if (pContext->elemHeaders != stuff->elementHeader) {
1572        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
1573        pContext->elemHeaders = stuff->elementHeader;
1574    }
1575
1576    nClients = stuff->nClients;
1577    if (!nClients)
1578        /* if empty clients list, we're done. */
1579        return Success;
1580
1581    recordingClient = pContext->pRecordingClient ?
1582        pContext->pRecordingClient->clientAsMask : 0;
1583    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
1584                                                       &nClients,
1585                                                       recordingClient);
1586    if (!pCanonClients)
1587        return BadAlloc;
1588
1589    /* We may have to create as many as one set for each "predefined"
1590     * protocol types, plus one per range for extension requests, plus one per
1591     * range for extension replies.
1592     */
1593    maxSets = PREDEFSETS + 2 * stuff->nRanges;
1594    si = xallocarray(maxSets, sizeof(SetInfoRec));
1595    if (!si) {
1596        err = BadAlloc;
1597        goto bailout;
1598    }
1599    memset(si, 0, sizeof(SetInfoRec) * maxSets);
1600
1601    /* theoretically you must do this because NULL may not be all-bits-zero */
1602    for (i = 0; i < maxSets; i++)
1603        si[i].intervals = NULL;
1604
1605    pExtReqSets = si + PREDEFSETS;
1606    pExtRepSets = pExtReqSets + stuff->nRanges;
1607
1608    pRanges = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
1609
1610    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1611                                         offset_of(rr, coreRequestsFirst), NULL,
1612                                         NULL);
1613    if (err != Success)
1614        goto bailout;
1615
1616    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1617                                         offset_of(rr, extRequestsMajorFirst),
1618                                         pExtReqSets, &nExtReqSets);
1619    if (err != Success)
1620        goto bailout;
1621
1622    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1623                                         offset_of(rr, coreRepliesFirst), NULL,
1624                                         NULL);
1625    if (err != Success)
1626        goto bailout;
1627
1628    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1629                                         offset_of(rr, extRepliesMajorFirst),
1630                                         pExtRepSets, &nExtRepSets);
1631    if (err != Success)
1632        goto bailout;
1633
1634    err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
1635                                         offset_of(rr, errorsFirst), NULL,
1636                                         NULL);
1637    if (err != Success)
1638        goto bailout;
1639
1640    err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
1641                                         offset_of(rr, deliveredEventsFirst),
1642                                         NULL, NULL);
1643    if (err != Success)
1644        goto bailout;
1645
1646    err = RecordConvertRangesToIntervals(&si[DEV], pRanges, stuff->nRanges,
1647                                         offset_of(rr, deviceEventsFirst), NULL,
1648                                         NULL);
1649    if (err != Success)
1650        goto bailout;
1651
1652    /* collect client-started and client-died */
1653
1654    for (i = 0; i < stuff->nRanges; i++) {
1655        if (pRanges[i].clientStarted)
1656            clientStarted = TRUE;
1657        if (pRanges[i].clientDied)
1658            clientDied = TRUE;
1659    }
1660
1661    /*  We now have all the information collected to create all the sets,
1662     * and we can compute the total memory required for the RCAP.
1663     */
1664
1665    totRCAPsize = sizeof(RecordClientsAndProtocolRec);
1666
1667    /* leave a little room to grow before forcing a separate allocation */
1668    sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
1669    pad = RecordPadAlign(totRCAPsize, sizeof(XID));
1670    clientListOffset = totRCAPsize + pad;
1671    totRCAPsize += pad + sizeClients * sizeof(XID);
1672
1673    if (nExtReqSets) {
1674        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1675        extReqSetsOffset = totRCAPsize + pad;
1676        totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
1677    }
1678    if (nExtRepSets) {
1679        pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1680        extRepSetsOffset = totRCAPsize + pad;
1681        totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
1682    }
1683
1684    for (i = 0; i < maxSets; i++) {
1685        if (si[i].nintervals) {
1686            si[i].size =
1687                RecordSetMemoryRequirements(si[i].intervals, si[i].nintervals,
1688                                            &si[i].align);
1689            pad = RecordPadAlign(totRCAPsize, si[i].align);
1690            si[i].offset = pad + totRCAPsize;
1691            totRCAPsize += pad + si[i].size;
1692        }
1693    }
1694
1695    /* allocate memory for the whole RCAP */
1696
1697    pRCAP = (RecordClientsAndProtocolPtr) malloc(totRCAPsize);
1698    if (!pRCAP) {
1699        err = BadAlloc;
1700        goto bailout;
1701    }
1702
1703    /* fill in the RCAP */
1704
1705    pRCAP->pContext = pContext;
1706    pRCAP->pClientIDs = (XID *) ((char *) pRCAP + clientListOffset);
1707    pRCAP->numClients = nClients;
1708    pRCAP->sizeClients = sizeClients;
1709    pRCAP->clientIDsSeparatelyAllocated = 0;
1710    for (i = 0; i < nClients; i++) {
1711        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
1712        pRCAP->pClientIDs[i] = pCanonClients[i];
1713    }
1714
1715    /* create all the sets */
1716
1717    if (si[REQ].intervals) {
1718        pRCAP->pRequestMajorOpSet =
1719            RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
1720                            (RecordSetPtr) ((char *) pRCAP + si[REQ].offset),
1721                            si[REQ].size);
1722    }
1723    else
1724        pRCAP->pRequestMajorOpSet = NULL;
1725
1726    if (si[REP].intervals) {
1727        pRCAP->pReplyMajorOpSet =
1728            RecordCreateSet(si[REP].intervals, si[REP].nintervals,
1729                            (RecordSetPtr) ((char *) pRCAP + si[REP].offset),
1730                            si[REP].size);
1731    }
1732    else
1733        pRCAP->pReplyMajorOpSet = NULL;
1734
1735    if (si[ERR].intervals) {
1736        pRCAP->pErrorSet =
1737            RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
1738                            (RecordSetPtr) ((char *) pRCAP + si[ERR].offset),
1739                            si[ERR].size);
1740    }
1741    else
1742        pRCAP->pErrorSet = NULL;
1743
1744    if (si[DEV].intervals) {
1745        pRCAP->pDeviceEventSet =
1746            RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
1747                            (RecordSetPtr) ((char *) pRCAP + si[DEV].offset),
1748                            si[DEV].size);
1749    }
1750    else
1751        pRCAP->pDeviceEventSet = NULL;
1752
1753    if (si[DLEV].intervals) {
1754        pRCAP->pDeliveredEventSet =
1755            RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
1756                            (RecordSetPtr) ((char *) pRCAP + si[DLEV].offset),
1757                            si[DLEV].size);
1758    }
1759    else
1760        pRCAP->pDeliveredEventSet = NULL;
1761
1762    if (nExtReqSets) {
1763        pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
1764            ((char *) pRCAP + extReqSetsOffset);
1765        pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
1766        for (i = 0; i < nExtReqSets; i++, pExtReqSets++) {
1767            pRCAP->pRequestMinOpInfo[i + 1].major.first = pExtReqSets->first;
1768            pRCAP->pRequestMinOpInfo[i + 1].major.last = pExtReqSets->last;
1769            pRCAP->pRequestMinOpInfo[i + 1].major.pMinOpSet =
1770                RecordCreateSet(pExtReqSets->intervals,
1771                                pExtReqSets->nintervals,
1772                                (RecordSetPtr) ((char *) pRCAP +
1773                                                pExtReqSets->offset),
1774                                pExtReqSets->size);
1775        }
1776    }
1777    else
1778        pRCAP->pRequestMinOpInfo = NULL;
1779
1780    if (nExtRepSets) {
1781        pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
1782            ((char *) pRCAP + extRepSetsOffset);
1783        pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
1784        for (i = 0; i < nExtRepSets; i++, pExtRepSets++) {
1785            pRCAP->pReplyMinOpInfo[i + 1].major.first = pExtRepSets->first;
1786            pRCAP->pReplyMinOpInfo[i + 1].major.last = pExtRepSets->last;
1787            pRCAP->pReplyMinOpInfo[i + 1].major.pMinOpSet =
1788                RecordCreateSet(pExtRepSets->intervals,
1789                                pExtRepSets->nintervals,
1790                                (RecordSetPtr) ((char *) pRCAP +
1791                                                pExtRepSets->offset),
1792                                pExtRepSets->size);
1793        }
1794    }
1795    else
1796        pRCAP->pReplyMinOpInfo = NULL;
1797
1798    pRCAP->clientStarted = clientStarted;
1799    pRCAP->clientDied = clientDied;
1800
1801    /* link the RCAP onto the context */
1802
1803    pRCAP->pNextRCAP = pContext->pListOfRCAP;
1804    pContext->pListOfRCAP = pRCAP;
1805
1806    if (pContext->pRecordingClient)     /* context enabled */
1807        RecordInstallHooks(pRCAP, 0);
1808
1809 bailout:
1810    if (si) {
1811        for (i = 0; i < maxSets; i++)
1812            free(si[i].intervals);
1813        free(si);
1814    }
1815    if (pCanonClients && pCanonClients != (XID *) &stuff[1])
1816        free(pCanonClients);
1817    return err;
1818}                               /* RecordRegisterClients */
1819
1820/* Proc functions all take a client argument, execute the request in
1821 * client->requestBuffer, and return a protocol error status.
1822 */
1823
1824static int
1825ProcRecordQueryVersion(ClientPtr client)
1826{
1827    /* REQUEST(xRecordQueryVersionReq); */
1828    xRecordQueryVersionReply rep = {
1829        .type = X_Reply,
1830        .sequenceNumber = client->sequence,
1831        .length = 0,
1832        .majorVersion = SERVER_RECORD_MAJOR_VERSION,
1833        .minorVersion = SERVER_RECORD_MINOR_VERSION
1834    };
1835
1836    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
1837    if (client->swapped) {
1838        swaps(&rep.sequenceNumber);
1839        swaps(&rep.majorVersion);
1840        swaps(&rep.minorVersion);
1841    }
1842    WriteToClient(client, sizeof(xRecordQueryVersionReply), &rep);
1843    return Success;
1844}                               /* ProcRecordQueryVersion */
1845
1846static int
1847ProcRecordCreateContext(ClientPtr client)
1848{
1849    REQUEST(xRecordCreateContextReq);
1850    RecordContextPtr pContext;
1851    RecordContextPtr *ppNewAllContexts = NULL;
1852    int err = BadAlloc;
1853
1854    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
1855    LEGAL_NEW_RESOURCE(stuff->context, client);
1856
1857    pContext = (RecordContextPtr) malloc(sizeof(RecordContextRec));
1858    if (!pContext)
1859        goto bailout;
1860
1861    /* make sure there is room in ppAllContexts to store the new context */
1862
1863    ppNewAllContexts =
1864        reallocarray(ppAllContexts, numContexts + 1, sizeof(RecordContextPtr));
1865    if (!ppNewAllContexts)
1866        goto bailout;
1867    ppAllContexts = ppNewAllContexts;
1868
1869    pContext->id = stuff->context;
1870    pContext->pRecordingClient = NULL;
1871    pContext->pListOfRCAP = NULL;
1872    pContext->elemHeaders = 0;
1873    pContext->bufCategory = 0;
1874    pContext->numBufBytes = 0;
1875    pContext->pBufClient = NULL;
1876    pContext->continuedReply = 0;
1877    pContext->inFlush = 0;
1878
1879    err = RecordRegisterClients(pContext, client,
1880                                (xRecordRegisterClientsReq *) stuff);
1881    if (err != Success)
1882        goto bailout;
1883
1884    if (AddResource(pContext->id, RTContext, pContext)) {
1885        ppAllContexts[numContexts++] = pContext;
1886        return Success;
1887    }
1888    else {
1889        return BadAlloc;
1890    }
1891 bailout:
1892    free(pContext);
1893    return err;
1894}                               /* ProcRecordCreateContext */
1895
1896static int
1897ProcRecordRegisterClients(ClientPtr client)
1898{
1899    RecordContextPtr pContext;
1900
1901    REQUEST(xRecordRegisterClientsReq);
1902
1903    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
1904    VERIFY_CONTEXT(pContext, stuff->context, client);
1905
1906    return RecordRegisterClients(pContext, client, stuff);
1907}                               /* ProcRecordRegisterClients */
1908
1909static int
1910ProcRecordUnregisterClients(ClientPtr client)
1911{
1912    RecordContextPtr pContext;
1913    int err;
1914
1915    REQUEST(xRecordUnregisterClientsReq);
1916    XID *pCanonClients;
1917    int nClients;
1918    int i;
1919
1920    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
1921    if (INT_MAX / 4 < stuff->nClients ||
1922        (client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
1923        4 * stuff->nClients)
1924        return BadLength;
1925    VERIFY_CONTEXT(pContext, stuff->context, client);
1926    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
1927                                            stuff->nClients, 0);
1928    if (err != Success)
1929        return err;
1930
1931    nClients = stuff->nClients;
1932    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
1933                                                       &nClients, 0);
1934    if (!pCanonClients)
1935        return BadAlloc;
1936
1937    for (i = 0; i < nClients; i++) {
1938        RecordDeleteClientFromContext(pContext, pCanonClients[i]);
1939    }
1940    if (pCanonClients != (XID *) &stuff[1])
1941        free(pCanonClients);
1942    return Success;
1943}                               /* ProcRecordUnregisterClients */
1944
1945/****************************************************************************/
1946
1947/* stuff for GetContext */
1948
1949/* This is a tactical structure used to hold the xRecordRanges as they are
1950 * being reconstituted from the sets in the RCAPs.
1951 */
1952
1953typedef struct {
1954    xRecordRange *pRanges;      /* array of xRecordRanges for one RCAP */
1955    int size;                   /* number of elements in pRanges, >= nRanges */
1956    int nRanges;                /* number of occupied element of pRanges */
1957} GetContextRangeInfoRec, *GetContextRangeInfoPtr;
1958
1959/* RecordAllocRanges
1960 *
1961 * Arguments:
1962 *	pri is a pointer to a GetContextRangeInfoRec to allocate for.
1963 *	nRanges is the number of xRecordRanges desired for pri.
1964 *
1965 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1966 *
1967 * Side Effects:
1968 *	If Success is returned, pri->pRanges points to at least nRanges
1969 *	ranges.  pri->nRanges is set to nRanges.  pri->size is the actual
1970 *	number of ranges.  Newly allocated ranges are zeroed.
1971 */
1972static int
1973RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
1974{
1975    int newsize;
1976    xRecordRange *pNewRange;
1977
1978#define SZINCR 8
1979
1980    newsize = max(pri->size + SZINCR, nRanges);
1981    pNewRange = reallocarray(pri->pRanges, newsize, sizeof(xRecordRange));
1982    if (!pNewRange)
1983        return BadAlloc;
1984
1985    pri->pRanges = pNewRange;
1986    pri->size = newsize;
1987    memset(&pri->pRanges[pri->size - SZINCR], 0, SZINCR * sizeof(xRecordRange));
1988    if (pri->nRanges < nRanges)
1989        pri->nRanges = nRanges;
1990    return Success;
1991}                               /* RecordAllocRanges */
1992
1993/* RecordConvertSetToRanges
1994 *
1995 * Arguments:
1996 *	pSet is the set to be converted.
1997 *	pri is where the result should be stored.
1998 *	byteoffset is the offset from the start of an xRecordRange of the
1999 *	  two vales (first, last) we are interested in.
2000 *	card8 is TRUE if the vales are one byte each and FALSE if two bytes
2001 *	  each.
2002 *	imax is the largest set value to store in pri->pRanges.
2003 *	pStartIndex, if non-NULL, is the index of the first range in
2004 *	  pri->pRanges that should be stored to.  If NULL,
2005 *	  start at index 0.
2006 *
2007 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2008 *
2009 * Side Effects:
2010 *	If Success is returned, the slice of pri->pRanges indicated by
2011 *	byteoffset and card8 is filled in with the intervals from pSet.
2012 *	if pStartIndex was non-NULL, *pStartIndex is filled in with one
2013 *	more than the index of the last xRecordRange that was touched.
2014 */
2015static int
2016RecordConvertSetToRanges(RecordSetPtr pSet,
2017                         GetContextRangeInfoPtr pri,
2018                         int byteoffset,
2019                         Bool card8, unsigned int imax, int *pStartIndex)
2020{
2021    int nRanges;
2022    RecordSetIteratePtr pIter = NULL;
2023    RecordSetInterval interval;
2024    CARD8 *pCARD8;
2025    CARD16 *pCARD16;
2026    int err;
2027
2028    if (!pSet)
2029        return Success;
2030
2031    nRanges = pStartIndex ? *pStartIndex : 0;
2032    while ((pIter = RecordIterateSet(pSet, pIter, &interval))) {
2033        if (interval.first > imax)
2034            break;
2035        if (interval.last > imax)
2036            interval.last = imax;
2037        nRanges++;
2038        if (nRanges > pri->size) {
2039            err = RecordAllocRanges(pri, nRanges);
2040            if (err != Success)
2041                return err;
2042        }
2043        else
2044            pri->nRanges = max(pri->nRanges, nRanges);
2045        if (card8) {
2046            pCARD8 = ((CARD8 *) &pri->pRanges[nRanges - 1]) + byteoffset;
2047            *pCARD8++ = interval.first;
2048            *pCARD8 = interval.last;
2049        }
2050        else {
2051            pCARD16 = (CARD16 *)
2052                (((char *) &pri->pRanges[nRanges - 1]) + byteoffset);
2053            *pCARD16++ = interval.first;
2054            *pCARD16 = interval.last;
2055        }
2056    }
2057    if (pStartIndex)
2058        *pStartIndex = nRanges;
2059    return Success;
2060}                               /* RecordConvertSetToRanges */
2061
2062/* RecordConvertMinorOpInfoToRanges
2063 *
2064 * Arguments:
2065 *	pMinOpInfo is the minor opcode info to convert to xRecordRanges.
2066 *	pri is where the result should be stored.
2067 *	byteoffset is the offset from the start of an xRecordRange of the
2068 *	  four vales (CARD8 major_first, CARD8 major_last,
2069 *	  CARD16 minor_first, CARD16 minor_last) we are going to store.
2070 *
2071 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2072 *
2073 * Side Effects:
2074 *	If Success is returned, the slice of pri->pRanges indicated by
2075 *	byteoffset is filled in with the information from pMinOpInfo.
2076 */
2077static int
2078RecordConvertMinorOpInfoToRanges(RecordMinorOpPtr pMinOpInfo,
2079                                 GetContextRangeInfoPtr pri, int byteoffset)
2080{
2081    int nsets;
2082    int start;
2083    int i;
2084    int err;
2085
2086    if (!pMinOpInfo)
2087        return Success;
2088
2089    nsets = pMinOpInfo->count;
2090    pMinOpInfo++;
2091    start = 0;
2092    for (i = 0; i < nsets; i++) {
2093        int j, s;
2094
2095        s = start;
2096        err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
2097                                       byteoffset + 2, FALSE, 65535, &start);
2098        if (err != Success)
2099            return err;
2100        for (j = s; j < start; j++) {
2101            CARD8 *pCARD8 = ((CARD8 *) &pri->pRanges[j]) + byteoffset;
2102
2103            *pCARD8++ = pMinOpInfo[i].major.first;
2104            *pCARD8 = pMinOpInfo[i].major.last;
2105        }
2106    }
2107    return Success;
2108}                               /* RecordConvertMinorOpInfoToRanges */
2109
2110/* RecordSwapRanges
2111 *
2112 * Arguments:
2113 *	pRanges is an array of xRecordRanges.
2114 *	nRanges is the number of elements in pRanges.
2115 *
2116 * Returns: nothing.
2117 *
2118 * Side Effects:
2119 *	The 16 bit fields of each xRecordRange are byte swapped.
2120 */
2121static void
2122RecordSwapRanges(xRecordRange * pRanges, int nRanges)
2123{
2124    int i;
2125
2126    for (i = 0; i < nRanges; i++, pRanges++) {
2127        swaps(&pRanges->extRequestsMinorFirst);
2128        swaps(&pRanges->extRequestsMinorLast);
2129        swaps(&pRanges->extRepliesMinorFirst);
2130        swaps(&pRanges->extRepliesMinorLast);
2131    }
2132}                               /* RecordSwapRanges */
2133
2134static int
2135ProcRecordGetContext(ClientPtr client)
2136{
2137    RecordContextPtr pContext;
2138
2139    REQUEST(xRecordGetContextReq);
2140    xRecordGetContextReply rep;
2141    RecordClientsAndProtocolPtr pRCAP;
2142    int nRCAPs = 0;
2143    GetContextRangeInfoPtr pRangeInfo;
2144    GetContextRangeInfoPtr pri;
2145    int i;
2146    int err;
2147    CARD32 nClients, length;
2148
2149    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2150    VERIFY_CONTEXT(pContext, stuff->context, client);
2151
2152    /* how many RCAPs are there on this context? */
2153
2154    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2155        nRCAPs++;
2156
2157    /* allocate and initialize space for record range info */
2158
2159    pRangeInfo = xallocarray(nRCAPs, sizeof(GetContextRangeInfoRec));
2160    if (!pRangeInfo && nRCAPs > 0)
2161        return BadAlloc;
2162    for (i = 0; i < nRCAPs; i++) {
2163        pRangeInfo[i].pRanges = NULL;
2164        pRangeInfo[i].size = 0;
2165        pRangeInfo[i].nRanges = 0;
2166    }
2167
2168    /* convert the RCAP (internal) representation of the recorded protocol
2169     * to the wire protocol (external) representation, storing the information
2170     * for the ith RCAP in pri[i]
2171     */
2172
2173    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2174         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
2175        xRecordRange rr;
2176
2177        err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
2178                                       offset_of(rr, coreRequestsFirst), TRUE,
2179                                       127, NULL);
2180        if (err != Success)
2181            goto bailout;
2182
2183        err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
2184                                       offset_of(rr, coreRepliesFirst), TRUE,
2185                                       127, NULL);
2186        if (err != Success)
2187            goto bailout;
2188
2189        err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
2190                                       offset_of(rr, deliveredEventsFirst),
2191                                       TRUE, 255, NULL);
2192        if (err != Success)
2193            goto bailout;
2194
2195        err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
2196                                       offset_of(rr, deviceEventsFirst), TRUE,
2197                                       255, NULL);
2198        if (err != Success)
2199            goto bailout;
2200
2201        err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
2202                                       offset_of(rr, errorsFirst), TRUE, 255,
2203                                       NULL);
2204        if (err != Success)
2205            goto bailout;
2206
2207        err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
2208                                               pri, offset_of(rr,
2209                                                              extRequestsMajorFirst));
2210        if (err != Success)
2211            goto bailout;
2212
2213        err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
2214                                               pri, offset_of(rr,
2215                                                              extRepliesMajorFirst));
2216        if (err != Success)
2217            goto bailout;
2218
2219        if (pRCAP->clientStarted || pRCAP->clientDied) {
2220            if (pri->nRanges == 0)
2221                RecordAllocRanges(pri, 1);
2222            pri->pRanges[0].clientStarted = pRCAP->clientStarted;
2223            pri->pRanges[0].clientDied = pRCAP->clientDied;
2224        }
2225    }
2226
2227    /* calculate number of clients and reply length */
2228
2229    nClients = 0;
2230    length = 0;
2231    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2232         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
2233        nClients += pRCAP->numClients;
2234        length += pRCAP->numClients *
2235            (bytes_to_int32(sizeof(xRecordClientInfo)) +
2236             pri->nRanges * bytes_to_int32(sizeof(xRecordRange)));
2237    }
2238
2239    /* write the reply header */
2240
2241    rep = (xRecordGetContextReply) {
2242        .type = X_Reply,
2243        .enabled = pContext->pRecordingClient != NULL,
2244        .sequenceNumber = client->sequence,
2245        .length = length,
2246        .elementHeader = pContext->elemHeaders,
2247        .nClients = nClients
2248    };
2249    if (client->swapped) {
2250        swaps(&rep.sequenceNumber);
2251        swapl(&rep.length);
2252        swapl(&rep.nClients);
2253    }
2254    WriteToClient(client, sizeof(xRecordGetContextReply), &rep);
2255
2256    /* write all the CLIENT_INFOs */
2257
2258    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2259         pRCAP; pRCAP = pRCAP->pNextRCAP, pri++) {
2260        xRecordClientInfo rci;
2261
2262        rci.nRanges = pri->nRanges;
2263        if (client->swapped) {
2264            swapl(&rci.nRanges);
2265            RecordSwapRanges(pri->pRanges, pri->nRanges);
2266        }
2267        for (i = 0; i < pRCAP->numClients; i++) {
2268            rci.clientResource = pRCAP->pClientIDs[i];
2269            if (client->swapped)
2270                swapl(&rci.clientResource);
2271            WriteToClient(client, sizeof(xRecordClientInfo), &rci);
2272            WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
2273                          pri->pRanges);
2274        }
2275    }
2276    err = Success;
2277
2278 bailout:
2279    for (i = 0; i < nRCAPs; i++) {
2280        free(pRangeInfo[i].pRanges);
2281    }
2282    free(pRangeInfo);
2283    return err;
2284}                               /* ProcRecordGetContext */
2285
2286static int
2287ProcRecordEnableContext(ClientPtr client)
2288{
2289    RecordContextPtr pContext;
2290
2291    REQUEST(xRecordEnableContextReq);
2292    int i;
2293    RecordClientsAndProtocolPtr pRCAP;
2294
2295    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2296    VERIFY_CONTEXT(pContext, stuff->context, client);
2297    if (pContext->pRecordingClient)
2298        return BadMatch;        /* already enabled */
2299
2300    /* install record hooks for each RCAP */
2301
2302    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
2303        int err = RecordInstallHooks(pRCAP, 0);
2304
2305        if (err != Success) {   /* undo the previous installs */
2306            RecordClientsAndProtocolPtr pUninstallRCAP;
2307
2308            for (pUninstallRCAP = pContext->pListOfRCAP;
2309                 pUninstallRCAP != pRCAP;
2310                 pUninstallRCAP = pUninstallRCAP->pNextRCAP) {
2311                RecordUninstallHooks(pUninstallRCAP, 0);
2312            }
2313            return err;
2314        }
2315    }
2316
2317    /* Disallow further request processing on this connection until
2318     * the context is disabled.
2319     */
2320    IgnoreClient(client);
2321    pContext->pRecordingClient = client;
2322
2323    /* Don't allow the data connection to record itself; unregister it. */
2324    RecordDeleteClientFromContext(pContext,
2325                                  pContext->pRecordingClient->clientAsMask);
2326
2327    /* move the newly enabled context to the front part of ppAllContexts,
2328     * where all the enabled contexts are
2329     */
2330    i = RecordFindContextOnAllContexts(pContext);
2331    assert(i >= numEnabledContexts);
2332    if (i != numEnabledContexts) {
2333        ppAllContexts[i] = ppAllContexts[numEnabledContexts];
2334        ppAllContexts[numEnabledContexts] = pContext;
2335    }
2336
2337    ++numEnabledContexts;
2338    assert(numEnabledContexts > 0);
2339
2340    /* send StartOfData */
2341    RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0, 0);
2342    RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2343    return Success;
2344}                               /* ProcRecordEnableContext */
2345
2346/* RecordDisableContext
2347 *
2348 * Arguments:
2349 *	pContext is the context to disable.
2350 *	nRanges is the number of elements in pRanges.
2351 *
2352 * Returns: nothing.
2353 *
2354 * Side Effects:
2355 *	If the context was enabled, it is disabled.  An EndOfData
2356 *	message is sent to the recording client.  Recording hooks for
2357 *	this context are uninstalled.  The context is moved to the
2358 *	rear part of the ppAllContexts array.  numEnabledContexts is
2359 *	decremented.  Request processing for the formerly recording client
2360 *	is resumed.
2361 */
2362static void
2363RecordDisableContext(RecordContextPtr pContext)
2364{
2365    RecordClientsAndProtocolPtr pRCAP;
2366    int i;
2367
2368    if (!pContext->pRecordingClient)
2369        return;
2370    if (!pContext->pRecordingClient->clientGone) {
2371        RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0, 0);
2372        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2373    }
2374    /* Re-enable request processing on this connection. */
2375    AttendClient(pContext->pRecordingClient);
2376
2377    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
2378        RecordUninstallHooks(pRCAP, 0);
2379    }
2380
2381    pContext->pRecordingClient = NULL;
2382
2383    /* move the newly disabled context to the rear part of ppAllContexts,
2384     * where all the disabled contexts are
2385     */
2386    i = RecordFindContextOnAllContexts(pContext);
2387    assert(i != -1);
2388    assert(i < numEnabledContexts);
2389    if (i != (numEnabledContexts - 1)) {
2390        ppAllContexts[i] = ppAllContexts[numEnabledContexts - 1];
2391        ppAllContexts[numEnabledContexts - 1] = pContext;
2392    }
2393    --numEnabledContexts;
2394    assert(numEnabledContexts >= 0);
2395}                               /* RecordDisableContext */
2396
2397static int
2398ProcRecordDisableContext(ClientPtr client)
2399{
2400    RecordContextPtr pContext;
2401
2402    REQUEST(xRecordDisableContextReq);
2403
2404    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2405    VERIFY_CONTEXT(pContext, stuff->context, client);
2406    RecordDisableContext(pContext);
2407    return Success;
2408}                               /* ProcRecordDisableContext */
2409
2410/* RecordDeleteContext
2411 *
2412 * Arguments:
2413 *	value is the context to delete.
2414 *	id is its resource ID.
2415 *
2416 * Returns: Success.
2417 *
2418 * Side Effects:
2419 *	Disables the context, frees all associated memory, and removes
2420 *	it from the ppAllContexts array.
2421 */
2422static int
2423RecordDeleteContext(void *value, XID id)
2424{
2425    int i;
2426    RecordContextPtr pContext = (RecordContextPtr) value;
2427    RecordClientsAndProtocolPtr pRCAP;
2428
2429    RecordDisableContext(pContext);
2430
2431    /*  Remove all the clients from all the RCAPs.
2432     *  As a result, the RCAPs will be freed.
2433     */
2434
2435    while ((pRCAP = pContext->pListOfRCAP)) {
2436        int numClients = pRCAP->numClients;
2437
2438        /* when the last client is deleted, the RCAP will go away. */
2439        while (numClients--) {
2440            RecordDeleteClientFromRCAP(pRCAP, numClients);
2441        }
2442    }
2443
2444    /* remove context from AllContexts list */
2445
2446    if (-1 != (i = RecordFindContextOnAllContexts(pContext))) {
2447        ppAllContexts[i] = ppAllContexts[numContexts - 1];
2448        if (--numContexts == 0) {
2449            free(ppAllContexts);
2450            ppAllContexts = NULL;
2451        }
2452    }
2453    free(pContext);
2454
2455    return Success;
2456}                               /* RecordDeleteContext */
2457
2458static int
2459ProcRecordFreeContext(ClientPtr client)
2460{
2461    RecordContextPtr pContext;
2462
2463    REQUEST(xRecordFreeContextReq);
2464
2465    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2466    VERIFY_CONTEXT(pContext, stuff->context, client);
2467    FreeResource(stuff->context, RT_NONE);
2468    return Success;
2469}                               /* ProcRecordFreeContext */
2470
2471static int
2472ProcRecordDispatch(ClientPtr client)
2473{
2474    REQUEST(xReq);
2475
2476    switch (stuff->data) {
2477    case X_RecordQueryVersion:
2478        return ProcRecordQueryVersion(client);
2479    case X_RecordCreateContext:
2480        return ProcRecordCreateContext(client);
2481    case X_RecordRegisterClients:
2482        return ProcRecordRegisterClients(client);
2483    case X_RecordUnregisterClients:
2484        return ProcRecordUnregisterClients(client);
2485    case X_RecordGetContext:
2486        return ProcRecordGetContext(client);
2487    case X_RecordEnableContext:
2488        return ProcRecordEnableContext(client);
2489    case X_RecordDisableContext:
2490        return ProcRecordDisableContext(client);
2491    case X_RecordFreeContext:
2492        return ProcRecordFreeContext(client);
2493    default:
2494        return BadRequest;
2495    }
2496}                               /* ProcRecordDispatch */
2497
2498static int _X_COLD
2499SProcRecordQueryVersion(ClientPtr client)
2500{
2501    REQUEST(xRecordQueryVersionReq);
2502
2503    swaps(&stuff->length);
2504    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
2505    swaps(&stuff->majorVersion);
2506    swaps(&stuff->minorVersion);
2507    return ProcRecordQueryVersion(client);
2508}                               /* SProcRecordQueryVersion */
2509
2510static int _X_COLD
2511SwapCreateRegister(ClientPtr client, xRecordRegisterClientsReq * stuff)
2512{
2513    int i;
2514    XID *pClientID;
2515
2516    swapl(&stuff->context);
2517    swapl(&stuff->nClients);
2518    swapl(&stuff->nRanges);
2519    pClientID = (XID *) &stuff[1];
2520    if (stuff->nClients >
2521        client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq))
2522        return BadLength;
2523    for (i = 0; i < stuff->nClients; i++, pClientID++) {
2524        swapl(pClientID);
2525    }
2526    if (stuff->nRanges >
2527        (client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq)
2528        - stuff->nClients) / bytes_to_int32(sz_xRecordRange))
2529        return BadLength;
2530    RecordSwapRanges((xRecordRange *) pClientID, stuff->nRanges);
2531    return Success;
2532}                               /* SwapCreateRegister */
2533
2534static int _X_COLD
2535SProcRecordCreateContext(ClientPtr client)
2536{
2537    REQUEST(xRecordCreateContextReq);
2538    int status;
2539
2540    swaps(&stuff->length);
2541    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
2542    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
2543        return status;
2544    return ProcRecordCreateContext(client);
2545}                               /* SProcRecordCreateContext */
2546
2547static int _X_COLD
2548SProcRecordRegisterClients(ClientPtr client)
2549{
2550    REQUEST(xRecordRegisterClientsReq);
2551    int status;
2552
2553    swaps(&stuff->length);
2554    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
2555    if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
2556        return status;
2557    return ProcRecordRegisterClients(client);
2558}                               /* SProcRecordRegisterClients */
2559
2560static int _X_COLD
2561SProcRecordUnregisterClients(ClientPtr client)
2562{
2563    REQUEST(xRecordUnregisterClientsReq);
2564
2565    swaps(&stuff->length);
2566    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
2567    swapl(&stuff->context);
2568    swapl(&stuff->nClients);
2569    SwapRestL(stuff);
2570    return ProcRecordUnregisterClients(client);
2571}                               /* SProcRecordUnregisterClients */
2572
2573static int _X_COLD
2574SProcRecordGetContext(ClientPtr client)
2575{
2576    REQUEST(xRecordGetContextReq);
2577
2578    swaps(&stuff->length);
2579    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2580    swapl(&stuff->context);
2581    return ProcRecordGetContext(client);
2582}                               /* SProcRecordGetContext */
2583
2584static int _X_COLD
2585SProcRecordEnableContext(ClientPtr client)
2586{
2587    REQUEST(xRecordEnableContextReq);
2588
2589    swaps(&stuff->length);
2590    REQUEST_SIZE_MATCH(xRecordEnableContextReq);
2591    swapl(&stuff->context);
2592    return ProcRecordEnableContext(client);
2593}                               /* SProcRecordEnableContext */
2594
2595static int _X_COLD
2596SProcRecordDisableContext(ClientPtr client)
2597{
2598    REQUEST(xRecordDisableContextReq);
2599
2600    swaps(&stuff->length);
2601    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2602    swapl(&stuff->context);
2603    return ProcRecordDisableContext(client);
2604}                               /* SProcRecordDisableContext */
2605
2606static int _X_COLD
2607SProcRecordFreeContext(ClientPtr client)
2608{
2609    REQUEST(xRecordFreeContextReq);
2610
2611    swaps(&stuff->length);
2612    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2613    swapl(&stuff->context);
2614    return ProcRecordFreeContext(client);
2615}                               /* SProcRecordFreeContext */
2616
2617static int _X_COLD
2618SProcRecordDispatch(ClientPtr client)
2619{
2620    REQUEST(xReq);
2621
2622    switch (stuff->data) {
2623    case X_RecordQueryVersion:
2624        return SProcRecordQueryVersion(client);
2625    case X_RecordCreateContext:
2626        return SProcRecordCreateContext(client);
2627    case X_RecordRegisterClients:
2628        return SProcRecordRegisterClients(client);
2629    case X_RecordUnregisterClients:
2630        return SProcRecordUnregisterClients(client);
2631    case X_RecordGetContext:
2632        return SProcRecordGetContext(client);
2633    case X_RecordEnableContext:
2634        return SProcRecordEnableContext(client);
2635    case X_RecordDisableContext:
2636        return SProcRecordDisableContext(client);
2637    case X_RecordFreeContext:
2638        return SProcRecordFreeContext(client);
2639    default:
2640        return BadRequest;
2641    }
2642}                               /* SProcRecordDispatch */
2643
2644/* RecordConnectionSetupInfo
2645 *
2646 * Arguments:
2647 *	pContext is an enabled context that specifies recording of
2648 *	  connection setup info.
2649 *	pci holds the connection setup info.
2650 *
2651 * Returns: nothing.
2652 *
2653 * Side Effects:
2654 *	The connection setup info is sent to the recording client.
2655 */
2656static void
2657RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec * pci)
2658{
2659    int prefixsize = SIZEOF(xConnSetupPrefix);
2660    int restsize = pci->prefix->length * 4;
2661
2662    if (pci->client->swapped) {
2663        char *pConnSetup = (char *) malloc(prefixsize + restsize);
2664
2665        if (!pConnSetup)
2666            return;
2667        SwapConnSetupPrefix(pci->prefix, (xConnSetupPrefix *) pConnSetup);
2668        SwapConnSetupInfo((char *) pci->setup,
2669                          (char *) (pConnSetup + prefixsize));
2670        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2671                               (void *) pConnSetup, prefixsize + restsize, 0,
2672                               0);
2673        free(pConnSetup);
2674    }
2675    else {
2676        /* don't alloc and copy as in the swapped case; just send the
2677         * data in two pieces
2678         */
2679        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2680                               (void *) pci->prefix, prefixsize, 0, restsize);
2681        RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2682                               (void *) pci->setup, restsize, 0,
2683                               /* continuation */ -1);
2684    }
2685}                               /* RecordConnectionSetupInfo */
2686
2687/* RecordDeleteContext
2688 *
2689 * Arguments:
2690 *	pcbl is &ClientStateCallback.
2691 *	nullata is NULL.
2692 *	calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
2693 *	which contains information about client state changes.
2694 *
2695 * Returns: nothing.
2696 *
2697 * Side Effects:
2698 *	If a new client has connected and any contexts have specified
2699 *	XRecordFutureClients, the new client is registered on those contexts.
2700 *	If any of those contexts specify recording of the connection setup
2701 *	info, it is recorded.
2702 *
2703 *	If an existing client has disconnected, it is deleted from any
2704 *	contexts that it was registered on.  If any of those contexts
2705 *	specified XRecordClientDied, they record a ClientDied protocol element.
2706 *	If the disconnectiong client happened to be the data connection of an
2707 *	enabled context, the context is disabled.
2708 */
2709
2710static void
2711RecordAClientStateChange(CallbackListPtr *pcbl, void *nulldata,
2712                         void *calldata)
2713{
2714    NewClientInfoRec *pci = (NewClientInfoRec *) calldata;
2715    int i;
2716    ClientPtr pClient = pci->client;
2717    RecordContextPtr *ppAllContextsCopy = NULL;
2718    int numContextsCopy = 0;
2719
2720    switch (pClient->clientState) {
2721    case ClientStateRunning:   /* new client */
2722        for (i = 0; i < numContexts; i++) {
2723            RecordClientsAndProtocolPtr pRCAP;
2724            RecordContextPtr pContext = ppAllContexts[i];
2725
2726            if ((pRCAP = RecordFindClientOnContext(pContext,
2727                                                   XRecordFutureClients, NULL)))
2728            {
2729                RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
2730                if (pContext->pRecordingClient && pRCAP->clientStarted)
2731                    RecordConnectionSetupInfo(pContext, pci);
2732            }
2733        }
2734        break;
2735
2736    case ClientStateGone:
2737    case ClientStateRetained:  /* client disconnected */
2738
2739        /* RecordDisableContext modifies contents of ppAllContexts. */
2740        if (!(numContextsCopy = numContexts))
2741            break;
2742        ppAllContextsCopy = xallocarray(numContextsCopy,
2743                                        sizeof(RecordContextPtr));
2744        assert(ppAllContextsCopy);
2745        memcpy(ppAllContextsCopy, ppAllContexts,
2746               numContextsCopy * sizeof(RecordContextPtr));
2747
2748        for (i = 0; i < numContextsCopy; i++) {
2749            RecordClientsAndProtocolPtr pRCAP;
2750            RecordContextPtr pContext = ppAllContextsCopy[i];
2751            int pos;
2752
2753            if (pContext->pRecordingClient == pClient)
2754                RecordDisableContext(pContext);
2755            if ((pRCAP = RecordFindClientOnContext(pContext,
2756                                                   pClient->clientAsMask,
2757                                                   &pos))) {
2758                if (pContext->pRecordingClient && pRCAP->clientDied)
2759                    RecordAProtocolElement(pContext, pClient,
2760                                           XRecordClientDied, NULL, 0, 0, 0);
2761                RecordDeleteClientFromRCAP(pRCAP, pos);
2762            }
2763        }
2764
2765        free(ppAllContextsCopy);
2766        break;
2767
2768    default:
2769        break;
2770    }                           /* end switch on client state */
2771}                               /* RecordAClientStateChange */
2772
2773/* RecordCloseDown
2774 *
2775 * Arguments:
2776 *	extEntry is the extension information for RECORD.
2777 *
2778 * Returns: nothing.
2779 *
2780 * Side Effects:
2781 *	Performs any cleanup needed by RECORD at server shutdown time.
2782 *
2783 */
2784static void
2785RecordCloseDown(ExtensionEntry * extEntry)
2786{
2787    DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2788}                               /* RecordCloseDown */
2789
2790/* RecordExtensionInit
2791 *
2792 * Arguments: none.
2793 *
2794 * Returns: nothing.
2795 *
2796 * Side Effects:
2797 *	Enables the RECORD extension if possible.
2798 */
2799void
2800RecordExtensionInit(void)
2801{
2802    ExtensionEntry *extentry;
2803
2804    RTContext = CreateNewResourceType(RecordDeleteContext, "RecordContext");
2805    if (!RTContext)
2806        return;
2807
2808    if (!dixRegisterPrivateKey(RecordClientPrivateKey, PRIVATE_CLIENT, 0))
2809        return;
2810
2811    ppAllContexts = NULL;
2812    numContexts = numEnabledContexts = numEnabledRCAPs = 0;
2813
2814    if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
2815        return;
2816
2817    extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
2818                            ProcRecordDispatch, SProcRecordDispatch,
2819                            RecordCloseDown, StandardMinorOpcode);
2820    if (!extentry) {
2821        DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2822        return;
2823    }
2824    SetResourceTypeErrorValue(RTContext,
2825                              extentry->errorBase + XRecordBadContext);
2826
2827}                               /* RecordExtensionInit */
2828