xres.c revision 5a112b11
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 */
40typedef 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 */
50typedef 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. */
64typedef 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 cross-references */
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*/
101static void *
102AddFragment(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*/
120static void
121WriteFragmentsToClient(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*/
133static void
134DestroyFragments(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 */
145static void
146InitConstructClientIdCtx(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) */
156static void
157DestroyConstructClientIdCtx(ConstructClientIdCtx *ctx)
158{
159    DestroyFragments(&ctx->response);
160}
161
162static Bool
163InitConstructResourceBytesCtx(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
186static void
187DestroyConstructResourceBytesCtx(ConstructResourceBytesCtx *ctx)
188{
189    DestroyFragments(&ctx->response);
190    ht_destroy(ctx->visitedResources);
191}
192
193static int
194ProcXResQueryVersion(ClientPtr client)
195{
196    xXResQueryVersionReply rep = {
197        .type = X_Reply,
198        .sequenceNumber = client->sequence,
199        .length = 0,
200        .server_major = SERVER_XRES_MAJOR_VERSION,
201        .server_minor = SERVER_XRES_MINOR_VERSION
202    };
203
204    REQUEST_SIZE_MATCH(xXResQueryVersionReq);
205
206    if (client->swapped) {
207        swaps(&rep.sequenceNumber);
208        swapl(&rep.length);
209        swaps(&rep.server_major);
210        swaps(&rep.server_minor);
211    }
212    WriteToClient(client, sizeof(xXResQueryVersionReply), &rep);
213    return Success;
214}
215
216static int
217ProcXResQueryClients(ClientPtr client)
218{
219    /* REQUEST(xXResQueryClientsReq); */
220    xXResQueryClientsReply rep;
221    int *current_clients;
222    int i, num_clients;
223
224    REQUEST_SIZE_MATCH(xXResQueryClientsReq);
225
226    current_clients = xallocarray(currentMaxClients, sizeof(int));
227
228    num_clients = 0;
229    for (i = 0; i < currentMaxClients; i++) {
230        if (clients[i]) {
231            current_clients[num_clients] = i;
232            num_clients++;
233        }
234    }
235
236    rep = (xXResQueryClientsReply) {
237        .type = X_Reply,
238        .sequenceNumber = client->sequence,
239        .length = bytes_to_int32(num_clients * sz_xXResClient),
240        .num_clients = num_clients
241    };
242    if (client->swapped) {
243        swaps(&rep.sequenceNumber);
244        swapl(&rep.length);
245        swapl(&rep.num_clients);
246    }
247    WriteToClient(client, sizeof(xXResQueryClientsReply), &rep);
248
249    if (num_clients) {
250        xXResClient scratch;
251
252        for (i = 0; i < num_clients; i++) {
253            scratch.resource_base = clients[current_clients[i]]->clientAsMask;
254            scratch.resource_mask = RESOURCE_ID_MASK;
255
256            if (client->swapped) {
257                swapl(&scratch.resource_base);
258                swapl(&scratch.resource_mask);
259            }
260            WriteToClient(client, sz_xXResClient, &scratch);
261        }
262    }
263
264    free(current_clients);
265
266    return Success;
267}
268
269static void
270ResFindAllRes(void *value, XID id, RESTYPE type, void *cdata)
271{
272    int *counts = (int *) cdata;
273
274    counts[(type & TypeMask) - 1]++;
275}
276
277static CARD32
278resourceTypeAtom(int i)
279{
280    CARD32 ret;
281
282    const char *name = LookupResourceName(i);
283    if (strcmp(name, XREGISTRY_UNKNOWN))
284        ret = MakeAtom(name, strlen(name), TRUE);
285    else {
286        char buf[40];
287
288        snprintf(buf, sizeof(buf), "Unregistered resource %i", i + 1);
289        ret = MakeAtom(buf, strlen(buf), TRUE);
290    }
291
292    return ret;
293}
294
295static int
296ProcXResQueryClientResources(ClientPtr client)
297{
298    REQUEST(xXResQueryClientResourcesReq);
299    xXResQueryClientResourcesReply rep;
300    int i, clientID, num_types;
301    int *counts;
302
303    REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq);
304
305    clientID = CLIENT_ID(stuff->xid);
306
307    if ((clientID >= currentMaxClients) || !clients[clientID]) {
308        client->errorValue = stuff->xid;
309        return BadValue;
310    }
311
312    counts = calloc(lastResourceType + 1, sizeof(int));
313
314    FindAllClientResources(clients[clientID], ResFindAllRes, counts);
315
316    num_types = 0;
317
318    for (i = 0; i <= lastResourceType; i++) {
319        if (counts[i])
320            num_types++;
321    }
322
323    rep = (xXResQueryClientResourcesReply) {
324        .type = X_Reply,
325        .sequenceNumber = client->sequence,
326        .length = bytes_to_int32(num_types * sz_xXResType),
327        .num_types = num_types
328    };
329    if (client->swapped) {
330        swaps(&rep.sequenceNumber);
331        swapl(&rep.length);
332        swapl(&rep.num_types);
333    }
334
335    WriteToClient(client, sizeof(xXResQueryClientResourcesReply), &rep);
336
337    if (num_types) {
338        xXResType scratch;
339
340        for (i = 0; i < lastResourceType; i++) {
341            if (!counts[i])
342                continue;
343
344            scratch.resource_type = resourceTypeAtom(i + 1);
345            scratch.count = counts[i];
346
347            if (client->swapped) {
348                swapl(&scratch.resource_type);
349                swapl(&scratch.count);
350            }
351            WriteToClient(client, sz_xXResType, &scratch);
352        }
353    }
354
355    free(counts);
356
357    return Success;
358}
359
360static void
361ResFindResourcePixmaps(void *value, XID id, RESTYPE type, void *cdata)
362{
363    SizeType sizeFunc = GetResourceTypeSizeFunc(type);
364    ResourceSizeRec size = { 0, 0, 0 };
365    unsigned long *bytes = cdata;
366
367    sizeFunc(value, id, &size);
368    *bytes += size.pixmapRefSize;
369}
370
371static int
372ProcXResQueryClientPixmapBytes(ClientPtr client)
373{
374    REQUEST(xXResQueryClientPixmapBytesReq);
375    xXResQueryClientPixmapBytesReply rep;
376    int clientID;
377    unsigned long bytes;
378
379    REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq);
380
381    clientID = CLIENT_ID(stuff->xid);
382
383    if ((clientID >= currentMaxClients) || !clients[clientID]) {
384        client->errorValue = stuff->xid;
385        return BadValue;
386    }
387
388    bytes = 0;
389
390    FindAllClientResources(clients[clientID], ResFindResourcePixmaps,
391                           (void *) (&bytes));
392
393    rep = (xXResQueryClientPixmapBytesReply) {
394        .type = X_Reply,
395        .sequenceNumber = client->sequence,
396        .length = 0,
397        .bytes = bytes,
398#ifdef _XSERVER64
399        .bytes_overflow = bytes >> 32
400#else
401        .bytes_overflow = 0
402#endif
403    };
404    if (client->swapped) {
405        swaps(&rep.sequenceNumber);
406        swapl(&rep.length);
407        swapl(&rep.bytes);
408        swapl(&rep.bytes_overflow);
409    }
410    WriteToClient(client, sizeof(xXResQueryClientPixmapBytesReply), &rep);
411
412    return Success;
413}
414
415/** @brief Finds out if a client's information need to be put into the
416    response; marks client having been handled, if that is the case.
417
418    @param client   The client to send information about
419    @param mask     The request mask (0 to send everything, otherwise a
420                    bitmask of X_XRes*Mask)
421    @param ctx      The context record that tells which clients and id types
422                    have been already handled
423    @param sendMask Which id type are we now considering. One of X_XRes*Mask.
424
425    @return Returns TRUE if the client information needs to be on the
426            response, otherwise FALSE.
427*/
428static Bool
429WillConstructMask(ClientPtr client, CARD32 mask,
430                  ConstructClientIdCtx *ctx, int sendMask)
431{
432    if ((!mask || (mask & sendMask))
433        && !(ctx->sentClientMasks[client->index] & sendMask)) {
434        ctx->sentClientMasks[client->index] |= sendMask;
435        return TRUE;
436    } else {
437        return FALSE;
438    }
439}
440
441/** @brief Constructs a response about a single client, based on a certain
442           client id spec
443
444    @param sendClient Which client wishes to receive this answer. Used for
445                      byte endianness.
446    @param client     Which client are we considering.
447    @param mask       The client id spec mask indicating which information
448                      we want about this client.
449    @param ctx        The context record containing the constructed response
450                      and information on which clients and masks have been
451                      already handled.
452
453    @return Return TRUE if everything went OK, otherwise FALSE which indicates
454            a memory allocation problem.
455*/
456static Bool
457ConstructClientIdValue(ClientPtr sendClient, ClientPtr client, CARD32 mask,
458                       ConstructClientIdCtx *ctx)
459{
460    xXResClientIdValue rep;
461
462    rep.spec.client = client->clientAsMask;
463    if (client->swapped) {
464        swapl (&rep.spec.client);
465    }
466
467    if (WillConstructMask(client, mask, ctx, X_XResClientXIDMask)) {
468        void *ptr = AddFragment(&ctx->response, sizeof(rep));
469        if (!ptr) {
470            return FALSE;
471        }
472
473        rep.spec.mask = X_XResClientXIDMask;
474        rep.length = 0;
475        if (sendClient->swapped) {
476            swapl (&rep.spec.mask);
477            /* swapl (&rep.length, n); - not required for rep.length = 0 */
478        }
479
480        memcpy(ptr, &rep, sizeof(rep));
481
482        ctx->resultBytes += sizeof(rep);
483        ++ctx->numIds;
484    }
485    if (WillConstructMask(client, mask, ctx, X_XResLocalClientPIDMask)) {
486        pid_t pid = GetClientPid(client);
487
488        if (pid != -1) {
489            void *ptr = AddFragment(&ctx->response,
490                                    sizeof(rep) + sizeof(CARD32));
491            CARD32 *value = (void*) ((char*) ptr + sizeof(rep));
492
493            if (!ptr) {
494                return FALSE;
495            }
496
497            rep.spec.mask = X_XResLocalClientPIDMask;
498            rep.length = 4;
499
500            if (sendClient->swapped) {
501                swapl (&rep.spec.mask);
502                swapl (&rep.length);
503            }
504
505            if (sendClient->swapped) {
506                swapl (value);
507            }
508            memcpy(ptr, &rep, sizeof(rep));
509            *value = pid;
510
511            ctx->resultBytes += sizeof(rep) + sizeof(CARD32);
512            ++ctx->numIds;
513        }
514    }
515
516    /* memory allocation errors earlier may return with FALSE */
517    return TRUE;
518}
519
520/** @brief Constructs a response about all clients, based on a client id specs
521
522    @param client   Which client which we are constructing the response for.
523    @param numSpecs Number of client id specs in specs
524    @param specs    Client id specs
525
526    @return Return Success if everything went OK, otherwise a Bad* (currently
527            BadAlloc or BadValue)
528*/
529static int
530ConstructClientIds(ClientPtr client,
531                   int numSpecs, xXResClientIdSpec* specs,
532                   ConstructClientIdCtx *ctx)
533{
534    int specIdx;
535
536    for (specIdx = 0; specIdx < numSpecs; ++specIdx) {
537        if (specs[specIdx].client == 0) {
538            int c;
539            for (c = 0; c < currentMaxClients; ++c) {
540                if (clients[c]) {
541                    if (!ConstructClientIdValue(client, clients[c],
542                                                specs[specIdx].mask, ctx)) {
543                        return BadAlloc;
544                    }
545                }
546            }
547        } else {
548            int clientID = CLIENT_ID(specs[specIdx].client);
549
550            if ((clientID < currentMaxClients) && clients[clientID]) {
551                if (!ConstructClientIdValue(client, clients[clientID],
552                                            specs[specIdx].mask, ctx)) {
553                    return BadAlloc;
554                }
555            }
556        }
557    }
558
559    /* memory allocation errors earlier may return with BadAlloc */
560    return Success;
561}
562
563/** @brief Response to XResQueryClientIds request introduced in XResProto v1.2
564
565    @param client Which client which we are constructing the response for.
566
567    @return Returns the value returned from ConstructClientIds with the same
568            semantics
569*/
570static int
571ProcXResQueryClientIds (ClientPtr client)
572{
573    REQUEST(xXResQueryClientIdsReq);
574
575    xXResClientIdSpec        *specs = (void*) ((char*) stuff + sizeof(*stuff));
576    int                       rc;
577    ConstructClientIdCtx      ctx;
578
579    InitConstructClientIdCtx(&ctx);
580
581    REQUEST_AT_LEAST_SIZE(xXResQueryClientIdsReq);
582    REQUEST_FIXED_SIZE(xXResQueryClientIdsReq,
583                       stuff->numSpecs * sizeof(specs[0]));
584
585    rc = ConstructClientIds(client, stuff->numSpecs, specs, &ctx);
586
587    if (rc == Success) {
588        xXResQueryClientIdsReply  rep = {
589            .type = X_Reply,
590            .sequenceNumber = client->sequence,
591            .length = bytes_to_int32(ctx.resultBytes),
592            .numIds = ctx.numIds
593        };
594
595        assert((ctx.resultBytes & 3) == 0);
596
597        if (client->swapped) {
598            swaps (&rep.sequenceNumber);
599            swapl (&rep.length);
600            swapl (&rep.numIds);
601        }
602
603        WriteToClient(client, sizeof(rep), &rep);
604        WriteFragmentsToClient(client, &ctx.response);
605    }
606
607    DestroyConstructClientIdCtx(&ctx);
608
609    return rc;
610}
611
612/** @brief Swaps xXResResourceIdSpec endianness */
613static void
614SwapXResResourceIdSpec(xXResResourceIdSpec *spec)
615{
616    swapl(&spec->resource);
617    swapl(&spec->type);
618}
619
620/** @brief Swaps xXResResourceSizeSpec endianness */
621static void
622SwapXResResourceSizeSpec(xXResResourceSizeSpec *size)
623{
624    SwapXResResourceIdSpec(&size->spec);
625    swapl(&size->bytes);
626    swapl(&size->refCount);
627    swapl(&size->useCount);
628}
629
630/** @brief Swaps xXResResourceSizeValue endianness */
631static void
632SwapXResResourceSizeValue(xXResResourceSizeValue *rep)
633{
634    SwapXResResourceSizeSpec(&rep->size);
635    swapl(&rep->numCrossReferences);
636}
637
638/** @brief Swaps the response bytes */
639static void
640SwapXResQueryResourceBytes(struct xorg_list *response)
641{
642    struct xorg_list *it = response->next;
643    int c;
644
645    while (it != response) {
646        xXResResourceSizeValue *value = FRAGMENT_DATA(it);
647        it = it->next;
648        for (c = 0; c < value->numCrossReferences; ++c) {
649            xXResResourceSizeSpec *spec = FRAGMENT_DATA(it);
650            SwapXResResourceSizeSpec(spec);
651            it = it->next;
652        }
653        SwapXResResourceSizeValue(value);
654    }
655}
656
657/** @brief Adds xXResResourceSizeSpec describing a resource's size into
658           the buffer contained in the context. The resource is considered
659           to be a subresource.
660
661   @see AddResourceSizeValue
662
663   @param[in] value     The X resource object on which to add information
664                        about to the buffer
665   @param[in] id        The ID of the X resource
666   @param[in] type      The type of the X resource
667   @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
668                        Void pointer type is used here to satisfy the type
669                        FindRes
670*/
671static void
672AddSubResourceSizeSpec(void *value,
673                       XID id,
674                       RESTYPE type,
675                       void *cdata)
676{
677    ConstructResourceBytesCtx *ctx = cdata;
678
679    if (ctx->status == Success) {
680        xXResResourceSizeSpec **prevCrossRef =
681          ht_find(ctx->visitedSubResources, &value);
682        if (!prevCrossRef) {
683            Bool ok = TRUE;
684            xXResResourceSizeSpec *crossRef =
685                AddFragment(&ctx->response, sizeof(xXResResourceSizeSpec));
686            ok = ok && crossRef != NULL;
687            if (ok) {
688                xXResResourceSizeSpec **p;
689                p = ht_add(ctx->visitedSubResources, &value);
690                if (!p) {
691                    ok = FALSE;
692                } else {
693                    *p = crossRef;
694                }
695            }
696            if (!ok) {
697                ctx->status = BadAlloc;
698            } else {
699                SizeType sizeFunc = GetResourceTypeSizeFunc(type);
700                ResourceSizeRec size = { 0, 0, 0 };
701                sizeFunc(value, id, &size);
702
703                crossRef->spec.resource = id;
704                crossRef->spec.type = resourceTypeAtom(type);
705                crossRef->bytes = size.resourceSize;
706                crossRef->refCount = size.refCnt;
707                crossRef->useCount = 1;
708
709                ++ctx->sizeValue->numCrossReferences;
710
711                ctx->resultBytes += sizeof(*crossRef);
712            }
713        } else {
714            /* if we have visited the subresource earlier (from current parent
715               resource), just increase its use count by one */
716            ++(*prevCrossRef)->useCount;
717        }
718    }
719}
720
721/** @brief Adds xXResResourceSizeValue describing a resource's size into
722           the buffer contained in the context. In addition, the
723           subresources are iterated and added as xXResResourceSizeSpec's
724           by using AddSubResourceSizeSpec
725
726   @see AddSubResourceSizeSpec
727
728   @param[in] value     The X resource object on which to add information
729                        about to the buffer
730   @param[in] id        The ID of the X resource
731   @param[in] type      The type of the X resource
732   @param[in/out] cdata The context object of type ConstructResourceBytesCtx.
733                        Void pointer type is used here to satisfy the type
734                        FindRes
735*/
736static void
737AddResourceSizeValue(void *ptr, XID id, RESTYPE type, void *cdata)
738{
739    ConstructResourceBytesCtx *ctx = cdata;
740    if (ctx->status == Success &&
741        !ht_find(ctx->visitedResources, &id)) {
742        Bool ok = TRUE;
743        HashTable ht;
744        HtGenericHashSetupRec htSetup = {
745            .keySize = sizeof(void*)
746        };
747
748        /* it doesn't matter that we don't undo the work done here
749         * immediately. All but ht_init will be undone at the end
750         * of the request and there can happen no failure after
751         * ht_init, so we don't need to clean it up here in any
752         * special way */
753
754        xXResResourceSizeValue *value =
755            AddFragment(&ctx->response, sizeof(xXResResourceSizeValue));
756        if (!value) {
757            ok = FALSE;
758        }
759        ok = ok && ht_add(ctx->visitedResources, &id);
760        if (ok) {
761            ht = ht_create(htSetup.keySize,
762                           sizeof(xXResResourceSizeSpec*),
763                           ht_generic_hash, ht_generic_compare,
764                           &htSetup);
765            ok = ok && ht;
766        }
767
768        if (!ok) {
769            ctx->status = BadAlloc;
770        } else {
771            SizeType sizeFunc = GetResourceTypeSizeFunc(type);
772            ResourceSizeRec size = { 0, 0, 0 };
773
774            sizeFunc(ptr, id, &size);
775
776            value->size.spec.resource = id;
777            value->size.spec.type = resourceTypeAtom(type);
778            value->size.bytes = size.resourceSize;
779            value->size.refCount = size.refCnt;
780            value->size.useCount = 1;
781            value->numCrossReferences = 0;
782
783            ctx->sizeValue = value;
784            ctx->visitedSubResources = ht;
785            FindSubResources(ptr, type, AddSubResourceSizeSpec, ctx);
786            ctx->visitedSubResources = NULL;
787            ctx->sizeValue = NULL;
788
789            ctx->resultBytes += sizeof(*value);
790            ++ctx->numSizes;
791
792            ht_destroy(ht);
793        }
794    }
795}
796
797/** @brief A variant of AddResourceSizeValue that passes the resource type
798           through the context object to satisfy the type FindResType
799
800   @see AddResourceSizeValue
801
802   @param[in] ptr        The resource
803   @param[in] id         The resource ID
804   @param[in/out] cdata  The context object that contains the resource type
805*/
806static void
807AddResourceSizeValueWithResType(void *ptr, XID id, void *cdata)
808{
809    ConstructResourceBytesCtx *ctx = cdata;
810    AddResourceSizeValue(ptr, id, ctx->resType, cdata);
811}
812
813/** @brief Adds the information of a resource into the buffer if it matches
814           the match condition.
815
816   @see AddResourceSizeValue
817
818   @param[in] ptr        The resource
819   @param[in] id         The resource ID
820   @param[in] type       The resource type
821   @param[in/out] cdata  The context object as a void pointer to satisfy the
822                         type FindAllRes
823*/
824static void
825AddResourceSizeValueByResource(void *ptr, XID id, RESTYPE type, void *cdata)
826{
827    ConstructResourceBytesCtx *ctx = cdata;
828    xXResResourceIdSpec *spec = ctx->curSpec;
829
830    if ((!spec->type || spec->type == type) &&
831        (!spec->resource || spec->resource == id)) {
832        AddResourceSizeValue(ptr, id, type, ctx);
833    }
834}
835
836/** @brief Add all resources of the client into the result buffer
837           disregarding all those specifications that specify the
838           resource by its ID. Those are handled by
839           ConstructResourceBytesByResource
840
841   @see ConstructResourceBytesByResource
842
843   @param[in] aboutClient  Which client is being considered
844   @param[in/out] ctx      The context that contains the resource id
845                           specifications as well as the result buffer
846*/
847static void
848ConstructClientResourceBytes(ClientPtr aboutClient,
849                             ConstructResourceBytesCtx *ctx)
850{
851    int specIdx;
852    for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
853        xXResResourceIdSpec* spec = ctx->specs + specIdx;
854        if (spec->resource) {
855            /* these specs are handled elsewhere */
856        } else if (spec->type) {
857            ctx->resType = spec->type;
858            FindClientResourcesByType(aboutClient, spec->type,
859                                      AddResourceSizeValueWithResType, ctx);
860        } else {
861            FindAllClientResources(aboutClient, AddResourceSizeValue, ctx);
862        }
863    }
864}
865
866/** @brief Add the sizes of all such resources that can are specified by
867           their ID in the resource id specification. The scan can
868           by limited to a client with the aboutClient parameter
869
870   @see ConstructResourceBytesByResource
871
872   @param[in] aboutClient  Which client is being considered. This may be None
873                           to mean all clients.
874   @param[in/out] ctx      The context that contains the resource id
875                           specifications as well as the result buffer. In
876                           addition this function uses the curSpec field to
877                           keep a pointer to the current resource id
878                           specification in it, which can be used by
879                           AddResourceSizeValueByResource .
880*/
881static void
882ConstructResourceBytesByResource(XID aboutClient, ConstructResourceBytesCtx *ctx)
883{
884    int specIdx;
885    for (specIdx = 0; specIdx < ctx->numSpecs; ++specIdx) {
886        xXResResourceIdSpec *spec = ctx->specs + specIdx;
887        if (spec->resource) {
888            int cid = CLIENT_ID(spec->resource);
889            if (cid < currentMaxClients &&
890                (aboutClient == None || cid == aboutClient)) {
891                ClientPtr client = clients[cid];
892                if (client) {
893                    ctx->curSpec = spec;
894                    FindAllClientResources(client,
895                                           AddResourceSizeValueByResource,
896                                           ctx);
897                }
898            }
899        }
900    }
901}
902
903/** @brief Build the resource size response for the given client
904           (or all if not specified) per the parameters set up
905           in the context object.
906
907  @param[in] aboutClient  Which client to consider or None for all clients
908  @param[in/out] ctx      The context object that contains the request as well
909                          as the response buffer.
910*/
911static int
912ConstructResourceBytes(XID aboutClient,
913                       ConstructResourceBytesCtx *ctx)
914{
915    if (aboutClient) {
916        int clientIdx = CLIENT_ID(aboutClient);
917        ClientPtr client = NullClient;
918
919        if ((clientIdx >= currentMaxClients) || !clients[clientIdx]) {
920            ctx->sendClient->errorValue = aboutClient;
921            return BadValue;
922        }
923
924        client = clients[clientIdx];
925
926        ConstructClientResourceBytes(client, ctx);
927        ConstructResourceBytesByResource(aboutClient, ctx);
928    } else {
929        int clientIdx;
930
931        ConstructClientResourceBytes(NULL, ctx);
932
933        for (clientIdx = 0; clientIdx < currentMaxClients; ++clientIdx) {
934            ClientPtr client = clients[clientIdx];
935
936            if (client) {
937                ConstructClientResourceBytes(client, ctx);
938            }
939        }
940
941        ConstructResourceBytesByResource(None, ctx);
942    }
943
944
945    return ctx->status;
946}
947
948/** @brief Implements the XResQueryResourceBytes of XResProto v1.2 */
949static int
950ProcXResQueryResourceBytes (ClientPtr client)
951{
952    REQUEST(xXResQueryResourceBytesReq);
953
954    int                          rc;
955    ConstructResourceBytesCtx    ctx;
956
957    REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
958    if (stuff->numSpecs > UINT32_MAX / sizeof(ctx.specs[0]))
959        return BadLength;
960    REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
961                       stuff->numSpecs * sizeof(ctx.specs[0]));
962
963    if (!InitConstructResourceBytesCtx(&ctx, client,
964                                       stuff->numSpecs,
965                                       (void*) ((char*) stuff +
966                                                sz_xXResQueryResourceBytesReq))) {
967        return BadAlloc;
968    }
969
970    rc = ConstructResourceBytes(stuff->client, &ctx);
971
972    if (rc == Success) {
973        xXResQueryResourceBytesReply rep = {
974            .type = X_Reply,
975            .sequenceNumber = client->sequence,
976            .length = bytes_to_int32(ctx.resultBytes),
977            .numSizes = ctx.numSizes
978        };
979
980        if (client->swapped) {
981            swaps (&rep.sequenceNumber);
982            swapl (&rep.length);
983            swapl (&rep.numSizes);
984
985            SwapXResQueryResourceBytes(&ctx.response);
986        }
987
988        WriteToClient(client, sizeof(rep), &rep);
989        WriteFragmentsToClient(client, &ctx.response);
990    }
991
992    DestroyConstructResourceBytesCtx(&ctx);
993
994    return rc;
995}
996
997static int
998ProcResDispatch(ClientPtr client)
999{
1000    REQUEST(xReq);
1001    switch (stuff->data) {
1002    case X_XResQueryVersion:
1003        return ProcXResQueryVersion(client);
1004    case X_XResQueryClients:
1005        return ProcXResQueryClients(client);
1006    case X_XResQueryClientResources:
1007        return ProcXResQueryClientResources(client);
1008    case X_XResQueryClientPixmapBytes:
1009        return ProcXResQueryClientPixmapBytes(client);
1010    case X_XResQueryClientIds:
1011        return ProcXResQueryClientIds(client);
1012    case X_XResQueryResourceBytes:
1013        return ProcXResQueryResourceBytes(client);
1014    default: break;
1015    }
1016
1017    return BadRequest;
1018}
1019
1020static int _X_COLD
1021SProcXResQueryVersion(ClientPtr client)
1022{
1023    REQUEST_SIZE_MATCH(xXResQueryVersionReq);
1024    return ProcXResQueryVersion(client);
1025}
1026
1027static int _X_COLD
1028SProcXResQueryClientResources(ClientPtr client)
1029{
1030    REQUEST(xXResQueryClientResourcesReq);
1031    REQUEST_SIZE_MATCH(xXResQueryClientResourcesReq);
1032    swapl(&stuff->xid);
1033    return ProcXResQueryClientResources(client);
1034}
1035
1036static int _X_COLD
1037SProcXResQueryClientPixmapBytes(ClientPtr client)
1038{
1039    REQUEST(xXResQueryClientPixmapBytesReq);
1040    REQUEST_SIZE_MATCH(xXResQueryClientPixmapBytesReq);
1041    swapl(&stuff->xid);
1042    return ProcXResQueryClientPixmapBytes(client);
1043}
1044
1045static int _X_COLD
1046SProcXResQueryClientIds (ClientPtr client)
1047{
1048    REQUEST(xXResQueryClientIdsReq);
1049
1050    REQUEST_AT_LEAST_SIZE (xXResQueryClientIdsReq);
1051    swapl(&stuff->numSpecs);
1052    return ProcXResQueryClientIds(client);
1053}
1054
1055/** @brief Implements the XResQueryResourceBytes of XResProto v1.2.
1056    This variant byteswaps request contents before issuing the
1057    rest of the work to ProcXResQueryResourceBytes */
1058static int _X_COLD
1059SProcXResQueryResourceBytes (ClientPtr client)
1060{
1061    REQUEST(xXResQueryResourceBytesReq);
1062    int c;
1063    xXResResourceIdSpec *specs = (void*) ((char*) stuff + sizeof(*stuff));
1064
1065    REQUEST_AT_LEAST_SIZE(xXResQueryResourceBytesReq);
1066    swapl(&stuff->numSpecs);
1067    REQUEST_FIXED_SIZE(xXResQueryResourceBytesReq,
1068                       stuff->numSpecs * sizeof(specs[0]));
1069
1070    for (c = 0; c < stuff->numSpecs; ++c) {
1071        SwapXResResourceIdSpec(specs + c);
1072    }
1073
1074    return ProcXResQueryResourceBytes(client);
1075}
1076
1077static int _X_COLD
1078SProcResDispatch (ClientPtr client)
1079{
1080    REQUEST(xReq);
1081    swaps(&stuff->length);
1082
1083    switch (stuff->data) {
1084    case X_XResQueryVersion:
1085        return SProcXResQueryVersion(client);
1086    case X_XResQueryClients:   /* nothing to swap */
1087        return ProcXResQueryClients(client);
1088    case X_XResQueryClientResources:
1089        return SProcXResQueryClientResources(client);
1090    case X_XResQueryClientPixmapBytes:
1091        return SProcXResQueryClientPixmapBytes(client);
1092    case X_XResQueryClientIds:
1093        return SProcXResQueryClientIds(client);
1094    case X_XResQueryResourceBytes:
1095        return SProcXResQueryResourceBytes(client);
1096    default: break;
1097    }
1098
1099    return BadRequest;
1100}
1101
1102void
1103ResExtensionInit(void)
1104{
1105    (void) AddExtension(XRES_NAME, 0, 0,
1106                        ProcResDispatch, SProcResDispatch,
1107                        NULL, StandardMinorOpcode);
1108}
1109