1 /************************************************************
3 Copyright (c) 1987 X Consortium
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of the X Consortium shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from the X Consortium.
27 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
31 Permission to use, copy, modify, and distribute this software and its
32 documentation for any purpose and without fee is hereby granted,
33 provided that the above copyright notice appear in all copies and that
34 both that copyright notice and this permission notice appear in
35 supporting documentation, and that the name of Digital not be
36 used in advertising or publicity pertaining to distribution of the
37 software without specific, written prior permission.
39 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
40 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
41 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
42 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
43 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
44 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
47 ********************************************************/
49 /* $XConsortium: resource.c /main/39 1996/10/30 11:17:56 rws $ */
50 /* $XFree86: xc/programs/Xserver/dix/resource.c,v 3.1 1996/12/23 06:29:51 dawes Exp $ */
52 /* Routines to manage various kinds of resources:
54 * CreateNewResourceType, CreateNewResourceClass, InitClientResources,
55 * FakeClientID, AddResource, FreeResource, FreeClientResources,
56 * FreeAllResources, LookupIDByType, LookupIDByClass, GetXIDRange
60 * A resource ID is a 32 bit quantity, the upper 3 bits of which are
61 * off-limits for client-visible resources. The next 7 bits are
62 * used as client ID, and the low 22 bits come from the client.
63 * A resource ID is "hashed" by extracting and xoring subfields
64 * (varying with the size of the hash table).
66 * It is sometimes necessary for the server to create an ID that looks
67 * like it belongs to a client. This ID, however, must not be one
68 * the client actually can create, or we have the potential for conflict.
69 * The 30th bit of the ID is reserved for the server's use for this
70 * purpose. By setting CLIENT_ID(id) to the client, the SERVER_BIT to
71 * 1, and an otherwise arbitrary ID in the low 22 bits, we can create a
72 * resource "owned" by the client.
80 #include "dixstruct.h"
82 #include "windowstr.h"
85 #include "dixevents.h"
91 extern WindowPtr *WindowTable;
93 static void RebuildTable(
94 #if NeedFunctionPrototypes
99 #define SERVER_MINID 32
101 #define INITBUCKETS 64
102 #define INITHASHSIZE 6
103 #define MAXHASHSIZE 11
105 typedef struct _Resource {
106 struct _Resource *next;
110 } ResourceRec, *ResourcePtr;
111 #define NullResource ((ResourcePtr)NULL)
113 typedef struct _ClientResource {
114 ResourcePtr *resources;
117 int hashsize; /* log(2)(buckets) */
123 static RESTYPE lastResourceType;
124 static RESTYPE lastResourceClass;
125 static RESTYPE TypeMask;
127 static DeleteType *DeleteFuncs = (DeleteType *)NULL;
130 CreateNewResourceType(deleteFunc)
131 DeleteType deleteFunc;
133 RESTYPE next = lastResourceType + 1;
136 if (next & lastResourceClass)
138 funcs = (DeleteType *)xrealloc(DeleteFuncs,
139 (next + 1) * sizeof(DeleteType));
142 lastResourceType = next;
144 DeleteFuncs[next] = deleteFunc;
149 CreateNewResourceClass()
151 RESTYPE next = lastResourceClass >> 1;
153 if (next & lastResourceType)
155 lastResourceClass = next;
160 ClientResourceRec clientTable[MAXCLIENTS];
163 * InitClientResources
164 * When a new client is created, call this to allocate space
169 InitClientResources(client)
174 if (client == serverClient)
176 lastResourceType = RT_LASTPREDEF;
177 lastResourceClass = RC_LASTPREDEF;
178 TypeMask = RC_LASTPREDEF - 1;
181 DeleteFuncs = (DeleteType *)xalloc((lastResourceType + 1) *
185 DeleteFuncs[RT_NONE & TypeMask] = (DeleteType)NoopDDA;
186 DeleteFuncs[RT_WINDOW & TypeMask] = DeleteWindow;
187 DeleteFuncs[RT_PIXMAP & TypeMask] = dixDestroyPixmap;
188 DeleteFuncs[RT_GC & TypeMask] = FreeGC;
189 DeleteFuncs[RT_FONT & TypeMask] = CloseFont;
190 DeleteFuncs[RT_CURSOR & TypeMask] = FreeCursor;
191 DeleteFuncs[RT_COLORMAP & TypeMask] = FreeColormap;
192 DeleteFuncs[RT_CMAPENTRY & TypeMask] = FreeClientPixels;
193 DeleteFuncs[RT_OTHERCLIENT & TypeMask] = OtherClientGone;
194 DeleteFuncs[RT_PASSIVEGRAB & TypeMask] = DeletePassiveGrab;
196 clientTable[i = client->index].resources =
197 (ResourcePtr *)xalloc(INITBUCKETS*sizeof(ResourcePtr));
198 if (!clientTable[i].resources)
200 clientTable[i].buckets = INITBUCKETS;
201 clientTable[i].elements = 0;
202 clientTable[i].hashsize = INITHASHSIZE;
203 /* Many IDs allocated from the server client are visible to clients,
204 * so we don't use the SERVER_BIT for them, but we have to start
205 * past the magic value constants used in the protocol. For normal
206 * clients, we can start from zero, with SERVER_BIT set.
208 clientTable[i].fakeID = client->clientAsMask |
209 (client->index ? SERVER_BIT : SERVER_MINID);
210 clientTable[i].endFakeID = (clientTable[i].fakeID | RESOURCE_ID_MASK) + 1;
211 clientTable[i].expectID = client->clientAsMask;
212 for (j=0; j<INITBUCKETS; j++)
214 clientTable[i].resources[j] = NullResource;
220 #if NeedFunctionPrototypes
221 Hash(int client, register XID id)
228 id &= RESOURCE_ID_MASK;
229 switch (clientTable[client].hashsize)
232 return ((int)(0x03F & (id ^ (id>>6) ^ (id>>12))));
234 return ((int)(0x07F & (id ^ (id>>7) ^ (id>>13))));
236 return ((int)(0x0FF & (id ^ (id>>8) ^ (id>>16))));
238 return ((int)(0x1FF & (id ^ (id>>9))));
240 return ((int)(0x3FF & (id ^ (id>>10))));
242 return ((int)(0x7FF & (id ^ (id>>11))));
248 #if NeedFunctionPrototypes
255 AvailableID(client, id, maxid, goodid)
257 register XID id, maxid, goodid;
260 register ResourcePtr res;
262 if ((goodid >= id) && (goodid <= maxid))
264 for (; id <= maxid; id++)
266 res = clientTable[client].resources[Hash(client, id)];
267 while (res && (res->id != id))
276 GetXIDRange(client, server, minp, maxp)
281 register XID id, maxid;
282 register ResourcePtr *resp;
283 register ResourcePtr res;
287 id = (Mask)client << CLIENTOFFSET;
289 id |= client ? SERVER_BIT : SERVER_MINID;
290 maxid = id | RESOURCE_ID_MASK;
292 for (resp = clientTable[client].resources, i = clientTable[client].buckets;
295 for (res = *resp++; res; res = res->next)
297 if ((res->id < id) || (res->id > maxid))
299 if (((res->id - id) >= (maxid - res->id)) ?
300 (goodid = AvailableID(client, id, res->id - 1, goodid)) :
301 !(goodid = AvailableID(client, res->id + 1, maxid, goodid)))
313 /* GetXIDList is called by the XC-MISC extension's MiscGetXIDList function.
314 * This function tries to find count unused XIDs for the given client. It
315 * puts the IDs in the array pids and returns the number found, which should
316 * almost always be the number requested.
318 * The circumstances that lead to a call to this function are very rare.
319 * Xlib must run out of IDs while trying to generate a request that wants
320 * multiple ID's, like the Multi-buffering CreateImageBuffers request.
322 * No rocket science in the implementation; just iterate over all
323 * possible IDs for the given client and pick the first count IDs
324 * that aren't in use. A more efficient algorithm could probably be
325 * invented, but this will be used so rarely that this should suffice.
329 GetXIDList(pClient, count, pids)
334 unsigned int found = 0;
335 XID id = pClient->clientAsMask;
338 maxid = id | RESOURCE_ID_MASK;
339 while ( (found < count) && (id <= maxid) )
341 if (!LookupIDByClass(id, RC_ANY))
351 * Return the next usable fake client ID.
353 * Normally this is just the next one in line, but if we've used the last
354 * in the range, we need to find a new range of safe IDs to avoid
355 * over-running another client.
364 id = clientTable[client].fakeID++;
365 if (id != clientTable[client].endFakeID)
367 GetXIDRange(client, TRUE, &id, &maxid);
370 FatalError("FakeClientID: server internal ids exhausted\n");
371 MarkClientException(clients[client]);
372 id = ((Mask)client << CLIENTOFFSET) | (SERVER_BIT * 3);
373 maxid = id | RESOURCE_ID_MASK;
375 clientTable[client].fakeID = id + 1;
376 clientTable[client].endFakeID = maxid + 1;
381 AddResource(id, type, value)
387 register ClientResourceRec *rrec;
388 register ResourcePtr res, *head;
390 client = CLIENT_ID(id);
391 rrec = &clientTable[client];
394 ErrorF("AddResource(%x, %x, %x), client=%d \n",
395 id, type, (unsigned long)value, client);
396 FatalError("client not in use\n");
398 if ((rrec->elements >= 4*rrec->buckets) &&
399 (rrec->hashsize < MAXHASHSIZE))
400 RebuildTable(client);
401 head = &rrec->resources[Hash(client, id)];
402 res = (ResourcePtr)xalloc(sizeof(ResourceRec));
405 (*DeleteFuncs[type & TypeMask])(value, id);
414 if (!(id & SERVER_BIT) && (id >= rrec->expectID))
415 rrec->expectID = id + 1;
424 register ResourcePtr res, next;
425 ResourcePtr **tails, *resources;
426 register ResourcePtr **tptr, *rptr;
429 * For now, preserve insertion order, since some ddx layers depend
430 * on resources being free in the opposite order they are added.
433 j = 2 * clientTable[client].buckets;
434 tails = (ResourcePtr **)ALLOCATE_LOCAL(j * sizeof(ResourcePtr *));
437 resources = (ResourcePtr *)xalloc(j * sizeof(ResourcePtr));
440 DEALLOCATE_LOCAL(tails);
443 for (rptr = resources, tptr = tails; --j >= 0; rptr++, tptr++)
445 *rptr = NullResource;
448 clientTable[client].hashsize++;
449 for (j = clientTable[client].buckets,
450 rptr = clientTable[client].resources;
454 for (res = *rptr; res; res = next)
457 res->next = NullResource;
458 tptr = &tails[Hash(client, res->id)];
463 DEALLOCATE_LOCAL(tails);
464 clientTable[client].buckets *= 2;
465 xfree(clientTable[client].resources);
466 clientTable[client].resources = resources;
470 FreeResource(id, skipDeleteFuncType)
472 RESTYPE skipDeleteFuncType;
475 register ResourcePtr res;
476 register ResourcePtr *prev, *head;
477 register int *eltptr;
481 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
483 head = &clientTable[cid].resources[Hash(cid, id)];
484 eltptr = &clientTable[cid].elements;
487 while ( (res = *prev) )
491 RESTYPE rtype = res->type;
493 elements = --*eltptr;
494 if (rtype & RC_CACHED)
495 FlushClientCaches(res->id);
496 if (rtype != skipDeleteFuncType)
497 (*DeleteFuncs[rtype & TypeMask])(res->value, res->id);
499 if (*eltptr != elements)
500 prev = head; /* prev may no longer be valid */
506 if(clients[cid] && (id == clients[cid]->lastDrawableID))
508 clients[cid]->lastDrawable = (DrawablePtr)WindowTable[0];
509 clients[cid]->lastDrawableID = WindowTable[0]->drawable.id;
513 FatalError("Freeing resource id=%X which isn't there", id);
518 FreeResourceByType(id, type, skipFree)
524 register ResourcePtr res;
525 register ResourcePtr *prev, *head;
527 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
529 head = &clientTable[cid].resources[Hash(cid, id)];
532 while ( (res = *prev) )
534 if (res->id == id && res->type == type)
537 if (type & RC_CACHED)
538 FlushClientCaches(res->id);
540 (*DeleteFuncs[type & TypeMask])(res->value, res->id);
547 if(clients[cid] && (id == clients[cid]->lastDrawableID))
549 clients[cid]->lastDrawable = (DrawablePtr)WindowTable[0];
550 clients[cid]->lastDrawableID = WindowTable[0]->drawable.id;
556 * Change the value associated with a resource id. Caller
557 * is responsible for "doing the right thing" with the old
562 ChangeResourceValue (id, rtype, value)
568 register ResourcePtr res;
570 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) && clientTable[cid].buckets)
572 res = clientTable[cid].resources[Hash(cid, id)];
574 for (; res; res = res->next)
575 if ((res->id == id) && (res->type == rtype))
577 if (rtype & RC_CACHED)
578 FlushClientCaches(res->id);
586 /* Note: if func adds or deletes resources, then func can get called
587 * more than once for some resources. If func adds new resources,
588 * func might or might not get called for them. func cannot both
589 * add and delete an equal number of resources!
593 FindClientResourcesByType(client, type, func, cdata)
599 register ResourcePtr *resources;
600 register ResourcePtr this, next;
602 register int *eltptr;
605 client = serverClient;
607 resources = clientTable[client->index].resources;
608 eltptr = &clientTable[client->index].elements;
609 for (i = 0; i < clientTable[client->index].buckets; i++)
611 for (this = resources[i]; this; this = next)
614 if (!type || this->type == type) {
616 (*func)(this->value, this->id, cdata);
617 if (*eltptr != elements)
618 next = resources[i]; /* start over */
625 FreeClientNeverRetainResources(client)
628 ResourcePtr *resources;
636 resources = clientTable[client->index].resources;
637 for (j=0; j < clientTable[client->index].buckets; j++)
639 prev = &resources[j];
640 while ( (this = *prev) )
642 RESTYPE rtype = this->type;
643 if (rtype & RC_NEVERRETAIN)
646 if (rtype & RC_CACHED)
647 FlushClientCaches(this->id);
648 (*DeleteFuncs[rtype & TypeMask])(this->value, this->id);
658 FreeClientResources(client)
661 register ResourcePtr *resources;
662 register ResourcePtr this;
665 /* This routine shouldn't be called with a null client, but just in
671 HandleSaveSet(client);
673 resources = clientTable[client->index].resources;
674 for (j=0; j < clientTable[client->index].buckets; j++)
676 /* It may seem silly to update the head of this resource list as
677 we delete the members, since the entire list will be deleted any way,
678 but there are some resource deletion functions "FreeClientPixels" for
679 one which do a LookupID on another resource id (a Colormap id in this
680 case), so the resource list must be kept valid up to the point that
681 it is deleted, so every time we delete a resource, we must update the
682 head, just like in FreeResource. I hope that this doesn't slow down
683 mass deletion appreciably. PRH */
687 head = &resources[j];
689 for (this = *head; this; this = *head)
691 RESTYPE rtype = this->type;
693 if (rtype & RC_CACHED)
694 FlushClientCaches(this->id);
695 (*DeleteFuncs[rtype & TypeMask])(this->value, this->id);
699 xfree(clientTable[client->index].resources);
700 clientTable[client->index].buckets = 0;
708 for (i = currentMaxClients; --i >= 0; )
710 if (clientTable[i].buckets)
711 FreeClientResources(clients[i]);
716 LegalNewID(id, client)
718 register ClientPtr client;
720 return ((client->clientAsMask == (id & ~RESOURCE_ID_MASK)) &&
721 ((clientTable[client->index].expectID <= id) ||
722 !LookupIDByClass(id, RC_ANY)));
727 /* SecurityLookupIDByType and SecurityLookupIDByClass:
728 * These are the heart of the resource ID security system. They take
729 * two additional arguments compared to the old LookupID functions:
730 * the client doing the lookup, and the access mode (see resource.h).
731 * The resource is returned if it exists and the client is allowed access,
732 * else NULL is returned.
736 SecurityLookupIDByType(client, id, rtype, mode)
743 register ResourcePtr res;
744 pointer retval = NULL;
746 assert(client == NullClient ||
747 (client->index <= currentMaxClients && clients[client->index] == client));
748 assert( (rtype & TypeMask) <= lastResourceType);
750 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
751 clientTable[cid].buckets)
753 res = clientTable[cid].resources[Hash(cid, id)];
755 for (; res; res = res->next)
756 if ((res->id == id) && (res->type == rtype))
762 if (retval && client && client->CheckAccess)
763 retval = (* client->CheckAccess)(client, id, rtype, mode, retval);
769 SecurityLookupIDByClass(client, id, classes, mode)
776 register ResourcePtr res;
777 pointer retval = NULL;
779 assert(client == NullClient ||
780 (client->index <= currentMaxClients && clients[client->index] == client));
781 assert (classes >= lastResourceClass);
783 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
784 clientTable[cid].buckets)
786 res = clientTable[cid].resources[Hash(cid, id)];
788 for (; res; res = res->next)
789 if ((res->id == id) && (res->type & classes))
795 if (retval && client && client->CheckAccess)
796 retval = (* client->CheckAccess)(client, id, classes, mode, retval);
800 /* We can't replace the LookupIDByType and LookupIDByClass functions with
801 * macros because of compatibility with loadable servers.
805 LookupIDByType(id, rtype)
809 return SecurityLookupIDByType(NullClient, id, rtype,
810 SecurityUnknownAccess);
814 LookupIDByClass(id, classes)
818 return SecurityLookupIDByClass(NullClient, id, classes,
819 SecurityUnknownAccess);
822 #else /* not XCSECURITY */
825 * LookupIDByType returns the object with the given id and type, else NULL.
828 LookupIDByType(id, rtype)
833 register ResourcePtr res;
835 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
836 clientTable[cid].buckets)
838 res = clientTable[cid].resources[Hash(cid, id)];
840 for (; res; res = res->next)
841 if ((res->id == id) && (res->type == rtype))
844 return (pointer)NULL;
848 * LookupIDByClass returns the object with the given id and any one of the
849 * given classes, else NULL.
852 LookupIDByClass(id, classes)
857 register ResourcePtr res;
859 if (((cid = CLIENT_ID(id)) < MAXCLIENTS) &&
860 clientTable[cid].buckets)
862 res = clientTable[cid].resources[Hash(cid, id)];
864 for (; res; res = res->next)
865 if ((res->id == id) && (res->type & classes))
868 return (pointer)NULL;
871 #endif /* XCSECURITY */