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