resource.c revision 05b261ec
1/************************************************************
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
45
46********************************************************/
47/* The panoramix components contained the following notice */
48/*****************************************************************
49
50Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
51
52Permission is hereby granted, free of charge, to any person obtaining a copy
53of this software and associated documentation files (the "Software"), to deal
54in the Software without restriction, including without limitation the rights
55to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56copies of the Software.
57
58The above copyright notice and this permission notice shall be included in
59all copies or substantial portions of the Software.
60
61THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
63FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
64DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
65BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
66WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
67IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
68
69Except as contained in this notice, the name of Digital Equipment Corporation
70shall not be used in advertising or otherwise to promote the sale, use or other
71dealings in this Software without prior written authorization from Digital
72Equipment Corporation.
73
74******************************************************************/
75/* XSERVER_DTRACE additions:
76 * Copyright 2005-2006 Sun Microsystems, Inc.  All rights reserved.
77 *
78 * Permission is hereby granted, free of charge, to any person obtaining a
79 * copy of this software and associated documentation files (the
80 * "Software"), to deal in the Software without restriction, including
81 * without limitation the rights to use, copy, modify, merge, publish,
82 * distribute, and/or sell copies of the Software, and to permit persons
83 * to whom the Software is furnished to do so, provided that the above
84 * copyright notice(s) and this permission notice appear in all copies of
85 * the Software and that both the above copyright notice(s) and this
86 * permission notice appear in supporting documentation.
87 *
88 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
89 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
90 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
91 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
92 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
93 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
94 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
95 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
96 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
97 *
98 * Except as contained in this notice, the name of a copyright holder
99 * shall not be used in advertising or otherwise to promote the sale, use
100 * or other dealings in this Software without prior written authorization
101 * of the copyright holder.
102 */
103
104/*	Routines to manage various kinds of resources:
105 *
106 *	CreateNewResourceType, CreateNewResourceClass, InitClientResources,
107 *	FakeClientID, AddResource, FreeResource, FreeClientResources,
108 *	FreeAllResources, LookupIDByType, LookupIDByClass, GetXIDRange
109 */
110
111/*
112 *      A resource ID is a 32 bit quantity, the upper 2 bits of which are
113 *	off-limits for client-visible resources.  The next 8 bits are
114 *      used as client ID, and the low 22 bits come from the client.
115 *	A resource ID is "hashed" by extracting and xoring subfields
116 *      (varying with the size of the hash table).
117 *
118 *      It is sometimes necessary for the server to create an ID that looks
119 *      like it belongs to a client.  This ID, however,  must not be one
120 *      the client actually can create, or we have the potential for conflict.
121 *      The 31st bit of the ID is reserved for the server's use for this
122 *      purpose.  By setting CLIENT_ID(id) to the client, the SERVER_BIT to
123 *      1, and an otherwise arbitrary ID in the low 22 bits, we can create a
124 *      resource "owned" by the client.
125 */
126
127#define NEED_EVENTS
128#ifdef HAVE_DIX_CONFIG_H
129#include <dix-config.h>
130#endif
131
132#include <X11/X.h>
133#include "misc.h"
134#include "os.h"
135#include "resource.h"
136#include "dixstruct.h"
137#include "opaque.h"
138#include "windowstr.h"
139#include "dixfont.h"
140#include "colormap.h"
141#include "inputstr.h"
142#include "dixevents.h"
143#include "dixgrabs.h"
144#include "cursor.h"
145#ifdef PANORAMIX
146#include "panoramiX.h"
147#include "panoramiXsrv.h"
148#endif
149#include "xace.h"
150#include <assert.h>
151
152#ifdef XSERVER_DTRACE
153#include <sys/types.h>
154typedef const char *string;
155#include "Xserver-dtrace.h"
156
157#define TypeNameString(t) NameForAtom(ResourceNames[t & TypeMask])
158#endif
159
160static void RebuildTable(
161    int /*client*/
162);
163
164#define SERVER_MINID 32
165
166#define INITBUCKETS 64
167#define INITHASHSIZE 6
168#define MAXHASHSIZE 11
169
170typedef struct _Resource {
171    struct _Resource	*next;
172    XID			id;
173    RESTYPE		type;
174    pointer		value;
175} ResourceRec, *ResourcePtr;
176#define NullResource ((ResourcePtr)NULL)
177
178typedef struct _ClientResource {
179    ResourcePtr *resources;
180    int		elements;
181    int		buckets;
182    int		hashsize;	/* log(2)(buckets) */
183    XID		fakeID;
184    XID		endFakeID;
185    XID		expectID;
186} ClientResourceRec;
187
188_X_EXPORT RESTYPE lastResourceType;
189static RESTYPE lastResourceClass;
190_X_EXPORT RESTYPE TypeMask;
191
192static DeleteType *DeleteFuncs = (DeleteType *)NULL;
193
194#ifdef XResExtension
195
196_X_EXPORT Atom * ResourceNames = NULL;
197
198_X_EXPORT void RegisterResourceName (RESTYPE type, char *name)
199{
200    ResourceNames[type & TypeMask] =  MakeAtom(name, strlen(name), TRUE);
201}
202
203#endif
204
205_X_EXPORT RESTYPE
206CreateNewResourceType(DeleteType deleteFunc)
207{
208    RESTYPE next = lastResourceType + 1;
209    DeleteType *funcs;
210
211    if (next & lastResourceClass)
212	return 0;
213    funcs = (DeleteType *)xrealloc(DeleteFuncs,
214				   (next + 1) * sizeof(DeleteType));
215    if (!funcs)
216	return 0;
217
218#ifdef XResExtension
219    {
220       Atom *newnames;
221       newnames = xrealloc(ResourceNames, (next + 1) * sizeof(Atom));
222       if(!newnames)
223           return 0;
224       ResourceNames = newnames;
225       ResourceNames[next] = 0;
226    }
227#endif
228
229    lastResourceType = next;
230    DeleteFuncs = funcs;
231    DeleteFuncs[next] = deleteFunc;
232    return next;
233}
234
235_X_EXPORT RESTYPE
236CreateNewResourceClass(void)
237{
238    RESTYPE next = lastResourceClass >> 1;
239
240    if (next & lastResourceType)
241	return 0;
242    lastResourceClass = next;
243    TypeMask = next - 1;
244    return next;
245}
246
247static ClientResourceRec clientTable[MAXCLIENTS];
248
249/*****************
250 * InitClientResources
251 *    When a new client is created, call this to allocate space
252 *    in resource table
253 *****************/
254
255Bool
256InitClientResources(ClientPtr client)
257{
258    int i, j;
259
260    if (client == serverClient)
261    {
262	lastResourceType = RT_LASTPREDEF;
263	lastResourceClass = RC_LASTPREDEF;
264	TypeMask = RC_LASTPREDEF - 1;
265	if (DeleteFuncs)
266	    xfree(DeleteFuncs);
267	DeleteFuncs = (DeleteType *)xalloc((lastResourceType + 1) *
268					   sizeof(DeleteType));
269	if (!DeleteFuncs)
270	    return FALSE;
271	DeleteFuncs[RT_NONE & TypeMask] = (DeleteType)NoopDDA;
272	DeleteFuncs[RT_WINDOW & TypeMask] = DeleteWindow;
273	DeleteFuncs[RT_PIXMAP & TypeMask] = dixDestroyPixmap;
274	DeleteFuncs[RT_GC & TypeMask] = FreeGC;
275	DeleteFuncs[RT_FONT & TypeMask] = CloseFont;
276	DeleteFuncs[RT_CURSOR & TypeMask] = FreeCursor;
277	DeleteFuncs[RT_COLORMAP & TypeMask] = FreeColormap;
278	DeleteFuncs[RT_CMAPENTRY & TypeMask] = FreeClientPixels;
279	DeleteFuncs[RT_OTHERCLIENT & TypeMask] = OtherClientGone;
280	DeleteFuncs[RT_PASSIVEGRAB & TypeMask] = DeletePassiveGrab;
281
282#ifdef XResExtension
283        if(ResourceNames)
284            xfree(ResourceNames);
285        ResourceNames = xalloc((lastResourceType + 1) * sizeof(Atom));
286        if(!ResourceNames)
287           return FALSE;
288#endif
289    }
290    clientTable[i = client->index].resources =
291	(ResourcePtr *)xalloc(INITBUCKETS*sizeof(ResourcePtr));
292    if (!clientTable[i].resources)
293	return FALSE;
294    clientTable[i].buckets = INITBUCKETS;
295    clientTable[i].elements = 0;
296    clientTable[i].hashsize = INITHASHSIZE;
297    /* Many IDs allocated from the server client are visible to clients,
298     * so we don't use the SERVER_BIT for them, but we have to start
299     * past the magic value constants used in the protocol.  For normal
300     * clients, we can start from zero, with SERVER_BIT set.
301     */
302    clientTable[i].fakeID = client->clientAsMask |
303			    (client->index ? SERVER_BIT : SERVER_MINID);
304    clientTable[i].endFakeID = (clientTable[i].fakeID | RESOURCE_ID_MASK) + 1;
305    clientTable[i].expectID = client->clientAsMask;
306    for (j=0; j<INITBUCKETS; j++)
307    {
308        clientTable[i].resources[j] = NullResource;
309    }
310    return TRUE;
311}
312
313
314static int
315Hash(int client, XID id)
316{
317    id &= RESOURCE_ID_MASK;
318    switch (clientTable[client].hashsize)
319    {
320	case 6:
321	    return ((int)(0x03F & (id ^ (id>>6) ^ (id>>12))));
322	case 7:
323	    return ((int)(0x07F & (id ^ (id>>7) ^ (id>>13))));
324	case 8:
325	    return ((int)(0x0FF & (id ^ (id>>8) ^ (id>>16))));
326	case 9:
327	    return ((int)(0x1FF & (id ^ (id>>9))));
328	case 10:
329	    return ((int)(0x3FF & (id ^ (id>>10))));
330	case 11:
331	    return ((int)(0x7FF & (id ^ (id>>11))));
332    }
333    return -1;
334}
335
336static XID
337AvailableID(
338    int client,
339    XID id,
340    XID maxid,
341    XID goodid)
342{
343    ResourcePtr res;
344
345    if ((goodid >= id) && (goodid <= maxid))
346	return goodid;
347    for (; id <= maxid; id++)
348    {
349	res = clientTable[client].resources[Hash(client, id)];
350	while (res && (res->id != id))
351	    res = res->next;
352	if (!res)
353	    return id;
354    }
355    return 0;
356}
357
358_X_EXPORT void
359GetXIDRange(int client, Bool server, XID *minp, XID *maxp)
360{
361    XID id, maxid;
362    ResourcePtr *resp;
363    ResourcePtr res;
364    int i;
365    XID goodid;
366
367    id = (Mask)client << CLIENTOFFSET;
368    if (server)
369	id |= client ? SERVER_BIT : SERVER_MINID;
370    maxid = id | RESOURCE_ID_MASK;
371    goodid = 0;
372    for (resp = clientTable[client].resources, i = clientTable[client].buckets;
373	 --i >= 0;)
374    {
375	for (res = *resp++; res; res = res->next)
376	{
377	    if ((res->id < id) || (res->id > maxid))
378		continue;
379	    if (((res->id - id) >= (maxid - res->id)) ?
380		(goodid = AvailableID(client, id, res->id - 1, goodid)) :
381		!(goodid = AvailableID(client, res->id + 1, maxid, goodid)))
382		maxid = res->id - 1;
383	    else
384		id = res->id + 1;
385	}
386    }
387    if (id > maxid)
388	id = maxid = 0;
389    *minp = id;
390    *maxp = maxid;
391}
392
393/**
394 *  GetXIDList is called by the XC-MISC extension's MiscGetXIDList function.
395 *  This function tries to find count unused XIDs for the given client.  It
396 *  puts the IDs in the array pids and returns the number found, which should
397 *  almost always be the number requested.
398 *
399 *  The circumstances that lead to a call to this function are very rare.
400 *  Xlib must run out of IDs while trying to generate a request that wants
401 *  multiple ID's, like the Multi-buffering CreateImageBuffers request.
402 *
403 *  No rocket science in the implementation; just iterate over all
404 *  possible IDs for the given client and pick the first count IDs
405 *  that aren't in use.  A more efficient algorithm could probably be
406 *  invented, but this will be used so rarely that this should suffice.
407 */
408
409_X_EXPORT unsigned int
410GetXIDList(ClientPtr pClient, unsigned count, XID *pids)
411{
412    unsigned int found = 0;
413    XID id = pClient->clientAsMask;
414    XID maxid;
415
416    maxid = id | RESOURCE_ID_MASK;
417    while ( (found < count) && (id <= maxid) )
418    {
419	if (!LookupIDByClass(id, RC_ANY))
420	{
421	    pids[found++] = id;
422	}
423	id++;
424    }
425    return found;
426}
427
428/*
429 * Return the next usable fake client ID.
430 *
431 * Normally this is just the next one in line, but if we've used the last
432 * in the range, we need to find a new range of safe IDs to avoid
433 * over-running another client.
434 */
435
436_X_EXPORT XID
437FakeClientID(int client)
438{
439    XID id, maxid;
440
441    id = clientTable[client].fakeID++;
442    if (id != clientTable[client].endFakeID)
443	return id;
444    GetXIDRange(client, TRUE, &id, &maxid);
445    if (!id) {
446	if (!client)
447	    FatalError("FakeClientID: server internal ids exhausted\n");
448	MarkClientException(clients[client]);
449	id = ((Mask)client << CLIENTOFFSET) | (SERVER_BIT * 3);
450	maxid = id | RESOURCE_ID_MASK;
451    }
452    clientTable[client].fakeID = id + 1;
453    clientTable[client].endFakeID = maxid + 1;
454    return id;
455}
456
457_X_EXPORT Bool
458AddResource(XID id, RESTYPE type, pointer value)
459{
460    int client;
461    ClientResourceRec *rrec;
462    ResourcePtr res, *head;
463
464#ifdef XSERVER_DTRACE
465    XSERVER_RESOURCE_ALLOC(id, type, value, TypeNameString(type));
466#endif
467    client = CLIENT_ID(id);
468    rrec = &clientTable[client];
469    if (!rrec->buckets)
470    {
471	ErrorF("AddResource(%lx, %lx, %lx), client=%d \n",
472		(unsigned long)id, type, (unsigned long)value, client);
473        FatalError("client not in use\n");
474    }
475    if ((rrec->elements >= 4*rrec->buckets) &&
476	(rrec->hashsize < MAXHASHSIZE))
477	RebuildTable(client);
478    head = &rrec->resources[Hash(client, id)];
479    res = (ResourcePtr)xalloc(sizeof(ResourceRec));
480    if (!res)
481    {
482	(*DeleteFuncs[type & TypeMask])(value, id);
483	return FALSE;
484    }
485    res->next = *head;
486    res->id = id;
487    res->type = type;
488    res->value = value;
489    *head = res;
490    rrec->elements++;
491    if (!(id & SERVER_BIT) && (id >= rrec->expectID))
492	rrec->expectID = id + 1;
493    return TRUE;
494}
495
496static void
497RebuildTable(int client)
498{
499    int j;
500    ResourcePtr res, next;
501    ResourcePtr **tails, *resources;
502    ResourcePtr **tptr, *rptr;
503
504    /*
505     * For now, preserve insertion order, since some ddx layers depend
506     * on resources being free in the opposite order they are added.
507     */
508
509    j = 2 * clientTable[client].buckets;
510    tails = (ResourcePtr **)ALLOCATE_LOCAL(j * sizeof(ResourcePtr *));
511    if (!tails)
512	return;
513    resources = (ResourcePtr *)xalloc(j * sizeof(ResourcePtr));
514    if (!resources)
515    {
516	DEALLOCATE_LOCAL(tails);
517	return;
518    }
519    for (rptr = resources, tptr = tails; --j >= 0; rptr++, tptr++)
520    {
521	*rptr = NullResource;
522	*tptr = rptr;
523    }
524    clientTable[client].hashsize++;
525    for (j = clientTable[client].buckets,
526	 rptr = clientTable[client].resources;
527	 --j >= 0;
528	 rptr++)
529    {
530	for (res = *rptr; res; res = next)
531	{
532	    next = res->next;
533	    res->next = NullResource;
534	    tptr = &tails[Hash(client, res->id)];
535	    **tptr = res;
536	    *tptr = &res->next;
537	}
538    }
539    DEALLOCATE_LOCAL(tails);
540    clientTable[client].buckets *= 2;
541    xfree(clientTable[client].resources);
542    clientTable[client].resources = resources;
543}
544
545_X_EXPORT void
546FreeResource(XID id, RESTYPE skipDeleteFuncType)
547{
548    int		cid;
549    ResourcePtr res;
550    ResourcePtr *prev, *head;
551    int *eltptr;
552    int		elements;
553    Bool	gotOne = FALSE;
554
555    if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
556    {
557	head = &clientTable[cid].resources[Hash(cid, id)];
558	eltptr = &clientTable[cid].elements;
559
560	prev = head;
561	while ( (res = *prev) )
562	{
563	    if (res->id == id)
564	    {
565		RESTYPE rtype = res->type;
566
567#ifdef XSERVER_DTRACE
568		XSERVER_RESOURCE_FREE(res->id, res->type,
569			      res->value, TypeNameString(res->type));
570#endif
571		*prev = res->next;
572		elements = --*eltptr;
573		if (rtype & RC_CACHED)
574		    FlushClientCaches(res->id);
575		if (rtype != skipDeleteFuncType)
576		    (*DeleteFuncs[rtype & TypeMask])(res->value, res->id);
577		xfree(res);
578		if (*eltptr != elements)
579		    prev = head; /* prev may no longer be valid */
580		gotOne = TRUE;
581	    }
582	    else
583		prev = &res->next;
584        }
585	if(clients[cid] && (id == clients[cid]->lastDrawableID))
586	{
587	    clients[cid]->lastDrawable = (DrawablePtr)WindowTable[0];
588	    clients[cid]->lastDrawableID = WindowTable[0]->drawable.id;
589	}
590    }
591    if (!gotOne)
592	ErrorF("Freeing resource id=%lX which isn't there.\n",
593		   (unsigned long)id);
594}
595
596
597_X_EXPORT void
598FreeResourceByType(XID id, RESTYPE type, Bool skipFree)
599{
600    int		cid;
601    ResourcePtr res;
602    ResourcePtr *prev, *head;
603    if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
604    {
605	head = &clientTable[cid].resources[Hash(cid, id)];
606
607	prev = head;
608	while ( (res = *prev) )
609	{
610	    if (res->id == id && res->type == type)
611	    {
612#ifdef XSERVER_DTRACE
613		XSERVER_RESOURCE_FREE(res->id, res->type,
614			      res->value, TypeNameString(res->type));
615#endif
616		*prev = res->next;
617		if (type & RC_CACHED)
618		    FlushClientCaches(res->id);
619		if (!skipFree)
620		    (*DeleteFuncs[type & TypeMask])(res->value, res->id);
621		xfree(res);
622		break;
623	    }
624	    else
625		prev = &res->next;
626        }
627	if(clients[cid] && (id == clients[cid]->lastDrawableID))
628	{
629	    clients[cid]->lastDrawable = (DrawablePtr)WindowTable[0];
630	    clients[cid]->lastDrawableID = WindowTable[0]->drawable.id;
631	}
632    }
633}
634
635/*
636 * Change the value associated with a resource id.  Caller
637 * is responsible for "doing the right thing" with the old
638 * data
639 */
640
641_X_EXPORT Bool
642ChangeResourceValue (XID id, RESTYPE rtype, pointer value)
643{
644    int    cid;
645    ResourcePtr res;
646
647    if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
648    {
649	res = clientTable[cid].resources[Hash(cid, id)];
650
651	for (; res; res = res->next)
652	    if ((res->id == id) && (res->type == rtype))
653	    {
654		if (rtype & RC_CACHED)
655		    FlushClientCaches(res->id);
656		res->value = value;
657		return TRUE;
658	    }
659    }
660    return FALSE;
661}
662
663/* Note: if func adds or deletes resources, then func can get called
664 * more than once for some resources.  If func adds new resources,
665 * func might or might not get called for them.  func cannot both
666 * add and delete an equal number of resources!
667 */
668
669_X_EXPORT void
670FindClientResourcesByType(
671    ClientPtr client,
672    RESTYPE type,
673    FindResType func,
674    pointer cdata
675){
676    ResourcePtr *resources;
677    ResourcePtr this, next;
678    int i, elements;
679    int *eltptr;
680
681    if (!client)
682	client = serverClient;
683
684    resources = clientTable[client->index].resources;
685    eltptr = &clientTable[client->index].elements;
686    for (i = 0; i < clientTable[client->index].buckets; i++)
687    {
688        for (this = resources[i]; this; this = next)
689	{
690	    next = this->next;
691	    if (!type || this->type == type) {
692		elements = *eltptr;
693		(*func)(this->value, this->id, cdata);
694		if (*eltptr != elements)
695		    next = resources[i]; /* start over */
696	    }
697	}
698    }
699}
700
701_X_EXPORT void
702FindAllClientResources(
703    ClientPtr client,
704    FindAllRes func,
705    pointer cdata
706){
707    ResourcePtr *resources;
708    ResourcePtr this, next;
709    int i, elements;
710    int *eltptr;
711
712    if (!client)
713        client = serverClient;
714
715    resources = clientTable[client->index].resources;
716    eltptr = &clientTable[client->index].elements;
717    for (i = 0; i < clientTable[client->index].buckets; i++)
718    {
719        for (this = resources[i]; this; this = next)
720        {
721            next = this->next;
722            elements = *eltptr;
723            (*func)(this->value, this->id, this->type, cdata);
724            if (*eltptr != elements)
725                next = resources[i]; /* start over */
726        }
727    }
728}
729
730
731pointer
732LookupClientResourceComplex(
733    ClientPtr client,
734    RESTYPE type,
735    FindComplexResType func,
736    pointer cdata
737){
738    ResourcePtr *resources;
739    ResourcePtr this;
740    int i;
741
742    if (!client)
743	client = serverClient;
744
745    resources = clientTable[client->index].resources;
746    for (i = 0; i < clientTable[client->index].buckets; i++) {
747        for (this = resources[i]; this; this = this->next) {
748	    if (!type || this->type == type) {
749		if((*func)(this->value, this->id, cdata))
750		    return this->value;
751	    }
752	}
753    }
754    return NULL;
755}
756
757
758void
759FreeClientNeverRetainResources(ClientPtr client)
760{
761    ResourcePtr *resources;
762    ResourcePtr this;
763    ResourcePtr *prev;
764    int j;
765
766    if (!client)
767	return;
768
769    resources = clientTable[client->index].resources;
770    for (j=0; j < clientTable[client->index].buckets; j++)
771    {
772	prev = &resources[j];
773        while ( (this = *prev) )
774	{
775	    RESTYPE rtype = this->type;
776	    if (rtype & RC_NEVERRETAIN)
777	    {
778#ifdef XSERVER_DTRACE
779		XSERVER_RESOURCE_FREE(this->id, this->type,
780			      this->value, TypeNameString(this->type));
781#endif
782		*prev = this->next;
783		if (rtype & RC_CACHED)
784		    FlushClientCaches(this->id);
785		(*DeleteFuncs[rtype & TypeMask])(this->value, this->id);
786		xfree(this);
787	    }
788	    else
789		prev = &this->next;
790	}
791    }
792}
793
794void
795FreeClientResources(ClientPtr client)
796{
797    ResourcePtr *resources;
798    ResourcePtr this;
799    int j;
800
801    /* This routine shouldn't be called with a null client, but just in
802	case ... */
803
804    if (!client)
805	return;
806
807    HandleSaveSet(client);
808
809    resources = clientTable[client->index].resources;
810    for (j=0; j < clientTable[client->index].buckets; j++)
811    {
812        /* It may seem silly to update the head of this resource list as
813	we delete the members, since the entire list will be deleted any way,
814	but there are some resource deletion functions "FreeClientPixels" for
815	one which do a LookupID on another resource id (a Colormap id in this
816	case), so the resource list must be kept valid up to the point that
817	it is deleted, so every time we delete a resource, we must update the
818	head, just like in FreeResource. I hope that this doesn't slow down
819	mass deletion appreciably. PRH */
820
821	ResourcePtr *head;
822
823	head = &resources[j];
824
825        for (this = *head; this; this = *head)
826	{
827	    RESTYPE rtype = this->type;
828#ifdef XSERVER_DTRACE
829	    XSERVER_RESOURCE_FREE(this->id, this->type,
830			  this->value, TypeNameString(this->type));
831#endif
832	    *head = this->next;
833	    if (rtype & RC_CACHED)
834		FlushClientCaches(this->id);
835	    (*DeleteFuncs[rtype & TypeMask])(this->value, this->id);
836	    xfree(this);
837	}
838    }
839    xfree(clientTable[client->index].resources);
840    clientTable[client->index].resources = NULL;
841    clientTable[client->index].buckets = 0;
842}
843
844void
845FreeAllResources(void)
846{
847    int	i;
848
849    for (i = currentMaxClients; --i >= 0; )
850    {
851        if (clientTable[i].buckets)
852	    FreeClientResources(clients[i]);
853    }
854}
855
856_X_EXPORT Bool
857LegalNewID(XID id, ClientPtr client)
858{
859
860#ifdef PANORAMIX
861    XID 	minid, maxid;
862
863	if (!noPanoramiXExtension) {
864	    minid = client->clientAsMask | (client->index ?
865			                    SERVER_BIT : SERVER_MINID);
866	    maxid = (clientTable[client->index].fakeID | RESOURCE_ID_MASK) + 1;
867            if ((id >= minid) && (id <= maxid))
868	        return TRUE;
869	}
870#endif /* PANORAMIX */
871	return ((client->clientAsMask == (id & ~RESOURCE_ID_MASK)) &&
872	    ((clientTable[client->index].expectID <= id) ||
873	     !LookupIDByClass(id, RC_ANY)));
874}
875
876/* SecurityLookupIDByType and SecurityLookupIDByClass:
877 * These are the heart of the resource ID security system.  They take
878 * two additional arguments compared to the old LookupID functions:
879 * the client doing the lookup, and the access mode (see resource.h).
880 * The resource is returned if it exists and the client is allowed access,
881 * else NULL is returned.
882 */
883
884_X_EXPORT pointer
885SecurityLookupIDByType(ClientPtr client, XID id, RESTYPE rtype, Mask mode)
886{
887    int    cid;
888    ResourcePtr res;
889    pointer retval = NULL;
890
891    if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
892	clientTable[cid].buckets)
893    {
894	res = clientTable[cid].resources[Hash(cid, id)];
895
896	for (; res; res = res->next)
897	    if ((res->id == id) && (res->type == rtype))
898	    {
899		retval = res->value;
900		break;
901	    }
902    }
903    if (retval && client &&
904	!XaceHook(XACE_RESOURCE_ACCESS, client, id, rtype, mode, retval))
905	retval = NULL;
906
907    return retval;
908}
909
910
911_X_EXPORT pointer
912SecurityLookupIDByClass(ClientPtr client, XID id, RESTYPE classes, Mask mode)
913{
914    int    cid;
915    ResourcePtr res = NULL;
916    pointer retval = NULL;
917
918    if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
919	clientTable[cid].buckets)
920    {
921	res = clientTable[cid].resources[Hash(cid, id)];
922
923	for (; res; res = res->next)
924	    if ((res->id == id) && (res->type & classes))
925	    {
926		retval = res->value;
927		break;
928	    }
929    }
930    if (retval && client &&
931	!XaceHook(XACE_RESOURCE_ACCESS, client, id, res->type, mode, retval))
932	retval = NULL;
933
934    return retval;
935}
936
937/* We can't replace the LookupIDByType and LookupIDByClass functions with
938 * macros because of compatibility with loadable servers.
939 */
940
941_X_EXPORT pointer
942LookupIDByType(XID id, RESTYPE rtype)
943{
944    return SecurityLookupIDByType(NullClient, id, rtype,
945				  DixUnknownAccess);
946}
947
948_X_EXPORT pointer
949LookupIDByClass(XID id, RESTYPE classes)
950{
951    return SecurityLookupIDByClass(NullClient, id, classes,
952				   DixUnknownAccess);
953}
954