]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/class.c
Import new release
[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: zacheiss $
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: class.c,v 1.27 2001/02/27 04:43:59 zacheiss Exp $";
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                                  Realm *realm));
85 static Code_t insert_client __P((Triplet *triplet, Client *client,
86                                  Realm *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 ZDest_eq(d1, d2)
105     Destination *d1, *d2;
106 {
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));
111 }
112
113
114 /* the client as interested in a triplet */
115
116 Code_t
117 triplet_register(client, dest, realm)
118     Client *client;
119     Destination *dest;
120     Realm *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, dest, realm)
141     Client *client;
142     Destination *dest;
143     Realm *realm;
144 {
145     Triplet *triplet;
146     int retval;
147     unsigned long hashval;
148
149 #if 0
150     zdbug((LOG_DEBUG, "class_dereg: %s %s", dest->classname->string,
151            dest->inst->string));
152 #endif
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)
158                 return retval;
159             if (*triplet->clients == NULL && !triplet->acl) {
160                 LIST_DELETE(triplet);
161                 free_triplet(triplet);
162                 return ZSRV_EMPTYCLASS;
163             }
164             return ZERR_NONE;
165         }
166     }
167     return(ZSRV_BADASSOC);
168 }
169         
170 /* return a linked list of what clients are interested in this triplet */
171
172 Client **
173 triplet_lookup(dest)
174     Destination *dest;
175 {
176     Triplet *triplet;
177     unsigned long hashval;
178
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;
183     }
184     return NULL;
185 }
186
187 /*
188  * return the acl structure associated with class, or NULL if there is
189  * no such acl struct
190  */
191
192 Acl *
193 class_get_acl(class_name)
194     String *class_name;
195 {
196     Triplet *triplet;
197     unsigned long hashval;
198
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)
203             return triplet->acl;
204     }
205
206     /* No acl found, not restricted. */
207     return NULL;
208 }
209
210 /*
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.
214  */
215
216 Code_t
217 class_restrict(class_name, acl)
218     char *class_name;
219     Acl *acl;
220 {
221     Triplet *triplet;
222     String *d;
223     unsigned long hashval;
224
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) {
230             if (triplet->acl)
231                 return ZSRV_CLASSRESTRICTED;
232             triplet->acl = acl;
233             free_string(d);
234             return ZERR_NONE;
235         }
236     }
237
238     free_string(d);
239     return ZSRV_NOCLASS;
240 }
241
242 /*
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.
246  */
247
248 Code_t
249 class_setup_restricted(class_name, acl)
250     char *class_name;
251     Acl *acl;
252 {
253     Triplet *triplet;
254     String *d;
255     unsigned long hashval;
256
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) {
262             free_string(d);
263             return ZSRV_CLASSXISTS;
264         }
265     }
266
267     /* Triplet not present in hash table, insert it. */
268     triplet = triplet_alloc(d, empty, empty);
269     free_string(d);
270     if (!triplet)
271         return ENOMEM;
272     triplet->acl = acl;
273     LIST_INSERT(&triplet_bucket[hashval], triplet);
274     return ZERR_NONE;
275 }
276
277 /* private routines */
278
279 /* allocate space for a class structure */
280
281 static Triplet *
282 triplet_alloc(classname,inst,recipient)
283     String *classname, *inst, *recipient;
284 {
285     Triplet *triplet;
286     Client *clist;
287
288     triplet = (Triplet *) malloc(sizeof(Triplet));
289     if (!triplet)
290         return NULL;
291
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;
296     triplet->acl = NULL;
297
298     return triplet;
299 }
300
301 /* insert a client into the list associated with the class *ptr */
302
303 static Code_t
304 insert_client(triplet, client, realm)
305     Triplet *triplet;
306     Client *client;
307     Realm *realm;
308 {
309     Client **clientp, **newclients;
310     int new_size;
311
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;
317         }
318
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)
324                 return ENOMEM;
325             clientp = newclients + (clientp - triplet->clients);
326             triplet->clients = newclients;
327             triplet->clients_size = new_size;
328         }
329     } else {
330         /* Allocate an initial list of client pointers. */
331         triplet->clients = (Client **) malloc(ALLOC_INIT * sizeof(Client *));
332         if (triplet->clients == NULL)
333             return ENOMEM;
334         triplet->clients_size = ALLOC_INIT;
335         clientp = triplet->clients;
336     }
337
338     *clientp = client;
339     clientp[1] = NULL;
340     return ZERR_NONE;
341 }
342
343 /* 
344  * remove the client from the list associated with class *ptr, garbage
345  * collecting if appropriate
346  */
347
348 static Code_t remove_client(triplet, client, realm)
349     Triplet *triplet;
350     Client *client;
351     Realm *realm;
352 {
353     Client **clientp;
354
355     for (clientp = triplet->clients; *clientp; clientp++) {
356         if (*clientp == client || (realm && (*clientp)->realm == realm)) {
357             for (; *clientp; clientp++)
358                 *clientp = clientp[1];
359             return ZERR_NONE;
360         }
361     }
362
363     return ZSRV_BADASSOC;
364 }
365
366 static void free_triplet(triplet)
367     Triplet *triplet;
368 {
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);
374     free(triplet);
375 }
376
377 void triplet_dump_subs(fp)
378     FILE *fp;
379 {
380     int i;
381     Triplet *triplet;
382     Client **clientp;
383
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);
388             fputs("' '", fp);
389             dump_quote(triplet->dest.inst->string, fp);
390             fputs("' '", fp);
391             dump_quote(triplet->dest.recip->string, fp);
392             fputs("':\n", 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);
399                 }
400             }
401         }
402     }
403 }
404