1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains functions for the Zephyr server class manager subsystem.
4 * Created by: John T. Kohl
6 * $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/class.c,v $
9 * Copyright (c) 1987 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h" /* includes zephyr/zephyr.h */
18 #if !defined (lint) && !defined (SABER)
19 static const char rcsid_class_c[] =
20 "$Id: class.c,v 1.27 2001/02/27 04:43:59 zacheiss Exp $";
24 * Class manager subsystem.
27 * External functions are:
29 * Code_t triplet_register(client, subs, realm)
31 * Code_t triplet_deregister(client, subs, realm)
33 * Client *triplet_lookup(subs)
37 * Acl *class_get_acl(class_name)
40 * Code_t class_restrict(class_name, acl)
44 * Code_t class_setup_restricted(class_name, acl)
48 * and several Destination methods.
52 * The data structure used for the class manager is an array of hash buckets
53 * each containing a pointer to a doubly linked circular list (in the style
54 * of insque/remque). Each element of this list contains a class.instance
55 * name (which hashes into the bucket associated with this list) and a
56 * doubly linked list of clients which are interested in this class.
57 * The data pointed to by these clients is owned by other modules. Care
58 * must be taken by the caller not to a free()'d client
61 * If any hash bucket is empty, the pointer is null.
63 * The first element in the hash bucket is a special header unused for
64 * storing classes, and is used for finding the end of the list.
66 * If any list of interested clients is empty, the class name is garbage
67 * collected, unless the class has been registered as restricted.
70 /* Private variables */
71 #define EMPTY_CLASS 2000
73 #define ALLOC_OFFSET 8 /* Allocate 32 bytes less than a power of 2. */
74 #define ALLOC_INIT 8 /* Initial number of subscriptions. */
77 #define HASHVAL(c, i, r) (((c)->hash_val ^ (i)->hash_val ^ (r)->hash_val) \
79 #define DEST_HASHVAL(dest) HASHVAL((dest).classname, (dest).inst, (dest).recip)
81 static Triplet *triplet_bucket[HASHSIZE]; /* the hash table of pointers */
83 static Code_t remove_client __P((Triplet *triplet, Client *client,
85 static Code_t insert_client __P((Triplet *triplet, Client *client,
87 static Triplet *triplet_alloc __P((String *classname, String *inst,
89 static void free_triplet __P((Triplet *));
94 * Determine if two destination triplets are equal. Note the backup
95 * case-insensitive recipient check in the third term. Recipients are
96 * not downcased at subscription time (in order to preserve case for,
97 * say, "zctl ret"), but traditional zephyr server behavior has not
98 * been case-sensitive in the recipient string. In most cases, a
99 * failed match will fail on the classname or instance, and a successful
100 * match will succeed on the (d1->recip == d2->recip) check, so this
101 * shouldn't affect performance.
105 Destination *d1, *d2;
107 return((d1->classname == d2->classname) &&
108 (d1->inst == d2->inst) &&
109 (d1->recip == d2->recip ||
110 strcasecmp(d1->recip->string, d2->recip->string) == 0));
114 /* the client as interested in a triplet */
117 triplet_register(client, dest, realm)
123 unsigned long hashval;
125 hashval = DEST_HASHVAL(*dest);
126 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
127 if (ZDest_eq(&triplet->dest, dest))
128 return insert_client(triplet, client, realm);
131 /* Triplet not present in hash table, insert it. */
132 triplet = triplet_alloc(dest->classname, dest->inst, dest->recip);
133 LIST_INSERT(&triplet_bucket[hashval], triplet);
134 return insert_client(triplet, client, realm);
137 /* dissociate client from the class, garbage collecting if appropriate */
140 triplet_deregister(client, dest, realm)
147 unsigned long hashval;
150 zdbug((LOG_DEBUG, "class_dereg: %s %s", dest->classname->string,
151 dest->inst->string));
153 hashval = DEST_HASHVAL(*dest);
154 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
155 if (ZDest_eq(&triplet->dest, dest)) {
156 retval = remove_client(triplet, client, realm);
157 if (retval != ZERR_NONE)
159 if (*triplet->clients == NULL && !triplet->acl) {
160 LIST_DELETE(triplet);
161 free_triplet(triplet);
162 return ZSRV_EMPTYCLASS;
167 return(ZSRV_BADASSOC);
170 /* return a linked list of what clients are interested in this triplet */
177 unsigned long hashval;
179 hashval = DEST_HASHVAL(*dest);
180 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
181 if (ZDest_eq(&triplet->dest, dest))
182 return triplet->clients;
188 * return the acl structure associated with class, or NULL if there is
193 class_get_acl(class_name)
197 unsigned long hashval;
199 hashval = HASHVAL(class_name, empty, empty);
200 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
201 if (triplet->dest.classname == class_name &&
202 triplet->dest.inst == empty && triplet->dest.recip == empty)
206 /* No acl found, not restricted. */
211 * restrict class by associating it with the acl structure acl.
212 * return ZERR_NONE if no error, or ZSRV_NOCLASS if there is no such
213 * class, or ZSRV_CLASSRESTRICTED if it is already restricted.
217 class_restrict(class_name, acl)
223 unsigned long hashval;
225 d = make_string(class_name,1);
226 hashval = HASHVAL(d, empty, empty);
227 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
228 if (triplet->dest.classname == d && triplet->dest.inst == empty &&
229 triplet->dest.recip == empty) {
231 return ZSRV_CLASSRESTRICTED;
243 * restrict class by registering it and associating it with the acl
244 * structure acl. return ZERR_NONE if no error, or ZSRV_CLASSXISTS
245 * if the class is already registered, or ENOMEM in case of malloc failure.
249 class_setup_restricted(class_name, acl)
255 unsigned long hashval;
257 d = make_string(class_name,1);
258 hashval = HASHVAL(d, empty, empty);
259 for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
260 if (triplet->dest.classname == d && triplet->dest.inst == empty &&
261 triplet->dest.recip == d) {
263 return ZSRV_CLASSXISTS;
267 /* Triplet not present in hash table, insert it. */
268 triplet = triplet_alloc(d, empty, empty);
273 LIST_INSERT(&triplet_bucket[hashval], triplet);
277 /* private routines */
279 /* allocate space for a class structure */
282 triplet_alloc(classname,inst,recipient)
283 String *classname, *inst, *recipient;
288 triplet = (Triplet *) malloc(sizeof(Triplet));
292 triplet->dest.classname = dup_string(classname);
293 triplet->dest.inst = dup_string(inst);
294 triplet->dest.recip = dup_string(recipient);
295 triplet->clients = NULL;
301 /* insert a client into the list associated with the class *ptr */
304 insert_client(triplet, client, realm)
309 Client **clientp, **newclients;
312 if (triplet->clients) {
313 /* Avoid duplication. */
314 for (clientp = triplet->clients; *clientp; clientp++) {
315 if (*clientp == client || (realm && (*clientp)->realm == realm))
316 return ZSRV_CLASSXISTS;
319 if (clientp + 1 - triplet->clients >= triplet->clients_size) {
320 new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
321 newclients = (Client **) realloc(triplet->clients,
322 new_size * sizeof(Client *));
323 if (newclients == NULL)
325 clientp = newclients + (clientp - triplet->clients);
326 triplet->clients = newclients;
327 triplet->clients_size = new_size;
330 /* Allocate an initial list of client pointers. */
331 triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
332 if (triplet->clients == NULL)
334 triplet->clients_size = ALLOC_INIT;
335 clientp = triplet->clients;
344 * remove the client from the list associated with class *ptr, garbage
345 * collecting if appropriate
348 static Code_t remove_client(triplet, client, realm)
355 for (clientp = triplet->clients; *clientp; clientp++) {
356 if (*clientp == client || (realm && (*clientp)->realm == realm)) {
357 for (; *clientp; clientp++)
358 *clientp = clientp[1];
363 return ZSRV_BADASSOC;
366 static void free_triplet(triplet)
369 if (triplet->clients)
370 free(triplet->clients);
371 free_string(triplet->dest.classname);
372 free_string(triplet->dest.inst);
373 free_string(triplet->dest.recip);
377 void triplet_dump_subs(fp)
384 for (i = 0; i < HASHSIZE; i++) {
385 for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
386 fputs("Triplet '", fp);
387 dump_quote(triplet->dest.classname->string, fp);
389 dump_quote(triplet->dest.inst->string, fp);
391 dump_quote(triplet->dest.recip->string, fp);
393 if (triplet->clients) {
394 for (clientp = triplet->clients; *clientp; clientp++) {
395 fprintf(fp, "\t%s %d (%s)\n",
396 inet_ntoa((*clientp)->addr.sin_addr),
397 ntohs((*clientp)->addr.sin_port),
398 (*clientp)->principal->string);