]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/class.c
r4275@bucket (orig r265): kcr | 2008-01-21 02:57:32 -0500
[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  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/class.c,v $
7  *      $Author$
8  *
9  *      Copyright (c) 1987 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h"                    /* includes zephyr/zephyr.h */
16 #include <assert.h>
17
18 #if !defined (lint) && !defined (SABER)
19 static const char rcsid_class_c[] =
20 "$Id$";
21 #endif
22
23 /*
24  * Class manager subsystem.
25  *
26  *
27  * External functions are:
28  *
29  * Code_t triplet_register(client, subs, realm)
30  *
31  * Code_t triplet_deregister(client, subs, realm)
32  *
33  * Client *triplet_lookup(subs)
34  *      Client *client;
35  *      Destlist *subs;
36  *
37  * Acl *class_get_acl(class_name)
38  *      String *class_name;
39  *
40  * Code_t class_restrict(class_name, acl)
41  *      char *class_name;
42  *      Acl *acl;
43  *
44  * Code_t class_setup_restricted(class_name, acl)
45  *      char *class_name;
46  *      Acl *acl;
47  *
48  * and several Destination methods.
49  */
50
51 /*
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
59  * structure.
60  *
61  * If any hash bucket is empty, the pointer is null.
62  *
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.
65  *
66  * If any list of interested clients is empty, the class name is garbage
67  * collected, unless the class has been registered as restricted.
68  */
69
70 /* Private variables */ 
71 #define EMPTY_CLASS     2000
72
73 #define ALLOC_OFFSET    8       /* Allocate 32 bytes less than a power of 2. */
74 #define ALLOC_INIT      8       /* Initial number of subscriptions. */
75
76 #define HASHSIZE        1023
77 #define HASHVAL(c, i, r) (((c)->hash_val ^ (i)->hash_val ^ (r)->hash_val) \
78                           % HASHSIZE)
79 #define DEST_HASHVAL(dest) HASHVAL((dest).classname, (dest).inst, (dest).recip)
80
81 static Triplet *triplet_bucket[HASHSIZE]; /* the hash table of pointers */
82
83 static Code_t remove_client __P((Triplet *triplet, Client *client,
84                                  ZRealm *realm));
85 static Code_t insert_client __P((Triplet *triplet, Client *client,
86                                  ZRealm *realm));
87 static Triplet *triplet_alloc __P((String *classname, String *inst,
88                                    String *recipient));
89 static void free_triplet __P((Triplet *));
90
91 /* public routines */
92
93 /*
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.
102  */
103
104 int
105 ZDest_eq(Destination *d1,
106          Destination *d2)
107 {
108     return((d1->classname == d2->classname) &&
109            (d1->inst == d2->inst) &&
110            (d1->recip == d2->recip ||
111             strcasecmp(d1->recip->string, d2->recip->string) == 0));
112 }
113
114
115 /* the client as interested in a triplet */
116
117 Code_t
118 triplet_register(Client *client,
119                  Destination *dest,
120                  ZRealm *realm)
121 {
122     Triplet *triplet;
123     unsigned long hashval;
124
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);
129     }
130
131     /* Triplet not present in hash table, insert it. */
132     triplet = triplet_alloc(dest->classname, dest->inst, dest->recip);
133     Triplet_insert(&triplet_bucket[hashval], triplet);
134     return insert_client(triplet, client, realm);
135 }
136
137 /* dissociate client from the class, garbage collecting if appropriate */
138
139 Code_t
140 triplet_deregister(Client *client,
141                    Destination *dest,
142                    ZRealm *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                 Triplet_delete(triplet);
160                 free_triplet(triplet);
161                 return ZSRV_EMPTYCLASS;
162             }
163             return ZERR_NONE;
164         }
165     }
166     return(ZSRV_BADASSOC);
167 }
168         
169 /* return a linked list of what clients are interested in this triplet */
170
171 Client **
172 triplet_lookup(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(String *class_name)
192 {
193     Triplet *triplet;
194     unsigned long hashval;
195
196     hashval = HASHVAL(class_name, empty, empty);
197     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
198         if (triplet->dest.classname == class_name &&
199             triplet->dest.inst == empty && triplet->dest.recip == empty)
200             return triplet->acl;
201     }
202
203     /* No acl found, not restricted. */
204     return NULL;
205 }
206
207 /*
208  * restrict class by associating it with the acl structure acl.
209  * return ZERR_NONE if no error, or ZSRV_NOCLASS if there is no such
210  * class, or ZSRV_CLASSRESTRICTED if it is already restricted.
211  */
212
213 Code_t
214 class_restrict(char *class_name,
215                Acl *acl)
216 {
217     Triplet *triplet;
218     String *d;
219     unsigned long hashval;
220
221     d = make_string(class_name,1);
222     hashval = HASHVAL(d, empty, empty);
223     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
224         if (triplet->dest.classname == d && triplet->dest.inst == empty &&
225             triplet->dest.recip == empty) {
226             if (triplet->acl)
227                 return ZSRV_CLASSRESTRICTED;
228             triplet->acl = acl;
229             free_string(d);
230             return ZERR_NONE;
231         }
232     }
233
234     free_string(d);
235     return ZSRV_NOCLASS;
236 }
237
238 /*
239  * restrict class by registering it and  associating it with the acl
240  * structure acl.  return ZERR_NONE if no error, or ZSRV_CLASSXISTS
241  * if the class is already registered, or ENOMEM in case of malloc failure.
242  */
243
244 Code_t
245 class_setup_restricted(char *class_name,
246                        Acl *acl)
247 {
248     Triplet *triplet;
249     String *d;
250     unsigned long hashval;
251
252     d = make_string(class_name,1);
253     hashval = HASHVAL(d, empty, empty);
254     for (triplet = triplet_bucket[hashval]; triplet; triplet = triplet->next) {
255         if (triplet->dest.classname == d && triplet->dest.inst == empty &&
256             triplet->dest.recip == d) {
257             free_string(d);
258             return ZSRV_CLASSXISTS;
259         }
260     }
261
262     /* Triplet not present in hash table, insert it. */
263     triplet = triplet_alloc(d, empty, empty);
264     free_string(d);
265     if (!triplet)
266         return ENOMEM;
267     triplet->acl = acl;
268     Triplet_insert(&triplet_bucket[hashval], triplet);
269     return ZERR_NONE;
270 }
271
272 /* private routines */
273
274 /* allocate space for a class structure */
275
276 static Triplet *
277 triplet_alloc(String *classname,
278               String *inst,
279               String *recipient)
280 {
281     Triplet *triplet;
282
283     triplet = (Triplet *) malloc(sizeof(Triplet));
284     if (!triplet)
285         return NULL;
286
287     triplet->dest.classname = dup_string(classname);
288     triplet->dest.inst = dup_string(inst);
289     triplet->dest.recip = dup_string(recipient);
290     triplet->clients = NULL;
291     triplet->acl = NULL;
292
293     return triplet;
294 }
295
296 /* insert a client into the list associated with the class *ptr */
297
298 static Code_t
299 insert_client(Triplet *triplet,
300               Client *client,
301               ZRealm *realm)
302 {
303     Client **clientp, **newclients;
304     int new_size;
305
306     if (triplet->clients) {
307         /* Avoid duplication. */
308         for (clientp = triplet->clients; *clientp; clientp++) {
309             if (*clientp == client || (realm && (*clientp)->realm == realm))
310                 return ZSRV_CLASSXISTS;
311         }
312
313         if (clientp + 1 - triplet->clients >= triplet->clients_size) {
314             new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
315             newclients = (Client **) realloc(triplet->clients,
316                                              new_size * sizeof(Client *));
317             if (newclients == NULL)
318                 return ENOMEM;
319             clientp = newclients + (clientp - triplet->clients);
320             triplet->clients = newclients;
321             triplet->clients_size = new_size;
322         }
323     } else {
324         /* Allocate an initial list of client pointers. */
325         triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
326         if (triplet->clients == NULL)
327             return ENOMEM;
328         triplet->clients_size = ALLOC_INIT;
329         clientp = triplet->clients;
330     }
331
332     *clientp = client;
333     clientp[1] = NULL;
334     return ZERR_NONE;
335 }
336
337 /* 
338  * remove the client from the list associated with class *ptr, garbage
339  * collecting if appropriate
340  */
341
342 static Code_t
343 remove_client(Triplet *triplet,
344               Client *client,
345               ZRealm *realm)
346 {
347     Client **clientp;
348
349     for (clientp = triplet->clients; *clientp; clientp++) {
350         if (*clientp == client || (realm && (*clientp)->realm == realm)) {
351             for (; *clientp; clientp++)
352                 *clientp = clientp[1];
353             return ZERR_NONE;
354         }
355     }
356
357     return ZSRV_BADASSOC;
358 }
359
360 static void
361 free_triplet(Triplet *triplet)
362 {
363     if (triplet->clients)
364         free(triplet->clients);
365     free_string(triplet->dest.classname);
366     free_string(triplet->dest.inst);
367     free_string(triplet->dest.recip);
368     free(triplet);
369 }
370
371 void
372 triplet_dump_subs(FILE *fp)
373 {
374     int i;
375     Triplet *triplet;
376     Client **clientp;
377
378     for (i = 0; i < HASHSIZE; i++) {
379         for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
380             fputs("Triplet '", fp);
381             dump_quote(triplet->dest.classname->string, fp);
382             fputs("' '", fp);
383             dump_quote(triplet->dest.inst->string, fp);
384             fputs("' '", fp);
385             dump_quote(triplet->dest.recip->string, fp);
386             fputs("':\n", fp);
387             if (triplet->clients) {
388                 for (clientp = triplet->clients; *clientp; clientp++) {
389                     fprintf(fp, "\t%s %d (%s)\n",
390                             inet_ntoa((*clientp)->addr.sin_addr),
391                             ntohs((*clientp)->addr.sin_port),
392                             (*clientp)->principal->string);
393                 }
394             }
395         }
396     }
397 }
398