record.c 98.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

/*

Copyright 1995, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from The Open Group.

Author: David P. Wiggins, The Open Group

This work benefited from earlier work done by Martha Zimet of NCD
and Jim Haggerty of Metheus.

*/

35
36
37
38
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

39
40
#include "dixstruct.h"
#include "extnsionst.h"
41
#include "extinit.h"
42
#include <X11/extensions/recordproto.h>
43
#include "set.h"
Adam Jackson's avatar
Adam Jackson committed
44
#include "swaprep.h"
45
#include "inputstr.h"
Chris Dekter's avatar
Chris Dekter committed
46
#include "eventconvert.h"
47
#include "scrnintstr.h"
Chris Dekter's avatar
Chris Dekter committed
48

Kaleb Keithley Keithley's avatar
Kaleb Keithley Keithley committed
49
50
51
52
53
54
55
56
57
58
#include <stdio.h>
#include <assert.h>

#ifdef PANORAMIX
#include "globals.h"
#include "panoramiX.h"
#include "panoramiXsrv.h"
#include "cursor.h"
#endif

59
60
#include "protocol-versions.h"

61
static RESTYPE RTContext;       /* internal resource type for Record contexts */
62
63
64
65
66
67
68
69
70

/* How many bytes of protocol data to buffer in a context. Don't set to less
 * than 32.
 */
#define REPLY_BUF_SIZE 1024

/* Record Context structure */

typedef struct {
71
72
73
74
75
76
77
78
79
80
    XID id;                     /* resource id of context */
    ClientPtr pRecordingClient; /* client that has context enabled */
    struct _RecordClientsAndProtocolRec *pListOfRCAP;   /* all registered info */
    ClientPtr pBufClient;       /* client whose protocol is in replyBuffer */
    unsigned int continuedReply:1;      /* recording a reply that is split up? */
    char elemHeaders;           /* element header flags (time/seq no.) */
    char bufCategory;           /* category of protocol in replyBuffer */
    int numBufBytes;            /* number of bytes in replyBuffer */
    char replyBuffer[REPLY_BUF_SIZE];   /* buffered recorded protocol */
    int inFlush;                /*  are we inside RecordFlushReplyBuffer */
81
82
83
84
85
86
87
} RecordContextRec, *RecordContextPtr;

/*  RecordMinorOpRec - to hold minor opcode selections for extension requests
 *  and replies
 */

typedef union {
88
89
90
91
92
    int count;                  /* first element of array: how many "major" structs to follow */
    struct {                    /* rest of array elements are this */
        short first;            /* first major opcode */
        short last;             /* last major opcode */
        RecordSetPtr pMinOpSet; /*  minor opcode set for above major range */
93
94
95
    } major;
} RecordMinorOpRec, *RecordMinorOpPtr;

Peter Hutterer's avatar
Peter Hutterer committed
96
/*  RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
97
98
99
100
101
102
103
 *  protocol selections passed in a single CreateContext or RegisterClients.
 *  Generally, a context will have one of these from the create and an
 *  additional one for each RegisterClients.  RCAPs are freed when all their
 *  clients are unregistered.
 */

typedef struct _RecordClientsAndProtocolRec {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    RecordContextPtr pContext;  /* context that owns this RCAP */
    struct _RecordClientsAndProtocolRec *pNextRCAP;     /* next RCAP on context */
    RecordSetPtr pRequestMajorOpSet;    /* requests to record */
    RecordMinorOpPtr pRequestMinOpInfo; /* extension requests to record */
    RecordSetPtr pReplyMajorOpSet;      /* replies to record */
    RecordMinorOpPtr pReplyMinOpInfo;   /* extension replies to record */
    RecordSetPtr pDeviceEventSet;       /* device events to record */
    RecordSetPtr pDeliveredEventSet;    /* delivered events to record */
    RecordSetPtr pErrorSet;     /* errors to record */
    XID *pClientIDs;            /* array of clients to record */
    short numClients;           /* number of clients in pClientIDs */
    short sizeClients;          /* size of pClientIDs array */
    unsigned int clientStarted:1;       /* record new client connections? */
    unsigned int clientDied:1;  /* record client disconnections? */
    unsigned int clientIDsSeparatelyAllocated:1;        /* pClientIDs malloced? */
119
120
121
122
123
124
125
126
127
128
129
130
131
132
} RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;

/* how much bigger to make pRCAP->pClientIDs when reallocing */
#define CLIENT_ARRAY_GROWTH_INCREMENT 4

/* counts the total number of RCAPs belonging to enabled contexts. */
static int numEnabledRCAPs;

/*  void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
 *  In the spirit of the VERIFY_* macros in dix.h, this macro fills in
 *  the context pointer if the given ID is a valid Record Context, else it
 *  returns an error.
 */
#define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
133
    int rc = dixLookupResourceByType((void **)&(_pContext), _contextid, \
134
135
                                     RTContext, _client, DixUseAccess); \
    if (rc != Success) \
136
	return rc; \
137
138
}

139
140
static int RecordDeleteContext(void     *value,
                               XID      id);
141
142
143
144
145
146
147
148

/***************************************************************************/

/* client private stuff */

/*  To make declarations less obfuscated, have a typedef for a pointer to a
 *  Proc function.
 */
149
150
typedef int (*ProcFunctionPtr) (ClientPtr       /*pClient */
    );
151
152
153
154
155
156

/* Record client private.  Generally a client only has one of these if
 * any of its requests are being recorded.
 */
typedef struct {
/* ptr to client's proc vector before Record stuck its nose in */
157
158
    ProcFunctionPtr *originalVector;

159
160
161
/* proc vector with pointers for recorded requests redirected to the
 * function RecordARequest
 */
162
    ProcFunctionPtr recordVector[256];
163
164
} RecordClientPrivateRec, *RecordClientPrivatePtr;

165
static DevPrivateKeyRec RecordClientPrivateKeyRec;
166

167
#define RecordClientPrivateKey (&RecordClientPrivateKeyRec)
168
169
170
171
172

/*  RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
 *  gets the client private of the given client.  Syntactic sugar.
 */
#define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
173
    dixLookupPrivate(&(_pClient)->devPrivates, RecordClientPrivateKey)
174
175
176
177
178
179
180

/***************************************************************************/

/* global list of all contexts */

static RecordContextPtr *ppAllContexts;

181
static int numContexts;         /* number of contexts in ppAllContexts */
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

/* number of currently enabled contexts.  All enabled contexts are bunched
 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
 * past disabled contexts.
 */
static int numEnabledContexts;

/* RecordFindContextOnAllContexts
 *
 * Arguments:
 *	pContext is the context to search for.
 *
 * Returns:
 *	The index into the array ppAllContexts at which pContext is stored.
 *	If pContext is not found in ppAllContexts, returns -1.
 *
 * Side Effects: none.
 */
static int
Adam Jackson's avatar
Adam Jackson committed
202
RecordFindContextOnAllContexts(RecordContextPtr pContext)
203
204
205
206
{
    int i;

    assert(numContexts >= numEnabledContexts);
207
208
209
    for (i = 0; i < numContexts; i++) {
        if (ppAllContexts[i] == pContext)
            return i;
210
211
    }
    return -1;
212
}                               /* RecordFindContextOnAllContexts */
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

/***************************************************************************/

/* RecordFlushReplyBuffer
 *
 * Arguments:
 *	pContext is the context to flush.
 *	data1 is a pointer to additional data, and len1 is its length in bytes.
 *	data2 is a pointer to additional data, and len2 is its length in bytes.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	If the context is enabled, any buffered (recorded) protocol is written
 *	to the recording client, and the number of buffered bytes is set to
 *	zero.  If len1 is not zero, data1/len1 are then written to the
 *	recording client, and similarly for data2/len2 (written after
 *	data1/len1).
 */
static void
233
RecordFlushReplyBuffer(RecordContextPtr pContext,
234
                       void *data1, int len1, void *data2, int len2)
235
{
236
237
238
    if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone ||
        pContext->inFlush)
        return;
239
    ++pContext->inFlush;
240
    if (pContext->numBufBytes)
241
        WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
242
                      pContext->replyBuffer);
243
244
    pContext->numBufBytes = 0;
    if (len1)
245
        WriteToClient(pContext->pRecordingClient, len1, data1);
246
    if (len2)
247
        WriteToClient(pContext->pRecordingClient, len2, data2);
248
    --pContext->inFlush;
249
}                               /* RecordFlushReplyBuffer */
250
251
252
253
254
255
256
257
258

/* RecordAProtocolElement
 *
 * Arguments:
 *	pContext is the context that is recording a protocol element.
 *	pClient is the client whose protocol is being recorded.  For
 *	  device events and EndOfData, pClient is NULL.
 *	category is the category of the protocol element, as defined
 *	  by the RECORD spec.
259
260
261
 *	data is a pointer to the protocol data, and datalen - padlen
 *	  is its length in bytes.
 *	padlen is the number of pad bytes from a zeroed array.
262
 *	futurelen is the number of bytes that will be sent in subsequent
Peter Hutterer's avatar
Peter Hutterer committed
263
 *	  calls to this function to complete this protocol element.
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
 *	  In those subsequent calls, futurelen will be -1 to indicate
 *	  that the current data is a continuation of the same protocol
 *	  element.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	The context may be flushed.  The new protocol element will be
 *	added to the context's protocol buffer with appropriate element
 *	headers prepended (sequence number and timestamp).  If the data
 *	is continuation data (futurelen == -1), element headers won't
 *	be added.  If the protocol element and headers won't fit in
 *	the context's buffer, it is sent directly to the recording
 *	client (after any buffered data).
 */
static void
280
RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
281
                       int category, void *data, int datalen, int padlen,
282
                       int futurelen)
283
284
285
286
{
    CARD32 elemHeaderData[2];
    int numElemHeaders = 0;
    Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
Kaleb Keithley Keithley's avatar
Kaleb Keithley Keithley committed
287
    CARD32 serverTime = 0;
288
289
290
    Bool gotServerTime = FALSE;
    int replylen;

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
    if (futurelen >= 0) {       /* start of new protocol element */
        xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
            pContext->replyBuffer;

        if (pContext->pBufClient != pClient ||
            pContext->bufCategory != category) {
            RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
            pContext->pBufClient = pClient;
            pContext->bufCategory = category;
        }

        if (!pContext->numBufBytes) {
            serverTime = GetTimeInMillis();
            gotServerTime = TRUE;
            pRep->type = X_Reply;
            pRep->category = category;
            pRep->sequenceNumber = pContext->pRecordingClient->sequence;
            pRep->length = 0;
            pRep->elementHeader = pContext->elemHeaders;
            pRep->serverTime = serverTime;
            if (pClient) {
                pRep->clientSwapped =
                    (pClient->swapped != recordingClientSwapped);
                pRep->idBase = pClient->clientAsMask;
                pRep->recordedSequenceNumber = pClient->sequence;
            }
            else {              /* it's a device event, StartOfData, or EndOfData */

                pRep->clientSwapped = (category != XRecordFromServer) &&
                    recordingClientSwapped;
                pRep->idBase = 0;
                pRep->recordedSequenceNumber = 0;
            }

            if (recordingClientSwapped) {
                swaps(&pRep->sequenceNumber);
                swapl(&pRep->length);
                swapl(&pRep->idBase);
                swapl(&pRep->serverTime);
                swapl(&pRep->recordedSequenceNumber);
            }
            pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
        }

        /* generate element headers if needed */

        if (((pContext->elemHeaders & XRecordFromClientTime)
             && category == XRecordFromClient)
            || ((pContext->elemHeaders & XRecordFromServerTime)
                && category == XRecordFromServer)) {
            if (gotServerTime)
                elemHeaderData[numElemHeaders] = serverTime;
            else
                elemHeaderData[numElemHeaders] = GetTimeInMillis();
            if (recordingClientSwapped)
                swapl(&elemHeaderData[numElemHeaders]);
            numElemHeaders++;
        }

        if ((pContext->elemHeaders & XRecordFromClientSequence)
            && (category == XRecordFromClient || category == XRecordClientDied)) {
            elemHeaderData[numElemHeaders] = pClient->sequence;
            if (recordingClientSwapped)
                swapl(&elemHeaderData[numElemHeaders]);
            numElemHeaders++;
        }

        /* adjust reply length */

        replylen = pRep->length;
        if (recordingClientSwapped)
            swapl(&replylen);
        replylen += numElemHeaders + bytes_to_int32(datalen) +
364
            bytes_to_int32(futurelen);
365
366
367
368
        if (recordingClientSwapped)
            swapl(&replylen);
        pRep->length = replylen;
    }                           /* end if not continued reply */
369
370
371
372
373

    numElemHeaders *= 4;

    /* if space available >= space needed, buffer the data */

374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
    if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders) {
        if (numElemHeaders) {
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   elemHeaderData, numElemHeaders);
            pContext->numBufBytes += numElemHeaders;
        }
        if (datalen) {
            static char padBuffer[3];   /* as in FlushClient */

            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   data, datalen - padlen);
            pContext->numBufBytes += datalen - padlen;
            memcpy(pContext->replyBuffer + pContext->numBufBytes,
                   padBuffer, padlen);
            pContext->numBufBytes += padlen;
        }
390
    }
391
    else {
392
393
        RecordFlushReplyBuffer(pContext, (void *) elemHeaderData,
                               numElemHeaders, (void *) data,
394
                               datalen - padlen);
395
    }
396
}                               /* RecordAProtocolElement */
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

/* RecordFindClientOnContext
 *
 * Arguments:
 *	pContext is the context to search.
 *	clientspec is the resource ID mask identifying the client to search
 *	  for, or XRecordFutureClients.
 *	pposition is a pointer to an int, or NULL.  See Returns.
 *
 * Returns:
 *	The RCAP on which clientspec was found, or NULL if not found on
 *	any RCAP on the given context.
 *	If pposition was not NULL and the returned RCAP is not NULL,
 *	*pposition will be set to the index into the returned the RCAP's
 *	pClientIDs array that holds clientspec.
 *
 * Side Effects: none.
 */
static RecordClientsAndProtocolPtr
416
417
RecordFindClientOnContext(RecordContextPtr pContext,
                          XID clientspec, int *pposition)
418
419
420
{
    RecordClientsAndProtocolPtr pRCAP;

421
422
423
424
425
426
427
428
429
430
    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
        int i;

        for (i = 0; i < pRCAP->numClients; i++) {
            if (pRCAP->pClientIDs[i] == clientspec) {
                if (pposition)
                    *pposition = i;
                return pRCAP;
            }
        }
431
432
    }
    return NULL;
433
}                               /* RecordFindClientOnContext */
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

/* RecordABigRequest
 *
 * Arguments:
 *	pContext is the recording context.
 *	client is the client being recorded.
 *	stuff is a pointer to the big request of client (see the Big Requests
 *	extension for details.)
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	The big request is recorded with the correct length field re-inserted.
 *	
 * Note: this function exists mainly to make RecordARequest smaller.
 */
static void
451
RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq * stuff)
452
453
454
455
456
457
458
459
460
461
462
463
464
{
    CARD32 bigLength;
    int bytesLeft;

    /* note: client->req_len has been frobbed by ReadRequestFromClient
     * (os/io.c) to discount the extra 4 bytes taken by the extended length
     * field in a big request.  The actual request length to record is
     * client->req_len + 1 (measured in CARD32s).
     */

    /* record the request header */
    bytesLeft = client->req_len << 2;
    RecordAProtocolElement(pContext, client, XRecordFromClient,
465
                           (void *) stuff, SIZEOF(xReq), 0, bytesLeft);
466
467

    /* reinsert the extended length field that was squished out */
468
    bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
469
    if (client->swapped)
470
        swapl(&bigLength);
471
    RecordAProtocolElement(pContext, client, XRecordFromClient,
472
                           (void *) &bigLength, sizeof(bigLength), 0,
473
                           /* continuation */ -1);
474
475
476
477
    bytesLeft -= sizeof(bigLength);

    /* record the rest of the request after the length */
    RecordAProtocolElement(pContext, client, XRecordFromClient,
478
                           (void *) (stuff + 1), bytesLeft, 0,
479
480
                           /* continuation */ -1);
}                               /* RecordABigRequest */
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

/* RecordARequest
 *
 * Arguments:
 *	client is a client that the server has dispatched a request to by
 *	calling client->requestVector[request opcode] .
 *	The request is in client->requestBuffer.
 *
 * Returns:
 *	Whatever is returned by the "real" Proc function for this request.
 *	The "real" Proc function is the function that was in
 *	client->requestVector[request opcode]  before it was replaced by
 *	RecordARequest.  (See the function RecordInstallHooks.)
 *
 * Side Effects:
 *	The request is recorded by all contexts that have registered this
 *	request for this client.  The real Proc function is called.
 */
static int
Adam Jackson's avatar
Adam Jackson committed
500
RecordARequest(ClientPtr client)
501
502
503
504
505
{
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int i;
    RecordClientPrivatePtr pClientPriv;
506

507
508
509
510
    REQUEST(xReq);
    int majorop;

    majorop = stuff->reqType;
511
512
513
514
515
516
517
518
519
520
521
    for (i = 0; i < numEnabledContexts; i++) {
        pContext = ppAllContexts[i];
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
        if (pRCAP && pRCAP->pRequestMajorOpSet &&
            RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop)) {
            if (majorop <= 127) {       /* core request */

                if (stuff->length == 0)
                    RecordABigRequest(pContext, client, stuff);
                else
                    RecordAProtocolElement(pContext, client, XRecordFromClient,
522
                                           (void *) stuff,
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
                                           client->req_len << 2, 0, 0);
            }
            else {              /* extension, check minor opcode */

                int minorop = client->minorOp;
                int numMinOpInfo;
                RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;

                assert(pMinorOpInfo);
                numMinOpInfo = pMinorOpInfo->count;
                pMinorOpInfo++;
                assert(numMinOpInfo);
                for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
                    if (majorop >= pMinorOpInfo->major.first &&
                        majorop <= pMinorOpInfo->major.last &&
                        RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
                                            minorop)) {
                        if (stuff->length == 0)
                            RecordABigRequest(pContext, client, stuff);
                        else
                            RecordAProtocolElement(pContext, client,
                                                   XRecordFromClient,
545
                                                   (void *) stuff,
546
547
548
549
550
551
552
                                                   client->req_len << 2, 0, 0);
                        break;
                    }
                }               /* end for each minor op info */
            }                   /* end extension request */
        }                       /* end this RCAP wants this major opcode */
    }                           /* end for each context */
553
554
    pClientPriv = RecordClientPrivate(client);
    assert(pClientPriv);
555
556
    return (*pClientPriv->originalVector[majorop]) (client);
}                               /* RecordARequest */
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577

/* RecordAReply
 *
 * Arguments:
 *	pcbl is &ReplyCallback.
 *	nulldata is NULL.
 *	calldata is a pointer to a ReplyInfoRec (include/os.h)
 *	  which provides information about replies that are being sent
 *	  to clients.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	The reply is recorded by all contexts that have registered this
 *	reply type for this client.  If more data belonging to the same
 *	reply is expected, and if the reply is being recorded by any
 *	context, pContext->continuedReply is set to 1.
 *	If pContext->continuedReply was already 1 and this is the last
 *	chunk of data belonging to this reply, it is set to 0.
 */
static void
578
RecordAReply(CallbackListPtr *pcbl, void *nulldata, void *calldata)
579
580
581
582
{
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
    int eci;
583
    ReplyInfoRec *pri = (ReplyInfoRec *) calldata;
584
585
    ClientPtr client = pri->client;

586
587
588
589
590
591
592
593
    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask, NULL);
        if (pRCAP) {
            int majorop = client->majorOp;

            if (pContext->continuedReply) {
                RecordAProtocolElement(pContext, client, XRecordFromServer,
594
                                       (void *) pri->replyData,
595
596
597
598
599
600
601
602
603
                                       pri->dataLenBytes, pri->padBytes,
                                       /* continuation */ -1);
                if (!pri->bytesRemaining)
                    pContext->continuedReply = 0;
            }
            else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
                     RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop)) {
                if (majorop <= 127) {   /* core reply */
                    RecordAProtocolElement(pContext, client, XRecordFromServer,
604
                                           (void *) pri->replyData,
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
                                           pri->dataLenBytes, 0,
                                           pri->bytesRemaining);
                    if (pri->bytesRemaining)
                        pContext->continuedReply = 1;
                }
                else {          /* extension, check minor opcode */

                    int minorop = client->minorOp;
                    int numMinOpInfo;
                    RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;

                    assert(pMinorOpInfo);
                    numMinOpInfo = pMinorOpInfo->count;
                    pMinorOpInfo++;
                    assert(numMinOpInfo);
                    for (; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++) {
                        if (majorop >= pMinorOpInfo->major.first &&
                            majorop <= pMinorOpInfo->major.last &&
                            RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
                                                minorop)) {
                            RecordAProtocolElement(pContext, client,
                                                   XRecordFromServer,
627
                                                   (void *) pri->replyData,
628
629
630
631
632
633
634
635
636
637
638
639
                                                   pri->dataLenBytes, 0,
                                                   pri->bytesRemaining);
                            if (pri->bytesRemaining)
                                pContext->continuedReply = 1;
                            break;
                        }
                    }           /* end for each minor op info */
                }               /* end extension reply */
            }                   /* end continued reply vs. start of reply */
        }                       /* end client is registered on this context */
    }                           /* end for each context */
}                               /* RecordAReply */
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656

/* RecordADeliveredEventOrError
 *
 * Arguments:
 *	pcbl is &EventCallback.
 *	nulldata is NULL.
 *	calldata is a pointer to a EventInfoRec (include/dix.h)
 *	  which provides information about events that are being sent
 *	  to clients.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	The event or error is recorded by all contexts that have registered
 *	it for this client.
 */
static void
657
658
RecordADeliveredEventOrError(CallbackListPtr *pcbl, void *nulldata,
                             void *calldata)
659
{
660
    EventInfoRec *pei = (EventInfoRec *) calldata;
661
662
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
663
    int eci;                    /* enabled context index */
664
665
    ClientPtr pClient = pei->client;

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
                                          NULL);
        if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet)) {
            int ev;             /* event index */
            xEvent *pev = pei->events;

            for (ev = 0; ev < pei->count; ev++, pev++) {
                int recordit = 0;

                if (pRCAP->pErrorSet) {
                    recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
                                                   ((xError *) (pev))->
                                                   errorCode);
                }
                else if (pRCAP->pDeliveredEventSet) {
                    recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
                                                   pev->u.u.type & 0177);
                }
                if (recordit) {
                    xEvent swappedEvent;
                    xEvent *pEvToRecord = pev;

                    if (pClient->swapped) {
                        (*EventSwapVector[pev->u.u.type & 0177])
                            (pev, &swappedEvent);
                        pEvToRecord = &swappedEvent;

                    }
                    RecordAProtocolElement(pContext, pClient,
                                           XRecordFromServer, pEvToRecord,
                                           SIZEOF(xEvent), 0, 0);
                }
            }                   /* end for each event */
        }                       /* end this client is on this context */
    }                           /* end for each enabled context */
}                               /* RecordADeliveredEventOrError */
704

Chris Dekter's avatar
Chris Dekter committed
705
706
static void
RecordSendProtocolEvents(RecordClientsAndProtocolPtr pRCAP,
707
                         RecordContextPtr pContext, xEvent *pev, int count)
Chris Dekter's avatar
Chris Dekter committed
708
{
709
710
711
712
713
714
715
    int ev;                     /* event index */

    for (ev = 0; ev < count; ev++, pev++) {
        if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet, pev->u.u.type & 0177)) {
            xEvent swappedEvent;
            xEvent *pEvToRecord = pev;

Chris Dekter's avatar
Chris Dekter committed
716
#ifdef PANORAMIX
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
            xEvent shiftedEvent;

            if (!noPanoramiXExtension &&
                (pev->u.u.type == MotionNotify ||
                 pev->u.u.type == ButtonPress ||
                 pev->u.u.type == ButtonRelease ||
                 pev->u.u.type == KeyPress || pev->u.u.type == KeyRelease)) {
                int scr = XineramaGetCursorScreen(inputInfo.pointer);

                memcpy(&shiftedEvent, pev, sizeof(xEvent));
                shiftedEvent.u.keyButtonPointer.rootX +=
                    screenInfo.screens[scr]->x - screenInfo.screens[0]->x;
                shiftedEvent.u.keyButtonPointer.rootY +=
                    screenInfo.screens[scr]->y - screenInfo.screens[0]->y;
                pEvToRecord = &shiftedEvent;
            }
#endif                          /* PANORAMIX */

            if (pContext->pRecordingClient->swapped) {
                (*EventSwapVector[pEvToRecord->u.u.type & 0177])
                    (pEvToRecord, &swappedEvent);
                pEvToRecord = &swappedEvent;
            }

            RecordAProtocolElement(pContext, NULL,
                                   XRecordFromServer, pEvToRecord,
                                   SIZEOF(xEvent), 0, 0);
            /* make sure device events get flushed in the absence
             * of other client activity
             */
            SetCriticalOutputPending();
        }
    }                           /* end for each event */

}                               /* RecordADeviceEvent */
Chris Dekter's avatar
Chris Dekter committed
752

753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
/* RecordADeviceEvent
 *
 * Arguments:
 *	pcbl is &DeviceEventCallback.
 *	nulldata is NULL.
 *	calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
 *	  which provides information about device events that occur.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	The device event is recorded by all contexts that have registered
 *	it for this client.
 */
static void
768
RecordADeviceEvent(CallbackListPtr *pcbl, void *nulldata, void *calldata)
769
{
770
    DeviceEventInfoRec *pei = (DeviceEventInfoRec *) calldata;
771
772
    RecordContextPtr pContext;
    RecordClientsAndProtocolPtr pRCAP;
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
    int eci;                    /* enabled context index */

    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
        for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP) {
            if (pRCAP->pDeviceEventSet) {
                int count;
                xEvent *xi_events = NULL;

                /* TODO check return values */
                if (IsMaster(pei->device)) {
                    xEvent *core_events;

                    EventToCore(pei->event, &core_events, &count);
                    RecordSendProtocolEvents(pRCAP, pContext, core_events,
788
                                             count);
789
790
791
792
793
794
795
796
797
                    free(core_events);
                }

                EventToXI(pei->event, &xi_events, &count);
                RecordSendProtocolEvents(pRCAP, pContext, xi_events, count);
                free(xi_events);
            }                   /* end this RCAP selects device events */
        }                       /* end for each RCAP on this context */
    }                           /* end for each enabled context */
Chris Dekter's avatar
Chris Dekter committed
798
}
799
800
801
802
803
804
805
806
807
808
809
810
811
812

/* RecordFlushAllContexts
 *
 * Arguments:
 *	pcbl is &FlushCallback.
 *	nulldata and calldata are NULL.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	All buffered reply data of all enabled contexts is written to
 *	the recording clients.
 */
static void
813
RecordFlushAllContexts(CallbackListPtr *pcbl,
814
                       void *nulldata, void *calldata)
815
{
816
    int eci;                    /* enabled context index */
817
818
    RecordContextPtr pContext;

819
820
    for (eci = 0; eci < numEnabledContexts; eci++) {
        pContext = ppAllContexts[eci];
821

822
823
824
825
826
827
828
829
830
        /* In most cases we leave it to RecordFlushReplyBuffer to make
         * this check, but this function could be called very often, so we
         * check before calling hoping to save the function call cost
         * most of the time.
         */
        if (pContext->numBufBytes)
            RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
    }
}                               /* RecordFlushAllContexts */
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850

/* RecordInstallHooks
 *
 * Arguments:
 *	pRCAP is an RCAP on an enabled or being-enabled context.
 *	oneclient can be zero or the resource ID mask identifying a client.
 *
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 *
 * Side Effects:
 *	Recording hooks needed by RCAP are installed.
 *	If oneclient is zero, recording hooks needed for all clients and
 *	protocol on the RCAP are installed.  If oneclient is non-zero,
 *	only those hooks needed for the specified client are installed.
 *	
 *	Client requestVectors may be altered.  numEnabledRCAPs will be
 *	incremented if oneclient == 0.  Callbacks may be added to
 *	various callback lists.
 */
static int
Adam Jackson's avatar
Adam Jackson committed
851
RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
852
853
854
855
856
{
    int i = 0;
    XID client;

    if (oneclient)
857
        client = oneclient;
858
    else
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;

    while (client) {
        if (client != XRecordFutureClients) {
            if (pRCAP->pRequestMajorOpSet) {
                RecordSetIteratePtr pIter = NULL;
                RecordSetInterval interval;
                ClientPtr pClient = clients[CLIENT_ID(client)];

                if (pClient && !RecordClientPrivate(pClient)) {
                    RecordClientPrivatePtr pClientPriv;

                    /* no Record proc vector; allocate one */
                    pClientPriv = (RecordClientPrivatePtr)
                        malloc(sizeof(RecordClientPrivateRec));
                    if (!pClientPriv)
                        return BadAlloc;
                    /* copy old proc vector to new */
                    memcpy(pClientPriv->recordVector, pClient->requestVector,
                           sizeof(pClientPriv->recordVector));
                    pClientPriv->originalVector = pClient->requestVector;
                    dixSetPrivate(&pClient->devPrivates,
                                  RecordClientPrivateKey, pClientPriv);
                    pClient->requestVector = pClientPriv->recordVector;
                }
                while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
                                                 pIter, &interval))) {
                    unsigned int j;

                    for (j = interval.first; j <= interval.last; j++)
                        pClient->requestVector[j] = RecordARequest;
                }
            }
        }
        if (oneclient)
            client = 0;
        else
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
897
898
899
    }

    assert(numEnabledRCAPs >= 0);
900
901
902
903
904
905
906
907
908
909
910
911
912
    if (!oneclient && ++numEnabledRCAPs == 1) { /* we're enabling the first context */
        if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
            return BadAlloc;
        if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
            return BadAlloc;
        if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
            return BadAlloc;
        if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
            return BadAlloc;
        /* Alternate context flushing scheme: delete the line above
         * and call RegisterBlockAndWakeupHandlers here passing
         * RecordFlushAllContexts.  Is this any better?
         */
913
914
    }
    return Success;
915
}                               /* RecordInstallHooks */
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935

/* RecordUninstallHooks
 *
 * Arguments:
 *	pRCAP is an RCAP on an enabled or being-disabled context.
 *	oneclient can be zero or the resource ID mask identifying a client.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	Recording hooks needed by RCAP may be uninstalled.
 *	If oneclient is zero, recording hooks needed for all clients and
 *	protocol on the RCAP may be uninstalled.  If oneclient is non-zero,
 *	only those hooks needed for the specified client may be uninstalled.
 *	
 *	Client requestVectors may be altered.  numEnabledRCAPs will be
 *	decremented if oneclient == 0.  Callbacks may be deleted from
 *	various callback lists.
 */
static void
Adam Jackson's avatar
Adam Jackson committed
936
RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
937
938
939
940
941
{
    int i = 0;
    XID client;

    if (oneclient)
942
        client = oneclient;
943
    else
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
        client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;

    while (client) {
        if (client != XRecordFutureClients) {
            if (pRCAP->pRequestMajorOpSet) {
                ClientPtr pClient = clients[CLIENT_ID(client)];
                int c;
                Bool otherRCAPwantsProcVector = FALSE;
                RecordClientPrivatePtr pClientPriv = NULL;

                assert(pClient);
                pClientPriv = RecordClientPrivate(pClient);
                assert(pClientPriv);
                memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
                       sizeof(pClientPriv->recordVector));

                for (c = 0; c < numEnabledContexts; c++) {
                    RecordClientsAndProtocolPtr pOtherRCAP;
                    RecordContextPtr pContext = ppAllContexts[c];

                    if (pContext == pRCAP->pContext)
                        continue;
                    pOtherRCAP = RecordFindClientOnContext(pContext, client,
                                                           NULL);
                    if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet) {
                        RecordSetIteratePtr pIter = NULL;
                        RecordSetInterval interval;

                        otherRCAPwantsProcVector = TRUE;
                        while ((pIter =
                                RecordIterateSet(pOtherRCAP->pRequestMajorOpSet,
                                                 pIter, &interval))) {
                            unsigned int j;

                            for (j = interval.first; j <= interval.last; j++)
                                pClient->requestVector[j] = RecordARequest;
                        }
                    }
                }
                if (!otherRCAPwantsProcVector) {        /* nobody needs it, so free it */
                    pClient->requestVector = pClientPriv->originalVector;
                    dixSetPrivate(&pClient->devPrivates,
                                  RecordClientPrivateKey, NULL);
                    free(pClientPriv);
                }
            }                   /* end if this RCAP specifies any requests */
        }                       /* end if not future clients */
        if (oneclient)
            client = 0;
        else
            client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
995
996
997
    }

    assert(numEnabledRCAPs >= 1);
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
    if (!oneclient && --numEnabledRCAPs == 0) { /* we're disabling the last context */
        DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
        DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
        DeleteCallback(&ReplyCallback, RecordAReply, NULL);
        DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
        /* Alternate context flushing scheme: delete the line above
         * and call RemoveBlockAndWakeupHandlers here passing
         * RecordFlushAllContexts.  Is this any better?
         */
        /* Having deleted the callback, call it one last time. -gildea */
        RecordFlushAllContexts(&FlushCallback, NULL, NULL);
1009
    }
1010
}                               /* RecordUninstallHooks */
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022

/* RecordDeleteClientFromRCAP
 *
 * Arguments:
 *	pRCAP is an RCAP to delete the client from.
 *	position is the index into the array pRCAP->pClientIDs of the
 *	client to delete.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	Recording hooks needed by client will be uninstalled if the context
Peter Hutterer's avatar
Peter Hutterer committed
1023
1024
 *	is enabled.  The designated client will be removed from the
 *	pRCAP->pClientIDs array.  If it was the only client on the RCAP,
1025
1026
1027
1028
 *	the RCAP is removed from the context and freed.  (Invariant: RCAPs
 *	have at least one client.)
 */
static void
Adam Jackson's avatar
Adam Jackson committed
1029
RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
1030
1031
{
    if (pRCAP->pContext->pRecordingClient)
1032
        RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
1033
    if (position != pRCAP->numClients - 1)
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
        pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
    if (--pRCAP->numClients == 0) {     /* no more clients; remove RCAP from context's list */
        RecordContextPtr pContext = pRCAP->pContext;

        if (pContext->pRecordingClient)
            RecordUninstallHooks(pRCAP, 0);
        if (pContext->pListOfRCAP == pRCAP)
            pContext->pListOfRCAP = pRCAP->pNextRCAP;
        else {
            RecordClientsAndProtocolPtr prevRCAP;

            for (prevRCAP = pContext->pListOfRCAP;
                 prevRCAP->pNextRCAP != pRCAP; prevRCAP = prevRCAP->pNextRCAP);
            prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
        }
        /* free the RCAP */
        if (pRCAP->clientIDsSeparatelyAllocated)
            free(pRCAP->pClientIDs);
        free(pRCAP);
1053
    }
1054
}                               /* RecordDeleteClientFromRCAP */
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066

/* RecordAddClientToRCAP
 *
 * Arguments:
 *	pRCAP is an RCAP to add the client to.
 *	clientspec is the resource ID mask identifying a client, or
 *	  XRecordFutureClients.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	Recording hooks needed by client will be installed if the context
Peter Hutterer's avatar
Peter Hutterer committed
1067
 *	is enabled.  The designated client will be added to the
1068
1069
1070
1071
1072
 *	pRCAP->pClientIDs array, which may be realloced.
 *	pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
 *	is no more room to hold clients internal to the RCAP.
 */
static void
Adam Jackson's avatar
Adam Jackson committed
1073
RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
1074
{
1075
1076
    if (pRCAP->numClients == pRCAP->sizeClients) {
        if (pRCAP->clientIDsSeparatelyAllocated) {
1077
1078
1079
1080
            XID *pNewIDs =
                reallocarray(pRCAP->pClientIDs,
                             pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
                             sizeof(XID));
1081
1082
1083
1084
1085
1086
            if (!pNewIDs)
                return;
            pRCAP->pClientIDs = pNewIDs;
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
        }
        else {
1087
1088
1089
            XID *pNewIDs =
                xallocarray(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT,
                            sizeof(XID));
1090
1091
1092
1093
1094
1095
1096
            if (!pNewIDs)
                return;
            memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients * sizeof(XID));
            pRCAP->pClientIDs = pNewIDs;
            pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
            pRCAP->clientIDsSeparatelyAllocated = 1;
        }
1097
1098
1099
    }
    pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
    if (pRCAP->pContext->pRecordingClient)
1100
1101
        RecordInstallHooks(pRCAP, clientspec);
}                               /* RecordDeleteClientFromRCAP */
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116

/* RecordDeleteClientFromContext
 *
 * Arguments:
 *	pContext is the context to delete from.
 *	clientspec is the resource ID mask identifying a client, or
 *	  XRecordFutureClients.
 *
 * Returns: nothing.
 *
 * Side Effects:
 *	If clientspec is on any RCAP of the context, it is deleted from that
 *	RCAP.  (A given clientspec can only be on one RCAP of a context.)
 */
static void
Adam Jackson's avatar
Adam Jackson committed
1117
RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
1118
1119
1120
1121
{
    RecordClientsAndProtocolPtr pRCAP;
    int position;

Kaleb Keithley Keithley's avatar
Kaleb Keithley Keithley committed
1122
    if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
1123
1124
        RecordDeleteClientFromRCAP(pRCAP, position);
}                               /* RecordDeleteClientFromContext */
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138

/* RecordSanityCheckClientSpecifiers
 *
 * Arguments:
 *	clientspecs is an array of alleged CLIENTSPECs passed by the client.
 *	nspecs is the number of elements in clientspecs.
 *	errorspec, if non-zero, is the resource id base of a client that
 *	  must not appear in clienspecs.
 *
 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
 *
 * Side Effects: none.
 */
static int
1139
1140
RecordSanityCheckClientSpecifiers(ClientPtr client, XID *clientspecs,
                                  int nspecs, XID errorspec)
1141
1142
1143
{
    int i;
    int clientIndex;
1144
    int rc;
1145
    void *value;
1146

1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
    for (i = 0; i < nspecs; i++) {
        if (clientspecs[i] == XRecordCurrentClients ||
            clientspecs[i] == XRecordFutureClients ||
            clientspecs[i] == XRecordAllClients)
            continue;
        if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec))
            return BadMatch;
        clientIndex = CLIENT_ID(clientspecs[i]);
        if (clientIndex && clients[clientIndex] &&
            clients[clientIndex]->clientState == ClientStateRunning) {
            if (clientspecs[i] == clients[clientIndex]->clientAsMask)
                continue;
1159
1160
1161
1162
            rc = dixLookupResourceByClass(&value, clientspecs[i], RC_ANY,
                                          client, DixGetAttrAccess);
            if (rc != Success)
                return rc;
1163
1164
1165
        }
        else
            return BadMatch;
1166
1167
    }
    return Success;
1168
}                               /* RecordSanityCheckClientSpecifiers */
1169
1170
1171
1172
1173
1174
1175

/* RecordCanonicalizeClientSpecifiers
 *
 * Arguments:
 *	pClientspecs is an array of CLIENTSPECs that have been sanity
 *	  checked.
 *	pNumClientspecs is a pointer to the number of elements in pClientspecs.
Peter Hutterer's avatar
Peter Hutterer committed
1176
1177
 *	excludespec, if non-zero, is the resource id base of a client that
 *	  should not be included in the expansion of XRecordAllClients or
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
 *	  XRecordCurrentClients.
 *
 * Returns:
 *	A pointer to an array of CLIENTSPECs that is the same as the
 *	passed array with the following modifications:
 *	  - all but the client id bits of resource IDs are stripped off.
 *	  - duplicates removed.
 *	  - XRecordAllClients expanded to a list of all currently connected
 *	    clients + XRecordFutureClients - excludespec (if non-zero)
 *	  - XRecordCurrentClients expanded to a list of all currently
 *	    connected clients - excludespec (if non-zero)
 *	The returned array may be the passed array modified in place, or
1190
 *	it may be an malloc'ed array.  The caller should keep a pointer to the
1191
1192
1193
1194
1195
1196
1197
1198
1199
 *	original array and free the returned array if it is different.
 *
 *	*pNumClientspecs is set to the number of elements in the returned
 *	array.
 *
 * Side Effects:
 *	pClientspecs may be modified in place.
 */
static XID *
1200
1201
RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs,
                                   XID excludespec)
1202
1203
1204
1205
1206
1207
1208
1209
{
    int i;
    int numClients = *pNumClientspecs;

    /*  first pass strips off the resource index bits, leaving just the
     *  client id bits.  This makes searching for a particular client simpler
     *  (and faster.)
     */
1210
1211
1212
1213
1214
    for (i = 0; i < numClients; i++) {
        XID cs = pClientspecs[i];

        if (cs > XRecordAllClients)
            pClientspecs[i] = CLIENT_BITS(cs);
1215
1216
    }

1217
1218
1219
    for (i = 0; i < numClients; i++) {
        if (pClientspecs[i] == XRecordAllClients || pClientspecs[i] == XRecordCurrentClients) { /* expand All/Current */
            int j, nc;
1220
            XID *pCanon = xallocarray(currentMaxClients + 1, sizeof(XID));
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250

            if (!pCanon)
                return NULL;
            for (nc = 0, j = 1; j < currentMaxClients; j++) {
                ClientPtr client = clients[j];

                if (client != NullClient &&
                    client->clientState == ClientStateRunning &&
                    client->clientAsMask != excludespec) {
                    pCanon[nc++] = client->clientAsMask;
                }
            }
            if (pClientspecs[i] == XRecordAllClients)
                pCanon[nc++] = XRecordFutureClients;
            *pNumClientspecs = nc;
            return pCanon;
        }
        else {                  /* not All or Current */

            int j;

            for (j = i + 1; j < numClients;) {
                if (pClientspecs[i] == pClientspecs[j]) {
                    pClientspecs[j] = pClientspecs[--numClients];
                }
                else
                    j++;
            }
        }
    }                           /* end for each clientspec */
1251
1252
    *pNumClientspecs = numClients;
    return pClientspecs;
1253
}                               /* RecordCanonicalizeClientSpecifiers */
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272

/****************************************************************************/

/* stuff for RegisterClients */

/* RecordPadAlign
 *
 * Arguments:
 *	size is the number of bytes taken by an object.
 *	align is a byte boundary (e.g. 4, 8)
 *
 * Returns:
 *	the number of pad bytes to add at the end of an object of the
 *	given size so that an object placed immediately behind it will
 *	begin on an <align>-byte boundary.
 *
 * Side Effects: none.
 */
static int
1273
RecordPadAlign(int size, int align)
1274
1275
{
    return (align - (size & (align - 1))) & (align - 1);
1276
}                               /* RecordPadAlign */
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292

/* RecordSanityCheckRegisterClients
 *
 * Arguments:
 *	pContext is the context being registered on.
 *	client is the client that issued a RecordCreateContext or
 *	  RecordRegisterClients request.
 *	stuff is a pointer to the request.
 *
 * Returns:
 *	Any one of several possible error values if any of the request
 *	arguments are invalid.  Success if everything is OK.
 *
 * Side Effects: none.
 */
static int
1293
1294
RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client,
                                 xRecordRegisterClientsReq * stuff)
1295
1296
1297
1298
1299
1300
1301
{
    int err;
    xRecordRange *pRange;
    int i;
    XID recordingClient;

    if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
1302
1303
        4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
        return BadLength;
1304
1305

    if (stuff->elementHeader &
1306
1307
1308
1309
        ~(XRecordFromClientSequence | XRecordFromClientTime |
          XRecordFromServerTime)) {
        client->errorValue = stuff->elementHeader;
        return BadValue;
1310
1311
1312
    }

    recordingClient = pContext->pRecordingClient ?
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
        pContext->pRecordingClient->clientAsMask : 0;
    err = RecordSanityCheckClientSpecifiers(client, (XID *) &stuff[1],
                                            stuff->nClients, recordingClient);
    if (err != Success)
        return err;

    pRange = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
    for (i = 0; i < stuff->nRanges; i++, pRange++) {
        if (pRange->coreRequestsFirst > pRange->coreRequestsLast) {
            client->errorValue = pRange->coreRequestsFirst;
            return BadValue;
        }
        if (pRange->coreRepliesFirst > pRange->coreRepliesLast) {
            client->errorValue = pRange->coreRepliesFirst;
            return BadValue;
        }
        if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
            (pRange->extRequestsMajorFirst < 128 ||
             pRange->extRequestsMajorLast < 128 ||
             pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast)) {
            client->errorValue = pRange->extRequestsMajorFirst;
            return BadValue;
        }
        if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast) {
            client->errorValue = pRange->extRequestsMinorFirst;
            return BadValue;
        }
        if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
            (pRange->extRepliesMajorFirst < 128 ||
             pRange->extRepliesMajorLast < 128 ||
             pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast)) {
            client->errorValue = pRange->extRepliesMajorFirst;
            return BadValue;
        }
        if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast) {
            client->errorValue = pRange->extRepliesMinorFirst;
            return BadValue;
        }
        if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
            (pRange->deliveredEventsFirst < 2 ||
             pRange->deliveredEventsLast < 2 ||
             pRange->deliveredEventsFirst > pRange->deliveredEventsLast)) {
            client->errorValue = pRange->deliveredEventsFirst;
            return BadValue;
        }
        if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
            (pRange->deviceEventsFirst < 2 ||
             pRange->deviceEventsLast < 2 ||
             pRange->deviceEventsFirst > pRange->deviceEventsLast)) {
            client->errorValue = pRange->deviceEventsFirst;
            return BadValue;
        }
        if (pRange->errorsFirst > pRange->errorsLast) {
            client->errorValue = pRange->errorsFirst;
            return BadValue;
        }
        if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue) {
            client->errorValue = pRange->clientStarted;
            return BadValue;
        }
        if (pRange->clientDied != xFalse && pRange->clientDied != xTrue) {
            client->errorValue = pRange->clientDied;
            return BadValue;
        }
    }                           /* end for each range */
1378
    return Success;
1379
}                               /* end RecordSanityCheckRegisterClients */
1380
1381

/* This is a tactical structure used to gather information about all the sets
Peter Hutterer's avatar
Peter Hutterer committed
1382
 * (RecordSetPtr) that need to be created for an RCAP in the process of
1383
1384
1385
 * digesting a list of RECORDRANGEs (converting it to the internal
 * representation).
 */
1386
1387
1388
typedef struct {
    int nintervals;             /* number of intervals in following array */
    RecordSetInterval *intervals;       /* array of intervals for this set */
Alan Coopersmith's avatar
Alan Coopersmith committed
1389
    int size;                   /* size of intervals array; >= nintervals */
1390
1391
1392
    int align;                  /* alignment restriction for set */
    int offset;                 /* where to store set pointer rel. to start of RCAP */
    short first, last;          /* if for extension, major opcode interval */
1393
1394
} SetInfoRec, *SetInfoPtr;

1395
1396
1397
1398
#if defined(ERR) && defined(__sun)
#undef ERR /* Avoid conflict with Solaris <sys/regset.h> */
#endif

1399
/* These constant are used to index into an array of SetInfoRec. */
1400
1401
1402
1403
1404
1405
1406
enum { REQ,                     /* set info for requests */
    REP,                        /* set info for replies */
    ERR,                        /* set info for errors */
    DEV,                        /* set info for device events */
    DLEV,                       /* set info for delivered events */
    PREDEFSETS
};                              /* number of predefined array entries */
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420

/* RecordAllocIntervals
 *
 * Arguments:
 *	psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
 *	nIntervals is the desired size of the intervals array.
 *
 * Returns: BadAlloc if a memory allocation error occurred, else Success.
 *
 * Side Effects:
 *	If Success is returned, psi->intervals is a pointer to size
 *	RecordSetIntervals, all zeroed, and psi->size is set to size.
 */
static int
Adam Jackson's avatar
Adam Jackson committed
1421
RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
1422
1423
{
    assert(!psi->intervals);
1424
    psi->intervals = xallocarray(nIntervals, sizeof(RecordSetInterval));
1425
    if (!psi->intervals)
1426
        return BadAlloc;
1427
    memset(psi->intervals, 0, nIntervals * sizeof(RecordSetInterval));
1428
1429
    psi->size = nIntervals;
    return Success;
1430
}                               /* end RecordAllocIntervals */
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448

/* RecordConvertRangesToIntervals
 *
 * Arguments:
 *	psi is a pointer to the SetInfoRec we are building.
 *	pRanges is an array of xRecordRanges.
 *	nRanges is the number of elements in pRanges.
 *	byteoffset is the offset from the start of an xRecordRange of the
 *	  two bytes (1 for first, 1 for last) we are interested in.
 *	pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
 *	  above are followed by four bytes (2 for first, 2 for last)
 *	  representing a minor opcode range, and this information should be
 *	  stored in one of the SetInfoRecs starting at pExtSetInfo.
 *	pnExtSetInfo is the number of elements in the pExtSetInfo array.
 *
 * Returns:  BadAlloc if a memory allocation error occurred, else Success.
 *
 * Side Effects:
Peter Hutterer's avatar
Peter Hutterer committed
1449
 *	The slice of pRanges indicated by byteoffset is stored in psi.
1450
1451
1452
1453
1454
1455
 *	If pExtSetInfo is non-NULL, minor opcode intervals are stored
 *	in an existing SetInfoRec if the major opcode interval matches, else
 *	they are stored in a new SetInfoRec, and *pnExtSetInfo is
 *	increased accordingly.
 */
static int
1456
1457
1458
1459
1460
RecordConvertRangesToIntervals(SetInfoPtr psi,
                               xRecordRange * pRanges,
                               int nRanges,
                               int byteoffset,
                               SetInfoPtr pExtSetInfo, int *pnExtSetInfo)
1461
1462
1463
1464
1465
1466
{
    int i;
    CARD8 *pCARD8;
    int first, last;
    int err;

1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
    for (i = 0; i < nRanges; i++, pRanges++) {
        pCARD8 = ((CARD8 *) pRanges) + byteoffset;
        first = pCARD8[0];
        last = pCARD8[1];
        if (first || last) {
            if (!psi->intervals) {
                err = RecordAllocIntervals(psi, 2 * (nRanges - i));
                if (err != Success)
                    return err;
            }
            psi->intervals[psi->nintervals].first = first;
            psi->intervals[psi->nintervals].last = last;
            psi->nintervals++;
            assert(psi->nintervals <= psi->size);
            if (pExtSetInfo) {
                SetInfoPtr pesi = pExtSetInfo;
                CARD16 *pCARD16 = (CARD16 *) (pCARD8 + 2);
                int j;

                for (j = 0; j < *pnExtSetInfo; j++, pesi++) {
                    if ((first == pesi->first) && (last == pesi->last))
                        break;
                }
                if (j == *pnExtSetInfo) {
                    err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
                    if (err != Success)
                        return err;
                    pesi->first = first;
                    pesi->last = last;
                    (*pnExtSetInfo)++;
                }
                pesi->intervals[pesi->nintervals].first = pCARD16[0];
                pesi->intervals[pesi->nintervals].last = pCARD16[1];
                pesi->nintervals++;
                assert(pesi->nintervals <= pesi->size);
            }
        }
1504
1505
    }
    return Success;
1506
}                               /* end RecordConvertRangesToIntervals */
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531

#define offset_of(_structure, _field) \
    ((char *)(& (_structure . _field)) - (char *)(&_structure))

/* RecordRegisterClients
 *
 * Arguments:
 *	pContext is the context on which to register the clients.
 *	client is the client that issued the RecordCreateContext or
 *	  RecordRegisterClients request.
 *	stuff is a pointer to the request.
 *
 * Returns:
 *	Any one of several possible error values defined by the protocol.
 *	Success if everything is OK.
 *
 * Side Effects:
 *	If different element headers are specified, the context is flushed.
 *	If any of the specified clients are already registered on the
 *	context, they are first unregistered.  A new RCAP is created to
 *	hold the specified protocol and clients, and it is linked onto the
 *	context.  If the context is enabled, appropriate hooks are installed
 *	to record the new clients and protocol.
 */
static int
1532
1533
RecordRegisterClients(RecordContextPtr pContext, ClientPtr client,
                      xRecordRegisterClientsReq * stuff)
1534
1535
1536
1537
1538
1539
1540
{
    int err;
    int i;
    SetInfoPtr si;
    int maxSets;
    int nExtReqSets = 0;
    int nExtRepSets = 0;
Kaleb Keithley Keithley's avatar
Kaleb Keithley Keithley committed
1541
1542
    int extReqSetsOffset = 0;
    int extRepSetsOffset = 0;
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
    SetInfoPtr pExtReqSets, pExtRepSets;
    int clientListOffset;
    XID *pCanonClients;
    int clientStarted = 0, clientDied = 0;
    xRecordRange *pRanges, rr;
    int nClients;
    int sizeClients;
    int totRCAPsize;
    RecordClientsAndProtocolPtr pRCAP;
    int pad;
    XID recordingClient;

    /* do all sanity checking up front */

    err = RecordSanityCheckRegisterClients(pContext, client, stuff);
    if (err != Success)
1559
        return err;
1560
1561

    /* if element headers changed, flush buffer */
1562
1563
1564
1565

    if (pContext->elemHeaders != stuff->elementHeader) {
        RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
        pContext->elemHeaders = stuff->elementHeader;
1566
1567
1568
1569
    }

    nClients = stuff->nClients;
    if (!nClients)
1570
1571
        /* if empty clients list, we're done. */
        return Success;
1572
1573

    recordingClient = pContext->pRecordingClient ?
1574
1575
1576
1577
        pContext->pRecordingClient->clientAsMask : 0;
    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *) &stuff[1],
                                                       &nClients,
                                                       recordingClient);
1578
    if (!pCanonClients)
1579
        return BadAlloc;
1580
1581

    /* We may have to create as many as one set for each "predefined"
Alan Coopersmith's avatar
Alan Coopersmith committed
1582
     * protocol types, plus one per range for extension requests, plus one per
1583
1584
1585
     * range for extension replies.
     */
    maxSets = PREDEFSETS + 2 * stuff->nRanges;
1586
    si = xallocarray(maxSets, sizeof(SetInfoRec));
1587
1588
1589
    if (!si) {
        err = BadAlloc;
        goto bailout;
1590
    }
1591
    memset(si, 0, sizeof(SetInfoRec) * maxSets);
1592
1593
1594

    /* theoretically you must do this because NULL may not be all-bits-zero */
    for (i = 0; i < maxSets; i++)
1595
        si[i].intervals = NULL;
1596
1597
1598
1599

    pExtReqSets = si + PREDEFSETS;
    pExtRepSets = pExtReqSets + stuff->nRanges;

1600
    pRanges = (xRecordRange *) (((XID *) &stuff[1]) + stuff->nClients);
1601
1602

    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1603
1604
1605
1606
                                         offset_of(rr, coreRequestsFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;
1607
1608

    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1609
1610
1611
1612
                                         offset_of(rr, extRequestsMajorFirst),
                                         pExtReqSets, &nExtReqSets);
    if (err != Success)
        goto bailout;
1613
1614

    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1615
1616
1617
1618
                                         offset_of(rr, coreRepliesFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;
1619
1620

    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1621
1622
1623
1624
                                         offset_of(rr, extRepliesMajorFirst),
                                         pExtRepSets, &nExtRepSets);
    if (err != Success)
        goto bailout;
1625
1626

    err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
1627
1628
1629
1630
                                         offset_of(rr, errorsFirst), NULL,
                                         NULL);
    if (err != Success)
        goto bailout;
1631
1632

    err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,