]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/class.c
r4262@bucket (orig r252): kcr | 2008-01-20 22:11:00 -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     LIST_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                 LIST_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     LIST_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     Client *clist;
283
284     triplet = (Triplet *) malloc(sizeof(Triplet));
285     if (!triplet)
286         return NULL;
287
288     triplet->dest.classname = dup_string(classname);
289     triplet->dest.inst = dup_string(inst);
290     triplet->dest.recip = dup_string(recipient);
291     triplet->clients = NULL;
292     triplet->acl = NULL;
293
294     return triplet;
295 }
296
297 /* insert a client into the list associated with the class *ptr */
298
299 static Code_t
300 insert_client(Triplet *triplet,
301               Client *client,
302               ZRealm *realm)
303 {
304     Client **clientp, **newclients;
305     int new_size;
306
307     if (triplet->clients) {
308         /* Avoid duplication. */
309         for (clientp = triplet->clients; *clientp; clientp++) {
310             if (*clientp == client || (realm && (*clientp)->realm == realm))
311                 return ZSRV_CLASSXISTS;
312         }
313
314         if (clientp + 1 - triplet->clients >= triplet->clients_size) {
315             new_size = triplet->clients_size * 2 + ALLOC_OFFSET;
316             newclients = (Client **) realloc(triplet->clients,
317                                              new_size * sizeof(Client *));
318             if (newclients == NULL)
319                 return ENOMEM;
320             clientp = newclients + (clientp - triplet->clients);
321             triplet->clients = newclients;
322             triplet->clients_size = new_size;
323         }
324     } else {
325         /* Allocate an initial list of client pointers. */
326         triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
327         if (triplet->clients == NULL)
328             return ENOMEM;
329         triplet->clients_size = ALLOC_INIT;
330         clientp = triplet->clients;
331     }
332
333     *clientp = client;
334     clientp[1] = NULL;
335     return ZERR_NONE;
336 }
337
338 /* 
339  * remove the client from the list associated with class *ptr, garbage
340  * collecting if appropriate
341  */
342
343 static Code_t
344 remove_client(Triplet *triplet,
345               Client *client,
346               ZRealm *realm)
347 {
348     Client **clientp;
349
350     for (clientp = triplet->clients; *clientp; clientp++) {
351         if (*clientp == client || (realm && (*clientp)->realm == realm)) {
352             for (; *clientp; clientp++)
353                 *clientp = clientp[1];
354             return ZERR_NONE;
355         }
356     }
357
358     return ZSRV_BADASSOC;
359 }
360
361 static void
362 free_triplet(Triplet *triplet)
363 {
364     if (triplet->clients)
365         free(triplet->clients);
366     free_string(triplet->dest.classname);
367     free_string(triplet->dest.inst);
368     free_string(triplet->dest.recip);
369     free(triplet);
370 }
371
372 void
373 triplet_dump_subs(FILE *fp)
374 {
375     int i;
376     Triplet *triplet;
377     Client **clientp;
378
379     for (i = 0; i < HASHSIZE; i++) {
380         for (triplet = triplet_bucket[i]; triplet; triplet = triplet->next) {
381             fputs("Triplet '", fp);
382             dump_quote(triplet->dest.classname->string, fp);
383             fputs("' '", fp);
384             dump_quote(triplet->dest.inst->string, fp);
385             fputs("' '", fp);
386             dump_quote(triplet->dest.recip->string, fp);
387             fputs("':\n", fp);
388             if (triplet->clients) {
389                 for (clientp = triplet->clients; *clientp; clientp++) {
390                     fprintf(fp, "\t%s %d (%s)\n",
391                             inet_ntoa((*clientp)->addr.sin_addr),
392                             ntohs((*clientp)->addr.sin_port),
393                             (*clientp)->principal->string);
394                 }
395             }
396         }
397     }
398 }
399