Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | |
2 | /* | |
3 | ||
4 | Copyright 1995, 1998 The Open Group | |
5 | ||
6 | Permission to use, copy, modify, distribute, and sell this software and its | |
7 | documentation for any purpose is hereby granted without fee, provided that | |
8 | the above copyright notice appear in all copies and that both that | |
9 | copyright notice and this permission notice appear in supporting | |
10 | documentation. | |
11 | ||
12 | The above copyright notice and this permission notice shall be | |
13 | included in all copies or substantial portions of the Software. | |
14 | ||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
20 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
21 | OTHER DEALINGS IN THE SOFTWARE. | |
22 | ||
23 | Except as contained in this notice, the name of The Open Group shall | |
24 | not be used in advertising or otherwise to promote the sale, use or | |
25 | other dealings in this Software without prior written authorization | |
26 | from The Open Group. | |
27 | ||
28 | Author: David P. Wiggins, The Open Group | |
29 | ||
30 | This work benefited from earlier work done by Martha Zimet of NCD | |
31 | and Jim Haggerty of Metheus. | |
32 | ||
33 | */ | |
34 | ||
35 | #ifdef HAVE_DIX_CONFIG_H | |
36 | #include <dix-config.h> | |
37 | #endif | |
38 | ||
39 | #include "dixstruct.h" | |
40 | #include "extnsionst.h" | |
41 | #include "extinit.h" | |
42 | #include <X11/extensions/recordproto.h> | |
43 | #include "set.h" | |
44 | #include "swaprep.h" | |
45 | #include "inputstr.h" | |
46 | #include "eventconvert.h" | |
47 | #include "scrnintstr.h" | |
48 | ||
49 | #include <stdio.h> | |
50 | #include <assert.h> | |
51 | ||
52 | #ifdef PANORAMIX | |
53 | #include "globals.h" | |
54 | #include "panoramiX.h" | |
55 | #include "panoramiXsrv.h" | |
56 | #include "cursor.h" | |
57 | #endif | |
58 | ||
59 | #include "protocol-versions.h" | |
60 | ||
61 | static RESTYPE RTContext; /* internal resource type for Record contexts */ | |
62 | ||
63 | /* How many bytes of protocol data to buffer in a context. Don't set to less | |
64 | * than 32. | |
65 | */ | |
66 | #define REPLY_BUF_SIZE 1024 | |
67 | ||
68 | /* Record Context structure */ | |
69 | ||
70 | typedef struct { | |
71 | XID id; /* resource id of context */ | |
72 | ClientPtr pRecordingClient; /* client that has context enabled */ | |
73 | struct _RecordClientsAndProtocolRec *pListOfRCAP; /* all registered info */ | |
74 | ClientPtr pBufClient; /* client whose protocol is in replyBuffer */ | |
75 | unsigned int continuedReply:1; /* recording a reply that is split up? */ | |
76 | char elemHeaders; /* element header flags (time/seq no.) */ | |
77 | char bufCategory; /* category of protocol in replyBuffer */ | |
78 | int numBufBytes; /* number of bytes in replyBuffer */ | |
79 | char replyBuffer[REPLY_BUF_SIZE]; /* buffered recorded protocol */ | |
80 | int inFlush; /* are we inside RecordFlushReplyBuffer */ | |
81 | } RecordContextRec, *RecordContextPtr; | |
82 | ||
83 | /* RecordMinorOpRec - to hold minor opcode selections for extension requests | |
84 | * and replies | |
85 | */ | |
86 | ||
87 | typedef union { | |
88 | int count; /* first element of array: how many "major" structs to follow */ | |
89 | struct { /* rest of array elements are this */ | |
90 | short first; /* first major opcode */ | |
91 | short last; /* last major opcode */ | |
92 | RecordSetPtr pMinOpSet; /* minor opcode set for above major range */ | |
93 | } major; | |
94 | } RecordMinorOpRec, *RecordMinorOpPtr; | |
95 | ||
96 | /* RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and | |
97 | * protocol selections passed in a single CreateContext or RegisterClients. | |
98 | * Generally, a context will have one of these from the create and an | |
99 | * additional one for each RegisterClients. RCAPs are freed when all their | |
100 | * clients are unregistered. | |
101 | */ | |
102 | ||
103 | typedef struct _RecordClientsAndProtocolRec { | |
104 | RecordContextPtr pContext; /* context that owns this RCAP */ | |
105 | struct _RecordClientsAndProtocolRec *pNextRCAP; /* next RCAP on context */ | |
106 | RecordSetPtr pRequestMajorOpSet; /* requests to record */ | |
107 | RecordMinorOpPtr pRequestMinOpInfo; /* extension requests to record */ | |
108 | RecordSetPtr pReplyMajorOpSet; /* replies to record */ | |
109 | RecordMinorOpPtr pReplyMinOpInfo; /* extension replies to record */ | |
110 | RecordSetPtr pDeviceEventSet; /* device events to record */ | |
111 | RecordSetPtr pDeliveredEventSet; /* delivered events to record */ | |
112 | RecordSetPtr pErrorSet; /* errors to record */ | |
113 | XID *pClientIDs; /* array of clients to record */ | |
114 | short numClients; /* number of clients in pClientIDs */ | |
115 | short sizeClients; /* size of pClientIDs array */ | |
116 | unsigned int clientStarted:1; /* record new client connections? */ | |
117 | unsigned int clientDied:1; /* record client disconnections? */ | |
118 | unsigned int clientIDsSeparatelyAllocated:1; /* pClientIDs malloced? */ | |
119 | } RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr; | |
120 | ||
121 | /* how much bigger to make pRCAP->pClientIDs when reallocing */ | |
122 | #define CLIENT_ARRAY_GROWTH_INCREMENT 4 | |
123 | ||
124 | /* counts the total number of RCAPs belonging to enabled contexts. */ | |
125 | static int numEnabledRCAPs; | |
126 | ||
127 | /* void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr) | |
128 | * In the spirit of the VERIFY_* macros in dix.h, this macro fills in | |
129 | * the context pointer if the given ID is a valid Record Context, else it | |
130 | * returns an error. | |
131 | */ | |
132 | #define VERIFY_CONTEXT(_pContext, _contextid, _client) { \ | |
133 | int rc = dixLookupResourceByType((pointer *)&(_pContext), _contextid, \ | |
134 | RTContext, _client, DixUseAccess); \ | |
135 | if (rc != Success) \ | |
136 | return rc; \ | |
137 | } | |
138 | ||
139 | static 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 | */ | |
150 | typedef 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 | */ | |
156 | typedef 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 | ||
166 | static 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 | ||
180 | static RecordContextPtr *ppAllContexts; | |
181 | ||
182 | static 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 | */ | |
189 | static 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 | */ | |
202 | static int | |
203 | RecordFindContextOnAllContexts(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 | */ | |
233 | static void | |
234 | RecordFlushReplyBuffer(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 | */ | |
280 | static void | |
281 | RecordAProtocolElement(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 | */ | |
416 | static RecordClientsAndProtocolPtr | |
417 | RecordFindClientOnContext(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 | */ | |
451 | static void | |
452 | RecordABigRequest(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 | */ | |
500 | static int | |
501 | RecordARequest(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 | */ | |
578 | static void | |
579 | RecordAReply(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 | */ | |
657 | static void | |
658 | RecordADeliveredEventOrError(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 | ||
706 | static void | |
707 | RecordSendProtocolEvents(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 | */ | |
768 | static void | |
769 | RecordADeviceEvent(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 | */ | |
813 | static void | |
814 | RecordFlushAllContexts(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 | */ | |
851 | static int | |
852 | RecordInstallHooks(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 | */ | |
936 | static void | |
937 | RecordUninstallHooks(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 | */ | |
1029 | static void | |
1030 | RecordDeleteClientFromRCAP(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 | */ | |
1073 | static void | |
1074 | RecordAddClientToRCAP(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 | */ | |
1117 | static void | |
1118 | RecordDeleteClientFromContext(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 | */ | |
1139 | static int | |
1140 | RecordSanityCheckClientSpecifiers(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 | */ | |
1200 | static XID * | |
1201 | RecordCanonicalizeClientSpecifiers(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 | */ | |
1273 | static int | |
1274 | RecordPadAlign(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 | */ | |
1293 | static int | |
1294 | RecordSanityCheckRegisterClients(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 | */ | |
1387 | typedef 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. */ | |
1401 | enum { 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 | */ | |
1421 | static int | |
1422 | RecordAllocIntervals(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 | */ | |
1457 | static int | |
1458 | RecordConvertRangesToIntervals(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 | */ | |
1533 | static int | |
1534 | RecordRegisterClients(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 | ||
1818 | static int | |
1819 | ProcRecordQueryVersion(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 | ||
1840 | static int | |
1841 | ProcRecordCreateContext(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 | ||
1891 | static int | |
1892 | ProcRecordRegisterClients(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 | ||
1904 | static int | |
1905 | ProcRecordUnregisterClients(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 | ||
1947 | typedef 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 | */ | |
1966 | static int | |
1967 | RecordAllocRanges(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 | */ | |
2010 | static int | |
2011 | RecordConvertSetToRanges(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 | */ | |
2072 | static int | |
2073 | RecordConvertMinorOpInfoToRanges(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 | */ | |
2116 | static void | |
2117 | RecordSwapRanges(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 | ||
2129 | static int | |
2130 | ProcRecordGetContext(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 | ||
2283 | static int | |
2284 | ProcRecordEnableContext(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 | */ | |
2359 | static void | |
2360 | RecordDisableContext(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 | ||
2393 | static int | |
2394 | ProcRecordDisableContext(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 | */ | |
2418 | static int | |
2419 | RecordDeleteContext(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 | ||
2454 | static int | |
2455 | ProcRecordFreeContext(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 | ||
2467 | static int | |
2468 | ProcRecordDispatch(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 | ||
2494 | static int | |
2495 | SProcRecordQueryVersion(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 | ||
2506 | static int | |
2507 | SwapCreateRegister(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 | ||
2530 | static int | |
2531 | SProcRecordCreateContext(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 | ||
2543 | static int | |
2544 | SProcRecordRegisterClients(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 | ||
2556 | static int | |
2557 | SProcRecordUnregisterClients(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 | ||
2569 | static int | |
2570 | SProcRecordGetContext(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 | ||
2580 | static int | |
2581 | SProcRecordEnableContext(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 | ||
2591 | static int | |
2592 | SProcRecordDisableContext(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 | ||
2602 | static int | |
2603 | SProcRecordFreeContext(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 | ||
2613 | static int | |
2614 | SProcRecordDispatch(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 | */ | |
2652 | static void | |
2653 | RecordConnectionSetupInfo(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 | ||
2706 | static void | |
2707 | RecordAClientStateChange(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 | */ | |
2778 | static void | |
2779 | RecordCloseDown(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 | */ | |
2793 | void | |
2794 | RecordExtensionInit(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 */ |