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 intevals 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 reuests, 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) && (i < numEnabledContexts));
2380 if (i != (numEnabledContexts - 1)) {
2381 ppAllContexts[i] = ppAllContexts[numEnabledContexts - 1];
2382 ppAllContexts[numEnabledContexts - 1] = pContext;
2383 }
2384 --numEnabledContexts;
2385 assert(numEnabledContexts >= 0);
2386 } /* RecordDisableContext */
2387
2388 static int
ProcRecordDisableContext(ClientPtr client)2389 ProcRecordDisableContext(ClientPtr client)
2390 {
2391 RecordContextPtr pContext;
2392
2393 REQUEST(xRecordDisableContextReq);
2394
2395 REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2396 VERIFY_CONTEXT(pContext, stuff->context, client);
2397 RecordDisableContext(pContext);
2398 return Success;
2399 } /* ProcRecordDisableContext */
2400
2401 /* RecordDeleteContext
2402 *
2403 * Arguments:
2404 * value is the context to delete.
2405 * id is its resource ID.
2406 *
2407 * Returns: Success.
2408 *
2409 * Side Effects:
2410 * Disables the context, frees all associated memory, and removes
2411 * it from the ppAllContexts array.
2412 */
2413 static int
RecordDeleteContext(void * value,XID id)2414 RecordDeleteContext(void *value, XID id)
2415 {
2416 int i;
2417 RecordContextPtr pContext = (RecordContextPtr) value;
2418 RecordClientsAndProtocolPtr pRCAP;
2419
2420 RecordDisableContext(pContext);
2421
2422 /* Remove all the clients from all the RCAPs.
2423 * As a result, the RCAPs will be freed.
2424 */
2425
2426 while ((pRCAP = pContext->pListOfRCAP)) {
2427 int numClients = pRCAP->numClients;
2428
2429 /* when the last client is deleted, the RCAP will go away. */
2430 while (numClients--) {
2431 RecordDeleteClientFromRCAP(pRCAP, numClients);
2432 }
2433 }
2434
2435 /* remove context from AllContexts list */
2436
2437 if (-1 != (i = RecordFindContextOnAllContexts(pContext))) {
2438 ppAllContexts[i] = ppAllContexts[numContexts - 1];
2439 if (--numContexts == 0) {
2440 free(ppAllContexts);
2441 ppAllContexts = NULL;
2442 }
2443 }
2444 free(pContext);
2445
2446 return Success;
2447 } /* RecordDeleteContext */
2448
2449 static int
ProcRecordFreeContext(ClientPtr client)2450 ProcRecordFreeContext(ClientPtr client)
2451 {
2452 RecordContextPtr pContext;
2453
2454 REQUEST(xRecordFreeContextReq);
2455
2456 REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2457 VERIFY_CONTEXT(pContext, stuff->context, client);
2458 FreeResource(stuff->context, RT_NONE);
2459 return Success;
2460 } /* ProcRecordFreeContext */
2461
2462 static int
ProcRecordDispatch(ClientPtr client)2463 ProcRecordDispatch(ClientPtr client)
2464 {
2465 REQUEST(xReq);
2466
2467 switch (stuff->data) {
2468 case X_RecordQueryVersion:
2469 return ProcRecordQueryVersion(client);
2470 case X_RecordCreateContext:
2471 return ProcRecordCreateContext(client);
2472 case X_RecordRegisterClients:
2473 return ProcRecordRegisterClients(client);
2474 case X_RecordUnregisterClients:
2475 return ProcRecordUnregisterClients(client);
2476 case X_RecordGetContext:
2477 return ProcRecordGetContext(client);
2478 case X_RecordEnableContext:
2479 return ProcRecordEnableContext(client);
2480 case X_RecordDisableContext:
2481 return ProcRecordDisableContext(client);
2482 case X_RecordFreeContext:
2483 return ProcRecordFreeContext(client);
2484 default:
2485 return BadRequest;
2486 }
2487 } /* ProcRecordDispatch */
2488
2489 static int _X_COLD
SProcRecordQueryVersion(ClientPtr client)2490 SProcRecordQueryVersion(ClientPtr client)
2491 {
2492 REQUEST(xRecordQueryVersionReq);
2493
2494 swaps(&stuff->length);
2495 REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
2496 swaps(&stuff->majorVersion);
2497 swaps(&stuff->minorVersion);
2498 return ProcRecordQueryVersion(client);
2499 } /* SProcRecordQueryVersion */
2500
2501 static int _X_COLD
SwapCreateRegister(ClientPtr client,xRecordRegisterClientsReq * stuff)2502 SwapCreateRegister(ClientPtr client, xRecordRegisterClientsReq * stuff)
2503 {
2504 int i;
2505 XID *pClientID;
2506
2507 swapl(&stuff->context);
2508 swapl(&stuff->nClients);
2509 swapl(&stuff->nRanges);
2510 pClientID = (XID *) &stuff[1];
2511 if (stuff->nClients >
2512 client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq))
2513 return BadLength;
2514 for (i = 0; i < stuff->nClients; i++, pClientID++) {
2515 swapl(pClientID);
2516 }
2517 if (stuff->nRanges >
2518 client->req_len - bytes_to_int32(sz_xRecordRegisterClientsReq)
2519 - stuff->nClients)
2520 return BadLength;
2521 RecordSwapRanges((xRecordRange *) pClientID, stuff->nRanges);
2522 return Success;
2523 } /* SwapCreateRegister */
2524
2525 static int _X_COLD
SProcRecordCreateContext(ClientPtr client)2526 SProcRecordCreateContext(ClientPtr client)
2527 {
2528 REQUEST(xRecordCreateContextReq);
2529 int status;
2530
2531 swaps(&stuff->length);
2532 REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
2533 if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
2534 return status;
2535 return ProcRecordCreateContext(client);
2536 } /* SProcRecordCreateContext */
2537
2538 static int _X_COLD
SProcRecordRegisterClients(ClientPtr client)2539 SProcRecordRegisterClients(ClientPtr client)
2540 {
2541 REQUEST(xRecordRegisterClientsReq);
2542 int status;
2543
2544 swaps(&stuff->length);
2545 REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
2546 if ((status = SwapCreateRegister(client, (void *) stuff)) != Success)
2547 return status;
2548 return ProcRecordRegisterClients(client);
2549 } /* SProcRecordRegisterClients */
2550
2551 static int _X_COLD
SProcRecordUnregisterClients(ClientPtr client)2552 SProcRecordUnregisterClients(ClientPtr client)
2553 {
2554 REQUEST(xRecordUnregisterClientsReq);
2555
2556 swaps(&stuff->length);
2557 REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
2558 swapl(&stuff->context);
2559 swapl(&stuff->nClients);
2560 SwapRestL(stuff);
2561 return ProcRecordUnregisterClients(client);
2562 } /* SProcRecordUnregisterClients */
2563
2564 static int _X_COLD
SProcRecordGetContext(ClientPtr client)2565 SProcRecordGetContext(ClientPtr client)
2566 {
2567 REQUEST(xRecordGetContextReq);
2568
2569 swaps(&stuff->length);
2570 REQUEST_SIZE_MATCH(xRecordGetContextReq);
2571 swapl(&stuff->context);
2572 return ProcRecordGetContext(client);
2573 } /* SProcRecordGetContext */
2574
2575 static int _X_COLD
SProcRecordEnableContext(ClientPtr client)2576 SProcRecordEnableContext(ClientPtr client)
2577 {
2578 REQUEST(xRecordEnableContextReq);
2579
2580 swaps(&stuff->length);
2581 REQUEST_SIZE_MATCH(xRecordEnableContextReq);
2582 swapl(&stuff->context);
2583 return ProcRecordEnableContext(client);
2584 } /* SProcRecordEnableContext */
2585
2586 static int _X_COLD
SProcRecordDisableContext(ClientPtr client)2587 SProcRecordDisableContext(ClientPtr client)
2588 {
2589 REQUEST(xRecordDisableContextReq);
2590
2591 swaps(&stuff->length);
2592 REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2593 swapl(&stuff->context);
2594 return ProcRecordDisableContext(client);
2595 } /* SProcRecordDisableContext */
2596
2597 static int _X_COLD
SProcRecordFreeContext(ClientPtr client)2598 SProcRecordFreeContext(ClientPtr client)
2599 {
2600 REQUEST(xRecordFreeContextReq);
2601
2602 swaps(&stuff->length);
2603 REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2604 swapl(&stuff->context);
2605 return ProcRecordFreeContext(client);
2606 } /* SProcRecordFreeContext */
2607
2608 static int _X_COLD
SProcRecordDispatch(ClientPtr client)2609 SProcRecordDispatch(ClientPtr client)
2610 {
2611 REQUEST(xReq);
2612
2613 switch (stuff->data) {
2614 case X_RecordQueryVersion:
2615 return SProcRecordQueryVersion(client);
2616 case X_RecordCreateContext:
2617 return SProcRecordCreateContext(client);
2618 case X_RecordRegisterClients:
2619 return SProcRecordRegisterClients(client);
2620 case X_RecordUnregisterClients:
2621 return SProcRecordUnregisterClients(client);
2622 case X_RecordGetContext:
2623 return SProcRecordGetContext(client);
2624 case X_RecordEnableContext:
2625 return SProcRecordEnableContext(client);
2626 case X_RecordDisableContext:
2627 return SProcRecordDisableContext(client);
2628 case X_RecordFreeContext:
2629 return SProcRecordFreeContext(client);
2630 default:
2631 return BadRequest;
2632 }
2633 } /* SProcRecordDispatch */
2634
2635 /* RecordConnectionSetupInfo
2636 *
2637 * Arguments:
2638 * pContext is an enabled context that specifies recording of
2639 * connection setup info.
2640 * pci holds the connection setup info.
2641 *
2642 * Returns: nothing.
2643 *
2644 * Side Effects:
2645 * The connection setup info is sent to the recording client.
2646 */
2647 static void
RecordConnectionSetupInfo(RecordContextPtr pContext,NewClientInfoRec * pci)2648 RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec * pci)
2649 {
2650 int prefixsize = SIZEOF(xConnSetupPrefix);
2651 int restsize = pci->prefix->length * 4;
2652
2653 if (pci->client->swapped) {
2654 char *pConnSetup = (char *) malloc(prefixsize + restsize);
2655
2656 if (!pConnSetup)
2657 return;
2658 SwapConnSetupPrefix(pci->prefix, (xConnSetupPrefix *) pConnSetup);
2659 SwapConnSetupInfo((char *) pci->setup,
2660 (char *) (pConnSetup + prefixsize));
2661 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2662 (void *) pConnSetup, prefixsize + restsize, 0,
2663 0);
2664 free(pConnSetup);
2665 }
2666 else {
2667 /* don't alloc and copy as in the swapped case; just send the
2668 * data in two pieces
2669 */
2670 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2671 (void *) pci->prefix, prefixsize, 0, restsize);
2672 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2673 (void *) pci->setup, restsize, 0,
2674 /* continuation */ -1);
2675 }
2676 } /* RecordConnectionSetupInfo */
2677
2678 /* RecordDeleteContext
2679 *
2680 * Arguments:
2681 * pcbl is &ClientStateCallback.
2682 * nullata is NULL.
2683 * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
2684 * which contains information about client state changes.
2685 *
2686 * Returns: nothing.
2687 *
2688 * Side Effects:
2689 * If a new client has connected and any contexts have specified
2690 * XRecordFutureClients, the new client is registered on those contexts.
2691 * If any of those contexts specify recording of the connection setup
2692 * info, it is recorded.
2693 *
2694 * If an existing client has disconnected, it is deleted from any
2695 * contexts that it was registered on. If any of those contexts
2696 * specified XRecordClientDied, they record a ClientDied protocol element.
2697 * If the disconnectiong client happened to be the data connection of an
2698 * enabled context, the context is disabled.
2699 */
2700
2701 static void
RecordAClientStateChange(CallbackListPtr * pcbl,void * nulldata,void * calldata)2702 RecordAClientStateChange(CallbackListPtr *pcbl, void *nulldata,
2703 void *calldata)
2704 {
2705 NewClientInfoRec *pci = (NewClientInfoRec *) calldata;
2706 int i;
2707 ClientPtr pClient = pci->client;
2708 RecordContextPtr *ppAllContextsCopy = NULL;
2709 int numContextsCopy = 0;
2710
2711 switch (pClient->clientState) {
2712 case ClientStateRunning: /* new client */
2713 for (i = 0; i < numContexts; i++) {
2714 RecordClientsAndProtocolPtr pRCAP;
2715 RecordContextPtr pContext = ppAllContexts[i];
2716
2717 if ((pRCAP = RecordFindClientOnContext(pContext,
2718 XRecordFutureClients, NULL)))
2719 {
2720 RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
2721 if (pContext->pRecordingClient && pRCAP->clientStarted)
2722 RecordConnectionSetupInfo(pContext, pci);
2723 }
2724 }
2725 break;
2726
2727 case ClientStateGone:
2728 case ClientStateRetained: /* client disconnected */
2729
2730 /* RecordDisableContext modifies contents of ppAllContexts. */
2731 numContextsCopy = numContexts;
2732 ppAllContextsCopy = xallocarray(numContextsCopy,
2733 sizeof(RecordContextPtr));
2734 assert(ppAllContextsCopy);
2735 memcpy(ppAllContextsCopy, ppAllContexts,
2736 numContextsCopy * sizeof(RecordContextPtr));
2737
2738 for (i = 0; i < numContextsCopy; i++) {
2739 RecordClientsAndProtocolPtr pRCAP;
2740 RecordContextPtr pContext = ppAllContextsCopy[i];
2741 int pos;
2742
2743 if (pContext->pRecordingClient == pClient)
2744 RecordDisableContext(pContext);
2745 if ((pRCAP = RecordFindClientOnContext(pContext,
2746 pClient->clientAsMask,
2747 &pos))) {
2748 if (pContext->pRecordingClient && pRCAP->clientDied)
2749 RecordAProtocolElement(pContext, pClient,
2750 XRecordClientDied, NULL, 0, 0, 0);
2751 RecordDeleteClientFromRCAP(pRCAP, pos);
2752 }
2753 }
2754
2755 free(ppAllContextsCopy);
2756 break;
2757
2758 default:
2759 break;
2760 } /* end switch on client state */
2761 } /* RecordAClientStateChange */
2762
2763 /* RecordCloseDown
2764 *
2765 * Arguments:
2766 * extEntry is the extension information for RECORD.
2767 *
2768 * Returns: nothing.
2769 *
2770 * Side Effects:
2771 * Performs any cleanup needed by RECORD at server shutdown time.
2772 *
2773 */
2774 static void
RecordCloseDown(ExtensionEntry * extEntry)2775 RecordCloseDown(ExtensionEntry * extEntry)
2776 {
2777 DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2778 } /* RecordCloseDown */
2779
2780 /* RecordExtensionInit
2781 *
2782 * Arguments: none.
2783 *
2784 * Returns: nothing.
2785 *
2786 * Side Effects:
2787 * Enables the RECORD extension if possible.
2788 */
2789 void
RecordExtensionInit(void)2790 RecordExtensionInit(void)
2791 {
2792 ExtensionEntry *extentry;
2793
2794 RTContext = CreateNewResourceType(RecordDeleteContext, "RecordContext");
2795 if (!RTContext)
2796 return;
2797
2798 if (!dixRegisterPrivateKey(RecordClientPrivateKey, PRIVATE_CLIENT, 0))
2799 return;
2800
2801 ppAllContexts = NULL;
2802 numContexts = numEnabledContexts = numEnabledRCAPs = 0;
2803
2804 if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
2805 return;
2806
2807 extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
2808 ProcRecordDispatch, SProcRecordDispatch,
2809 RecordCloseDown, StandardMinorOpcode);
2810 if (!extentry) {
2811 DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2812 return;
2813 }
2814 SetResourceTypeErrorValue(RTContext,
2815 extentry->errorBase + XRecordBadContext);
2816
2817 } /* RecordExtensionInit */
2818