Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | Copyright (c) 2002 XFree86 Inc | |
3 | */ | |
4 | ||
5 | #ifdef HAVE_DIX_CONFIG_H | |
6 | #include <dix-config.h> | |
7 | #endif | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <string.h> | |
11 | #include <X11/X.h> | |
12 | #include <X11/Xproto.h> | |
13 | #include <assert.h> | |
14 | #include "misc.h" | |
15 | #include "os.h" | |
16 | #include "dixstruct.h" | |
17 | #include "extnsionst.h" | |
18 | #include "swaprep.h" | |
19 | #include "registry.h" | |
20 | #include <X11/extensions/XResproto.h> | |
21 | #include "pixmapstr.h" | |
22 | #include "windowstr.h" | |
23 | #include "gcstruct.h" | |
24 | #include "extinit.h" | |
25 | #include "protocol-versions.h" | |
26 | #include "client.h" | |
27 | #include "list.h" | |
28 | #include "misc.h" | |
29 | #include <string.h> | |
30 | #include "hashtable.h" | |
31 | #include "picturestr.h" | |
32 | ||
33 | #ifdef COMPOSITE | |
34 | #include "compint.h" | |
35 | #endif | |
36 | ||
37 | /** @brief Holds fragments of responses for ConstructClientIds. | |
38 | * | |
39 | * note: there is no consideration for data alignment */ | |
40 | typedef struct { | |
41 | struct xorg_list l; | |
42 | int bytes; | |
43 | /* data follows */ | |
44 | } FragmentList; | |
45 | ||
46 | #define FRAGMENT_DATA(ptr) ((void*) ((char*) (ptr) + sizeof(FragmentList))) | |
47 | ||
48 | /** @brief Holds structure for the generated response to | |
49 | ProcXResQueryClientIds; used by ConstructClientId* -functions */ | |
50 | typedef struct { | |
51 | int numIds; | |
52 | int resultBytes; | |
53 | struct xorg_list response; | |
54 | int sentClientMasks[MAXCLIENTS]; | |
55 | } ConstructClientIdCtx; | |
56 | ||
57 | /** @brief Holds the structure for information required to | |
58 | generate the response to XResQueryResourceBytes. In addition | |
59 | to response it contains information on the query as well, | |
60 | as well as some volatile information required by a few | |
61 | functions that cannot take that information directly | |
62 | via a parameter, as they are called via already-existing | |
63 | higher order functions. */ | |
64 | typedef struct { | |
65 | ClientPtr sendClient; | |
66 | int numSizes; | |
67 | int resultBytes; | |
68 | struct xorg_list response; | |
69 | int status; | |
70 | long numSpecs; | |
71 | xXResResourceIdSpec *specs; | |
72 | HashTable visitedResources; | |
73 | ||
74 | /* Used by AddSubResourceSizeSpec when AddResourceSizeValue is | |
75 | handling crossreferences */ | |
76 | HashTable visitedSubResources; | |
77 | ||
78 | /* used when ConstructResourceBytesCtx is passed to | |
79 | AddResourceSizeValue2 via FindClientResourcesByType */ | |
80 | RESTYPE resType; | |
81 | ||
82 | /* used when ConstructResourceBytesCtx is passed to | |
83 | AddResourceSizeValueByResource from ConstructResourceBytesByResource */ | |
84 | xXResResourceIdSpec *curSpec; | |
85 | ||
86 | /** Used when iterating through a single resource's subresources | |
87 | ||
88 | @see AddSubResourceSizeSpec */ | |
89 | xXResResourceSizeValue *sizeValue; | |
90 | } ConstructResourceBytesCtx; | |
91 | ||
92 | /** @brief Allocate and add a sequence of bytes at the end of a fragment list. | |
93 | Call DestroyFragments to release the list. | |
94 | ||
95 | @param frags A pointer to head of an initialized linked list | |
96 | @param bytes Number of bytes to allocate | |
97 | @return Returns a pointer to the allocated non-zeroed region | |
98 | that is to be filled by the caller. On error (out of memory) | |
99 | returns NULL and makes no changes to the list. | |
100 | */ | |
101 | static void * | |
102 | AddFragment(struct xorg_list *frags, int bytes) | |
103 | { | |
104 | FragmentList *f = malloc(sizeof(FragmentList) + bytes); | |
105 | if (!f) { | |
106 | return NULL; | |
107 | } else { | |
108 | f->bytes = bytes; | |
109 | xorg_list_add(&f->l, frags->prev); | |
110 | return (char*) f + sizeof(*f); | |
111 | } | |
112 | } | |
113 | ||
114 | /** @brief Sends all fragments in the list to the client. Does not | |
115 | free anything. | |
116 | ||
117 | @param client The client to send the fragments to | |
118 | @param frags The head of the list of fragments | |
119 | */ | |
120 | static void | |
121 | WriteFragmentsToClient(ClientPtr client, struct xorg_list *frags) | |
122 | { | |
123 | FragmentList *it; | |
124 | xorg_list_for_each_entry(it, frags, l) { | |
125 | WriteToClient(client, it->bytes, (char*) it + sizeof(*it)); | |
126 | } | |
127 | } | |
128 | ||
129 | /** @brief Frees a list of fragments. Does not free() root node. | |
130 | ||
131 | @param frags The head of the list of fragments | |
132 | */ | |
133 | static void | |
134 | DestroyFragments(struct xorg_list *frags) | |
135 | { | |
136 | FragmentList *it, *tmp; | |
137 | xorg_list_for_each_entry_safe(it, tmp, frags, l) { | |
138 | xorg_list_del(&it->l); | |
139 | free(it); | |
140 | } | |
141 | } | |
142 | ||
143 | /** @brief Constructs a context record for ConstructClientId* functions | |
144 | to use */ | |
145 | static void | |
146 | InitConstructClientIdCtx(ConstructClientIdCtx *ctx) | |
147 | { | |
148 | ctx->numIds = 0; | |
149 | ctx->resultBytes = 0; | |
150 | xorg_list_init(&ctx->response); | |
151 | memset(ctx->sentClientMasks, 0, sizeof(ctx->sentClientMasks)); | |
152 | } | |
153 | ||
154 | /** @brief Destroys a context record, releases all memory (except the storage | |
155 | for *ctx itself) */ | |
156 | static void | |
157 | DestroyConstructClientIdCtx(ConstructClientIdCtx *ctx) | |
158 | { | |
159 | DestroyFragments(&ctx->response); | |
160 | } | |
161 | ||
162 | static Bool | |
163 | InitConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx, | |
164 | ClientPtr sendClient, | |
165 | long numSpecs, | |
166 | xXResResourceIdSpec *specs) | |
167 | { | |
168 | ctx->sendClient = sendClient; | |
169 | ctx->numSizes = 0; | |
170 | ctx->resultBytes = 0; | |
171 | xorg_list_init(&ctx->response); | |
172 | ctx->status = Success; | |
173 | ctx->numSpecs = numSpecs; | |
174 | ctx->specs = specs; | |
175 | ctx->visitedResources = ht_create(sizeof(XID), 0, | |
176 | ht_resourceid_hash, ht_resourceid_compare, | |
177 | NULL); | |
178 | ||
179 | if (!ctx->visitedResources) { | |
180 | return FALSE; | |
181 | } else { | |
182 | return TRUE; | |
183 | } | |
184 | } | |
185 | ||
186 | static void | |
187 | DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx) | |
188 | { | |
189 | DestroyFragments(&ctx->response); | |
190 | ht_destroy(ctx->visitedResources); | |
191 | } | |
192 | ||
193 | static int | |
194 | ProcXResQueryVersion(ClientPtr client) | |
195 | { | |
196 | REQUEST(xXResQueryVersionReq); | |
197 | xXResQueryVersionReply rep = { | |
198 | .type = X_Reply, | |
199 | .sequenceNumber = client->sequence, | |
200 | .length = 0, | |
201 | .server_major = SERVER_XRES_MAJOR_VERSION, | |
202 | .server_minor = SERVER_XRES_MINOR_VERSION | |
203 | }; | |
204 | ||
205 | REQUEST_SIZE_MATCH(xXResQueryVersionReq); | |
206 | ||
207 | if (client->swapped) { | |
208 | swaps(&rep.sequenceNumber); | |
209 | swapl(&rep.length); | |
210 | swaps(&rep.server_major); | |
211 | swaps(&rep.server_minor); | |
212 | } | |
213 | WriteToClient(client, sizeof(xXResQueryVersionReply), &rep); | |
214 | return Success; | |
215 | } | |
216 | ||
217 | static int | |
218 | ProcXResQueryClients(ClientPtr client) | |
219 | { | |
220 | /* REQUEST(xXResQueryClientsReq); */ | |
221 | xXResQueryClientsReply rep; | |
222 | int *current_clients; | |
223 | int i, num_clients; | |
224 | ||
225 | REQUEST_SIZE_MATCH(xXResQueryClientsReq); | |
226 | ||
227 | current_clients = malloc(currentMaxClients * sizeof(int)); | |
228 | ||
229 | num_clients = 0; | |
230 | for (i = 0; i < currentMaxClients; i++) { | |
231 | if (clients[i]) { | |
232 | current_clients[num_clients] = i; | |
233 | num_clients++; | |
234 | } | |
235 | } | |
236 | ||
237 | rep = (xXResQueryClientsReply) { | |
238 | .type = X_Reply, | |
239 | .sequenceNumber = client->sequence, | |
240 | .length = bytes_to_int32(num_clients * sz_xXResClient), | |
241 | .num_clients = num_clients | |
242 | }; | |
243 | if (client->swapped) { | |
244 | swaps(&rep.sequenceNumber); | |
245 | swapl(&rep.length); | |
246 | swapl(&rep.num_clients); | |
247 | } | |
248 | WriteToClient(client, sizeof(xXResQueryClientsReply), &rep); | |
249 | ||
250 | if (num_clients) { | |
251 | xXResClient scratch; | |
252 | ||
253 | for (i = 0; i < num_clients; i++) { | |
254 | scratch.resource_base = clients[current_clients[i]]->clientAsMask; | |
255 | scratch.resource_mask = RESOURCE_ID_MASK; | |
256 | ||
257 | if (client->swapped) { | |
258 | swapl(&scratch.resource_base); | |
259 | swapl(&scratch.resource_mask); | |
260 | } | |
261 | WriteToClient(client, sz_xXResClient, &scratch); | |
262 | } | |
263 | } | |
264 | ||
265 | free(current_clients); | |
266 | ||
267 | return Success; | |
268 | } | |
269 | ||
270 | static void | |
271 | ResFindAllRes(pointer value, XID id, RESTYPE type, pointer cdata) | |
272 | { | |
273 | int *counts = (int *) cdata; | |
274 | ||
275 | counts[(type & TypeMask) - 1]++; | |
276 | } | |
277 | ||
278 | static int | |
279 | ProcXResQueryClientResources(ClientPtr client) | |
280 | { | |
281 | REQUEST(xXResQueryClientResourcesReq); | |
282 | xXResQueryClientResourcesReply rep; | |
283 | int i, clientID, num_types; | |
284 | int *counts; | |
285 | ||
286 | REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq); | |
287 | ||
288 | clientID = CLIENT_ID(stuff->xid); | |
289 | ||
290 | if ((clientID >= currentMaxClients) || !clients[clientID]) { | |
291 | client->errorValue = stuff->xid; | |
292 | return BadValue; | |
293 | } | |
294 | ||
295 | counts = calloc(lastResourceType + 1, sizeof(int)); | |
296 | ||
297 | FindAllClientResources(clients[clientID], ResFindAllRes, counts); | |
298 | ||
299 | num_types = 0; | |
300 | ||
301 | for (i = 0; i <= lastResourceType; i++) { | |
302 | if (counts[i]) | |
303 | num_types++; | |
304 | } | |
305 | ||
306 | rep = (xXResQueryClientResourcesReply) { | |
307 | .type = X_Reply, | |
308 | .sequenceNumber = client->sequence, | |
309 | .length = bytes_to_int32(num_types * sz_xXResType), | |
310 | .num_types = num_types | |
311 | }; | |
312 | if (client->swapped) { | |
313 | swaps(&rep.sequenceNumber); | |
314 | swapl(&rep.length); | |
315 | swapl(&rep.num_types); | |
316 | } | |
317 | ||
318 | WriteToClient(client, sizeof(xXResQueryClientResourcesReply), &rep); | |
319 | ||
320 | if (num_types) { | |
321 | xXResType scratch; | |
322 | const char *name; | |
323 | ||
324 | for (i = 0; i < lastResourceType; i++) { | |
325 | if (!counts[i]) | |
326 | continue; | |
327 | ||
328 | name = LookupResourceName(i + 1); | |
329 | if (strcmp(name, XREGISTRY_UNKNOWN)) | |
330 | scratch.resource_type = MakeAtom(name, strlen(name), TRUE); | |
331 | else { | |
332 | char buf[40]; | |
333 | ||
334 | snprintf(buf, sizeof(buf), "Unregistered resource %i", i + 1); | |
335 | scratch.resource_type = MakeAtom(buf, strlen(buf), TRUE); | |
336 | } | |
337 | ||
338 | scratch.count = counts[i]; | |
339 | ||
340 | if (client->swapped) { | |
341 | swapl(&scratch.resource_type); | |
342 | swapl(&scratch.count); | |
343 | } | |
344 | WriteToClient(client, sz_xXResType, &scratch); | |
345 | } | |
346 | } | |
347 | ||
348 | free(counts); | |
349 | ||
350 | return Success; | |
351 | } | |
352 | ||
353 | static unsigned long | |
354 | ResGetApproxPixmapBytes(PixmapPtr pix) | |
355 | { | |
356 | unsigned long nPixels; | |
357 | int bytesPerPixel; | |
358 | ||
359 | bytesPerPixel = pix->drawable.bitsPerPixel >> 3; | |
360 | nPixels = pix->drawable.width * pix->drawable.height; | |
361 | ||
362 | /* Divide by refcnt as pixmap could be shared between clients, | |
363 | * so total pixmap mem is shared between these. | |
364 | */ | |
365 | return (nPixels * bytesPerPixel) / pix->refcnt; | |
366 | } | |
367 | ||
368 | static void | |
369 | ResFindResourcePixmaps(pointer value, XID id, RESTYPE type, pointer cdata) | |
370 | { | |
371 | SizeType sizeFunc = GetResourceTypeSizeFunc(type); | |
372 | ResourceSizeRec size = { 0, 0, 0 }; | |
373 | unsigned long *bytes = cdata; | |
374 | ||
375 | sizeFunc(value, id, &size); | |
376 | *bytes += size.pixmapRefSize; | |
377 | } | |
378 | ||
379 | static void | |
380 | ResFindPixmaps(pointer value, XID id, pointer cdata) | |
381 | { | |
382 | unsigned long *bytes = (unsigned long *) cdata; | |
383 | PixmapPtr pix = (PixmapPtr) value; | |
384 | ||
385 | *bytes += ResGetApproxPixmapBytes(pix); | |
386 | } | |
387 | ||
388 | static void | |
389 | ResFindWindowPixmaps(pointer value, XID id, pointer cdata) | |
390 | { | |
391 | unsigned long *bytes = (unsigned long *) cdata; | |
392 | WindowPtr pWin = (WindowPtr) value; | |
393 | ||
394 | if (pWin->backgroundState == BackgroundPixmap) | |
395 | *bytes += ResGetApproxPixmapBytes(pWin->background.pixmap); | |
396 | ||
397 | if (pWin->border.pixmap != NULL && !pWin->borderIsPixel) | |
398 | *bytes += ResGetApproxPixmapBytes(pWin->border.pixmap); | |
399 | } | |
400 | ||
401 | static void | |
402 | ResFindGCPixmaps(pointer value, XID id, pointer cdata) | |
403 | { | |
404 | unsigned long *bytes = (unsigned long *) cdata; | |
405 | GCPtr pGC = (GCPtr) value; | |
406 | ||
407 | if (pGC->stipple != NULL) | |
408 | *bytes += ResGetApproxPixmapBytes(pGC->stipple); | |
409 | ||
410 | if (pGC->tile.pixmap != NULL && !pGC->tileIsPixel) | |
411 | *bytes += ResGetApproxPixmapBytes(pGC->tile.pixmap); | |
412 | } | |
413 | ||
414 | static void | |
415 | ResFindPicturePixmaps(pointer value, XID id, pointer cdata) | |
416 | { | |
417 | #ifdef RENDER | |
418 | ResFindResourcePixmaps(value, id, PictureType, cdata); | |
419 | #endif | |
420 | } | |
421 | ||
422 | static void | |
423 | ResFindCompositeClientWindowPixmaps (pointer value, XID id, pointer cdata) | |
424 | { | |
425 | #ifdef COMPOSITE | |
426 | ResFindResourcePixmaps(value, id, CompositeClientWindowType, cdata); | |
427 | #endif | |
428 | } | |
429 | ||
430 | static int | |
431 | ProcXResQueryClientPixmapBytes(ClientPtr client) | |
432 | { | |
433 | REQUEST(xXResQueryClientPixmapBytesReq); | |
434 | xXResQueryClientPixmapBytesReply rep; | |
435 | int clientID; | |
436 | unsigned long bytes; | |
437 | ||
438 | REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq); | |
439 | ||
440 | clientID = CLIENT_ID(stuff->xid); | |
441 | ||
442 | if ((clientID >= currentMaxClients) || !clients[clientID]) { | |
443 | client->errorValue = stuff->xid; | |
444 | return BadValue; | |
445 | } | |
446 | ||
447 | bytes = 0; | |
448 | ||
449 | FindClientResourcesByType(clients[clientID], RT_PIXMAP, ResFindPixmaps, | |
450 | (pointer) (&bytes)); | |
451 | ||
452 | /* | |
453 | * Make sure win background pixmaps also held to account. | |
454 | */ | |
455 | FindClientResourcesByType(clients[clientID], RT_WINDOW, | |
456 | ResFindWindowPixmaps, (pointer) (&bytes)); | |
457 | ||
458 | /* | |
459 | * GC Tile & Stipple pixmaps too. | |
460 | */ | |
461 | FindClientResourcesByType(clients[clientID], RT_GC, | |
462 | ResFindGCPixmaps, (pointer) (&bytes)); | |
463 | ||
464 | #ifdef RENDER | |
465 | /* Render extension picture pixmaps. */ | |
466 | FindClientResourcesByType(clients[clientID], PictureType, | |
467 | ResFindPicturePixmaps, | |
468 | (pointer)(&bytes)); | |
469 | #endif | |
470 | ||
471 | #ifdef COMPOSITE | |
472 | /* Composite extension client window pixmaps. */ | |
473 | FindClientResourcesByType(clients[clientID], CompositeClientWindowType, | |
474 | ResFindCompositeClientWindowPixmaps, | |
475 | (pointer)(&bytes)); | |
476 | #endif | |
477 | ||
478 | rep = (xXResQueryClientPixmapBytesReply) { | |
479 | .type = X_Reply, | |
480 | .sequenceNumber = client->sequence, | |
481 | .length = 0, | |
482 | .bytes = bytes, | |
483 | #ifdef _XSERVER64 | |
484 | .bytes_overflow = bytes >> 32 | |
485 | #else | |
486 | .bytes_overflow = 0 | |
487 | #endif | |
488 | }; | |
489 | if (client->swapped) { | |
490 | swaps(&rep.sequenceNumber); | |
491 | swapl(&rep.length); | |
492 | swapl(&rep.bytes); | |
493 | swapl(&rep.bytes_overflow); | |
494 | } | |
495 | WriteToClient(client, sizeof(xXResQueryClientPixmapBytesReply), &rep); | |
496 | ||
497 | return Success; | |
498 | } | |
499 | ||
500 | /** @brief Finds out if a client's information need to be put into the | |
501 | response; marks client having been handled, if that is the case. | |
502 | ||
503 | @param client The client to send information about | |
504 | @param mask The request mask (0 to send everything, otherwise a | |
505 | bitmask of X_XRes*Mask) | |
506 | @param ctx The context record that tells which clients and id types | |
507 | have been already handled | |
508 | @param sendMask Which id type are we now considering. One of X_XRes*Mask. | |
509 | ||
510 | @return Returns TRUE if the client information needs to be on the | |
511 | response, otherwise FALSE. | |
512 | */ | |
513 | static Bool | |
514 | WillConstructMask(ClientPtr client, CARD32 mask, | |
515 | ConstructClientIdCtx *ctx, int sendMask) | |
516 | { | |
517 | if ((!mask || (mask & sendMask)) | |
518 | && !(ctx->sentClientMasks[client->index] & sendMask)) { | |
519 | ctx->sentClientMasks[client->index] |= sendMask; | |
520 | return TRUE; | |
521 | } else { | |
522 | return FALSE; | |
523 | } | |
524 | } | |
525 | ||
526 | /** @brief Constructs a response about a single client, based on a certain | |
527 | client id spec | |
528 | ||
529 | @param sendClient Which client wishes to receive this answer. Used for | |
530 | byte endianess. | |
531 | @param client Which client are we considering. | |
532 | @param mask The client id spec mask indicating which information | |
533 | we want about this client. | |
534 | @param ctx The context record containing the constructed response | |
535 | and information on which clients and masks have been | |
536 | already handled. | |
537 | ||
538 | @return Return TRUE if everything went OK, otherwise FALSE which indicates | |
539 | a memory allocation problem. | |
540 | */ | |
541 | static Bool | |
542 | ConstructClientIdValue(ClientPtr sendClient, ClientPtr client, CARD32 mask, | |
543 | ConstructClientIdCtx *ctx) | |
544 | { | |
545 | xXResClientIdValue rep; | |
546 | ||
547 | rep.spec.client = client->clientAsMask; | |
548 | if (client->swapped) { | |
549 | swapl (&rep.spec.client); | |
550 | } | |
551 | ||
552 | if (WillConstructMask(client, mask, ctx, X_XResClientXIDMask)) { | |
553 | void *ptr = AddFragment(&ctx->response, sizeof(rep)); | |
554 | if (!ptr) { | |
555 | return FALSE; | |
556 | } | |
557 | ||
558 | rep.spec.mask = X_XResClientXIDMask; | |
559 | rep.length = 0; | |
560 | if (sendClient->swapped) { | |
561 | swapl (&rep.spec.mask); | |
562 | /* swapl (&rep.length, n); - not required for rep.length = 0 */ | |
563 | } | |
564 | ||
565 | memcpy(ptr, &rep, sizeof(rep)); | |
566 | ||
567 | ctx->resultBytes += sizeof(rep); | |
568 | ++ctx->numIds; | |
569 | } | |
570 | if (WillConstructMask(client, mask, ctx, X_XResLocalClientPIDMask)) { | |
571 | pid_t pid = GetClientPid(client); | |
572 | ||
573 | if (pid != -1) { | |
574 | void *ptr = AddFragment(&ctx->response, | |
575 | sizeof(rep) + sizeof(CARD32)); | |
576 | CARD32 *value = (void*) ((char*) ptr + sizeof(rep)); | |
577 | ||
578 | if (!ptr) { | |
579 | return FALSE; | |
580 | } | |
581 | ||
582 | rep.spec.mask = X_XResLocalClientPIDMask; | |
583 | rep.length = 4; | |
584 | ||
585 | if (sendClient->swapped) { | |
586 | swapl (&rep.spec.mask); | |
587 | swapl (&rep.length); | |
588 | } | |
589 | ||
590 | if (sendClient->swapped) { | |
591 | swapl (value); | |
592 | } | |
593 | memcpy(ptr, &rep, sizeof(rep)); | |
594 | *value = pid; | |
595 | ||
596 | ctx->resultBytes += sizeof(rep) + sizeof(CARD32); | |
597 | ++ctx->numIds; | |
598 | } | |
599 | } | |
600 | ||
601 | /* memory allocation errors earlier may return with FALSE */ | |
602 | return TRUE; | |
603 | } | |
604 | ||
605 | /** @brief Constructs a response about all clients, based on a client id specs | |
606 | ||
607 | @param client Which client which we are constructing the response for. | |
608 | @param numSpecs Number of client id specs in specs | |
609 | @param specs Client id specs | |
610 | ||
611 | @return Return Success if everything went OK, otherwise a Bad* (currently | |
612 | BadAlloc or BadValue) | |
613 | */ | |
614 | static int | |
615 | ConstructClientIds(ClientPtr client, | |
616 | int numSpecs, xXResClientIdSpec* specs, | |
617 | ConstructClientIdCtx *ctx) | |
618 | { | |
619 | int specIdx; | |
620 | ||
621 | for (specIdx = 0; specIdx < numSpecs; ++specIdx) { | |
622 | if (specs[specIdx].client == 0) { | |
623 | int c; | |
624 | for (c = 0; c < currentMaxClients; ++c) { | |
625 | if (clients[c]) { | |
626 | if (!ConstructClientIdValue(client, clients[c], | |
627 | specs[specIdx].mask, ctx)) { | |
628 | return BadAlloc; | |
629 | } | |
630 | } | |
631 | } | |
632 | } else { | |
633 | int clientID = CLIENT_ID(specs[specIdx].client); | |
634 | ||
635 | if ((clientID < currentMaxClients) && clients[clientID]) { | |
636 | if (!ConstructClientIdValue(client, clients[clientID], | |
637 | specs[specIdx].mask, ctx)) { | |
638 | return BadAlloc; | |
639 | } | |
640 | } | |
641 | } | |
642 | } | |
643 | ||
644 | /* memory allocation errors earlier may return with BadAlloc */ | |
645 | return Success; | |
646 | } | |
647 | ||
648 | /** @brief Response to XResQueryClientIds request introduced in XResProto v1.2 | |
649 | ||
650 | @param client Which client which we are constructing the response for. | |
651 | ||
652 | @return Returns the value returned from ConstructClientIds with the same | |
653 | semantics | |
654 | */ | |
655 | static int | |
656 | ProcXResQueryClientIds (ClientPtr client) | |
657 | { | |
658 | REQUEST(xXResQueryClientIdsReq); | |
659 | ||
660 | xXResClientIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff)); | |
661 | int rc; | |
662 | ConstructClientIdCtx ctx; | |
663 | ||
664 | InitConstructClientIdCtx(&ctx); | |
665 | ||
666 | REQUEST_AT_LEAST_SIZE(xXResQueryClientIdsReq); | |
667 | REQUEST_FIXED_SIZE(xXResQueryClientIdsReq, | |
668 | stuff->numSpecs * sizeof(specs[0])); | |
669 | ||
670 | rc = ConstructClientIds(client, stuff->numSpecs, specs, &ctx); | |
671 | ||
672 | if (rc == Success) { | |
673 | xXResQueryClientIdsReply rep = { | |
674 | .type = X_Reply, | |
675 | .sequenceNumber = client->sequence, | |
676 | .length = bytes_to_int32(ctx.resultBytes), | |
677 | .numIds = ctx.numIds | |
678 | }; | |
679 | ||
680 | assert((ctx.resultBytes & 3) == 0); | |
681 | ||
682 | if (client->swapped) { | |
683 | swaps (&rep.sequenceNumber); | |
684 | swapl (&rep.length); | |
685 | swapl (&rep.numIds); | |
686 | } | |
687 | ||
688 | WriteToClient(client, sizeof(rep), &rep); | |
689 | WriteFragmentsToClient(client, &ctx.response); | |
690 | } | |
691 | ||
692 | DestroyConstructClientIdCtx(&ctx); | |
693 | ||
694 | return rc; | |
695 | } | |
696 | ||
697 | /** @brief Swaps xXResResourceIdSpec endianess */ | |
698 | static void | |
699 | SwapXResResourceIdSpec(xXResResourceIdSpec *spec) | |
700 | { | |
701 | swapl(&spec->resource); | |
702 | swapl(&spec->type); | |
703 | } | |
704 | ||
705 | /** @brief Swaps xXResResourceSizeSpec endianess */ | |
706 | static void | |
707 | SwapXResResourceSizeSpec(xXResResourceSizeSpec *size) | |
708 | { | |
709 | SwapXResResourceIdSpec(&size->spec); | |
710 | swapl(&size->bytes); | |
711 | swapl(&size->refCount); | |
712 | swapl(&size->useCount); | |
713 | } | |
714 | ||
715 | /** @brief Swaps xXResResourceSizeValue endianess */ | |
716 | static void | |
717 | SwapXResResourceSizeValue(xXResResourceSizeValue *rep) | |
718 | { | |
719 | SwapXResResourceSizeSpec(&rep->size); | |
720 | swapl(&rep->numCrossReferences); | |
721 | } | |
722 | ||
723 | /** @brief Swaps the response bytes */ | |
724 | static void | |
725 | SwapXResQueryResourceBytes(struct xorg_list *response) | |
726 | { | |
727 | struct xorg_list *it = response->next; | |
728 | int c; | |
729 | ||
730 | while (it != response) { | |
731 | xXResResourceSizeValue *value = FRAGMENT_DATA(it); | |
732 | it = it->next; | |
733 | for (c = 0; c < value->numCrossReferences; ++c) { | |
734 | xXResResourceSizeSpec *spec = FRAGMENT_DATA(it); | |
735 | SwapXResResourceSizeSpec(spec); | |
736 | it = it->next; | |
737 | } | |
738 | SwapXResResourceSizeValue(value); | |
739 | } | |
740 | } | |
741 | ||
742 | /** @brief Adds xXResResourceSizeSpec describing a resource's size into | |
743 | the buffer contained in the context. The resource is considered | |
744 | to be a subresource. | |
745 | ||
746 | @see AddResourceSizeValue | |
747 | ||
748 | @param[in] value The X resource object on which to add information | |
749 | about to the buffer | |
750 | @param[in] id The ID of the X resource | |
751 | @param[in] type The type of the X resource | |
752 | @param[in/out] cdata The context object of type ConstructResourceBytesCtx. | |
753 | Void pointer type is used here to satisfy the type | |
754 | FindRes | |
755 | */ | |
756 | static void | |
757 | AddSubResourceSizeSpec(pointer value, | |
758 | XID id, | |
759 | RESTYPE type, | |
760 | pointer cdata) | |
761 | { | |
762 | ConstructResourceBytesCtx *ctx = cdata; | |
763 | ||
764 | if (ctx->status == Success) { | |
765 | xXResResourceSizeSpec **prevCrossRef = | |
766 | ht_find(ctx->visitedSubResources, &value); | |
767 | if (!prevCrossRef) { | |
768 | Bool ok = TRUE; | |
769 | xXResResourceSizeSpec *crossRef = | |
770 | AddFragment(&ctx->response, sizeof(xXResResourceSizeSpec)); | |
771 | ok = ok && crossRef != NULL; | |
772 | if (ok) { | |
773 | xXResResourceSizeSpec **p; | |
774 | p = ht_add(ctx->visitedSubResources, &value); | |
775 | if (!p) { | |
776 | ok = FALSE; | |
777 | } else { | |
778 | *p = crossRef; | |
779 | } | |
780 | } | |
781 | if (!ok) { | |
782 | ctx->status = BadAlloc; | |
783 | } else { | |
784 | SizeType sizeFunc = GetResourceTypeSizeFunc(type); | |
785 | ResourceSizeRec size = { 0, 0, 0 }; | |
786 | sizeFunc(value, id, &size); | |
787 | ||
788 | crossRef->spec.resource = id; | |
789 | crossRef->spec.type = type; | |
790 | crossRef->bytes = size.resourceSize; | |
791 | crossRef->refCount = size.refCnt; | |
792 | crossRef->useCount = 1; | |
793 | ||
794 | ++ctx->sizeValue->numCrossReferences; | |
795 | ||
796 | ctx->resultBytes += sizeof(*crossRef); | |
797 | } | |
798 | } else { | |
799 | /* if we have visited the subresource earlier (from current parent | |
800 | resource), just increase its use count by one */ | |
801 | ++(*prevCrossRef)->useCount; | |
802 | } | |
803 | } | |
804 | } | |
805 | ||
806 | /** @brief Adds xXResResourceSizeValue describing a resource's size into | |
807 | the buffer contained in the context. In addition, the | |
808 | subresources are iterated and added as xXResResourceSizeSpec's | |
809 | by using AddSubResourceSizeSpec | |
810 | ||
811 | @see AddSubResourceSizeSpec | |
812 | ||
813 | @param[in] value The X resource object on which to add information | |
814 | about to the buffer | |
815 | @param[in] id The ID of the X resource | |
816 | @param[in] type The type of the X resource | |
817 | @param[in/out] cdata The context object of type ConstructResourceBytesCtx. | |
818 | Void pointer type is used here to satisfy the type | |
819 | FindRes | |
820 | */ | |
821 | static void | |
822 | AddResourceSizeValue(pointer ptr, XID id, RESTYPE type, pointer cdata) | |
823 | { | |
824 | ConstructResourceBytesCtx *ctx = cdata; | |
825 | if (ctx->status == Success && | |
826 | !ht_find(ctx->visitedResources, &id)) { | |
827 | Bool ok = TRUE; | |
828 | HashTable ht; | |
829 | HtGenericHashSetupRec htSetup = { | |
830 | .keySize = sizeof(void*) | |
831 | }; | |
832 | ||
833 | /* it doesn't matter that we don't undo the work done here | |
834 | * immediately. All but ht_init will be undone at the end | |
835 | * of the request and there can happen no failure after | |
836 | * ht_init, so we don't need to clean it up here in any | |
837 | * special way */ | |
838 | ||
839 | xXResResourceSizeValue *value = | |
840 | AddFragment(&ctx->response, sizeof(xXResResourceSizeValue)); | |
841 | if (!value) { | |
842 | ok = FALSE; | |
843 | } | |
844 | ok = ok && ht_add(ctx->visitedResources, &id); | |
845 | if (ok) { | |
846 | ht = ht_create(htSetup.keySize, | |
847 | sizeof(xXResResourceSizeSpec*), | |
848 | ht_generic_hash, ht_generic_compare, | |
849 | &htSetup); | |
850 | ok = ok && ht; | |
851 | } | |
852 | ||
853 | if (!ok) { | |
854 | ctx->status = BadAlloc; | |
855 | } else { | |
856 | SizeType sizeFunc = GetResourceTypeSizeFunc(type); | |
857 | ResourceSizeRec size = { 0, 0, 0 }; | |
858 | ||
859 | sizeFunc(ptr, id, &size); | |
860 | ||
861 | value->size.spec.resource = id; | |
862 | value->size.spec.type = type; | |
863 | value->size.bytes = size.resourceSize; | |
864 | value->size.refCount = size.refCnt; | |
865 | value->size.useCount = 1; | |
866 | value->numCrossReferences = 0; | |
867 | ||
868 | ctx->sizeValue = value; | |
869 | ctx->visitedSubResources = ht; | |
870 | FindSubResources(ptr, type, AddSubResourceSizeSpec, ctx); | |
871 | ctx->visitedSubResources = NULL; | |
872 | ctx->sizeValue = NULL; | |
873 | ||
874 | ctx->resultBytes += sizeof(*value); | |
875 | ++ctx->numSizes; | |
876 | ||
877 | ht_destroy(ht); | |
878 | } | |
879 | } | |
880 | } | |
881 | ||
882 | /** @brief A variant of AddResourceSizeValue that passes the resource type | |
883 | through the context object to satisfy the type FindResType | |
884 | ||
885 | @see AddResourceSizeValue | |
886 | ||
887 | @param[in] ptr The resource | |
888 | @param[in] id The resource ID | |
889 | @param[in/out] cdata The context object that contains the resource type | |
890 | */ | |
891 | static void | |
892 | AddResourceSizeValueWithResType(pointer ptr, XID id, pointer cdata) | |
893 | { | |
894 | ConstructResourceBytesCtx *ctx = cdata; | |
895 | AddResourceSizeValue(ptr, id, ctx->resType, cdata); | |
896 | } | |
897 | ||
898 | /** @brief Adds the information of a resource into the buffer if it matches | |
899 | the match condition. | |
900 | ||
901 | @see AddResourceSizeValue | |
902 | ||
903 | @param[in] ptr The resource | |
904 | @param[in] id The resource ID | |
905 | @param[in] type The resource type | |
906 | @param[in/out] cdata The context object as a void pointer to satisfy the | |
907 | type FindAllRes | |
908 | */ | |
909 | static void | |
910 | AddResourceSizeValueByResource(pointer ptr, XID id, RESTYPE type, pointer cdata) | |
911 | { | |
912 | ConstructResourceBytesCtx *ctx = cdata; | |
913 | xXResResourceIdSpec *spec = ctx->curSpec; | |
914 | ||
915 | if ((!spec->type || spec->type == type) && | |
916 | (!spec->resource || spec->resource == id)) { | |
917 | AddResourceSizeValue(ptr, id, type, ctx); | |
918 | } | |
919 | } | |
920 | ||
921 | /** @brief Add all resources of the client into the result buffer | |
922 | disregarding all those specifications that specify the | |
923 | resource by its ID. Those are handled by | |
924 | ConstructResourceBytesByResource | |
925 | ||
926 | @see ConstructResourceBytesByResource | |
927 | ||
928 | @param[in] aboutClient Which client is being considered | |
929 | @param[in/out] ctx The context that contains the resource id | |
930 | specifications as well as the result buffer | |
931 | */ | |
932 | static void | |
933 | ConstructClientResourceBytes(ClientPtr aboutClient, | |
934 | ConstructResourceBytesCtx *ctx) | |
935 | { | |
936 | int specIdx; | |
937 | for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) { | |
938 | xXResResourceIdSpec* spec = ctx->specs + specIdx; | |
939 | if (spec->resource) { | |
940 | /* these specs are handled elsewhere */ | |
941 | } else if (spec->type) { | |
942 | ctx->resType = spec->type; | |
943 | FindClientResourcesByType(aboutClient, spec->type, | |
944 | AddResourceSizeValueWithResType, ctx); | |
945 | } else { | |
946 | FindAllClientResources(aboutClient, AddResourceSizeValue, ctx); | |
947 | } | |
948 | } | |
949 | } | |
950 | ||
951 | /** @brief Add the sizes of all such resources that can are specified by | |
952 | their ID in the resource id specification. The scan can | |
953 | by limited to a client with the aboutClient parameter | |
954 | ||
955 | @see ConstructResourceBytesByResource | |
956 | ||
957 | @param[in] aboutClient Which client is being considered. This may be None | |
958 | to mean all clients. | |
959 | @param[in/out] ctx The context that contains the resource id | |
960 | specifications as well as the result buffer. In | |
961 | addition this function uses the curSpec field to | |
962 | keep a pointer to the current resource id | |
963 | specification in it, which can be used by | |
964 | AddResourceSizeValueByResource . | |
965 | */ | |
966 | static void | |
967 | ConstructResourceBytesByResource(XID aboutClient, ConstructResourceBytesCtx *ctx) | |
968 | { | |
969 | int specIdx; | |
970 | for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) { | |
971 | xXResResourceIdSpec *spec = ctx->specs + specIdx; | |
972 | if (spec->resource) { | |
973 | int cid = CLIENT_ID(spec->resource); | |
974 | if (cid < currentMaxClients && | |
975 | (aboutClient == None || cid == aboutClient)) { | |
976 | ClientPtr client = clients[cid]; | |
977 | if (client) { | |
978 | ctx->curSpec = spec; | |
979 | FindAllClientResources(client, | |
980 | AddResourceSizeValueByResource, | |
981 | ctx); | |
982 | } | |
983 | } | |
984 | } | |
985 | } | |
986 | } | |
987 | ||
988 | /** @brief Build the resource size response for the given client | |
989 | (or all if not specified) per the parameters set up | |
990 | in the context object. | |
991 | ||
992 | @param[in] aboutClient Which client to consider or None for all clients | |
993 | @param[in/out] ctx The context object that contains the request as well | |
994 | as the response buffer. | |
995 | */ | |
996 | static int | |
997 | ConstructResourceBytes(XID aboutClient, | |
998 | ConstructResourceBytesCtx *ctx) | |
999 | { | |
1000 | if (aboutClient) { | |
1001 | int clientIdx = CLIENT_ID(aboutClient); | |
1002 | ClientPtr client = NullClient; | |
1003 | ||
1004 | if ((clientIdx >= currentMaxClients) || !clients[clientIdx]) { | |
1005 | ctx->sendClient->errorValue = aboutClient; | |
1006 | return BadValue; | |
1007 | } | |
1008 | ||
1009 | client = clients[clientIdx]; | |
1010 | ||
1011 | ConstructClientResourceBytes(client, ctx); | |
1012 | ConstructResourceBytesByResource(aboutClient, ctx); | |
1013 | } else { | |
1014 | int clientIdx; | |
1015 | ||
1016 | ConstructClientResourceBytes(NULL, ctx); | |
1017 | ||
1018 | for (clientIdx = 0; clientIdx < currentMaxClients; ++clientIdx) { | |
1019 | ClientPtr client = clients[clientIdx]; | |
1020 | ||
1021 | if (client) { | |
1022 | ConstructClientResourceBytes(client, ctx); | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | ConstructResourceBytesByResource(None, ctx); | |
1027 | } | |
1028 | ||
1029 | ||
1030 | return ctx->status; | |
1031 | } | |
1032 | ||
1033 | /** @brief Implements the XResQueryResourceBytes of XResProto v1.2 */ | |
1034 | static int | |
1035 | ProcXResQueryResourceBytes (ClientPtr client) | |
1036 | { | |
1037 | REQUEST(xXResQueryResourceBytesReq); | |
1038 | ||
1039 | int rc; | |
1040 | ConstructResourceBytesCtx ctx; | |
1041 | ||
1042 | REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq); | |
1043 | REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq, | |
1044 | stuff->numSpecs * sizeof(ctx.specs[0])); | |
1045 | ||
1046 | if (!InitConstructResourceBytesCtx(&ctx, client, | |
1047 | stuff->numSpecs, | |
1048 | (void*) ((char*) stuff + | |
1049 | sz_xXResQueryResourceBytesReq))) { | |
1050 | return BadAlloc; | |
1051 | } | |
1052 | ||
1053 | rc = ConstructResourceBytes(stuff->client, &ctx); | |
1054 | ||
1055 | if (rc == Success) { | |
1056 | xXResQueryResourceBytesReply rep = { | |
1057 | .type = X_Reply, | |
1058 | .sequenceNumber = client->sequence, | |
1059 | .length = bytes_to_int32(ctx.resultBytes), | |
1060 | .numSizes = ctx.numSizes | |
1061 | }; | |
1062 | ||
1063 | if (client->swapped) { | |
1064 | swaps (&rep.sequenceNumber); | |
1065 | swapl (&rep.length); | |
1066 | swapl (&rep.numSizes); | |
1067 | ||
1068 | SwapXResQueryResourceBytes(&ctx.response); | |
1069 | } | |
1070 | ||
1071 | WriteToClient(client, sizeof(rep), &rep); | |
1072 | WriteFragmentsToClient(client, &ctx.response); | |
1073 | } | |
1074 | ||
1075 | DestroyConstructResourceBytesCtx(&ctx); | |
1076 | ||
1077 | return rc; | |
1078 | } | |
1079 | ||
1080 | static int | |
1081 | ProcResDispatch(ClientPtr client) | |
1082 | { | |
1083 | REQUEST(xReq); | |
1084 | switch (stuff->data) { | |
1085 | case X_XResQueryVersion: | |
1086 | return ProcXResQueryVersion(client); | |
1087 | case X_XResQueryClients: | |
1088 | return ProcXResQueryClients(client); | |
1089 | case X_XResQueryClientResources: | |
1090 | return ProcXResQueryClientResources(client); | |
1091 | case X_XResQueryClientPixmapBytes: | |
1092 | return ProcXResQueryClientPixmapBytes(client); | |
1093 | case X_XResQueryClientIds: | |
1094 | return ProcXResQueryClientIds(client); | |
1095 | case X_XResQueryResourceBytes: | |
1096 | return ProcXResQueryResourceBytes(client); | |
1097 | default: break; | |
1098 | } | |
1099 | ||
1100 | return BadRequest; | |
1101 | } | |
1102 | ||
1103 | static int | |
1104 | SProcXResQueryVersion(ClientPtr client) | |
1105 | { | |
1106 | REQUEST(xXResQueryVersionReq); | |
1107 | REQUEST_SIZE_MATCH(xXResQueryVersionReq); | |
1108 | return ProcXResQueryVersion(client); | |
1109 | } | |
1110 | ||
1111 | static int | |
1112 | SProcXResQueryClientResources(ClientPtr client) | |
1113 | { | |
1114 | REQUEST(xXResQueryClientResourcesReq); | |
1115 | REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq); | |
1116 | swapl(&stuff->xid); | |
1117 | return ProcXResQueryClientResources(client); | |
1118 | } | |
1119 | ||
1120 | static int | |
1121 | SProcXResQueryClientPixmapBytes(ClientPtr client) | |
1122 | { | |
1123 | REQUEST(xXResQueryClientPixmapBytesReq); | |
1124 | REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq); | |
1125 | swapl(&stuff->xid); | |
1126 | return ProcXResQueryClientPixmapBytes(client); | |
1127 | } | |
1128 | ||
1129 | static int | |
1130 | SProcXResQueryClientIds (ClientPtr client) | |
1131 | { | |
1132 | REQUEST(xXResQueryClientIdsReq); | |
1133 | ||
1134 | REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq); | |
1135 | swapl(&stuff->numSpecs); | |
1136 | return ProcXResQueryClientIds(client); | |
1137 | } | |
1138 | ||
1139 | /** @brief Implements the XResQueryResourceBytes of XResProto v1.2. | |
1140 | This variant byteswaps request contents before issuing the | |
1141 | rest of the work to ProcXResQueryResourceBytes */ | |
1142 | static int | |
1143 | SProcXResQueryResourceBytes (ClientPtr client) | |
1144 | { | |
1145 | REQUEST(xXResQueryResourceBytesReq); | |
1146 | int c; | |
1147 | xXResResourceIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff)); | |
1148 | ||
1149 | swapl(&stuff->numSpecs); | |
1150 | REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq); | |
1151 | REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq, | |
1152 | stuff->numSpecs * sizeof(specs[0])); | |
1153 | ||
1154 | for (c = 0; c < stuff->numSpecs; ++c) { | |
1155 | SwapXResResourceIdSpec(specs + c); | |
1156 | } | |
1157 | ||
1158 | return ProcXResQueryResourceBytes(client); | |
1159 | } | |
1160 | ||
1161 | static int | |
1162 | SProcResDispatch (ClientPtr client) | |
1163 | { | |
1164 | REQUEST(xReq); | |
1165 | swaps(&stuff->length); | |
1166 | ||
1167 | switch (stuff->data) { | |
1168 | case X_XResQueryVersion: | |
1169 | return SProcXResQueryVersion(client); | |
1170 | case X_XResQueryClients: /* nothing to swap */ | |
1171 | return ProcXResQueryClients(client); | |
1172 | case X_XResQueryClientResources: | |
1173 | return SProcXResQueryClientResources(client); | |
1174 | case X_XResQueryClientPixmapBytes: | |
1175 | return SProcXResQueryClientPixmapBytes(client); | |
1176 | case X_XResQueryClientIds: | |
1177 | return SProcXResQueryClientIds(client); | |
1178 | case X_XResQueryResourceBytes: | |
1179 | return SProcXResQueryResourceBytes(client); | |
1180 | default: break; | |
1181 | } | |
1182 | ||
1183 | return BadRequest; | |
1184 | } | |
1185 | ||
1186 | void | |
1187 | ResExtensionInit(void) | |
1188 | { | |
1189 | (void) AddExtension(XRES_NAME, 0, 0, | |
1190 | ProcResDispatch, SProcResDispatch, | |
1191 | NULL, StandardMinorOpcode); | |
1192 | } |