3 * $Id: netg.c,v 1.3 1998/01/12 16:46:17 mdw Exp $
5 * A local database of netgroups
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of `become'
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.3 1998/01/12 16:46:17 mdw
35 * Revision 1.2 1997/08/20 16:19:11 mdw
36 * Patch memory leak. Replace `name_reinit' by `name_end' for more sensible
37 * restart. Don't try to trace when tracing's turned off.
39 * Revision 1.1 1997/08/07 09:45:00 mdw
40 * New source file added to maintain a netgroups database.
44 /*----- Header files ------------------------------------------------------*/
46 /* --- ANSI headers --- */
53 /* --- Unix headers --- */
57 #include <sys/types.h>
61 # include <rpcsvc/ypclnt.h>
62 # include <rpcsvc/yp_prot.h>
65 #include <netinet/in.h>
67 #include <arpa/inet.h>
73 /* --- Local headers --- */
82 /*----- Type definitions --------------------------------------------------*/
84 /* --- Quick discussion --- *
86 * I've just noticed: netgroups are horrible. They form a directed graph
87 * which is really horrible; I'll have to try and turn it into something
88 * more sensible (which will essentially involve cutting cycles).
90 * The structure looks a little bit like a good ol' List (see Knuth 1 or
91 * any decent Lisp manual), but with more information in the cons cells.
94 /* --- @netg__cons@ --- */
96 typedef struct netg__cons {
99 struct netg__cons *cons;
100 struct netg__atom *atom;
102 struct netg__cons *cdr;
106 f_cons = 1, /* The @car@ is a cons cell */
107 f_visit = 2, /* Currently threaded on this cell */
108 f_uncycled = 4 /* Cycles removed from here on in */
111 /* --- @netg__atom@ --- */
113 typedef struct netg__atom {
114 char *n; /* Unresolved netgroup reference */
115 char *h; /* Matched hostname */
116 char *u; /* Matched user name */
117 char *d; /* Matched domain name */
120 /* --- @netg__sym@ --- */
122 typedef struct netg__sym {
127 /* --- Token types for the netgroup parser --- */
134 /*----- Static variables --------------------------------------------------*/
136 static sym_table netg__table; /* Netgroup table */
137 static sym_iter netg__iter; /* Iterator object for users */
139 /*----- Main code ---------------------------------------------------------*/
141 /* --- @netg__lex@ --- *
143 * Arguments: @const char **p@ = pointer to next unscanned character
144 * @char *q@ = pointer to output buffer
146 * Returns: Token type (either character code or a magic number).
148 * Use: Lexes a netgroups line into tokens.
151 static int netg__lex(char **p, char *q)
153 /* --- Skip any leading whitespace --- */
155 while (isspace((unsigned char)**p))
158 /* --- Now work out what we've got --- */
162 if (**p == '(' || **p == ')' || **p == ',')
166 while (**p != 0 && **p != '(' && **p != ')' &&
167 **p != ',' && !isspace((unsigned char)**p));
172 /* --- @netg__foreach@ --- *
174 * Arguments: @int st@ = YP protocol-level status code
175 * @char *k@ = pointer to string containing the key
176 * @int ksz@ = length of the key string
177 * @char *v@ = pointer to string containing the value
178 * @int vsz@ = length of the value string
179 * @char *data@ = pointer to my data information
181 * Returns: Zero to continue, nonzero for no more entries.
183 * Use: Handles each incoming netgroup, attaching it to the table.
186 static int netg__foreach(int st, char *k, int ksz,
187 char *v, int vsz, char *data)
192 netg__cons *c, **link;
196 /* --- If something is amiss, then quit now --- */
201 /* --- Ignore empty lines from the original file --- */
206 /* --- Build my own trashable copies of the key and value --- *
208 * Note the oddness when I copy the value string. The extra byte at the
209 * beginning allows me to use the same area of memory as an output buffer
210 * for the lexer. It must be big enough; the lexer doesn't back up; and
211 * that extra byte gives me somewhere to put a terminating null byte.
214 kc = xmalloc(ksz + 1);
218 vc = xmalloc(vsz + 2);
219 memcpy(vc + 1, v, vsz);
222 T( trace(TRACE_DEBUG, "debug: netgroup `%s': `%s'", kc, vc + 1); )
224 /* --- Allocate a symbol in my table --- */
226 sng = sym_find(&netg__table, kc, -1, sizeof(*sng), &f);
230 /* --- Run to the end of the list --- */
232 for (link = &sng->cons; *link; link = &((*link)->cdr))
235 /* --- Now start the tricky bit --- *
237 * I have to start parsing the netgroup value string. Oh, well, it
240 * The parser is written so as to avoid saying things more often than
241 * necessary. This tends to involve @goto@s. You've been warned.
245 t = netg__lex(&p, vc);
249 /* --- Start with a fresh cons cell, with an empty atom attached --- */
251 c = xmalloc(sizeof(*c));
252 c->car.atom = xmalloc(sizeof(*c->car.atom));
254 /* --- Restart here after an error --- *
256 * If I restart here, I can avoid freeing the cons cell reallocating
257 * it, which is a little silly.
261 c->car.atom->n = c->car.atom->h = c->car.atom->u = c->car.atom->d = 0;
265 /* --- Handle end-of-line --- */
270 /* --- Handle a netgroup reference --- */
272 if (t == tok_string) {
273 T( trace(TRACE_DEBUG, "debug: add reference to `%s'", vc); )
274 c->car.atom->n = xstrdup(vc);
277 t = netg__lex(&p, vc);
281 /* --- Parse our merry way through the host--user--domain triple --- */
285 t = netg__lex(&p, vc);
287 if (t == tok_string) {
288 T( trace(TRACE_DEBUG, "debug: add host `%s'", vc); )
289 c->car.atom->h = xstrdup(vc);
290 t = netg__lex(&p, vc);
295 t = netg__lex(&p, vc);
297 if (t == tok_string) {
298 T( trace(TRACE_DEBUG, "debug: add user `%s'", vc); )
299 c->car.atom->u = xstrdup(vc);
300 t = netg__lex(&p, vc);
305 t = netg__lex(&p, vc);
307 if (t == tok_string) {
308 T( trace(TRACE_DEBUG, "debug: add domain `%s'", vc); )
309 c->car.atom->d = xstrdup(vc);
310 t = netg__lex(&p, vc);
315 t = netg__lex(&p, vc);
317 /* --- Finished that, so insert this cons cell into the list --- */
323 /* --- Tidy up during scanning of a triple --- *
325 * I'll search for the closing paren, and hope that I won't miss out too
330 while (t != tok_eof && t != ')')
331 t = netg__lex(&p, vc);
333 /* --- Other syntax oddnesses come out here --- *
335 * Snarf the token which caused the error.
339 moan("syntax error in netgroups line for `%s'", kc);
340 if (c->car.atom->n) free(c->car.atom->n);
341 if (c->car.atom->h) free(c->car.atom->h);
342 if (c->car.atom->u) free(c->car.atom->u);
343 if (c->car.atom->d) free(c->car.atom->d);
344 t = netg__lex(&p, vc);
355 /* --- @netg__dumpGroup@ --- *
357 * Arguments: @netg__cons *c@ = pointer to a list head
358 * @int lev@ = indentation level
362 * Use: Dumps the netgroup given.
367 static void netg__dumpGroup(netg__cons *c, int lev)
374 /* --- Check for a cycle --- */
376 if (c->f & f_visit) {
377 trace(TRACE_DEBUG, "debug: %*scycle!", lev * 2, "");
381 /* --- Dump the netgroup --- */
385 for (cc = c; cc; cc = cc->cdr) {
386 if (cc->f & f_cons) {
387 trace(TRACE_DEBUG, "debug: %*ssubnetgroup...", lev * 2, "");
388 netg__dumpGroup(cc->car.cons, lev + 1);
389 } else if (cc->car.atom->n) {
390 trace(TRACE_DEBUG, "debug: %*sunresolved subgroup `%s'",
391 lev * 2, "", cc->car.atom->n);
393 trace(TRACE_DEBUG, "debug: %*s(%s, %s, %s)", lev * 2, "",
394 cc->car.atom->h ? cc->car.atom->h : "<all-hosts>",
395 cc->car.atom->u ? cc->car.atom->u : "<all-users>",
396 cc->car.atom->d ? cc->car.atom->d : "<all-domains>");
405 /* --- @netg__dump@ --- *
411 * Use: Dumps the netgroups table.
416 static void netg__dump(void)
421 trace(TRACE_DEBUG, "debug: dumping netgroups file");
422 for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
423 trace(TRACE_DEBUG, "debug: netgroup `%s'...", sng->_base.name);
424 sng->cons->f &= ~f_visit;
425 netg__dumpGroup(sng->cons, 1);
431 /* --- @netg_iterate@, @netg_iterate_r@ --- *
433 * Arguments: @netg_iter *i@ = pointer to a netgroup iterator object
437 * Use: Starts iterating over the netgroups.
440 void netg_iterate(void) { netg_iterate_r(&netg__iter); }
441 void netg_iterate_r(netg_iter *i) { sym_createIter(i, &netg__table); }
443 /* --- @netg_next@, @netg_next_r@ --- *
445 * Arguments: @netg_iter *i@ = pointer to a netgroup iterator object
447 * Returns: An opaque pointer to the next item, or null.
449 * Use: Returns the next netgroup.
452 netg *netg_next(void) { return (netg_next_r(&netg__iter)); }
453 netg *netg_next_r(netg_iter *i) { return (sym_next(i)); }
455 /* --- @netg_name@ --- *
457 * Arguments: @netg *n@ = netgroup handle returned by @netg_next@.
459 * Returns: A pointer to the name; you may not modify this string.
461 * Use: Returns the name of a netgroup.
464 const char *netg_name(netg *n) { return (n->_base.name); }
466 /* --- @netg_scan@ --- *
468 * Arguments: @netg *n@ = a netgroup handle returned by @netg_next@
469 * @int (*proc)(netg *n, const char *host, const char *user,@
470 * @const char *domain, void *ctx)@ = function to call
472 * @void *ctx@ = context pointer to pass to @proc@.
474 * Returns: Zero if all went well, or the nonzero return value from
477 * Use: Passes all the members of the netgroup to a given function.
478 * The function is given the names, directly from the NIS
479 * netgroup map, except that any empty entries are passed as
480 * null pointers rather than empty strings. You may not modify
481 * any of the strings. The enumeration function, @proc@, may
482 * return nonzero to stop itself from being called any more;
483 * if this happens, the value it returns becomes the result of
484 * this function. If all the items are enumerated OK, this
485 * function returns zero.
488 static int netg__doScan(netg__cons *c,
490 int (*proc)(netg */*n*/, const char */*host*/,
491 const char */*user*/,
492 const char */*domain*/, void */*ctx*/),
499 e = netg__doScan(c->car.cons, n, proc, ctx);
501 e = proc(n, c->car.atom->h, c->car.atom->u, c->car.atom->d, ctx);
509 int netg_scan(netg *n,
510 int (*proc)(netg */*n*/, const char */*host*/,
511 const char */*user*/, const char */*domain*/,
515 return (netg__doScan(n->cons, n, proc, ctx));
518 /* --- @netg__breakCycle@ --- *
520 * Arguments: @netg__cons *c@ = pointer to a list
524 * Use: Scans the given list (recursively) and breaks any cycles it
528 static void netg__breakCycle(netg__cons *c)
532 if (!c || c->f & f_uncycled)
536 for (cc = c; cc; cc = cc->cdr) {
539 if (cc->car.cons->f & f_visit) {
540 T( trace(TRACE_DEBUG, "debug: cycle in netgroups"); )
543 netg__breakCycle(cc->car.cons);
549 /* --- @netg_init@ --- *
555 * Use: Reads the netgroup database and turns it into something nice.
562 /* --- Initialise my symbol table --- */
564 sym_createTable(&netg__table);
566 /* --- Bind myself unto a YP server --- */
568 if (yp_get_default_domain(&ypdom) ||
572 /* --- Now try to read all the netgroup entries --- */
575 static struct ypall_callback ncb = { netg__foreach, 0 };
576 yp_all(ypdom, "netgroup", &ncb);
579 /* --- Unbind from the server --- */
583 /* --- Dump the table --- */
585 IF_TRACING(TRACE_DEBUG, netg__dump(); )
587 /* --- Now resolve all the remaining references --- */
595 for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
596 for (c = sng->cons; c; c = c->cdr) {
597 if ((c->f & f_cons) == 0 && c->car.atom->n) {
599 ng = sym_find(&netg__table, a->n, -1, 0, 0);
601 moan("undefined netgroup `%s' (ignored)", a->n);
604 c->car.cons = ng->cons;
614 /* --- Remove cycles in the netgroups table --- */
620 for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
621 sng->cons->f &= ~f_uncycled;
622 for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
623 netg__breakCycle(sng->cons);
626 /* --- Dump the table again --- */
628 IF_TRACING(TRACE_DEBUG, netg__dump(); )
631 /* --- @netg_end@ --- *
637 * Use: Empties the netgroups database.
646 /* --- Remove all the old netgroups rubbish --- */
648 for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
652 if (~c->f & f_cons) {
653 if (c->car.atom->n) free(c->car.atom->n);
654 if (c->car.atom->h) free(c->car.atom->h);
655 if (c->car.atom->u) free(c->car.atom->u);
656 if (c->car.atom->d) free(c->car.atom->d);
662 sym_remove(&netg__table, sng);
665 sym_destroyTable(&netg__table);
668 /*----- Test driver -------------------------------------------------------*/
672 int scanner(netg *n, const char *h, const char *u, const char *d, void *c)
674 fprintf(stderr, " %s, %s, %s\n",
675 h ? h : "<any>", u ? u : "<any>", d ? d : "<any>");
683 traceon(stderr, TRACE_ALL);
685 for (netg_iterate(); (n = netg_next()) != 0; ) {
686 fprintf(stderr, "netgroup %s\n", netg_name(n));
687 netg_scan(n, scanner, 0);
694 /*----- That's all, folks -------------------------------------------------*/