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