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