New source file added to maintain a netgroups database.
authormdw <mdw>
Thu, 7 Aug 1997 09:45:00 +0000 (09:45 +0000)
committermdw <mdw>
Thu, 7 Aug 1997 09:45:00 +0000 (09:45 +0000)
src/netg.c [new file with mode: 0644]
src/netg.h [new file with mode: 0644]

diff --git a/src/netg.c b/src/netg.c
new file mode 100644 (file)
index 0000000..87afce0
--- /dev/null
@@ -0,0 +1,684 @@
+/* -*-c-*-
+ *
+ * $Id: netg.c,v 1.1 1997/08/07 09:45:00 mdw Exp $
+ *
+ * A local database of netgroups
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * `Become' is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with `become'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: netg.c,v $
+ * Revision 1.1  1997/08/07 09:45:00  mdw
+ * New source file added to maintain a netgroups database.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Unix headers --- */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_YP
+#  include <rpc/rpc.h>
+#  include <rpcsvc/ypclnt.h>
+#  include <rpcsvc/yp_prot.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <pwd.h>
+#include <netdb.h>
+#include <unistd.h>
+
+/* --- Local headers --- */
+
+#include "become.h"
+#include "config.h"
+#include "netg.h"
+#include "sym.h"
+#include "userdb.h"
+#include "utils.h"
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Quick discussion --- *
+ *
+ * I've just noticed: netgroups are horrible.  They form a directed graph
+ * which is really horrible; I'll have to try and turn it into something
+ * more sensible (which will essentially involve cutting cycles).
+ *
+ * The structure looks a little bit like a good ol' List (see Knuth 1 or
+ * any decent Lisp manual), but with more information in the cons cells.
+ */
+
+/* --- @netg__cons@ --- */
+
+typedef struct netg__cons {
+  unsigned f;
+  union {
+    struct netg__cons *cons;
+    struct netg__atom *atom;
+  } car;
+  struct netg__cons *cdr;
+} netg__cons;
+
+enum {
+  f_cons = 1,                          /* The @car@ is a cons cell */
+  f_visit = 2,                         /* Currently threaded on this cell */
+  f_uncycled = 4                       /* Cycles removed from here on in */
+};
+
+/* --- @netg__atom@ --- */
+
+typedef struct netg__atom {
+  char *n;                             /* Unresolved netgroup reference */
+  char *h;                             /* Matched hostname */
+  char *u;                             /* Matched user name */
+  char *d;                             /* Matched domain name */
+} netg__atom;
+
+/* --- @netg__sym@ --- */
+
+typedef struct netg__sym {
+  sym_base _base;
+  netg__cons *cons;
+} netg__sym;
+
+/* --- Token types for the netgroup parser --- */
+
+enum {
+  tok_string = 256,
+  tok_eof
+};
+
+/*----- Static variables --------------------------------------------------*/
+
+static sym_table netg__table;          /* Netgroup table */
+static sym_iter netg__iter;            /* Iterator object for users */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @netg__lex@ --- *
+ *
+ * Arguments:  @const char **p@ = pointer to next unscanned character
+ *             @char *q@ = pointer to output buffer
+ *
+ * Returns:    Token type (either character code or a magic number).
+ *
+ * Use:                Lexes a netgroups line into tokens.
+ */
+
+static int netg__lex(char **p, char *q)
+{
+  /* --- Skip any leading whitespace --- */
+
+  while (isspace((unsigned char)**p))
+    (*p)++;
+
+  /* --- Now work out what we've got --- */
+
+  if (**p == 0)
+    return (tok_eof);
+  if (**p == '(' || **p == ')' || **p == ',')
+    return (*(*p)++);
+  do
+    *q++ = *(*p)++;
+  while (**p != 0 && **p != '(' && **p != ')' &&
+        **p != ',' && !isspace((unsigned char)**p));
+  *q++ = 0;
+  return (tok_string);
+}
+
+/* --- @netg__foreach@ --- *
+ *
+ * Arguments:  @int st@ = YP protocol-level status code
+ *             @char *k@ = pointer to string containing the key
+ *             @int ksz@ = length of the key string
+ *             @char *v@ = pointer to string containing the value
+ *             @int vsz@ = length of the value string
+ *             @char *data@ = pointer to my data information
+ *
+ * Returns:    Zero to continue, nonzero for no more entries.
+ *
+ * Use:                Handles each incoming netgroup, attaching it to the table.
+ */
+
+static int netg__foreach(int st, char *k, int ksz,
+                        char *v, int vsz, char *data)
+{
+  char *kc, *vc;
+  unsigned f;
+  netg__sym *sng;
+  netg__cons *c, **link;
+  char *p;
+  int t;
+
+  /* --- If something is amiss, then quit now --- */
+
+  if (st != YP_TRUE)
+    return (-1);
+
+  /* --- Ignore empty lines from the original file --- */
+
+  if (!ksz || !vsz)
+    return (0);
+
+  /* --- Build my own trashable copies of the key and value --- *
+   *
+   * Note the oddness when I copy the value string.  The extra byte at the
+   * beginning allows me to use the same area of memory as an output buffer
+   * for the lexer.  It must be big enough; the lexer doesn't back up; and
+   * that extra byte gives me somewhere to put a terminating null byte.
+   */
+
+  kc = xmalloc(ksz + 1);
+  memcpy(kc, k, ksz);
+  kc[ksz] = 0;
+
+  vc = xmalloc(vsz + 2);
+  memcpy(vc + 1, v, vsz);
+  vc[vsz + 1] = 0;
+
+  T( trace(TRACE_DEBUG, "debug: netgroup `%s': `%s'", kc, vc + 1); )
+
+  /* --- Allocate a symbol in my table --- */
+
+  sng = sym_find(&netg__table, kc, -1, sizeof(*sng), &f);
+  if (!f)
+    sng->cons = 0;
+
+  /* --- Run to the end of the list --- */
+
+  for (link = &sng->cons; *link; link = &((*link)->cdr))
+    ;
+
+  /* --- Now start the tricky bit --- *
+   *
+   * I have to start parsing the netgroup value string.  Oh, well, it
+   * could be worse.
+   *
+   * The parser is written so as to avoid saying things more often than
+   * necessary.  This tends to involve @goto@s.  You've been warned.
+   */
+
+  p = vc + 1;
+  t = netg__lex(&p, vc);
+
+  for (;;) {
+
+    /* --- Start with a fresh cons cell, with an empty atom attached --- */
+
+    c = xmalloc(sizeof(*c));
+    c->car.atom = xmalloc(sizeof(*c->car.atom));
+
+    /* --- Restart here after an error --- *
+     *
+     * If I restart here, I can avoid freeing the cons cell reallocating
+     * it, which is a little silly.
+     */
+
+  duff_restart:
+    c->car.atom->n = c->car.atom->h = c->car.atom->u = c->car.atom->d = 0;
+    c->f = 0;
+    c->cdr = 0;
+
+    /* --- Handle end-of-line --- */
+
+    if (t == tok_eof)
+      break;
+
+    /* --- Handle a netgroup reference --- */
+
+    if (t == tok_string) {
+      T( trace(TRACE_DEBUG, "debug: add reference to `%s'", vc); )
+      c->car.atom->n = xstrdup(vc);
+      *link = c;
+      link = &c->cdr;
+      t = netg__lex(&p, vc);
+      continue;
+    }
+
+    /* --- Parse our merry way through the host--user--domain triple --- */
+
+    if (t != '(')
+      goto duff;
+    t = netg__lex(&p, vc);
+
+    if (t == tok_string) {
+      T( trace(TRACE_DEBUG, "debug: add host `%s'", vc); )
+      c->car.atom->h = xstrdup(vc);
+      t = netg__lex(&p, vc);
+    }
+
+    if (t != ',')
+      goto duff_paren;
+    t = netg__lex(&p, vc);
+
+    if (t == tok_string) {
+      T( trace(TRACE_DEBUG, "debug: add user `%s'", vc); )
+      c->car.atom->u = xstrdup(vc);
+      t = netg__lex(&p, vc);
+    }
+
+    if (t != ',')
+      goto duff_paren;
+    t = netg__lex(&p, vc);
+
+    if (t == tok_string) {
+      T( trace(TRACE_DEBUG, "debug: add domain `%s'", vc); )
+      c->car.atom->d = xstrdup(vc);
+      t = netg__lex(&p, vc);
+    }
+
+    if (t != ')')
+      goto duff_paren;
+    t = netg__lex(&p, vc);
+
+    /* --- Finished that, so insert this cons cell into the list --- */
+
+    *link = c;
+    link = &c->cdr;
+    continue;
+
+    /* --- Tidy up during scanning of a triple --- *
+     *
+     * I'll search for the closing paren, and hope that I won't miss out too
+     * much.
+     */
+
+  duff_paren:
+    while (t != tok_eof && t != ')')
+      t = netg__lex(&p, vc);
+
+    /* --- Other syntax oddnesses come out here --- *
+     *
+     * Snarf the token which caused the error.
+     */
+
+  duff:
+    moan("syntax error in netgroups line for `%s'", kc);
+    if (c->car.atom->n) free(c->car.atom->n);
+    if (c->car.atom->h) free(c->car.atom->h);
+    if (c->car.atom->u) free(c->car.atom->u);
+    if (c->car.atom->d) free(c->car.atom->d);
+    t = netg__lex(&p, vc);
+    goto duff_restart;
+  }
+
+  free(kc);
+  free(vc);
+  return (0);
+}
+
+/* --- @netg__dumpGroup@ ---  *
+ *
+ * Arguments:  @netg__cons *c@ = pointer to a list head
+ *             @int lev@ = indentation level
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps the netgroup given.
+ */
+
+#ifdef TRACING
+
+static void netg__dumpGroup(netg__cons *c, int lev)
+{
+  netg__cons *cc;
+
+  if (!c)
+    return;
+
+  /* --- Check for a cycle --- */
+
+  if (c->f & f_visit) {
+    trace(TRACE_DEBUG, "debug: %*scycle!", lev * 2, "");
+    return;
+  }
+
+  /* --- Dump the netgroup --- */
+
+  c->f |= f_visit;
+
+  for (cc = c; cc; cc = cc->cdr) {
+    if (cc->f & f_cons) {
+      trace(TRACE_DEBUG, "debug: %*ssubnetgroup...", lev * 2, "");
+      netg__dumpGroup(cc->car.cons, lev + 1);
+    } else if (cc->car.atom->n) {
+      trace(TRACE_DEBUG, "debug: %*sunresolved subgroup `%s'",
+           lev * 2, "", cc->car.atom->n);
+    } else {
+      trace(TRACE_DEBUG, "debug: %*s(%s, %s, %s)", lev * 2, "",
+           cc->car.atom->h ? cc->car.atom->h : "<all-hosts>",
+           cc->car.atom->u ? cc->car.atom->u : "<all-users>",
+           cc->car.atom->d ? cc->car.atom->d : "<all-domains>");
+    }
+  }
+
+  c->f &= ~f_visit;
+}
+
+#endif
+
+/* --- @netg__dump@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps the netgroups table.
+ */
+
+static void netg__dump(void)
+{
+  sym_iter i;
+  netg__sym *sng;
+
+#ifdef TRACING
+  trace(TRACE_DEBUG, "debug: dumping netgroups file");
+  for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
+    trace(TRACE_DEBUG, "debug: netgroup `%s'...", sng->_base.name);
+    sng->cons->f &= ~f_visit;
+    netg__dumpGroup(sng->cons, 1);
+  }
+#endif
+}
+
+/* --- @netg_iterate@, @netg_iterate_r@ --- *
+ *
+ * Arguments:  @netg_iter *i@ = pointer to a netgroup iterator object
+ *
+ * Returns:    ---
+ *
+ * Use:                Starts iterating over the netgroups.
+ */
+
+void netg_iterate(void) { netg_iterate_r(&netg__iter); }
+void netg_iterate_r(netg_iter *i) { sym_createIter(i, &netg__table); }
+
+/* --- @netg_next@, @netg_next_r@ --- *
+ *
+ * Arguments:  @netg_iter *i@ = pointer to a netgroup iterator object
+ *
+ * Returns:    An opaque pointer to the next item, or null.
+ *
+ * Use:                Returns the next netgroup.
+ */
+
+netg *netg_next(void) { return (netg_next_r(&netg__iter)); }
+netg *netg_next_r(netg_iter *i) { return (sym_next(i)); }
+
+/* --- @netg_name@ --- *
+ *
+ * Arguments:  @netg *n@ = netgroup handle returned by @netg_next@.
+ *
+ * Returns:    A pointer to the name; you may not modify this string.
+ *
+ * Use:                Returns the name of a netgroup.
+ */
+
+const char *netg_name(netg *n) { return (n->_base.name); }
+
+/* --- @netg_scan@ --- *
+ *
+ * Arguments:  @netg *n@ = a netgroup handle returned by @netg_next@
+ *             @int (*proc)(netg *n, const char *host, const char *user,@
+ *                     @const char *domain, void *ctx)@ = function to call
+ *                             for each member.
+ *             @void *ctx@ = context pointer to pass to @proc@.
+ *
+ * Returns:    Zero if all went well, or the nonzero return value from
+ *             @proc@.
+ *
+ * Use:                Passes all the members of the netgroup to a given function.
+ *             The function is given the names, directly from the NIS
+ *             netgroup map, except that any empty entries are passed as
+ *             null pointers rather than empty strings.  You may not modify
+ *             any of the strings.  The enumeration function, @proc@, may
+ *             return nonzero to stop itself from being called any more;
+ *             if this happens, the value it returns becomes the result of
+ *             this function.  If all the items are enumerated OK, this
+ *             function returns zero.
+ */
+
+static int netg__doScan(netg__cons *c,
+                       netg *n,
+                       int (*proc)(netg */*n*/, const char */*host*/,
+                                   const char */*user*/,
+                                   const char */*domain*/, void */*ctx*/),
+                       void *ctx)
+{
+  int e;
+
+  while (c) {
+    if (c->f & f_cons)
+      e = netg__doScan(c->car.cons, n, proc, ctx);
+    else
+      e = proc(n, c->car.atom->h, c->car.atom->u, c->car.atom->d, ctx);
+    if (e)
+      return (e);
+    c = c->cdr;
+  }
+  return (0);
+}
+
+int netg_scan(netg *n,
+             int (*proc)(netg */*n*/, const char */*host*/,
+                         const char */*user*/, const char */*domain*/,
+                         void */*ctx*/),
+             void *ctx)
+{
+  return (netg__doScan(n->cons, n, proc, ctx));
+}
+
+/* --- @netg__breakCycle@ --- *
+ *
+ * Arguments:  @netg__cons *c@ = pointer to a list
+ *
+ * Returns:    ---
+ *
+ * Use:                Scans the given list (recursively) and breaks any cycles it
+ *             finds.
+ */
+
+static void netg__breakCycle(netg__cons *c)
+{
+  netg__cons *cc;
+
+  if (!c || c->f & f_uncycled)
+    return;
+
+  c->f |= f_visit;
+  for (cc = c; cc; cc = cc->cdr) {
+    if (~cc->f & f_cons)
+      continue;
+    if (cc->car.cons->f & f_visit) {
+      T( trace(TRACE_DEBUG, "debug: cycle in netgroups"); )
+      cc->car.cons = 0;
+    } else
+      netg__breakCycle(cc->car.cons);
+  }
+  c->f &= ~f_visit;
+  c->f |= f_uncycled;
+}
+
+/* --- @netg_init@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads the netgroup database and turns it into something nice.
+ */
+
+void netg_init(void)
+{
+  char *ypdom;
+
+  /* --- Initialise my symbol table --- */
+
+  sym_createTable(&netg__table);
+
+  /* --- Bind myself unto a YP server --- */
+
+  if (yp_get_default_domain(&ypdom) ||
+      yp_bind(ypdom))
+    return;
+  
+  /* --- Now try to read all the netgroup entries --- */
+
+  {
+    static struct ypall_callback ncb = { netg__foreach, 0 };
+    yp_all(ypdom, "netgroup", &ncb);
+  }
+
+  /* --- Unbind from the server --- */
+
+  yp_unbind(ypdom);
+
+  /* --- Dump the table --- */
+
+  IF_TRACING(TRACE_DEBUG, netg__dump(); )
+
+  /* --- Now resolve all the remaining references --- */
+
+  {
+    sym_iter i;
+    netg__sym *sng, *ng;
+    netg__cons *c;
+    netg__atom *a;
+
+    for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
+      for (c = sng->cons; c; c = c->cdr) {
+       if ((c->f & f_cons) == 0 && c->car.atom->n) {
+         a = c->car.atom;
+         ng = sym_find(&netg__table, a->n, -1, 0, 0);
+         if (!ng) {
+           moan("undefined netgroup `%s' (ignored)", a->n);
+           c->car.atom = 0;
+         } else {
+           c->car.cons = ng->cons;
+           c->f |= f_cons;
+         }
+         free(a->n);
+         free(a);
+       }
+      }
+    }
+  }
+
+  /* --- Remove cycles in the netgroups table --- */
+
+  {
+    sym_iter i;
+    netg__sym *sng;
+
+    for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
+      sng->cons->f &= ~f_uncycled;
+    for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; )
+      netg__breakCycle(sng->cons);
+  }    
+
+  /* --- Dump the table again --- */
+
+  IF_TRACING(TRACE_DEBUG, netg__dump(); )
+}
+
+/* --- @netg_reinit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Forces a re-read of the netgroups file.
+ */
+
+void netg_reinit(void)
+{
+  sym_iter i;
+  netg__sym *sng;
+  netg__cons *c, *cc;
+
+  /* --- Remove all the old netgroups rubbish --- */
+
+  for (sym_createIter(&i, &netg__table); (sng = sym_next(&i)) != 0; ) {
+    c = sng->cons;
+    while (c) {
+      cc = c->cdr;
+      if (~c->f & f_cons) {
+       if (c->car.atom->n) free(c->car.atom->n);
+       if (c->car.atom->h) free(c->car.atom->h);
+       if (c->car.atom->u) free(c->car.atom->u);
+       if (c->car.atom->d) free(c->car.atom->d);
+      }
+      free(c);
+      c = cc;
+    }
+    sym_remove(&netg__table, sng);
+  }
+
+  /* --- Now rebuild the world --- */
+
+  netg_init();
+}
+
+/*----- Test driver -------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+int scanner(netg *n, const char *h, const char *u, const char *d, void *c)
+{
+  fprintf(stderr, "  %s, %s, %s\n",
+         h ? h : "<any>", u ? u : "<any>", d ? d : "<any>");
+  return (0);
+}
+
+int main(void)
+{
+  netg *n;
+  ego("netg-test");
+  traceon(stderr, TRACE_ALL);
+  netg_init();
+  for (netg_iterate(); (n = netg_next()) != 0; ) {
+    fprintf(stderr, "netgroup %s\n", netg_name(n));
+    netg_scan(n, scanner, 0);
+  }
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/netg.h b/src/netg.h
new file mode 100644 (file)
index 0000000..964acb6
--- /dev/null
@@ -0,0 +1,148 @@
+/* -*-c-*-
+ *
+ * $Id: netg.h,v 1.1 1997/08/07 09:45:00 mdw Exp $
+ *
+ * A local database of netgroups
+ *
+ * (c) 1997 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of `become'
+ *
+ * `Become' is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * `Become' is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with `become'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+ /*----- Revision history --------------------------------------------------*
+ *
+ * $Log: netg.h,v $
+ * Revision 1.1  1997/08/07 09:45:00  mdw
+ * New source file added to maintain a netgroups database.
+ *
+ */
+
+#ifndef NETG_H
+#define NETG_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#ifndef SYM_H
+#  include "sym.h"
+#endif
+
+/*----- Type definitions --------------------------------------------------*/
+
+typedef sym_iter netg_iter;
+typedef struct netg__sym netg;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @netg_iterate@, @netg_iterate_r@ --- *
+ *
+ * Arguments:  @netg_iter *i@ = pointer to a netgroup iterator object
+ *
+ * Returns:    ---
+ *
+ * Use:                Starts iterating over the netgroups.
+ */
+
+extern void netg_iterate(void);
+extern void netg_iterate_r(netg_iter */*i*/);
+
+/* --- @netg_next@, @netg_next_r@ --- *
+ *
+ * Arguments:  @netg_iter *i@ = pointer to a netgroup iterator object
+ *
+ * Returns:    An opaque pointer to the next item, or null.
+ *
+ * Use:                Returns the next netgroup.
+ */
+
+extern netg *netg_next(void);
+extern netg *netg_next_r(netg_iter */*i*/);
+
+/* --- @netg_name@ --- *
+ *
+ * Arguments:  @netg *n@ = netgroup handle returned by @netg_next@.
+ *
+ * Returns:    A pointer to the name; you may not modify this string.
+ *
+ * Use:                Returns the name of a netgroup.
+ */
+
+extern const char *netg_name(netg */*n*/);
+
+/* --- @netg_scan@ --- *
+ *
+ * Arguments:  @netg *n@ = a netgroup handle returned by @netg_next@
+ *             @int (*proc)(netg *n, const char *host, const char *user,@
+ *                     @const char *domain, void *ctx)@ = function to call
+ *                             for each member.
+ *             @void *ctx@ = context pointer to pass to @proc@.
+ *
+ * Returns:    Zero if all went well, or the nonzero return value from
+ *             @proc@.
+ *
+ * Use:                Passes all the members of the netgroup to a given function.
+ *             The function is given the names, directly from the NIS
+ *             netgroup map, except that any empty entries are passed as
+ *             null pointers rather than empty strings.  You may not modify
+ *             any of the strings.  The enumeration function, @proc@, may
+ *             return nonzero to stop itself from being called any more;
+ *             if this happens, the value it returns becomes the result of
+ *             this function.  If all the items are enumerated OK, this
+ *             function returns zero.
+ */
+
+extern int netg_scan(netg */*n*/,
+                    int (*/*proc*/)(netg */*n*/, const char */*host*/,
+                                    const char */*user*/,
+                                    const char */*domain*/, void */*ctx*/),
+                    void */*ctx*/);
+
+/* --- @netg_init@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads the netgroup database and turns it into something nice.
+ */
+
+extern void netg_init(void);
+
+/* --- @netg_reinit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Forces a re-read of the netgroups file.
+ */
+
+extern void netg_reinit(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif