First import
[xorg_rtime.git] / xorg-server-1.4 / record / record.c
blob0ed8f84c2a726ce8d7e71e4ef675f4e84e853971
2 /*
4 Copyright 1995, 1998 The Open Group
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.
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
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.
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.
28 Author: David P. Wiggins, The Open Group
30 This work benefited from earlier work done by Martha Zimet of NCD
31 and Jim Haggerty of Metheus.
35 #define NEED_EVENTS
36 #ifdef HAVE_DIX_CONFIG_H
37 #include <dix-config.h>
38 #endif
40 #include "dixstruct.h"
41 #include "extnsionst.h"
42 #define _XRECORD_SERVER_
43 #include <X11/extensions/recordstr.h>
44 #include "set.h"
45 #include "swaprep.h"
47 #include <stdio.h>
48 #include <assert.h>
50 #ifdef PANORAMIX
51 #include "globals.h"
52 #include "panoramiX.h"
53 #include "panoramiXsrv.h"
54 #include "cursor.h"
55 #endif
57 static RESTYPE RTContext; /* internal resource type for Record contexts */
58 static int RecordErrorBase; /* first Record error number */
60 /* How many bytes of protocol data to buffer in a context. Don't set to less
61 * than 32.
63 #define REPLY_BUF_SIZE 1024
65 /* Record Context structure */
67 typedef struct {
68 XID id; /* resource id of context */
69 ClientPtr pRecordingClient; /* client that has context enabled */
70 struct _RecordClientsAndProtocolRec *pListOfRCAP; /* all registered info */
71 ClientPtr pBufClient; /* client whose protocol is in replyBuffer*/
72 unsigned int continuedReply:1; /* recording a reply that is split up? */
73 char elemHeaders; /* element header flags (time/seq no.) */
74 char bufCategory; /* category of protocol in replyBuffer */
75 int numBufBytes; /* number of bytes in replyBuffer */
76 char replyBuffer[REPLY_BUF_SIZE]; /* buffered recorded protocol */
77 } RecordContextRec, *RecordContextPtr;
79 /* RecordMinorOpRec - to hold minor opcode selections for extension requests
80 * and replies
83 typedef union {
84 int count; /* first element of array: how many "major" structs to follow */
85 struct { /* rest of array elements are this */
86 short first; /* first major opcode */
87 short last; /* last major opcode */
88 RecordSetPtr pMinOpSet; /* minor opcode set for above major range */
89 } major;
90 } RecordMinorOpRec, *RecordMinorOpPtr;
93 /* RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
94 * protocol selections passed in a single CreateContext or RegisterClients.
95 * Generally, a context will have one of these from the create and an
96 * additional one for each RegisterClients. RCAPs are freed when all their
97 * clients are unregistered.
100 typedef struct _RecordClientsAndProtocolRec {
101 RecordContextPtr pContext; /* context that owns this RCAP */
102 struct _RecordClientsAndProtocolRec *pNextRCAP; /* next RCAP on context */
103 RecordSetPtr pRequestMajorOpSet; /* requests to record */
104 RecordMinorOpPtr pRequestMinOpInfo; /* extension requests to record */
105 RecordSetPtr pReplyMajorOpSet; /* replies to record */
106 RecordMinorOpPtr pReplyMinOpInfo; /* extension replies to record */
107 RecordSetPtr pDeviceEventSet; /* device events to record */
108 RecordSetPtr pDeliveredEventSet; /* delivered events to record */
109 RecordSetPtr pErrorSet; /* errors to record */
110 XID * pClientIDs; /* array of clients to record */
111 short numClients; /* number of clients in pClientIDs */
112 short sizeClients; /* size of pClientIDs array */
113 unsigned int clientStarted:1; /* record new client connections? */
114 unsigned int clientDied:1; /* record client disconnections? */
115 unsigned int clientIDsSeparatelyAllocated:1; /* pClientIDs malloced? */
116 } RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;
118 /* how much bigger to make pRCAP->pClientIDs when reallocing */
119 #define CLIENT_ARRAY_GROWTH_INCREMENT 4
121 /* counts the total number of RCAPs belonging to enabled contexts. */
122 static int numEnabledRCAPs;
124 /* void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
125 * In the spirit of the VERIFY_* macros in dix.h, this macro fills in
126 * the context pointer if the given ID is a valid Record Context, else it
127 * returns an error.
129 #define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
130 (_pContext) = (RecordContextPtr)LookupIDByType((_contextid), RTContext); \
131 if (!(_pContext)) { \
132 (_client)->errorValue = (_contextid); \
133 return RecordErrorBase + XRecordBadContext; \
137 static int RecordDeleteContext(
138 pointer /*value*/,
139 XID /*id*/
143 /***************************************************************************/
145 /* client private stuff */
147 /* To make declarations less obfuscated, have a typedef for a pointer to a
148 * Proc function.
150 typedef int (*ProcFunctionPtr)(
151 ClientPtr /*pClient*/
154 /* Record client private. Generally a client only has one of these if
155 * any of its requests are being recorded.
157 typedef struct {
158 /* ptr to client's proc vector before Record stuck its nose in */
159 ProcFunctionPtr *originalVector;
161 /* proc vector with pointers for recorded requests redirected to the
162 * function RecordARequest
164 ProcFunctionPtr recordVector[256];
165 } RecordClientPrivateRec, *RecordClientPrivatePtr;
167 static int RecordClientPrivateIndex;
169 /* RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
170 * gets the client private of the given client. Syntactic sugar.
172 #define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
173 ((_pClient)->devPrivates[RecordClientPrivateIndex].ptr)
176 /***************************************************************************/
178 /* global list of all contexts */
180 static RecordContextPtr *ppAllContexts;
182 static int numContexts;/* number of contexts in ppAllContexts */
184 /* number of currently enabled contexts. All enabled contexts are bunched
185 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
186 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
187 * past disabled contexts.
189 static int numEnabledContexts;
191 /* RecordFindContextOnAllContexts
193 * Arguments:
194 * pContext is the context to search for.
196 * Returns:
197 * The index into the array ppAllContexts at which pContext is stored.
198 * If pContext is not found in ppAllContexts, returns -1.
200 * Side Effects: none.
202 static int
203 RecordFindContextOnAllContexts(RecordContextPtr pContext)
205 int i;
207 assert(numContexts >= numEnabledContexts);
208 for (i = 0; i < numContexts; i++)
210 if (ppAllContexts[i] == pContext)
211 return i;
213 return -1;
214 } /* RecordFindContextOnAllContexts */
217 /***************************************************************************/
219 /* RecordFlushReplyBuffer
221 * Arguments:
222 * pContext is the context to flush.
223 * data1 is a pointer to additional data, and len1 is its length in bytes.
224 * data2 is a pointer to additional data, and len2 is its length in bytes.
226 * Returns: nothing.
228 * Side Effects:
229 * If the context is enabled, any buffered (recorded) protocol is written
230 * to the recording client, and the number of buffered bytes is set to
231 * zero. If len1 is not zero, data1/len1 are then written to the
232 * recording client, and similarly for data2/len2 (written after
233 * data1/len1).
235 static void
236 RecordFlushReplyBuffer(
237 RecordContextPtr pContext,
238 pointer data1,
239 int len1,
240 pointer data2,
241 int len2
244 if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone)
245 return;
246 if (pContext->numBufBytes)
247 WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
248 (char *)pContext->replyBuffer);
249 pContext->numBufBytes = 0;
250 if (len1)
251 WriteToClient(pContext->pRecordingClient, len1, (char *)data1);
252 if (len2)
253 WriteToClient(pContext->pRecordingClient, len2, (char *)data2);
254 } /* RecordFlushReplyBuffer */
257 /* RecordAProtocolElement
259 * Arguments:
260 * pContext is the context that is recording a protocol element.
261 * pClient is the client whose protocol is being recorded. For
262 * device events and EndOfData, pClient is NULL.
263 * category is the category of the protocol element, as defined
264 * by the RECORD spec.
265 * data is a pointer to the protocol data, and datalen is its length
266 * in bytes.
267 * futurelen is the number of bytes that will be sent in subsequent
268 * calls to this function to complete this protocol element.
269 * In those subsequent calls, futurelen will be -1 to indicate
270 * that the current data is a continuation of the same protocol
271 * element.
273 * Returns: nothing.
275 * Side Effects:
276 * The context may be flushed. The new protocol element will be
277 * added to the context's protocol buffer with appropriate element
278 * headers prepended (sequence number and timestamp). If the data
279 * is continuation data (futurelen == -1), element headers won't
280 * be added. If the protocol element and headers won't fit in
281 * the context's buffer, it is sent directly to the recording
282 * client (after any buffered data).
284 static void
285 RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
286 int category, pointer data, int datalen, int futurelen)
288 CARD32 elemHeaderData[2];
289 int numElemHeaders = 0;
290 Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
291 int n;
292 CARD32 serverTime = 0;
293 Bool gotServerTime = FALSE;
294 int replylen;
296 if (futurelen >= 0)
297 { /* start of new protocol element */
298 xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
299 pContext->replyBuffer;
300 if (pContext->pBufClient != pClient ||
301 pContext->bufCategory != category)
303 RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
304 pContext->pBufClient = pClient;
305 pContext->bufCategory = category;
308 if (!pContext->numBufBytes)
310 serverTime = GetTimeInMillis();
311 gotServerTime = TRUE;
312 pRep->type = X_Reply;
313 pRep->category = category;
314 pRep->sequenceNumber = pContext->pRecordingClient->sequence;
315 pRep->length = 0;
316 pRep->elementHeader = pContext->elemHeaders;
317 pRep->serverTime = serverTime;
318 if (pClient)
320 pRep->clientSwapped =
321 (pClient->swapped != recordingClientSwapped);
322 pRep->idBase = pClient->clientAsMask;
323 pRep->recordedSequenceNumber = pClient->sequence;
325 else /* it's a device event, StartOfData, or EndOfData */
327 pRep->clientSwapped = (category != XRecordFromServer) &&
328 recordingClientSwapped;
329 pRep->idBase = 0;
330 pRep->recordedSequenceNumber = 0;
333 if (recordingClientSwapped)
335 swaps(&pRep->sequenceNumber, n);
336 swapl(&pRep->length, n);
337 swapl(&pRep->idBase, n);
338 swapl(&pRep->serverTime, n);
339 swapl(&pRep->recordedSequenceNumber, n);
341 pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
344 /* generate element headers if needed */
346 if ( ( (pContext->elemHeaders & XRecordFromClientTime)
347 && category == XRecordFromClient)
349 ( (pContext->elemHeaders & XRecordFromServerTime)
350 && category == XRecordFromServer))
352 if (gotServerTime)
353 elemHeaderData[numElemHeaders] = serverTime;
354 else
355 elemHeaderData[numElemHeaders] = GetTimeInMillis();
356 if (recordingClientSwapped)
357 swapl(&elemHeaderData[numElemHeaders], n);
358 numElemHeaders++;
361 if ( (pContext->elemHeaders & XRecordFromClientSequence)
363 (category == XRecordFromClient || category == XRecordClientDied))
365 elemHeaderData[numElemHeaders] = pClient->sequence;
366 if (recordingClientSwapped)
367 swapl(&elemHeaderData[numElemHeaders], n);
368 numElemHeaders++;
371 /* adjust reply length */
373 replylen = pRep->length;
374 if (recordingClientSwapped) swapl(&replylen, n);
375 replylen += numElemHeaders + (datalen >> 2) + (futurelen >> 2);
376 if (recordingClientSwapped) swapl(&replylen, n);
377 pRep->length = replylen;
378 } /* end if not continued reply */
380 numElemHeaders *= 4;
382 /* if space available >= space needed, buffer the data */
384 if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders)
386 if (numElemHeaders)
388 memcpy(pContext->replyBuffer + pContext->numBufBytes,
389 elemHeaderData, numElemHeaders);
390 pContext->numBufBytes += numElemHeaders;
392 if (datalen)
394 memcpy(pContext->replyBuffer + pContext->numBufBytes,
395 data, datalen);
396 pContext->numBufBytes += datalen;
399 else
400 RecordFlushReplyBuffer(pContext, (pointer)elemHeaderData,
401 numElemHeaders, (pointer)data, datalen);
403 } /* RecordAProtocolElement */
406 /* RecordFindClientOnContext
408 * Arguments:
409 * pContext is the context to search.
410 * clientspec is the resource ID mask identifying the client to search
411 * for, or XRecordFutureClients.
412 * pposition is a pointer to an int, or NULL. See Returns.
414 * Returns:
415 * The RCAP on which clientspec was found, or NULL if not found on
416 * any RCAP on the given context.
417 * If pposition was not NULL and the returned RCAP is not NULL,
418 * *pposition will be set to the index into the returned the RCAP's
419 * pClientIDs array that holds clientspec.
421 * Side Effects: none.
423 static RecordClientsAndProtocolPtr
424 RecordFindClientOnContext(
425 RecordContextPtr pContext,
426 XID clientspec,
427 int *pposition
430 RecordClientsAndProtocolPtr pRCAP;
432 for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
434 int i;
435 for (i = 0; i < pRCAP->numClients; i++)
437 if (pRCAP->pClientIDs[i] == clientspec)
439 if (pposition)
440 *pposition = i;
441 return pRCAP;
445 return NULL;
446 } /* RecordFindClientOnContext */
449 /* RecordABigRequest
451 * Arguments:
452 * pContext is the recording context.
453 * client is the client being recorded.
454 * stuff is a pointer to the big request of client (see the Big Requests
455 * extension for details.)
457 * Returns: nothing.
459 * Side Effects:
460 * The big request is recorded with the correct length field re-inserted.
462 * Note: this function exists mainly to make RecordARequest smaller.
464 static void
465 RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq *stuff)
467 CARD32 bigLength;
468 char n;
469 int bytesLeft;
471 /* note: client->req_len has been frobbed by ReadRequestFromClient
472 * (os/io.c) to discount the extra 4 bytes taken by the extended length
473 * field in a big request. The actual request length to record is
474 * client->req_len + 1 (measured in CARD32s).
477 /* record the request header */
478 bytesLeft = client->req_len << 2;
479 RecordAProtocolElement(pContext, client, XRecordFromClient,
480 (pointer)stuff, SIZEOF(xReq), bytesLeft);
482 /* reinsert the extended length field that was squished out */
483 bigLength = client->req_len + (sizeof(bigLength) >> 2);
484 if (client->swapped)
485 swapl(&bigLength, n);
486 RecordAProtocolElement(pContext, client, XRecordFromClient,
487 (pointer)&bigLength, sizeof(bigLength), /* continuation */ -1);
488 bytesLeft -= sizeof(bigLength);
490 /* record the rest of the request after the length */
491 RecordAProtocolElement(pContext, client, XRecordFromClient,
492 (pointer)(stuff + 1), bytesLeft, /* continuation */ -1);
493 } /* RecordABigRequest */
496 /* RecordARequest
498 * Arguments:
499 * client is a client that the server has dispatched a request to by
500 * calling client->requestVector[request opcode] .
501 * The request is in client->requestBuffer.
503 * Returns:
504 * Whatever is returned by the "real" Proc function for this request.
505 * The "real" Proc function is the function that was in
506 * client->requestVector[request opcode] before it was replaced by
507 * RecordARequest. (See the function RecordInstallHooks.)
509 * Side Effects:
510 * The request is recorded by all contexts that have registered this
511 * request for this client. The real Proc function is called.
513 static int
514 RecordARequest(ClientPtr client)
516 RecordContextPtr pContext;
517 RecordClientsAndProtocolPtr pRCAP;
518 int i;
519 RecordClientPrivatePtr pClientPriv;
520 REQUEST(xReq);
521 int majorop;
523 majorop = stuff->reqType;
524 for (i = 0; i < numEnabledContexts; i++)
526 pContext = ppAllContexts[i];
527 pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
528 NULL);
529 if (pRCAP && pRCAP->pRequestMajorOpSet &&
530 RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop))
532 if (majorop <= 127)
533 { /* core request */
535 if (stuff->length == 0)
536 RecordABigRequest(pContext, client, stuff);
537 else
538 RecordAProtocolElement(pContext, client, XRecordFromClient,
539 (pointer)stuff, client->req_len << 2, 0);
541 else /* extension, check minor opcode */
543 int minorop = MinorOpcodeOfRequest(client);
544 int numMinOpInfo;
545 RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
547 assert (pMinorOpInfo);
548 numMinOpInfo = pMinorOpInfo->count;
549 pMinorOpInfo++;
550 assert (numMinOpInfo);
551 for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
553 if (majorop >= pMinorOpInfo->major.first &&
554 majorop <= pMinorOpInfo->major.last &&
555 RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
556 minorop))
558 if (stuff->length == 0)
559 RecordABigRequest(pContext, client, stuff);
560 else
561 RecordAProtocolElement(pContext, client,
562 XRecordFromClient, (pointer)stuff,
563 client->req_len << 2, 0);
564 break;
566 } /* end for each minor op info */
567 } /* end extension request */
568 } /* end this RCAP wants this major opcode */
569 } /* end for each context */
570 pClientPriv = RecordClientPrivate(client);
571 assert(pClientPriv);
572 return (* pClientPriv->originalVector[majorop])(client);
573 } /* RecordARequest */
576 /* RecordASkippedRequest
578 * Arguments:
579 * pcbl is &SkippedRequestCallback.
580 * nulldata is NULL.
581 * calldata is a pointer to a SkippedRequestInfoRec (include/os.h)
582 * which provides information about requests that the server is
583 * skipping. The client's proc vector won't be called for skipped
584 * requests, so that's why we have to catch them here.
586 * Returns: nothing.
588 * Side Effects:
589 * The skipped requests are recorded by all contexts that have
590 * registered those requests for this client.
592 * Note: most servers don't skip requests, so calls to this will probably
593 * be rare. For more information on skipped requests, search for
594 * the word skip in ddx.tbl.ms (the porting layer document).
596 static void
597 RecordASkippedRequest(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
599 SkippedRequestInfoRec *psi = (SkippedRequestInfoRec *)calldata;
600 RecordContextPtr pContext;
601 RecordClientsAndProtocolPtr pRCAP;
602 xReqPtr stuff = psi->req;
603 ClientPtr client = psi->client;
604 int numSkippedRequests = psi->numskipped;
605 int reqlen;
606 int i;
607 int majorop;
609 while (numSkippedRequests--)
611 majorop = stuff->reqType;
612 reqlen = ReqLen(stuff, client);
613 /* handle big request */
614 if (stuff->length == 0)
615 reqlen += 4;
616 for (i = 0; i < numEnabledContexts; i++)
618 pContext = ppAllContexts[i];
619 pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
620 NULL);
621 if (pRCAP && pRCAP->pRequestMajorOpSet &&
622 RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop))
624 if (majorop <= 127)
625 { /* core request */
627 RecordAProtocolElement(pContext, client, XRecordFromClient,
628 (pointer)stuff, reqlen, 0);
630 else /* extension, check minor opcode */
632 int minorop = MinorOpcodeOfRequest(client);
633 int numMinOpInfo;
634 RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
636 assert (pMinorOpInfo);
637 numMinOpInfo = pMinorOpInfo->count;
638 pMinorOpInfo++;
639 assert (numMinOpInfo);
640 for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
642 if (majorop >= pMinorOpInfo->major.first &&
643 majorop <= pMinorOpInfo->major.last &&
644 RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
645 minorop))
647 RecordAProtocolElement(pContext, client,
648 XRecordFromClient, (pointer)stuff,
649 reqlen, 0);
650 break;
652 } /* end for each minor op info */
653 } /* end extension request */
654 } /* end this RCAP wants this major opcode */
655 } /* end for each context */
657 /* go to next request */
658 stuff = (xReqPtr)( ((char *)stuff) + reqlen);
660 } /* end for each skipped request */
661 } /* RecordASkippedRequest */
664 /* RecordAReply
666 * Arguments:
667 * pcbl is &ReplyCallback.
668 * nulldata is NULL.
669 * calldata is a pointer to a ReplyInfoRec (include/os.h)
670 * which provides information about replies that are being sent
671 * to clients.
673 * Returns: nothing.
675 * Side Effects:
676 * The reply is recorded by all contexts that have registered this
677 * reply type for this client. If more data belonging to the same
678 * reply is expected, and if the reply is being recorded by any
679 * context, pContext->continuedReply is set to 1.
680 * If pContext->continuedReply was already 1 and this is the last
681 * chunk of data belonging to this reply, it is set to 0.
683 static void
684 RecordAReply(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
686 RecordContextPtr pContext;
687 RecordClientsAndProtocolPtr pRCAP;
688 int eci;
689 int majorop;
690 ReplyInfoRec *pri = (ReplyInfoRec *)calldata;
691 ClientPtr client = pri->client;
692 REQUEST(xReq);
694 majorop = stuff->reqType;
695 for (eci = 0; eci < numEnabledContexts; eci++)
697 pContext = ppAllContexts[eci];
698 pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
699 NULL);
700 if (pRCAP)
702 if (pContext->continuedReply)
704 RecordAProtocolElement(pContext, client, XRecordFromServer,
705 pri->replyData, pri->dataLenBytes, /* continuation */ -1);
706 if (!pri->bytesRemaining)
707 pContext->continuedReply = 0;
709 else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
710 RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop))
712 if (majorop <= 127)
713 { /* core reply */
714 RecordAProtocolElement(pContext, client, XRecordFromServer,
715 pri->replyData, pri->dataLenBytes, pri->bytesRemaining);
716 if (pri->bytesRemaining)
717 pContext->continuedReply = 1;
719 else /* extension, check minor opcode */
721 int minorop = MinorOpcodeOfRequest(client);
722 int numMinOpInfo;
723 RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;
724 assert (pMinorOpInfo);
725 numMinOpInfo = pMinorOpInfo->count;
726 pMinorOpInfo++;
727 assert (numMinOpInfo);
728 for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
730 if (majorop >= pMinorOpInfo->major.first &&
731 majorop <= pMinorOpInfo->major.last &&
732 RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
733 minorop))
735 RecordAProtocolElement(pContext, client,
736 XRecordFromServer, pri->replyData,
737 pri->dataLenBytes, pri->bytesRemaining);
738 if (pri->bytesRemaining)
739 pContext->continuedReply = 1;
740 break;
742 } /* end for each minor op info */
743 } /* end extension reply */
744 } /* end continued reply vs. start of reply */
745 } /* end client is registered on this context */
746 } /* end for each context */
747 } /* RecordAReply */
750 /* RecordADeliveredEventOrError
752 * Arguments:
753 * pcbl is &EventCallback.
754 * nulldata is NULL.
755 * calldata is a pointer to a EventInfoRec (include/dix.h)
756 * which provides information about events that are being sent
757 * to clients.
759 * Returns: nothing.
761 * Side Effects:
762 * The event or error is recorded by all contexts that have registered
763 * it for this client.
765 static void
766 RecordADeliveredEventOrError(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
768 EventInfoRec *pei = (EventInfoRec *)calldata;
769 RecordContextPtr pContext;
770 RecordClientsAndProtocolPtr pRCAP;
771 int eci; /* enabled context index */
772 ClientPtr pClient = pei->client;
774 for (eci = 0; eci < numEnabledContexts; eci++)
776 pContext = ppAllContexts[eci];
777 pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
778 NULL);
779 if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet))
781 int ev; /* event index */
782 xEvent *pev = pei->events;
783 for (ev = 0; ev < pei->count; ev++, pev++)
785 int recordit = 0;
786 if (pRCAP->pErrorSet)
788 recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
789 ((xError *)(pev))->errorCode);
791 else if (pRCAP->pDeliveredEventSet)
793 recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
794 pev->u.u.type & 0177);
796 if (recordit)
798 xEvent swappedEvent;
799 xEvent *pEvToRecord = pev;
801 if (pClient->swapped)
803 (*EventSwapVector[pev->u.u.type & 0177])
804 (pev, &swappedEvent);
805 pEvToRecord = &swappedEvent;
808 RecordAProtocolElement(pContext, pClient,
809 XRecordFromServer, pEvToRecord, SIZEOF(xEvent), 0);
811 } /* end for each event */
812 } /* end this client is on this context */
813 } /* end for each enabled context */
814 } /* RecordADeliveredEventOrError */
817 /* RecordADeviceEvent
819 * Arguments:
820 * pcbl is &DeviceEventCallback.
821 * nulldata is NULL.
822 * calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
823 * which provides information about device events that occur.
825 * Returns: nothing.
827 * Side Effects:
828 * The device event is recorded by all contexts that have registered
829 * it for this client.
831 static void
832 RecordADeviceEvent(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
834 DeviceEventInfoRec *pei = (DeviceEventInfoRec *)calldata;
835 RecordContextPtr pContext;
836 RecordClientsAndProtocolPtr pRCAP;
837 int eci; /* enabled context index */
839 for (eci = 0; eci < numEnabledContexts; eci++)
841 pContext = ppAllContexts[eci];
842 for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
844 if (pRCAP->pDeviceEventSet)
846 int ev; /* event index */
847 xEvent *pev = pei->events;
848 for (ev = 0; ev < pei->count; ev++, pev++)
850 if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet,
851 pev->u.u.type & 0177))
853 xEvent swappedEvent;
854 xEvent *pEvToRecord = pev;
855 #ifdef PANORAMIX
856 xEvent shiftedEvent;
858 if (!noPanoramiXExtension &&
859 (pev->u.u.type == MotionNotify ||
860 pev->u.u.type == ButtonPress ||
861 pev->u.u.type == ButtonRelease ||
862 pev->u.u.type == KeyPress ||
863 pev->u.u.type == KeyRelease)) {
864 int scr = XineramaGetCursorScreen();
865 memcpy(&shiftedEvent, pev, sizeof(xEvent));
866 shiftedEvent.u.keyButtonPointer.rootX +=
867 panoramiXdataPtr[scr].x -
868 panoramiXdataPtr[0].x;
869 shiftedEvent.u.keyButtonPointer.rootY +=
870 panoramiXdataPtr[scr].y -
871 panoramiXdataPtr[0].y;
872 pEvToRecord = &shiftedEvent;
874 #endif /* PANORAMIX */
876 if (pContext->pRecordingClient->swapped)
878 (*EventSwapVector[pEvToRecord->u.u.type & 0177])
879 (pEvToRecord, &swappedEvent);
880 pEvToRecord = &swappedEvent;
883 RecordAProtocolElement(pContext, NULL,
884 XRecordFromServer, pEvToRecord, SIZEOF(xEvent), 0);
885 /* make sure device events get flushed in the absence
886 * of other client activity
888 SetCriticalOutputPending();
890 } /* end for each event */
891 } /* end this RCAP selects device events */
892 } /* end for each RCAP on this context */
893 } /* end for each enabled context */
894 } /* RecordADeviceEvent */
897 /* RecordFlushAllContexts
899 * Arguments:
900 * pcbl is &FlushCallback.
901 * nulldata and calldata are NULL.
903 * Returns: nothing.
905 * Side Effects:
906 * All buffered reply data of all enabled contexts is written to
907 * the recording clients.
909 static void
910 RecordFlushAllContexts(
911 CallbackListPtr *pcbl,
912 pointer nulldata,
913 pointer calldata
916 int eci; /* enabled context index */
917 RecordContextPtr pContext;
919 for (eci = 0; eci < numEnabledContexts; eci++)
921 pContext = ppAllContexts[eci];
923 /* In most cases we leave it to RecordFlushReplyBuffer to make
924 * this check, but this function could be called very often, so we
925 * check before calling hoping to save the function call cost
926 * most of the time.
928 if (pContext->numBufBytes)
929 RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
931 } /* RecordFlushAllContexts */
934 /* RecordInstallHooks
936 * Arguments:
937 * pRCAP is an RCAP on an enabled or being-enabled context.
938 * oneclient can be zero or the resource ID mask identifying a client.
940 * Returns: BadAlloc if a memory allocation error occurred, else Success.
942 * Side Effects:
943 * Recording hooks needed by RCAP are installed.
944 * If oneclient is zero, recording hooks needed for all clients and
945 * protocol on the RCAP are installed. If oneclient is non-zero,
946 * only those hooks needed for the specified client are installed.
948 * Client requestVectors may be altered. numEnabledRCAPs will be
949 * incremented if oneclient == 0. Callbacks may be added to
950 * various callback lists.
952 static int
953 RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
955 int i = 0;
956 XID client;
958 if (oneclient)
959 client = oneclient;
960 else
961 client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
963 while (client)
965 if (client != XRecordFutureClients)
967 if (pRCAP->pRequestMajorOpSet)
969 RecordSetIteratePtr pIter = NULL;
970 RecordSetInterval interval;
971 ClientPtr pClient = clients[CLIENT_ID(client)];
973 if (pClient && !RecordClientPrivate(pClient))
975 RecordClientPrivatePtr pClientPriv;
976 /* no Record proc vector; allocate one */
977 pClientPriv = (RecordClientPrivatePtr)
978 xalloc(sizeof(RecordClientPrivateRec));
979 if (!pClientPriv)
980 return BadAlloc;
981 /* copy old proc vector to new */
982 memcpy(pClientPriv->recordVector, pClient->requestVector,
983 sizeof (pClientPriv->recordVector));
984 pClientPriv->originalVector = pClient->requestVector;
985 pClient->devPrivates[RecordClientPrivateIndex].ptr =
986 (pointer)pClientPriv;
987 pClient->requestVector = pClientPriv->recordVector;
989 while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
990 pIter, &interval)))
992 unsigned int j;
993 for (j = interval.first; j <= interval.last; j++)
994 pClient->requestVector[j] = RecordARequest;
998 if (oneclient)
999 client = 0;
1000 else
1001 client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
1004 assert(numEnabledRCAPs >= 0);
1005 if (!oneclient && ++numEnabledRCAPs == 1)
1006 { /* we're enabling the first context */
1007 if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
1008 return BadAlloc;
1009 if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
1010 return BadAlloc;
1011 if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
1012 return BadAlloc;
1013 if (!AddCallback(&SkippedRequestsCallback, RecordASkippedRequest,
1014 NULL))
1015 return BadAlloc;
1016 if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
1017 return BadAlloc;
1018 /* Alternate context flushing scheme: delete the line above
1019 * and call RegisterBlockAndWakeupHandlers here passing
1020 * RecordFlushAllContexts. Is this any better?
1023 return Success;
1024 } /* RecordInstallHooks */
1027 /* RecordUninstallHooks
1029 * Arguments:
1030 * pRCAP is an RCAP on an enabled or being-disabled context.
1031 * oneclient can be zero or the resource ID mask identifying a client.
1033 * Returns: nothing.
1035 * Side Effects:
1036 * Recording hooks needed by RCAP may be uninstalled.
1037 * If oneclient is zero, recording hooks needed for all clients and
1038 * protocol on the RCAP may be uninstalled. If oneclient is non-zero,
1039 * only those hooks needed for the specified client may be uninstalled.
1041 * Client requestVectors may be altered. numEnabledRCAPs will be
1042 * decremented if oneclient == 0. Callbacks may be deleted from
1043 * various callback lists.
1045 static void
1046 RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
1048 int i = 0;
1049 XID client;
1051 if (oneclient)
1052 client = oneclient;
1053 else
1054 client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
1056 while (client)
1058 if (client != XRecordFutureClients)
1060 if (pRCAP->pRequestMajorOpSet)
1062 ClientPtr pClient = clients[CLIENT_ID(client)];
1063 int c;
1064 Bool otherRCAPwantsProcVector = FALSE;
1065 RecordClientPrivatePtr pClientPriv =
1066 RecordClientPrivate(pClient);
1068 assert (pClient && RecordClientPrivate(pClient));
1069 memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
1070 sizeof (pClientPriv->recordVector));
1072 for (c = 0; c < numEnabledContexts; c++)
1074 RecordClientsAndProtocolPtr pOtherRCAP;
1075 RecordContextPtr pContext = ppAllContexts[c];
1077 if (pContext == pRCAP->pContext) continue;
1078 pOtherRCAP = RecordFindClientOnContext(pContext, client,
1079 NULL);
1080 if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet)
1082 RecordSetIteratePtr pIter = NULL;
1083 RecordSetInterval interval;
1085 otherRCAPwantsProcVector = TRUE;
1086 while ((pIter = RecordIterateSet(
1087 pOtherRCAP->pRequestMajorOpSet,
1088 pIter, &interval)))
1090 unsigned int j;
1091 for (j = interval.first; j <= interval.last; j++)
1092 pClient->requestVector[j] = RecordARequest;
1096 if (!otherRCAPwantsProcVector)
1097 { /* nobody needs it, so free it */
1098 pClient->requestVector = pClientPriv->originalVector;
1099 pClient->devPrivates[RecordClientPrivateIndex].ptr = NULL;
1100 xfree(pClientPriv);
1102 } /* end if this RCAP specifies any requests */
1103 } /* end if not future clients */
1104 if (oneclient)
1105 client = 0;
1106 else
1107 client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
1110 assert(numEnabledRCAPs >= 1);
1111 if (!oneclient && --numEnabledRCAPs == 0)
1112 { /* we're disabling the last context */
1113 DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
1114 DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
1115 DeleteCallback(&ReplyCallback, RecordAReply, NULL);
1116 DeleteCallback(&SkippedRequestsCallback, RecordASkippedRequest, NULL);
1117 DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
1118 /* Alternate context flushing scheme: delete the line above
1119 * and call RemoveBlockAndWakeupHandlers here passing
1120 * RecordFlushAllContexts. Is this any better?
1122 /* Having deleted the callback, call it one last time. -gildea */
1123 RecordFlushAllContexts(&FlushCallback, NULL, NULL);
1125 } /* RecordUninstallHooks */
1128 /* RecordDeleteClientFromRCAP
1130 * Arguments:
1131 * pRCAP is an RCAP to delete the client from.
1132 * position is the index into the array pRCAP->pClientIDs of the
1133 * client to delete.
1135 * Returns: nothing.
1137 * Side Effects:
1138 * Recording hooks needed by client will be uninstalled if the context
1139 * is enabled. The designated client will be removed from the
1140 * pRCAP->pClientIDs array. If it was the only client on the RCAP,
1141 * the RCAP is removed from the context and freed. (Invariant: RCAPs
1142 * have at least one client.)
1144 static void
1145 RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
1147 if (pRCAP->pContext->pRecordingClient)
1148 RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
1149 if (position != pRCAP->numClients - 1)
1150 pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
1151 if (--pRCAP->numClients == 0)
1152 { /* no more clients; remove RCAP from context's list */
1153 RecordContextPtr pContext = pRCAP->pContext;
1154 if (pContext->pRecordingClient)
1155 RecordUninstallHooks(pRCAP, 0);
1156 if (pContext->pListOfRCAP == pRCAP)
1157 pContext->pListOfRCAP = pRCAP->pNextRCAP;
1158 else
1160 RecordClientsAndProtocolPtr prevRCAP;
1161 for (prevRCAP = pContext->pListOfRCAP;
1162 prevRCAP->pNextRCAP != pRCAP;
1163 prevRCAP = prevRCAP->pNextRCAP)
1165 prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
1167 /* free the RCAP */
1168 if (pRCAP->clientIDsSeparatelyAllocated)
1169 xfree(pRCAP->pClientIDs);
1170 xfree(pRCAP);
1172 } /* RecordDeleteClientFromRCAP */
1175 /* RecordAddClientToRCAP
1177 * Arguments:
1178 * pRCAP is an RCAP to add the client to.
1179 * clientspec is the resource ID mask identifying a client, or
1180 * XRecordFutureClients.
1182 * Returns: nothing.
1184 * Side Effects:
1185 * Recording hooks needed by client will be installed if the context
1186 * is enabled. The designated client will be added to the
1187 * pRCAP->pClientIDs array, which may be realloced.
1188 * pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
1189 * is no more room to hold clients internal to the RCAP.
1191 static void
1192 RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
1194 if (pRCAP->numClients == pRCAP->sizeClients)
1196 if (pRCAP->clientIDsSeparatelyAllocated)
1198 XID *pNewIDs = (XID *)xrealloc(pRCAP->pClientIDs,
1199 (pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT) *
1200 sizeof(XID));
1201 if (!pNewIDs)
1202 return;
1203 pRCAP->pClientIDs = pNewIDs;
1204 pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1206 else
1208 XID *pNewIDs = (XID *)xalloc((pRCAP->sizeClients +
1209 CLIENT_ARRAY_GROWTH_INCREMENT) * sizeof(XID));
1210 if (!pNewIDs)
1211 return;
1212 memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients *sizeof(XID));
1213 pRCAP->pClientIDs = pNewIDs;
1214 pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1215 pRCAP->clientIDsSeparatelyAllocated = 1;
1218 pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
1219 if (pRCAP->pContext->pRecordingClient)
1220 RecordInstallHooks(pRCAP, clientspec);
1221 } /* RecordDeleteClientFromRCAP */
1224 /* RecordDeleteClientFromContext
1226 * Arguments:
1227 * pContext is the context to delete from.
1228 * clientspec is the resource ID mask identifying a client, or
1229 * XRecordFutureClients.
1231 * Returns: nothing.
1233 * Side Effects:
1234 * If clientspec is on any RCAP of the context, it is deleted from that
1235 * RCAP. (A given clientspec can only be on one RCAP of a context.)
1237 static void
1238 RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
1240 RecordClientsAndProtocolPtr pRCAP;
1241 int position;
1243 if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
1244 RecordDeleteClientFromRCAP(pRCAP, position);
1245 } /* RecordDeleteClientFromContext */
1248 /* RecordSanityCheckClientSpecifiers
1250 * Arguments:
1251 * clientspecs is an array of alleged CLIENTSPECs passed by the client.
1252 * nspecs is the number of elements in clientspecs.
1253 * errorspec, if non-zero, is the resource id base of a client that
1254 * must not appear in clienspecs.
1256 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
1258 * Side Effects: none.
1260 static int
1261 RecordSanityCheckClientSpecifiers(XID *clientspecs, int nspecs, XID errorspec)
1263 int i;
1264 int clientIndex;
1266 for (i = 0; i < nspecs; i++)
1268 if (clientspecs[i] == XRecordCurrentClients ||
1269 clientspecs[i] == XRecordFutureClients ||
1270 clientspecs[i] == XRecordAllClients)
1271 continue;
1272 if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec) )
1273 return BadMatch;
1274 clientIndex = CLIENT_ID(clientspecs[i]);
1275 if (clientIndex && clients[clientIndex] &&
1276 clients[clientIndex]->clientState == ClientStateRunning)
1278 if (clientspecs[i] == clients[clientIndex]->clientAsMask)
1279 continue;
1280 if (!LookupIDByClass(clientspecs[i], RC_ANY))
1281 return BadMatch;
1283 else
1284 return BadMatch;
1286 return Success;
1287 } /* RecordSanityCheckClientSpecifiers */
1290 /* RecordCanonicalizeClientSpecifiers
1292 * Arguments:
1293 * pClientspecs is an array of CLIENTSPECs that have been sanity
1294 * checked.
1295 * pNumClientspecs is a pointer to the number of elements in pClientspecs.
1296 * excludespec, if non-zero, is the resource id base of a client that
1297 * should not be included in the expansion of XRecordAllClients or
1298 * XRecordCurrentClients.
1300 * Returns:
1301 * A pointer to an array of CLIENTSPECs that is the same as the
1302 * passed array with the following modifications:
1303 * - all but the client id bits of resource IDs are stripped off.
1304 * - duplicates removed.
1305 * - XRecordAllClients expanded to a list of all currently connected
1306 * clients + XRecordFutureClients - excludespec (if non-zero)
1307 * - XRecordCurrentClients expanded to a list of all currently
1308 * connected clients - excludespec (if non-zero)
1309 * The returned array may be the passed array modified in place, or
1310 * it may be an Xalloc'ed array. The caller should keep a pointer to the
1311 * original array and free the returned array if it is different.
1313 * *pNumClientspecs is set to the number of elements in the returned
1314 * array.
1316 * Side Effects:
1317 * pClientspecs may be modified in place.
1319 static XID *
1320 RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs, XID excludespec)
1322 int i;
1323 int numClients = *pNumClientspecs;
1325 /* first pass strips off the resource index bits, leaving just the
1326 * client id bits. This makes searching for a particular client simpler
1327 * (and faster.)
1329 for (i = 0; i < numClients; i++)
1331 XID cs = pClientspecs[i];
1332 if (cs > XRecordAllClients)
1333 pClientspecs[i] = CLIENT_BITS(cs);
1336 for (i = 0; i < numClients; i++)
1338 if (pClientspecs[i] == XRecordAllClients ||
1339 pClientspecs[i] == XRecordCurrentClients)
1340 { /* expand All/Current */
1341 int j, nc;
1342 XID *pCanon = (XID *)xalloc(sizeof(XID) * (currentMaxClients + 1));
1343 if (!pCanon) return NULL;
1344 for (nc = 0, j = 1; j < currentMaxClients; j++)
1346 ClientPtr client = clients[j];
1347 if (client != NullClient &&
1348 client->clientState == ClientStateRunning &&
1349 client->clientAsMask != excludespec)
1351 pCanon[nc++] = client->clientAsMask;
1354 if (pClientspecs[i] == XRecordAllClients)
1355 pCanon[nc++] = XRecordFutureClients;
1356 *pNumClientspecs = nc;
1357 return pCanon;
1359 else /* not All or Current */
1361 int j;
1362 for (j = i + 1; j < numClients; )
1364 if (pClientspecs[i] == pClientspecs[j])
1366 pClientspecs[j] = pClientspecs[--numClients];
1368 else
1369 j++;
1372 } /* end for each clientspec */
1373 *pNumClientspecs = numClients;
1374 return pClientspecs;
1375 } /* RecordCanonicalizeClientSpecifiers */
1378 /****************************************************************************/
1380 /* stuff for RegisterClients */
1382 /* RecordPadAlign
1384 * Arguments:
1385 * size is the number of bytes taken by an object.
1386 * align is a byte boundary (e.g. 4, 8)
1388 * Returns:
1389 * the number of pad bytes to add at the end of an object of the
1390 * given size so that an object placed immediately behind it will
1391 * begin on an <align>-byte boundary.
1393 * Side Effects: none.
1395 static int
1396 RecordPadAlign(int size, int align)
1398 return (align - (size & (align - 1))) & (align - 1);
1399 } /* RecordPadAlign */
1402 /* RecordSanityCheckRegisterClients
1404 * Arguments:
1405 * pContext is the context being registered on.
1406 * client is the client that issued a RecordCreateContext or
1407 * RecordRegisterClients request.
1408 * stuff is a pointer to the request.
1410 * Returns:
1411 * Any one of several possible error values if any of the request
1412 * arguments are invalid. Success if everything is OK.
1414 * Side Effects: none.
1416 static int
1417 RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
1419 int err;
1420 xRecordRange *pRange;
1421 int i;
1422 XID recordingClient;
1424 if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
1425 4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
1426 return BadLength;
1428 if (stuff->elementHeader &
1429 ~(XRecordFromClientSequence|XRecordFromClientTime|XRecordFromServerTime))
1431 client->errorValue = stuff->elementHeader;
1432 return BadValue;
1435 recordingClient = pContext->pRecordingClient ?
1436 pContext->pRecordingClient->clientAsMask : 0;
1437 err = RecordSanityCheckClientSpecifiers((XID *)&stuff[1], stuff->nClients,
1438 recordingClient);
1439 if (err != Success) return err;
1441 pRange = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
1442 for (i = 0; i < stuff->nRanges; i++, pRange++)
1444 if (pRange->coreRequestsFirst > pRange->coreRequestsLast)
1446 client->errorValue = pRange->coreRequestsFirst;
1447 return BadValue;
1449 if (pRange->coreRepliesFirst > pRange->coreRepliesLast)
1451 client->errorValue = pRange->coreRepliesFirst;
1452 return BadValue;
1454 if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
1455 (pRange->extRequestsMajorFirst < 128 ||
1456 pRange->extRequestsMajorLast < 128 ||
1457 pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast))
1459 client->errorValue = pRange->extRequestsMajorFirst;
1460 return BadValue;
1462 if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast)
1464 client->errorValue = pRange->extRequestsMinorFirst;
1465 return BadValue;
1467 if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
1468 (pRange->extRepliesMajorFirst < 128 ||
1469 pRange->extRepliesMajorLast < 128 ||
1470 pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast))
1472 client->errorValue = pRange->extRepliesMajorFirst;
1473 return BadValue;
1475 if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast)
1477 client->errorValue = pRange->extRepliesMinorFirst;
1478 return BadValue;
1480 if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
1481 (pRange->deliveredEventsFirst < 2 ||
1482 pRange->deliveredEventsLast < 2 ||
1483 pRange->deliveredEventsFirst > pRange->deliveredEventsLast))
1485 client->errorValue = pRange->deliveredEventsFirst;
1486 return BadValue;
1488 if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
1489 (pRange->deviceEventsFirst < 2 ||
1490 pRange->deviceEventsLast < 2 ||
1491 pRange->deviceEventsFirst > pRange->deviceEventsLast))
1493 client->errorValue = pRange->deviceEventsFirst;
1494 return BadValue;
1496 if (pRange->errorsFirst > pRange->errorsLast)
1498 client->errorValue = pRange->errorsFirst;
1499 return BadValue;
1501 if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue)
1503 client->errorValue = pRange->clientStarted;
1504 return BadValue;
1506 if (pRange->clientDied != xFalse && pRange->clientDied != xTrue)
1508 client->errorValue = pRange->clientDied;
1509 return BadValue;
1511 } /* end for each range */
1512 return Success;
1513 } /* end RecordSanityCheckRegisterClients */
1515 /* This is a tactical structure used to gather information about all the sets
1516 * (RecordSetPtr) that need to be created for an RCAP in the process of
1517 * digesting a list of RECORDRANGEs (converting it to the internal
1518 * representation).
1520 typedef struct
1522 int nintervals; /* number of intervals in following array */
1523 RecordSetInterval *intervals; /* array of intervals for this set */
1524 int size; /* size of intevals array; >= nintervals */
1525 int align; /* alignment restriction for set */
1526 int offset; /* where to store set pointer rel. to start of RCAP */
1527 short first, last; /* if for extension, major opcode interval */
1528 } SetInfoRec, *SetInfoPtr;
1530 /* These constant are used to index into an array of SetInfoRec. */
1531 enum {REQ, /* set info for requests */
1532 REP, /* set info for replies */
1533 ERR, /* set info for errors */
1534 DEV, /* set info for device events */
1535 DLEV, /* set info for delivered events */
1536 PREDEFSETS}; /* number of predefined array entries */
1539 /* RecordAllocIntervals
1541 * Arguments:
1542 * psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
1543 * nIntervals is the desired size of the intervals array.
1545 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1547 * Side Effects:
1548 * If Success is returned, psi->intervals is a pointer to size
1549 * RecordSetIntervals, all zeroed, and psi->size is set to size.
1551 static int
1552 RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
1554 assert(!psi->intervals);
1555 psi->intervals = (RecordSetInterval *)
1556 xalloc(nIntervals * sizeof(RecordSetInterval));
1557 if (!psi->intervals)
1558 return BadAlloc;
1559 bzero(psi->intervals, nIntervals * sizeof(RecordSetInterval));
1560 psi->size = nIntervals;
1561 return Success;
1562 } /* end RecordAllocIntervals */
1565 /* RecordConvertRangesToIntervals
1567 * Arguments:
1568 * psi is a pointer to the SetInfoRec we are building.
1569 * pRanges is an array of xRecordRanges.
1570 * nRanges is the number of elements in pRanges.
1571 * byteoffset is the offset from the start of an xRecordRange of the
1572 * two bytes (1 for first, 1 for last) we are interested in.
1573 * pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
1574 * above are followed by four bytes (2 for first, 2 for last)
1575 * representing a minor opcode range, and this information should be
1576 * stored in one of the SetInfoRecs starting at pExtSetInfo.
1577 * pnExtSetInfo is the number of elements in the pExtSetInfo array.
1579 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1581 * Side Effects:
1582 * The slice of pRanges indicated by byteoffset is stored in psi.
1583 * If pExtSetInfo is non-NULL, minor opcode intervals are stored
1584 * in an existing SetInfoRec if the major opcode interval matches, else
1585 * they are stored in a new SetInfoRec, and *pnExtSetInfo is
1586 * increased accordingly.
1588 static int
1589 RecordConvertRangesToIntervals(
1590 SetInfoPtr psi,
1591 xRecordRange *pRanges,
1592 int nRanges,
1593 int byteoffset,
1594 SetInfoPtr pExtSetInfo,
1595 int *pnExtSetInfo
1598 int i;
1599 CARD8 *pCARD8;
1600 int first, last;
1601 int err;
1603 for (i = 0; i < nRanges; i++, pRanges++)
1605 pCARD8 = ((CARD8 *)pRanges) + byteoffset;
1606 first = pCARD8[0];
1607 last = pCARD8[1];
1608 if (first || last)
1610 if (!psi->intervals)
1612 err = RecordAllocIntervals(psi, 2 * (nRanges - i));
1613 if (err != Success)
1614 return err;
1616 psi->intervals[psi->nintervals].first = first;
1617 psi->intervals[psi->nintervals].last = last;
1618 psi->nintervals++;
1619 assert(psi->nintervals <= psi->size);
1620 if (pExtSetInfo)
1622 SetInfoPtr pesi = pExtSetInfo;
1623 CARD16 *pCARD16 = (CARD16 *)(pCARD8 + 2);
1624 int j;
1626 for (j = 0; j < *pnExtSetInfo; j++, pesi++)
1628 if ( (first == pesi->first) && (last == pesi->last) )
1629 break;
1631 if (j == *pnExtSetInfo)
1633 err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
1634 if (err != Success)
1635 return err;
1636 pesi->first = first;
1637 pesi->last = last;
1638 (*pnExtSetInfo)++;
1640 pesi->intervals[pesi->nintervals].first = pCARD16[0];
1641 pesi->intervals[pesi->nintervals].last = pCARD16[1];
1642 pesi->nintervals++;
1643 assert(pesi->nintervals <= pesi->size);
1647 return Success;
1648 } /* end RecordConvertRangesToIntervals */
1650 #define offset_of(_structure, _field) \
1651 ((char *)(& (_structure . _field)) - (char *)(&_structure))
1653 /* RecordRegisterClients
1655 * Arguments:
1656 * pContext is the context on which to register the clients.
1657 * client is the client that issued the RecordCreateContext or
1658 * RecordRegisterClients request.
1659 * stuff is a pointer to the request.
1661 * Returns:
1662 * Any one of several possible error values defined by the protocol.
1663 * Success if everything is OK.
1665 * Side Effects:
1666 * If different element headers are specified, the context is flushed.
1667 * If any of the specified clients are already registered on the
1668 * context, they are first unregistered. A new RCAP is created to
1669 * hold the specified protocol and clients, and it is linked onto the
1670 * context. If the context is enabled, appropriate hooks are installed
1671 * to record the new clients and protocol.
1673 static int
1674 RecordRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
1676 int err;
1677 int i;
1678 SetInfoPtr si;
1679 int maxSets;
1680 int nExtReqSets = 0;
1681 int nExtRepSets = 0;
1682 int extReqSetsOffset = 0;
1683 int extRepSetsOffset = 0;
1684 SetInfoPtr pExtReqSets, pExtRepSets;
1685 int clientListOffset;
1686 XID *pCanonClients;
1687 int clientStarted = 0, clientDied = 0;
1688 xRecordRange *pRanges, rr;
1689 int nClients;
1690 int sizeClients;
1691 int totRCAPsize;
1692 RecordClientsAndProtocolPtr pRCAP;
1693 int pad;
1694 XID recordingClient;
1696 /* do all sanity checking up front */
1698 err = RecordSanityCheckRegisterClients(pContext, client, stuff);
1699 if (err != Success)
1700 return err;
1702 /* if element headers changed, flush buffer */
1704 if (pContext->elemHeaders != stuff->elementHeader)
1706 RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
1707 pContext->elemHeaders = stuff->elementHeader;
1710 nClients = stuff->nClients;
1711 if (!nClients)
1712 /* if empty clients list, we're done. */
1713 return Success;
1715 recordingClient = pContext->pRecordingClient ?
1716 pContext->pRecordingClient->clientAsMask : 0;
1717 pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
1718 &nClients, recordingClient);
1719 if (!pCanonClients)
1720 return BadAlloc;
1722 /* We may have to create as many as one set for each "predefined"
1723 * protocol types, plus one per range for extension reuests, plus one per
1724 * range for extension replies.
1726 maxSets = PREDEFSETS + 2 * stuff->nRanges;
1727 si = (SetInfoPtr)ALLOCATE_LOCAL(sizeof(SetInfoRec) * maxSets);
1728 if (!si)
1730 err = BadAlloc;
1731 goto bailout;
1733 bzero(si, sizeof(SetInfoRec) * maxSets);
1735 /* theoretically you must do this because NULL may not be all-bits-zero */
1736 for (i = 0; i < maxSets; i++)
1737 si[i].intervals = NULL;
1739 pExtReqSets = si + PREDEFSETS;
1740 pExtRepSets = pExtReqSets + stuff->nRanges;
1742 pRanges = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
1744 err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1745 offset_of(rr, coreRequestsFirst), NULL, NULL);
1746 if (err != Success) goto bailout;
1748 err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1749 offset_of(rr, extRequestsMajorFirst), pExtReqSets, &nExtReqSets);
1750 if (err != Success) goto bailout;
1752 err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1753 offset_of(rr, coreRepliesFirst), NULL, NULL);
1754 if (err != Success) goto bailout;
1756 err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1757 offset_of(rr, extRepliesMajorFirst), pExtRepSets, &nExtRepSets);
1758 if (err != Success) goto bailout;
1760 err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
1761 offset_of(rr, errorsFirst), NULL, NULL);
1762 if (err != Success) goto bailout;
1764 err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
1765 offset_of(rr, deliveredEventsFirst), NULL, NULL);
1766 if (err != Success) goto bailout;
1768 err = RecordConvertRangesToIntervals(&si[DEV], pRanges, stuff->nRanges,
1769 offset_of(rr, deviceEventsFirst), NULL, NULL);
1770 if (err != Success) goto bailout;
1772 /* collect client-started and client-died */
1774 for (i = 0; i < stuff->nRanges; i++)
1776 if (pRanges[i].clientStarted) clientStarted = TRUE;
1777 if (pRanges[i].clientDied) clientDied = TRUE;
1780 /* We now have all the information collected to create all the sets,
1781 * and we can compute the total memory required for the RCAP.
1784 totRCAPsize = sizeof(RecordClientsAndProtocolRec);
1786 /* leave a little room to grow before forcing a separate allocation */
1787 sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
1788 pad = RecordPadAlign(totRCAPsize, sizeof(XID));
1789 clientListOffset = totRCAPsize + pad;
1790 totRCAPsize += pad + sizeClients * sizeof(XID);
1792 if (nExtReqSets)
1794 pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1795 extReqSetsOffset = totRCAPsize + pad;
1796 totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
1798 if (nExtRepSets)
1800 pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1801 extRepSetsOffset = totRCAPsize + pad;
1802 totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
1805 for (i = 0; i < maxSets; i++)
1807 if (si[i].nintervals)
1809 si[i].size = RecordSetMemoryRequirements(
1810 si[i].intervals, si[i].nintervals, &si[i].align);
1811 pad = RecordPadAlign(totRCAPsize, si[i].align);
1812 si[i].offset = pad + totRCAPsize;
1813 totRCAPsize += pad + si[i].size;
1817 /* allocate memory for the whole RCAP */
1819 pRCAP = (RecordClientsAndProtocolPtr)xalloc(totRCAPsize);
1820 if (!pRCAP)
1822 err = BadAlloc;
1823 goto bailout;
1826 /* fill in the RCAP */
1828 pRCAP->pContext = pContext;
1829 pRCAP->pClientIDs = (XID *)((char *)pRCAP + clientListOffset);
1830 pRCAP->numClients = nClients;
1831 pRCAP->sizeClients = sizeClients;
1832 pRCAP->clientIDsSeparatelyAllocated = 0;
1833 for (i = 0; i < nClients; i++)
1835 RecordDeleteClientFromContext(pContext, pCanonClients[i]);
1836 pRCAP->pClientIDs[i] = pCanonClients[i];
1839 /* create all the sets */
1841 if (si[REQ].intervals)
1843 pRCAP->pRequestMajorOpSet =
1844 RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
1845 (RecordSetPtr)((char *)pRCAP + si[REQ].offset), si[REQ].size);
1847 else pRCAP->pRequestMajorOpSet = NULL;
1849 if (si[REP].intervals)
1851 pRCAP->pReplyMajorOpSet =
1852 RecordCreateSet(si[REP].intervals, si[REP].nintervals,
1853 (RecordSetPtr)((char *)pRCAP + si[REP].offset), si[REP].size);
1855 else pRCAP->pReplyMajorOpSet = NULL;
1857 if (si[ERR].intervals)
1859 pRCAP->pErrorSet =
1860 RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
1861 (RecordSetPtr)((char *)pRCAP + si[ERR].offset), si[ERR].size);
1863 else pRCAP->pErrorSet = NULL;
1865 if (si[DEV].intervals)
1867 pRCAP->pDeviceEventSet =
1868 RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
1869 (RecordSetPtr)((char *)pRCAP + si[DEV].offset), si[DEV].size);
1871 else pRCAP->pDeviceEventSet = NULL;
1873 if (si[DLEV].intervals)
1875 pRCAP->pDeliveredEventSet =
1876 RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
1877 (RecordSetPtr)((char *)pRCAP + si[DLEV].offset), si[DLEV].size);
1879 else pRCAP->pDeliveredEventSet = NULL;
1881 if (nExtReqSets)
1883 pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
1884 ((char *)pRCAP + extReqSetsOffset);
1885 pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
1886 for (i = 0; i < nExtReqSets; i++, pExtReqSets++)
1888 pRCAP->pRequestMinOpInfo[i+1].major.first = pExtReqSets->first;
1889 pRCAP->pRequestMinOpInfo[i+1].major.last = pExtReqSets->last;
1890 pRCAP->pRequestMinOpInfo[i+1].major.pMinOpSet =
1891 RecordCreateSet(pExtReqSets->intervals,
1892 pExtReqSets->nintervals,
1893 (RecordSetPtr)((char *)pRCAP + pExtReqSets->offset),
1894 pExtReqSets->size);
1897 else pRCAP->pRequestMinOpInfo = NULL;
1899 if (nExtRepSets)
1901 pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
1902 ((char *)pRCAP + extRepSetsOffset);
1903 pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
1904 for (i = 0; i < nExtRepSets; i++, pExtRepSets++)
1906 pRCAP->pReplyMinOpInfo[i+1].major.first = pExtRepSets->first;
1907 pRCAP->pReplyMinOpInfo[i+1].major.last = pExtRepSets->last;
1908 pRCAP->pReplyMinOpInfo[i+1].major.pMinOpSet =
1909 RecordCreateSet(pExtRepSets->intervals,
1910 pExtRepSets->nintervals,
1911 (RecordSetPtr)((char *)pRCAP + pExtRepSets->offset),
1912 pExtRepSets->size);
1915 else pRCAP->pReplyMinOpInfo = NULL;
1917 pRCAP->clientStarted = clientStarted;
1918 pRCAP->clientDied = clientDied;
1920 /* link the RCAP onto the context */
1922 pRCAP->pNextRCAP = pContext->pListOfRCAP;
1923 pContext->pListOfRCAP = pRCAP;
1925 if (pContext->pRecordingClient) /* context enabled */
1926 RecordInstallHooks(pRCAP, 0);
1928 bailout:
1929 if (si)
1931 for (i = 0; i < maxSets; i++)
1932 if (si[i].intervals)
1933 xfree(si[i].intervals);
1934 DEALLOCATE_LOCAL(si);
1936 if (pCanonClients && pCanonClients != (XID *)&stuff[1])
1937 xfree(pCanonClients);
1938 return err;
1939 } /* RecordRegisterClients */
1942 /* Proc functions all take a client argument, execute the request in
1943 * client->requestBuffer, and return a protocol error status.
1946 static int
1947 ProcRecordQueryVersion(ClientPtr client)
1949 /* REQUEST(xRecordQueryVersionReq); */
1950 xRecordQueryVersionReply rep;
1951 int n;
1953 REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
1954 rep.type = X_Reply;
1955 rep.sequenceNumber = client->sequence;
1956 rep.length = 0;
1957 rep.majorVersion = RECORD_MAJOR_VERSION;
1958 rep.minorVersion = RECORD_MINOR_VERSION;
1959 if(client->swapped)
1961 swaps(&rep.sequenceNumber, n);
1962 swaps(&rep.majorVersion, n);
1963 swaps(&rep.minorVersion, n);
1965 (void)WriteToClient(client, sizeof(xRecordQueryVersionReply),
1966 (char *)&rep);
1967 return (client->noClientException);
1968 } /* ProcRecordQueryVersion */
1971 static int
1972 ProcRecordCreateContext(ClientPtr client)
1974 REQUEST(xRecordCreateContextReq);
1975 RecordContextPtr pContext;
1976 RecordContextPtr *ppNewAllContexts = NULL;
1977 int err = BadAlloc;
1979 REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
1980 LEGAL_NEW_RESOURCE(stuff->context, client);
1982 pContext = (RecordContextPtr)xalloc(sizeof(RecordContextRec));
1983 if (!pContext)
1984 goto bailout;
1986 /* make sure there is room in ppAllContexts to store the new context */
1988 ppNewAllContexts = (RecordContextPtr *)
1989 xrealloc(ppAllContexts, sizeof(RecordContextPtr) * (numContexts + 1));
1990 if (!ppNewAllContexts)
1991 goto bailout;
1992 ppAllContexts = ppNewAllContexts;
1994 pContext->id = stuff->context;
1995 pContext->pRecordingClient = NULL;
1996 pContext->pListOfRCAP = NULL;
1997 pContext->elemHeaders = 0;
1998 pContext->bufCategory = 0;
1999 pContext->numBufBytes = 0;
2000 pContext->pBufClient = NULL;
2001 pContext->continuedReply = 0;
2003 err = RecordRegisterClients(pContext, client,
2004 (xRecordRegisterClientsReq *)stuff);
2005 if (err != Success)
2006 goto bailout;
2008 if (AddResource(pContext->id, RTContext, pContext))
2010 ppAllContexts[numContexts++] = pContext;
2011 return Success;
2013 else
2015 RecordDeleteContext((pointer)pContext, pContext->id);
2016 err = BadAlloc;
2018 bailout:
2019 if (pContext)
2020 xfree(pContext);
2021 return err;
2022 } /* ProcRecordCreateContext */
2025 static int
2026 ProcRecordRegisterClients(ClientPtr client)
2028 RecordContextPtr pContext;
2029 REQUEST(xRecordRegisterClientsReq);
2031 REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
2032 VERIFY_CONTEXT(pContext, stuff->context, client);
2034 return RecordRegisterClients(pContext, client, stuff);
2035 } /* ProcRecordRegisterClients */
2038 static int
2039 ProcRecordUnregisterClients(ClientPtr client)
2041 RecordContextPtr pContext;
2042 int err;
2043 REQUEST(xRecordUnregisterClientsReq);
2044 XID *pCanonClients;
2045 int nClients;
2046 int i;
2048 REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
2049 if ((client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
2050 4 * stuff->nClients)
2051 return BadLength;
2052 VERIFY_CONTEXT(pContext, stuff->context, client);
2053 err = RecordSanityCheckClientSpecifiers((XID *)&stuff[1],
2054 stuff->nClients, 0);
2055 if (err != Success)
2056 return err;
2058 nClients = stuff->nClients;
2059 pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
2060 &nClients, 0);
2061 if (!pCanonClients)
2062 return BadAlloc;
2064 for (i = 0; i < nClients; i++)
2066 RecordDeleteClientFromContext(pContext, pCanonClients[i]);
2068 if (pCanonClients != (XID *)&stuff[1])
2069 xfree(pCanonClients);
2070 return Success;
2071 } /* ProcRecordUnregisterClients */
2074 /****************************************************************************/
2076 /* stuff for GetContext */
2078 /* This is a tactical structure used to hold the xRecordRanges as they are
2079 * being reconstituted from the sets in the RCAPs.
2082 typedef struct {
2083 xRecordRange *pRanges; /* array of xRecordRanges for one RCAP */
2084 int size; /* number of elements in pRanges, >= nRanges */
2085 int nRanges; /* number of occupied element of pRanges */
2086 } GetContextRangeInfoRec, *GetContextRangeInfoPtr;
2089 /* RecordAllocRanges
2091 * Arguments:
2092 * pri is a pointer to a GetContextRangeInfoRec to allocate for.
2093 * nRanges is the number of xRecordRanges desired for pri.
2095 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2097 * Side Effects:
2098 * If Success is returned, pri->pRanges points to at least nRanges
2099 * ranges. pri->nRanges is set to nRanges. pri->size is the actual
2100 * number of ranges. Newly allocated ranges are zeroed.
2102 static int
2103 RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
2105 int newsize;
2106 xRecordRange *pNewRange;
2107 #define SZINCR 8
2109 newsize = max(pri->size + SZINCR, nRanges);
2110 pNewRange = (xRecordRange *)xrealloc(pri->pRanges,
2111 newsize * sizeof(xRecordRange));
2112 if (!pNewRange)
2113 return BadAlloc;
2115 pri->pRanges = pNewRange;
2116 pri->size = newsize;
2117 bzero(&pri->pRanges[pri->size - SZINCR], SZINCR * sizeof(xRecordRange));
2118 if (pri->nRanges < nRanges)
2119 pri->nRanges = nRanges;
2120 return Success;
2121 } /* RecordAllocRanges */
2124 /* RecordConvertSetToRanges
2126 * Arguments:
2127 * pSet is the set to be converted.
2128 * pri is where the result should be stored.
2129 * byteoffset is the offset from the start of an xRecordRange of the
2130 * two vales (first, last) we are interested in.
2131 * card8 is TRUE if the vales are one byte each and FALSE if two bytes
2132 * each.
2133 * imax is the largest set value to store in pri->pRanges.
2134 * pStartIndex, if non-NULL, is the index of the first range in
2135 * pri->pRanges that should be stored to. If NULL,
2136 * start at index 0.
2138 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2140 * Side Effects:
2141 * If Success is returned, the slice of pri->pRanges indicated by
2142 * byteoffset and card8 is filled in with the intervals from pSet.
2143 * if pStartIndex was non-NULL, *pStartIndex is filled in with one
2144 * more than the index of the last xRecordRange that was touched.
2146 static int
2147 RecordConvertSetToRanges(
2148 RecordSetPtr pSet,
2149 GetContextRangeInfoPtr pri,
2150 int byteoffset,
2151 Bool card8,
2152 unsigned int imax,
2153 int *pStartIndex
2156 int nRanges;
2157 RecordSetIteratePtr pIter = NULL;
2158 RecordSetInterval interval;
2159 CARD8 *pCARD8;
2160 CARD16 *pCARD16;
2161 int err;
2163 if (!pSet)
2164 return Success;
2166 nRanges = pStartIndex ? *pStartIndex : 0;
2167 while ((pIter = RecordIterateSet(pSet, pIter, &interval)))
2169 if (interval.first > imax) break;
2170 if (interval.last > imax) interval.last = imax;
2171 nRanges++;
2172 if (nRanges > pri->size)
2174 err = RecordAllocRanges(pri, nRanges);
2175 if (err != Success)
2176 return err;
2178 else
2179 pri->nRanges = max(pri->nRanges, nRanges);
2180 if (card8)
2182 pCARD8 = ((CARD8 *)&pri->pRanges[nRanges-1]) + byteoffset;
2183 *pCARD8++ = interval.first;
2184 *pCARD8 = interval.last;
2186 else
2188 pCARD16 = (CARD16 *)
2189 (((char *)&pri->pRanges[nRanges-1]) + byteoffset);
2190 *pCARD16++ = interval.first;
2191 *pCARD16 = interval.last;
2194 if (pStartIndex)
2195 *pStartIndex = nRanges;
2196 return Success;
2197 } /* RecordConvertSetToRanges */
2200 /* RecordConvertMinorOpInfoToRanges
2202 * Arguments:
2203 * pMinOpInfo is the minor opcode info to convert to xRecordRanges.
2204 * pri is where the result should be stored.
2205 * byteoffset is the offset from the start of an xRecordRange of the
2206 * four vales (CARD8 major_first, CARD8 major_last,
2207 * CARD16 minor_first, CARD16 minor_last) we are going to store.
2209 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2211 * Side Effects:
2212 * If Success is returned, the slice of pri->pRanges indicated by
2213 * byteoffset is filled in with the information from pMinOpInfo.
2215 static int
2216 RecordConvertMinorOpInfoToRanges(
2217 RecordMinorOpPtr pMinOpInfo,
2218 GetContextRangeInfoPtr pri,
2219 int byteoffset
2222 int nsets;
2223 int start;
2224 int i;
2225 int err;
2227 if (!pMinOpInfo)
2228 return Success;
2230 nsets = pMinOpInfo->count;
2231 pMinOpInfo++;
2232 start = 0;
2233 for (i = 0; i < nsets; i++)
2235 int j, s;
2236 s = start;
2237 err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
2238 byteoffset + 2, FALSE, 65535, &start);
2239 if (err != Success) return err;
2240 for (j = s; j < start; j++)
2242 CARD8 *pCARD8 = ((CARD8 *)&pri->pRanges[j]) + byteoffset;
2243 *pCARD8++ = pMinOpInfo[i].major.first;
2244 *pCARD8 = pMinOpInfo[i].major.last;
2247 return Success;
2248 } /* RecordConvertMinorOpInfoToRanges */
2251 /* RecordSwapRanges
2253 * Arguments:
2254 * pRanges is an array of xRecordRanges.
2255 * nRanges is the number of elements in pRanges.
2257 * Returns: nothing.
2259 * Side Effects:
2260 * The 16 bit fields of each xRecordRange are byte swapped.
2262 static void
2263 RecordSwapRanges(xRecordRange *pRanges, int nRanges)
2265 int i;
2266 register char n;
2267 for (i = 0; i < nRanges; i++, pRanges++)
2269 swaps(&pRanges->extRequestsMinorFirst, n);
2270 swaps(&pRanges->extRequestsMinorLast, n);
2271 swaps(&pRanges->extRepliesMinorFirst, n);
2272 swaps(&pRanges->extRepliesMinorLast, n);
2274 } /* RecordSwapRanges */
2277 static int
2278 ProcRecordGetContext(ClientPtr client)
2280 RecordContextPtr pContext;
2281 REQUEST(xRecordGetContextReq);
2282 xRecordGetContextReply rep;
2283 int n;
2284 RecordClientsAndProtocolPtr pRCAP;
2285 int nRCAPs = 0;
2286 GetContextRangeInfoPtr pRangeInfo;
2287 GetContextRangeInfoPtr pri;
2288 int i;
2289 int err;
2291 REQUEST_SIZE_MATCH(xRecordGetContextReq);
2292 VERIFY_CONTEXT(pContext, stuff->context, client);
2294 /* how many RCAPs are there on this context? */
2296 for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2297 nRCAPs++;
2299 /* allocate and initialize space for record range info */
2301 pRangeInfo = (GetContextRangeInfoPtr)ALLOCATE_LOCAL(
2302 nRCAPs * sizeof(GetContextRangeInfoRec));
2303 if (!pRangeInfo && nRCAPs > 0)
2304 return BadAlloc;
2305 for (i = 0; i < nRCAPs; i++)
2307 pRangeInfo[i].pRanges = NULL;
2308 pRangeInfo[i].size = 0;
2309 pRangeInfo[i].nRanges = 0;
2312 /* convert the RCAP (internal) representation of the recorded protocol
2313 * to the wire protocol (external) representation, storing the information
2314 * for the ith RCAP in pri[i]
2317 for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2318 pRCAP;
2319 pRCAP = pRCAP->pNextRCAP, pri++)
2321 xRecordRange rr;
2323 err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
2324 offset_of(rr, coreRequestsFirst), TRUE, 127, NULL);
2325 if (err != Success) goto bailout;
2327 err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
2328 offset_of(rr, coreRepliesFirst), TRUE, 127, NULL);
2329 if (err != Success) goto bailout;
2331 err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
2332 offset_of(rr, deliveredEventsFirst), TRUE, 255, NULL);
2333 if (err != Success) goto bailout;
2335 err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
2336 offset_of(rr, deviceEventsFirst), TRUE, 255, NULL);
2337 if (err != Success) goto bailout;
2339 err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
2340 offset_of(rr, errorsFirst), TRUE, 255, NULL);
2341 if (err != Success) goto bailout;
2343 err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
2344 pri, offset_of(rr, extRequestsMajorFirst));
2345 if (err != Success) goto bailout;
2347 err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
2348 pri, offset_of(rr, extRepliesMajorFirst));
2349 if (err != Success) goto bailout;
2351 if (pRCAP->clientStarted || pRCAP->clientDied)
2353 if (pri->nRanges == 0)
2354 RecordAllocRanges(pri, 1);
2355 pri->pRanges[0].clientStarted = pRCAP->clientStarted;
2356 pri->pRanges[0].clientDied = pRCAP->clientDied;
2360 /* calculate number of clients and reply length */
2362 rep.nClients = 0;
2363 rep.length = 0;
2364 for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2365 pRCAP;
2366 pRCAP = pRCAP->pNextRCAP, pri++)
2368 rep.nClients += pRCAP->numClients;
2369 rep.length += pRCAP->numClients *
2370 ( (sizeof(xRecordClientInfo) >> 2) +
2371 pri->nRanges * (sizeof(xRecordRange) >> 2));
2374 /* write the reply header */
2376 rep.type = X_Reply;
2377 rep.sequenceNumber = client->sequence;
2378 rep.enabled = pContext->pRecordingClient != NULL;
2379 rep.elementHeader = pContext->elemHeaders;
2380 if(client->swapped)
2382 swaps(&rep.sequenceNumber, n);
2383 swapl(&rep.length, n);
2384 swapl(&rep.nClients, n);
2386 (void)WriteToClient(client, sizeof(xRecordGetContextReply),
2387 (char *)&rep);
2389 /* write all the CLIENT_INFOs */
2391 for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2392 pRCAP;
2393 pRCAP = pRCAP->pNextRCAP, pri++)
2395 xRecordClientInfo rci;
2396 rci.nRanges = pri->nRanges;
2397 if (client->swapped)
2399 swapl(&rci.nRanges, n);
2400 RecordSwapRanges(pri->pRanges, pri->nRanges);
2402 for (i = 0; i < pRCAP->numClients; i++)
2404 rci.clientResource = pRCAP->pClientIDs[i];
2405 if (client->swapped) swapl(&rci.clientResource, n);
2406 WriteToClient(client, sizeof(xRecordClientInfo), (char *)&rci);
2407 WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
2408 (char *)pri->pRanges);
2411 err = client->noClientException;
2413 bailout:
2414 for (i = 0; i < nRCAPs; i++)
2416 if (pRangeInfo[i].pRanges) xfree(pRangeInfo[i].pRanges);
2418 DEALLOCATE_LOCAL(pRangeInfo);
2419 return err;
2420 } /* ProcRecordGetContext */
2423 static int
2424 ProcRecordEnableContext(ClientPtr client)
2426 RecordContextPtr pContext;
2427 REQUEST(xRecordEnableContextReq);
2428 int i;
2429 RecordClientsAndProtocolPtr pRCAP;
2431 REQUEST_SIZE_MATCH(xRecordGetContextReq);
2432 VERIFY_CONTEXT(pContext, stuff->context, client);
2433 if (pContext->pRecordingClient)
2434 return BadMatch; /* already enabled */
2436 /* install record hooks for each RCAP */
2438 for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2440 int err = RecordInstallHooks(pRCAP, 0);
2441 if (err != Success)
2442 { /* undo the previous installs */
2443 RecordClientsAndProtocolPtr pUninstallRCAP;
2444 for (pUninstallRCAP = pContext->pListOfRCAP;
2445 pUninstallRCAP != pRCAP;
2446 pUninstallRCAP = pUninstallRCAP->pNextRCAP)
2448 RecordUninstallHooks(pUninstallRCAP, 0);
2450 return err;
2454 /* Disallow further request processing on this connection until
2455 * the context is disabled.
2457 IgnoreClient(client);
2458 pContext->pRecordingClient = client;
2460 /* Don't allow the data connection to record itself; unregister it. */
2461 RecordDeleteClientFromContext(pContext,
2462 pContext->pRecordingClient->clientAsMask);
2464 /* move the newly enabled context to the front part of ppAllContexts,
2465 * where all the enabled contexts are
2467 i = RecordFindContextOnAllContexts(pContext);
2468 assert(i >= numEnabledContexts);
2469 if (i != numEnabledContexts)
2471 ppAllContexts[i] = ppAllContexts[numEnabledContexts];
2472 ppAllContexts[numEnabledContexts] = pContext;
2475 ++numEnabledContexts;
2476 assert(numEnabledContexts > 0);
2478 /* send StartOfData */
2479 RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0);
2480 RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2481 return Success;
2482 } /* ProcRecordEnableContext */
2485 /* RecordDisableContext
2487 * Arguments:
2488 * pContext is the context to disable.
2489 * nRanges is the number of elements in pRanges.
2491 * Returns: nothing.
2493 * Side Effects:
2494 * If the context was enabled, it is disabled. An EndOfData
2495 * message is sent to the recording client. Recording hooks for
2496 * this context are uninstalled. The context is moved to the
2497 * rear part of the ppAllContexts array. numEnabledContexts is
2498 * decremented. Request processing for the formerly recording client
2499 * is resumed.
2501 static void
2502 RecordDisableContext(RecordContextPtr pContext)
2504 RecordClientsAndProtocolPtr pRCAP;
2505 int i;
2507 if (!pContext->pRecordingClient) return;
2508 if (!pContext->pRecordingClient->clientGone)
2510 RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0);
2511 RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2512 /* Re-enable request processing on this connection. */
2513 AttendClient(pContext->pRecordingClient);
2516 for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2518 RecordUninstallHooks(pRCAP, 0);
2521 pContext->pRecordingClient = NULL;
2523 /* move the newly disabled context to the rear part of ppAllContexts,
2524 * where all the disabled contexts are
2526 i = RecordFindContextOnAllContexts(pContext);
2527 assert( (i != -1) && (i < numEnabledContexts) );
2528 if (i != (numEnabledContexts - 1) )
2530 ppAllContexts[i] = ppAllContexts[numEnabledContexts-1];
2531 ppAllContexts[numEnabledContexts-1] = pContext;
2533 --numEnabledContexts;
2534 assert(numEnabledContexts >= 0);
2535 } /* RecordDisableContext */
2538 static int
2539 ProcRecordDisableContext(ClientPtr client)
2541 RecordContextPtr pContext;
2542 REQUEST(xRecordDisableContextReq);
2544 REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2545 VERIFY_CONTEXT(pContext, stuff->context, client);
2546 RecordDisableContext(pContext);
2547 return Success;
2548 } /* ProcRecordDisableContext */
2551 /* RecordDeleteContext
2553 * Arguments:
2554 * value is the context to delete.
2555 * id is its resource ID.
2557 * Returns: Success.
2559 * Side Effects:
2560 * Disables the context, frees all associated memory, and removes
2561 * it from the ppAllContexts array.
2563 static int
2564 RecordDeleteContext(pointer value, XID id)
2566 int i;
2567 RecordContextPtr pContext = (RecordContextPtr)value;
2568 RecordClientsAndProtocolPtr pRCAP;
2570 RecordDisableContext(pContext);
2572 /* Remove all the clients from all the RCAPs.
2573 * As a result, the RCAPs will be freed.
2576 while ((pRCAP = pContext->pListOfRCAP))
2578 int numClients = pRCAP->numClients;
2579 /* when the last client is deleted, the RCAP will go away. */
2580 while(numClients--)
2582 RecordDeleteClientFromRCAP(pRCAP, numClients);
2586 xfree(pContext);
2588 /* remove context from AllContexts list */
2590 if (-1 != (i = RecordFindContextOnAllContexts(pContext)))
2592 ppAllContexts[i] = ppAllContexts[numContexts - 1];
2593 if (--numContexts == 0)
2595 xfree(ppAllContexts);
2596 ppAllContexts = NULL;
2599 return Success;
2600 } /* RecordDeleteContext */
2603 static int
2604 ProcRecordFreeContext(ClientPtr client)
2606 RecordContextPtr pContext;
2607 REQUEST(xRecordFreeContextReq);
2609 REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2610 VERIFY_CONTEXT(pContext, stuff->context, client);
2611 FreeResource(stuff->context, RT_NONE);
2612 return Success;
2613 } /* ProcRecordFreeContext */
2616 static int
2617 ProcRecordDispatch(ClientPtr client)
2619 REQUEST(xReq);
2621 switch (stuff->data)
2623 case X_RecordQueryVersion:
2624 return ProcRecordQueryVersion(client);
2625 case X_RecordCreateContext:
2626 return ProcRecordCreateContext(client);
2627 case X_RecordRegisterClients:
2628 return ProcRecordRegisterClients(client);
2629 case X_RecordUnregisterClients:
2630 return ProcRecordUnregisterClients(client);
2631 case X_RecordGetContext:
2632 return ProcRecordGetContext(client);
2633 case X_RecordEnableContext:
2634 return ProcRecordEnableContext(client);
2635 case X_RecordDisableContext:
2636 return ProcRecordDisableContext(client);
2637 case X_RecordFreeContext:
2638 return ProcRecordFreeContext(client);
2639 default:
2640 return BadRequest;
2642 } /* ProcRecordDispatch */
2645 static int
2646 SProcRecordQueryVersion(ClientPtr client)
2648 REQUEST(xRecordQueryVersionReq);
2649 register char n;
2651 swaps(&stuff->length, n);
2652 REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
2653 swaps(&stuff->majorVersion, n);
2654 swaps(&stuff->minorVersion,n);
2655 return ProcRecordQueryVersion(client);
2656 } /* SProcRecordQueryVersion */
2659 static void
2660 SwapCreateRegister(xRecordRegisterClientsReq *stuff)
2662 register char n;
2663 int i;
2664 XID *pClientID;
2666 swapl(&stuff->context, n);
2667 swapl(&stuff->nClients, n);
2668 swapl(&stuff->nRanges, n);
2669 pClientID = (XID *)&stuff[1];
2670 for (i = 0; i < stuff->nClients; i++, pClientID++)
2672 swapl(pClientID, n);
2674 RecordSwapRanges((xRecordRange *)pClientID, stuff->nRanges);
2675 } /* SwapCreateRegister */
2678 static int
2679 SProcRecordCreateContext(ClientPtr client)
2681 REQUEST(xRecordCreateContextReq);
2682 register char n;
2684 swaps(&stuff->length, n);
2685 REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
2686 SwapCreateRegister((pointer)stuff);
2687 return ProcRecordCreateContext(client);
2688 } /* SProcRecordCreateContext */
2691 static int
2692 SProcRecordRegisterClients(ClientPtr client)
2694 REQUEST(xRecordRegisterClientsReq);
2695 register char n;
2697 swaps(&stuff->length, n);
2698 REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
2699 SwapCreateRegister((pointer)stuff);
2700 return ProcRecordRegisterClients(client);
2701 } /* SProcRecordRegisterClients */
2704 static int
2705 SProcRecordUnregisterClients(ClientPtr client)
2707 REQUEST(xRecordUnregisterClientsReq);
2708 register char n;
2710 swaps(&stuff->length, n);
2711 REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
2712 swapl(&stuff->context, n);
2713 swapl(&stuff->nClients, n);
2714 SwapRestL(stuff);
2715 return ProcRecordUnregisterClients(client);
2716 } /* SProcRecordUnregisterClients */
2719 static int
2720 SProcRecordGetContext(ClientPtr client)
2722 REQUEST(xRecordGetContextReq);
2723 register char n;
2725 swaps(&stuff->length, n);
2726 REQUEST_SIZE_MATCH(xRecordGetContextReq);
2727 swapl(&stuff->context, n);
2728 return ProcRecordGetContext(client);
2729 } /* SProcRecordGetContext */
2731 static int
2732 SProcRecordEnableContext(ClientPtr client)
2734 REQUEST(xRecordEnableContextReq);
2735 register char n;
2737 swaps(&stuff->length, n);
2738 REQUEST_SIZE_MATCH(xRecordEnableContextReq);
2739 swapl(&stuff->context, n);
2740 return ProcRecordEnableContext(client);
2741 } /* SProcRecordEnableContext */
2744 static int
2745 SProcRecordDisableContext(ClientPtr client)
2747 REQUEST(xRecordDisableContextReq);
2748 register char n;
2750 swaps(&stuff->length, n);
2751 REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2752 swapl(&stuff->context, n);
2753 return ProcRecordDisableContext(client);
2754 } /* SProcRecordDisableContext */
2757 static int
2758 SProcRecordFreeContext(ClientPtr client)
2760 REQUEST(xRecordFreeContextReq);
2761 register char n;
2763 swaps(&stuff->length, n);
2764 REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2765 swapl(&stuff->context, n);
2766 return ProcRecordFreeContext(client);
2767 } /* SProcRecordFreeContext */
2770 static int
2771 SProcRecordDispatch(ClientPtr client)
2773 REQUEST(xReq);
2775 switch (stuff->data)
2777 case X_RecordQueryVersion:
2778 return SProcRecordQueryVersion(client);
2779 case X_RecordCreateContext:
2780 return SProcRecordCreateContext(client);
2781 case X_RecordRegisterClients:
2782 return SProcRecordRegisterClients(client);
2783 case X_RecordUnregisterClients:
2784 return SProcRecordUnregisterClients(client);
2785 case X_RecordGetContext:
2786 return SProcRecordGetContext(client);
2787 case X_RecordEnableContext:
2788 return SProcRecordEnableContext(client);
2789 case X_RecordDisableContext:
2790 return SProcRecordDisableContext(client);
2791 case X_RecordFreeContext:
2792 return SProcRecordFreeContext(client);
2793 default:
2794 return BadRequest;
2796 } /* SProcRecordDispatch */
2798 /* RecordConnectionSetupInfo
2800 * Arguments:
2801 * pContext is an enabled context that specifies recording of
2802 * connection setup info.
2803 * pci holds the connection setup info.
2805 * Returns: nothing.
2807 * Side Effects:
2808 * The connection setup info is sent to the recording client.
2810 static void
2811 RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec *pci)
2813 int prefixsize = SIZEOF(xConnSetupPrefix);
2814 int restsize = pci->prefix->length * 4;
2816 if (pci->client->swapped)
2818 char *pConnSetup = (char *)ALLOCATE_LOCAL(prefixsize + restsize);
2819 if (!pConnSetup)
2820 return;
2821 SwapConnSetupPrefix(pci->prefix, pConnSetup);
2822 SwapConnSetupInfo(pci->setup, pConnSetup + prefixsize);
2823 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2824 (pointer)pConnSetup, prefixsize + restsize, 0);
2825 DEALLOCATE_LOCAL(pConnSetup);
2827 else
2829 /* don't alloc and copy as in the swapped case; just send the
2830 * data in two pieces
2832 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2833 (pointer)pci->prefix, prefixsize, restsize);
2834 RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2835 (pointer)pci->setup, restsize, /* continuation */ -1);
2837 } /* RecordConnectionSetupInfo */
2840 /* RecordDeleteContext
2842 * Arguments:
2843 * pcbl is &ClientStateCallback.
2844 * nullata is NULL.
2845 * calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
2846 * which contains information about client state changes.
2848 * Returns: nothing.
2850 * Side Effects:
2851 * If a new client has connected and any contexts have specified
2852 * XRecordFutureClients, the new client is registered on those contexts.
2853 * If any of those contexts specify recording of the connection setup
2854 * info, it is recorded.
2856 * If an existing client has disconnected, it is deleted from any
2857 * contexts that it was registered on. If any of those contexts
2858 * specified XRecordClientDied, they record a ClientDied protocol element.
2859 * If the disconnectiong client happened to be the data connection of an
2860 * enabled context, the context is disabled.
2863 static void
2864 RecordAClientStateChange(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
2866 NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
2867 int i;
2868 ClientPtr pClient = pci->client;
2870 switch (pClient->clientState)
2872 case ClientStateRunning: /* new client */
2873 for (i = 0; i < numContexts; i++)
2875 RecordClientsAndProtocolPtr pRCAP;
2876 RecordContextPtr pContext = ppAllContexts[i];
2878 if ((pRCAP = RecordFindClientOnContext(pContext,
2879 XRecordFutureClients, NULL)))
2881 RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
2882 if (pContext->pRecordingClient && pRCAP->clientStarted)
2883 RecordConnectionSetupInfo(pContext, pci);
2886 break;
2888 case ClientStateGone:
2889 case ClientStateRetained: /* client disconnected */
2890 for (i = 0; i < numContexts; i++)
2892 RecordClientsAndProtocolPtr pRCAP;
2893 RecordContextPtr pContext = ppAllContexts[i];
2894 int pos;
2896 if (pContext->pRecordingClient == pClient)
2897 RecordDisableContext(pContext);
2898 if ((pRCAP = RecordFindClientOnContext(pContext,
2899 pClient->clientAsMask, &pos)))
2901 if (pContext->pRecordingClient && pRCAP->clientDied)
2902 RecordAProtocolElement(pContext, pClient,
2903 XRecordClientDied, NULL, 0, 0);
2904 RecordDeleteClientFromRCAP(pRCAP, pos);
2907 break;
2909 default:
2910 break;
2911 } /* end switch on client state */
2912 } /* RecordAClientStateChange */
2915 /* RecordCloseDown
2917 * Arguments:
2918 * extEntry is the extension information for RECORD.
2920 * Returns: nothing.
2922 * Side Effects:
2923 * Performs any cleanup needed by RECORD at server shutdown time.
2926 static void
2927 RecordCloseDown(ExtensionEntry *extEntry)
2929 DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2930 } /* RecordCloseDown */
2933 /* RecordExtensionInit
2935 * Arguments: none.
2937 * Returns: nothing.
2939 * Side Effects:
2940 * Enables the RECORD extension if possible.
2942 void
2943 RecordExtensionInit(void)
2945 ExtensionEntry *extentry;
2947 RTContext = CreateNewResourceType(RecordDeleteContext);
2948 if (!RTContext)
2949 return;
2951 RecordClientPrivateIndex = AllocateClientPrivateIndex();
2952 if (!AllocateClientPrivate(RecordClientPrivateIndex, 0))
2953 return;
2955 ppAllContexts = NULL;
2956 numContexts = numEnabledContexts = numEnabledRCAPs = 0;
2958 if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
2959 return;
2961 extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
2962 ProcRecordDispatch, SProcRecordDispatch,
2963 RecordCloseDown, StandardMinorOpcode);
2964 if (!extentry)
2966 DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2967 return;
2969 RecordErrorBase = extentry->errorBase;
2971 } /* RecordExtensionInit */