]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/class.c
Initial revision
[1ts-debian.git] / zephyr / server / class.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for the Zephyr server class manager subsystem.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Id: class.c,v 1.26 1999/01/22 23:19:40 ghudson Exp $
7  *
8  *      Copyright (c) 1987 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 #include <zephyr/mit-copyright.h>
14 #include "zserver.h"                    /* includes zephyr/zephyr.h */
15 #include <assert.h>
16
17 #if !defined (lint) && !defined (SABER)
18 static const char rcsid_class_c[] =
19 "$Id: class.c,v 1.26 1999/01/22 23:19:40 ghudson Exp $";
20 #endif
21
22 /*
23  * Class manager subsystem.
24  *
25  *
26  * External functions are:
27  *
28  * Code_t triplet_register(client, subs, realm)
29  *
30  * Code_t triplet_deregister(client, subs, realm)
31  *
32  * Client *triplet_lookup(subs)
33  *      Client *client;
34  *      Destlist *subs;
35  *
36  * Acl *class_get_acl(class_name)
37  *      String *class_name;
38  *
39  * Code_t class_restrict(class_name, acl)
40  *      char *class_name;
41  *      Acl *acl;
42  *
43  * Code_t class_setup_restricted(class_name, acl)
44  *      char *class_name;
45  *      Acl *acl;
46  *
47  * and several Destination methods.
48  */
49
50 /*
51  * The data structure used for the class manager is an array of hash buckets
52  * each containing a pointer to a doubly linked circular list (in the style
53  * of insque/remque).  Each element of this list contains a class.instance
54  * name (which hashes into the bucket associated with this list) and a
55  * doubly linked list of clients which are interested in this class.
56  * The data pointed to by these clients is owned by other modules.  Care
57  * must be taken by the caller not to a free()'d client
58  * structure.
59  *
60  * If any hash bucket is empty, the pointer is null.
61  *
62  * The first element in the hash bucket is a special header unused for
63  * storing classes, and is used for finding the end of the list.
64  *
65  * If any list of interested clients is empty, the class name is garbage
66  * collected, unless the class has been registered as restricted.
67  */
68
69 /* Private variables */ 
70 #define EMPTY_CLASS     2000
71
72 #define ALLOC_OFFSET    8       /* Allocate 32 bytes less than a power of 2. */
73 #define ALLOC_INIT      8       /* Initial number of subscriptions. */
74
75 #define HASHSIZE        1023
76 #define HASHVAL(c, i, r) (((c)->hash_val ^ (i)->hash_val ^ (r)->hash_val) \
77                           % HASHSIZE)
78 #define DEST_HASHVAL(dest) HASHVAL((dest).classname, (dest).inst, (dest).recip)
79
80 static Triplet *triplet_bucket[HASHSIZE]; /* the hash table of pointers */
81
82 static Code_t remove_client __P((Triplet *triplet, Client *client,
83                                  Realm *realm));
84 static Code_t insert_client __P((Triplet *triplet, Client *client,
85                                  Realm *realm));
86 static Triplet *triplet_alloc __P((String *classname, String *inst,
87                                    String *recipient));
88 static void free_triplet __P((Triplet *));
89
90 /* public routines */
91
92 /*
93  * Determine if two destination triplets are equal.  Note the backup
94  * case-insensitive recipient check in the third term.  Recipients are
95  * not downcased at subscription time (in order to preserve case for,
96  * say, "zctl ret"), but traditional zephyr server behavior has not
97  * been case-sensitive in the recipient string.  In most cases, a
98  * failed match will fail on the classname or instance, and a successful
99  * match will succeed on the (d1->recip == d2->recip) check, so this
100  * shouldn't affect performance.
101  */
102
103 int ZDest_eq(d1, d2)
104     Destination *d1, *d2;
105 {
106     return((d1->classname == d2->classname) &&
107            (d1->inst == d2->inst) &&
108            (d1->recip == d2->recip ||
109             strcasecmp(d1->recip->string, d2->recip->string) == 0));
110 }
111
112
113 /* the client as interested in a triplet */
114
115 Code_t
116 triplet_register(client, dest, realm)
117     Client *client;
118     Destination *dest;
119     Realm *realm;
120 {
121     Triplet *triplet;
122     unsigned long hashval;
123
124     hashval = DEST_HASHVAL(*dest);
125     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
126         if (ZDest_eq(&triplet->dest, dest))
127             return insert_client(triplet, client, realm);
128     }
129
130     /* Triplet not present in hash table, insert it. */
131     triplet = triplet_alloc(dest->classname, dest->inst, dest->recip);
132     LIST_INSERT(&triplet_bucket[hashval], triplet);
133     return insert_client(triplet, client, realm);
134 }
135
136 /* dissociate client from the class, garbage collecting if appropriate */
137
138 Code_t
139 triplet_deregister(client, dest, realm)
140     Client *client;
141     Destination *dest;
142     Realm *realm;
143 {
144     Triplet *triplet;
145     int retval;
146     unsigned long hashval;
147
148 #if 0
149     zdbug((LOG_DEBUG, "class_dereg: %s %s", dest->classname->string,
150            dest->inst->string));
151 #endif
152     hashval = DEST_HASHVAL(*dest);
153     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
154         if (ZDest_eq(&triplet->dest, dest)) {
155             retval = remove_client(triplet, client, realm);
156             if (retval != ZERR_NONE)
157                 return retval;
158             if (*triplet->clients == NULL && !triplet->acl) {
159                 LIST_DELETE(triplet);
160                 free_triplet(triplet);
161             }
162             return ZERR_NONE;
163         }
164     }
165     return(ZSRV_BADASSOC);
166 }
167         
168 /* return a linked list of what clients are interested in this triplet */
169
170 Client **
171 triplet_lookup(dest)
172     Destination *dest;
173 {
174     Triplet *triplet;
175     unsigned long hashval;
176
177     hashval = DEST_HASHVAL(*dest);
178     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
179         if (ZDest_eq(&triplet->dest, dest))
180             return triplet->clients;
181     }
182     return NULL;
183 }
184
185 /*
186  * return the acl structure associated with class, or NULL if there is
187  * no such acl struct
188  */
189
190 Acl *
191 class_get_acl(class_name)
192     String *class_name;
193 {
194     Triplet *triplet;
195     unsigned long hashval;
196
197     hashval = HASHVAL(class_name, empty, empty);
198     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
199         if (triplet->dest.classname == class_name &&
200             triplet->dest.inst == empty && triplet->dest.recip == empty)
201             return triplet->acl;
202     }
203
204     /* No acl found, not restricted. */
205     return NULL;
206 }
207
208 /*
209  * restrict class by associating it with the acl structure acl.
210  * return ZERR_NONE if no error, or ZSRV_NOCLASS if there is no such
211  * class, or ZSRV_CLASSRESTRICTED if it is already restricted.
212  */
213
214 Code_t
215 class_restrict(class_name, acl)
216     char *class_name;
217     Acl *acl;
218 {
219     Triplet *triplet;
220     String *d;
221     unsigned long hashval;
222
223     d = make_string(class_name,1);
224     hashval = HASHVAL(d, empty, empty);
225     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
226         if (triplet->dest.classname == d && triplet->dest.inst == empty &&
227             triplet->dest.recip == empty) {
228             if (triplet->acl)
229                 return ZSRV_CLASSRESTRICTED;
230             triplet->acl = acl;
231             free_string(d);
232             return ZERR_NONE;
233         }
234     }
235
236     free_string(d);
237     return ZSRV_NOCLASS;
238 }
239
240 /*
241  * restrict class by registering it and  associating it with the acl
242  * structure acl.  return ZERR_NONE if no error, or ZSRV_CLASSXISTS
243  * if the class is already registered, or ENOMEM in case of malloc failure.
244  */
245
246 Code_t
247 class_setup_restricted(class_name, acl)
248     char *class_name;
249     Acl *acl;
250 {
251     Triplet *triplet;
252     String *d;
253     unsigned long hashval;
254
255     d = make_string(class_name,1);
256     hashval = HASHVAL(d, empty, empty);
257     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
258         if (triplet->dest.classname == d && triplet->dest.inst == empty &&
259             triplet->dest.recip == d) {
260             free_string(d);
261             return ZSRV_CLASSXISTS;
262         }
263     }
264
265     /* Triplet not present in hash table, insert it. */
266     triplet = triplet_alloc(d, empty, empty);
267     free_string(d);
268     if (!triplet)
269         return ENOMEM;
270     triplet->acl = acl;
271     LIST_INSERT(&triplet_bucket[hashval], triplet);
272     return ZERR_NONE;
273 }
274
275 /* private routines */
276
277 /* allocate space for a class structure */
278
279 static Triplet *
280 triplet_alloc(classname,inst,recipient)
281     String *classname, *inst, *recipient;
282 {
283     Triplet *triplet;
284     Client *clist;
285
286     triplet = (Triplet *) malloc(sizeof(Triplet));
287     if (!triplet)
288         return NULL;
289
290     triplet->dest.classname = dup_string(classname);
291     triplet->dest.inst = dup_string(inst);
292     triplet->dest.recip = dup_string(recipient);
293     triplet->clients = NULL;
294     triplet->acl = NULL;
295
296     return triplet;
297 }
298
299 /* insert a client into the list associated with the class *ptr */
300
301 static Code_t
302 insert_client(triplet, client, realm)
303     Triplet *triplet;
304     Client *client;
305     Realm *realm;
306 {
307     Client **clientp, **newclients;
308     int new_size;
309
310     if (triplet->clients) {
311         /* Avoid duplication. */
312         for (clientp = triplet->clients; *clientp; clientp++) {
313             if (*clientp == client || (realm && (*clientp)->realm == realm))
314                 return ZSRV_CLASSXISTS;
315         }
316
317         if (clientp + 1 - triplet->clients >= triplet->clients_size) {
318             new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
319             newclients = (Client **) realloc(triplet->clients,
320                                              new_size * sizeof(Client *));
321             if (newclients == NULL)
322                 return ENOMEM;
323             clientp = newclients + (clientp - triplet->clients);
324             triplet->clients = newclients;
325             triplet->clients_size = new_size;
326         }
327     } else {
328         /* Allocate an initial list of client pointers. */
329         triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
330         if (triplet->clients == NULL)
331             return ENOMEM;
332         triplet->clients_size = ALLOC_INIT;
333         clientp = triplet->clients;
334     }
335
336     *clientp = client;
337     clientp[1] = NULL;
338     return ZERR_NONE;
339 }
340
341 /* 
342  * remove the client from the list associated with class *ptr, garbage
343  * collecting if appropriate
344  */
345
346 static Code_t remove_client(triplet, client, realm)
347     Triplet *triplet;
348     Client *client;
349     Realm *realm;
350 {
351     Client **clientp;
352
353     for (clientp = triplet->clients; *clientp; clientp++) {
354         if (*clientp == client || (realm && (*clientp)->realm == realm)) {
355             for (; *clientp; clientp++)
356                 *clientp = clientp[1];
357             return ZERR_NONE;
358         }
359     }
360
361     return ZSRV_BADASSOC;
362 }
363
364 static void free_triplet(triplet)
365     Triplet *triplet;
366 {
367     if (triplet->clients)
368         free(triplet->clients);
369     free_string(triplet->dest.classname);
370     free_string(triplet->dest.inst);
371     free_string(triplet->dest.recip);
372     free(triplet);
373 }
374
375 void triplet_dump_subs(fp)
376     FILE *fp;
377 {
378     int i;
379     Triplet *triplet;
380     Client **clientp;
381
382     for (i = 0; i < HASHSIZE; i++) {
383         for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
384             fputs("Triplet '", fp);
385             dump_quote(triplet->dest.classname->string, fp);
386             fputs("' '", fp);
387             dump_quote(triplet->dest.inst->string, fp);
388             fputs("' '", fp);
389             dump_quote(triplet->dest.recip->string, fp);
390             fputs("':\n", fp);
391             if (triplet->clients) {
392                 for (clientp = triplet->clients; *clientp; clientp++) {
393                     fprintf(fp, "\t%s %d (%s)\n",
394                             inet_ntoa((*clientp)->addr.sin_addr),
395                             ntohs((*clientp)->addr.sin_port),
396                             (*clientp)->principal->string);
397                 }
398             }
399         }
400     }
401 }
402