Reload keys and config files automatically.
[become] / src / daemon.c
index 3f1fa93..fbaf26b 100644 (file)
@@ -1,10 +1,10 @@
 /* -*-c-*-
  *
- * $Id: daemon.c,v 1.2 1997/08/04 10:24:21 mdw Exp $
+ * $Id: daemon.c,v 1.14 2003/10/17 16:30:22 mdw Exp $
  *
  * Running a `become' daemon
  *
- * (c) 1997 EBI
+ * (c) 1998 EBI
  */
 
 /*----- Licensing notice --------------------------------------------------*
 /*----- Revision history --------------------------------------------------*
  *
  * $Log: daemon.c,v $
+ * Revision 1.14  2003/10/17 16:30:22  mdw
+ * Reload keys and config files automatically.
+ *
+ * Revision 1.13  2003/10/12 10:00:06  mdw
+ * Fix for daemon mode.  Oops.
+ *
+ * Revision 1.12  2003/10/12 00:14:55  mdw
+ * Major overhaul.  Now uses DSA signatures rather than the bogus symmetric
+ * encrypt-and-hope thing.  Integrated with mLib and Catacomb.
+ *
+ * Revision 1.11  1999/05/04 16:17:12  mdw
+ * Change to header file name for parser.  See log for `parse.h' for
+ * details.
+ *
+ * Revision 1.10  1998/04/23  13:23:09  mdw
+ * Support new interface to configuration file parser.
+ *
+ * Revision 1.9  1998/01/12 16:45:59  mdw
+ * Fix copyright date.
+ *
+ * Revision 1.8  1997/09/26 09:14:58  mdw
+ * Merged blowfish branch into trunk.
+ *
+ * Revision 1.7.2.1  1997/09/26 09:08:05  mdw
+ * Use the Blowfish encryption algorithm instead of IDEA.  This is partly
+ * because I prefer Blowfish (without any particularly strong evidence) but
+ * mainly because IDEA is patented and Blowfish isn't.
+ *
+ * Revision 1.7  1997/09/17 10:23:23  mdw
+ * Fix a typo.  Port numbers are in network order now, so don't change them.
+ *
+ * Revision 1.6  1997/09/09 18:17:06  mdw
+ * Allow default port to be given as a service name or port number.
+ *
+ * Revision 1.5  1997/08/20  16:17:10  mdw
+ * More sensible restart routine: `_reinit' functions replaced by `_end' and
+ * `_init' functions.
+ *
+ * Revision 1.4  1997/08/07 10:00:37  mdw
+ * (Log entry for previous version is bogus.)  Read netgroups database.
+ * Give up privileges permanently on startup.
+ *
  * Revision 1.2  1997/08/04 10:24:21  mdw
  * Sources placed under CVS control.
  *
@@ -43,7 +85,6 @@
 
 #include <errno.h>
 #include <signal.h>
-#include <setjmp.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
 
+/* --- mLib headers --- */
+
+#include <mLib/fwatch.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sel.h>
+#include <mLib/sig.h>
+#include <mLib/sym.h>
+#include <mLib/trace.h>
+
+/* --- Catacomb headers --- */
+
+#include <catacomb/buf.h>
+#include <catacomb/dsa.h>
+#include <catacomb/key.h>
+#include <catacomb/mp.h>
+#include <catacomb/noise.h>
+#include <catacomb/paranoia.h>
+#include <catacomb/rand.h>
+#include <catacomb/sha.h>
+
 /* --- Local headers --- */
 
 #include "become.h"
 #include "config.h"
-#include "crypt.h"
 #include "daemon.h"
-#include "idea.h"
 #include "lexer.h"
 #include "name.h"
-#include "parser.h"
+#include "netg.h"
+#include "parse.h"
 #include "rule.h"
-#include "tx.h"
 #include "userdb.h"
-#include "utils.h"
 
 /*----- Arbitrary constants -----------------------------------------------*/
 
-#define daemon__awakeEvery (5 * 60)    /* Awaken this often to rescan */
+/* #define daemon__awakeEvery (5 * 60) /\* Awaken this often to rescan *\/ */
+#define daemon__awakeEvery (10)                /* Awaken this often to rescan */
 
 /*----- Static variables --------------------------------------------------*/
 
-static int daemon__running = 0;                /* Am I running as a daemon? */
 static int daemon__port = -1;          /* No particular port yet */
-static volatile sig_atomic_t daemon__rescan = 0; /* Rescan as soon as poss */
-#define daemon__signum daemon__rescan  /* Alias for readbility */
-static int daemon__readKey = 0;                /* Have I read a key? */
-static unsigned char daemon__key[IDEA_KEYSIZE];        /* encryption key */
-static jmp_buf daemon__dieBuf;         /* Jump here to kill the daemon */
+static fwatch daemon__cwatch, daemon__kwatch; /* Watching key / config files */
+static sel_timer daemon__timer;                /* Timer for reading */
+static sel_state daemon__sel;          /* Select context */
+static sel_file daemon__listen;                /* Listening socket selector */
+static const char *daemon__config;     /* Configuration file for daemon */
+static const char *daemon__keyfile;    /* Keyring file for daemon */
+static dsa_priv daemon__key;           /* The key data */
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -107,30 +168,64 @@ void daemon_usePort(int port)
   daemon__port = port;
 }
 
+/* --- @daemon__moan@ --- *
+ *
+ * Arguments:  @const char *f@ = offending file name
+ *             @int line@ = offending line of the file
+ *             @const char *msg@ = message
+ *             @void *p@ = ignored
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports an error message about a key file.
+ */
+
+static void daemon__moan(const char *f, int line, const char *msg, void *p)
+{
+  syslog(LOG_ERR, "key file error: %s: %d: %s", f, line, msg);
+  T( trace(TRACE_DAEMON, "daemon: key file error: %s: %d: %s",
+          f, line, msg); )
+}
 /* --- @daemon_readKey@ --- *
  *
- * Arguments:  @const char *kf@ = name of file containing key
+ * Arguments:  @const char *kf@ = pointer to key file name to use
  *
  * Returns:    ---
  *
- * Use:                Instructs the daemon to read the named key file.
+ * Use:                Loads the private key from the key file.
  */
 
 void daemon_readKey(const char *kf)
 {
-  FILE *fp;
+  key_packstruct kps[DSA_PRIVFETCHSZ];
+  key_packdef *kp;
+  key_file f;
+  key *k;
+  int err;
 
-  if (!daemon__running)
+  if (daemon__keyfile)
     return;
-
-  if ((fp = fopen(kf, "r")) == 0) {
-    syslog(LOG_WARNING, "couldn't read key file: %e");
+  T( trace(TRACE_DAEMON, "daemon: reading key from `%s'", kf); )
+  if (key_open(&f, kf, KOPEN_READ, daemon__moan, 0))
     return;
+  kp = key_fetchinit(dsa_privfetch, kps, &daemon__key);
+  if ((k = key_bytype(&f, "become-dsa")) == 0)
+    err = KERR_NOTFOUND;
+  else
+    err = key_fetch(kp, k);
+  if (err)
+    syslog(LOG_ERR, "couldn't load key: %s", key_strerror(err));
+  else {
+    mp_copy(daemon__key.dp.p);
+    mp_copy(daemon__key.dp.q);
+    mp_copy(daemon__key.dp.g);
+    mp_copy(daemon__key.x);
+    mp_copy(daemon__key.y);
   }
-  tx_getBits(daemon__key, 128, fp);
-  fclose(fp);
-  daemon__readKey = 1;
-  return;
+  key_fetchdone(kp);
+  key_close(&f);
+  daemon__keyfile = kf;
 }
 
 /* --- @daemon__readConfig@ --- *
@@ -146,139 +241,258 @@ static int daemon__readConfig(const char *cf)
 {
   FILE *fp;
 
-  daemon__readKey = 0;
+  daemon__keyfile = 0;
   if ((fp = fopen(cf, "r")) == 0)
     return (-1);
   lexer_scan(fp);
-  yyparse();
+  parse();
   fclose(fp);
-  if (!daemon__readKey)
+  if (!daemon__keyfile)
     daemon_readKey(file_KEY);
-  daemon__rescan = 0;
   T( trace(TRACE_DAEMON, "daemon: read config file"); )
   return (0);
 }
 
-/* --- @daemon__restart@ --- *
- *
- * Arguments:  @int sig@ = the signal number
- *
- * Returns:    ---
- *
- * Use:                Handles signals.  Causes the configuration file to be reread.
- */
-
-static void daemon__restart(int sig)
-{
-  daemon__rescan = 1;
-  signal(sig, daemon__restart);
-}
-
-/* --- @daemon__die@ --- *
- *
- * Arguments:  @int sig@ = the signal number
- *
- * Returns:    via @longjmp@
- *
- * Use:                Handles other signals.  Causes the daemon to die.
- */
-
-static void daemon__die(int sig)
-{
-  daemon__signum = sig;
-  longjmp(daemon__dieBuf, 1);
-}
-
 /* --- @daemon__read@ --- *
  *
  * Arguments:  @int fd@ = socket handle
+ *             @unsigned mode@ = ignored
+ *             @void *p@ = ignored
  *
  * Returns:    ---
  *
  * Use:                Examines a buffer, and returns a response.
  */
 
-void daemon__read(int fd)
+void daemon__read(int fd, unsigned mode, void *p)
 {
   unsigned char buff[65536];           /* Buffer for incoming packets */
-  unsigned char rpl[crp_size];         /* Buffer for outgoing replies */
   struct sockaddr_in sin;              /* Address of packet sender */
   char sender[64];                     /* Sender's hostname (resolved) */
-  unsigned char sk[IDEA_KEYSIZE];      /* Session key for reply */
+  octet h[SHA_HASHSZ];                 /* Hash of the transmission buffer */
+  sha_ctx hc;                          /* Hashing context */
   request rq;                          /* Request buffer for verification */
+  ssize_t sz;                          /* Length of incoming message */
+  socklen_t slen;                      /* Length of incoming address */
+  uint32 u;                            /* Scratch integer */
+  uint16 ul;                           /* And another */
+  struct hostent *he;                  /* Resolve structure */
+  mp *m, *k, *r, *s;                   /* Integers for signing */
+  int ans;                             /* Answer from the check */
+  buf b;                               /* Buffer for parsing request */
 
-  /* --- Read the message --- */
+  /* --- Kick some randomness in the pot --- */
 
-  {
-    int slen = sizeof(sin);
+  noise_timer(RAND_GLOBAL);
 
-    if (recvfrom(fd, (char *)buff, sizeof(buff), 0,
-                (struct sockaddr *)&sin, &slen) < 0) {
-      T( trace(TRACE_DAEMON, "daemon: error reading packet: %s",
-              strerror(errno)); )
-      syslog(LOG_INFO, "duff packet received: %e");
-      return;
-    }
+  /* --- Read the message --- */
+
+  slen = sizeof(sin);
+  if ((sz = recvfrom(fd, (char *)buff, sizeof(buff), 0,
+                    (struct sockaddr *)&sin, &slen)) < 0) {
+    T( trace(TRACE_DAEMON, "daemon: error reading packet: %s",
+            strerror(errno)); )
+    syslog(LOG_INFO, "duff packet received: %e");
+    return;
   }
 
   /* --- Resolve the host name --- */
 
-  {
-    struct hostent *he = gethostbyaddr((char *)&sin.sin_addr,
-                                      sizeof(sin.sin_addr),
-                                      AF_INET);
-    sender[0] = 0;
-    strncat(sender,
-           he ? he->h_name : inet_ntoa(sin.sin_addr),
-           sizeof(sender));
-    syslog(LOG_DEBUG, "packet received from %s", sender);
-    T( trace(TRACE_DAEMON, "daemon: received request from %s", sender); )
-  }
+  he = gethostbyaddr((char *)&sin.sin_addr, sizeof(sin.sin_addr), AF_INET);
+  sender[0] = 0;
+  strncat(sender, he ? he->h_name : inet_ntoa(sin.sin_addr),
+         sizeof(sender) - 1);
+  syslog(LOG_DEBUG, "packet received from %s", sender);
+  T( trace(TRACE_DAEMON, "daemon: received request from %s", sender); )
 
-  /* --- Unpack the block --- */
+  /* --- Sanity check --- */
 
-  if (crypt_unpackRequest(&rq, buff, daemon__key, sk, rpl) == 0) {
-    burn(buff);
-    T( trace(TRACE_DAEMON, "daemon: received corrupt or invalid request"); )
-    syslog(LOG_INFO, "packet from %s rejected", sender);
+  if (!daemon__keyfile) {
+    syslog(LOG_NOTICE, "no key file: ignoring request");
+    T( trace(TRACE_DAEMON, "daemon: no key file: ignoring request"); )
     return;
   }
-  burn(buff);
 
-  /* --- Fill in the sender's address in the request block --- */
+  /* --- Unpack the block --- */
 
   rq.host = sin.sin_addr;
+  buf_init(&b, buff, sz);
+  if (buf_ensure(&b, SHA_HASHSZ)) goto fail;  BSTEP(&b, SHA_HASHSZ);
+  if (buf_getu32(&b, &u)) goto fail;  rq.from = u;
+  if (buf_getu32(&b, &u)) goto fail;  rq.to = u;
+  if (buf_getu16(&b, &ul) || buf_ensure(&b, ul) || ul >= sizeof(rq.cmd))
+    goto fail;
+  memcpy(rq.cmd, BCUR(&b), ul);
+  rq.cmd[ul] = 0;
+  BSTEP(&b, ul);
+  if (BLEFT(&b)) goto fail;
+
+  /* --- Hash the request block --- */
+
+  sha_init(&hc);
+  sha_hash(&hc, buff, sz);
+  sha_done(&hc, h);
 
   /* --- Build a reply block --- */
 
-  {
-    int answer = rule_check(&rq);
-    syslog(LOG_INFO, "request from %s for %i to become %i to run %s %s",
-          sender, rq.from, rq.to, rq.cmd, answer ? "granted" : "denied");
-    crypt_packReply(rpl, sk, answer);
-    burn(sk);
-  }
+  ans = rule_check(&rq);
+  syslog(LOG_INFO, "request from %s for %i to become %i to run %s %s",
+        sender, rq.from, rq.to, rq.cmd, ans ? "granted" : "denied");
+  buf_init(&b, buff, sizeof(buff));
+  if (buf_put(&b, h, sizeof(h)) || buf_putbyte(&b, ans))
+    goto fail;
+
+  /* --- Sign the reply block --- */
+
+  sha_init(&hc);
+  sha_hash(&hc, BBASE(&b), BLEN(&b));
+  sha_done(&hc, h);
+  m = mp_loadb(MP_NEW, h, sizeof(h));
+  rand_get(RAND_GLOBAL, h, sizeof(h));
+  k = mp_loadb(MP_NEWSEC, h, sizeof(h));
+  r = s = MP_NEW;
+  dsa_mksig(&daemon__key.dp, daemon__key.x, m, k, &r, &s);
+  buf_putmp(&b, r);
+  buf_putmp(&b, s);
+  mp_drop(m);
+  mp_drop(k);
+  mp_drop(r);
+  mp_drop(s);
+  if (BBAD(&b))
+    goto fail;
 
   /* --- Send the reply off --- */
 
-  sendto(fd, (char *)rpl, crp_size, 0, (struct sockaddr *)&sin, sizeof(sin));
+  sendto(fd, BBASE(&b), BLEN(&b), 0, (struct sockaddr *)&sin, sizeof(sin));
   T( trace(TRACE_DAEMON, "daemon: reply sent"); )
-  burn(rpl);
+  return;
+
+fail:
+  syslog(LOG_ERR, "couldn't respond to query");
+  T( trace(TRACE_DAEMON, "daemon: failed to answer query"); )  
+}
+
+/* --- @daemon__die@ --- *
+ *
+ * Arguments:  @int n@ = signal number
+ *             @void *p@ = ignored
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Exits the daemon.
+ */
+
+static void daemon__die(int n, void *p)
+{
+  T( trace(TRACE_DAEMON, "daemon: killed by signal %i", n); )
+  syslog(LOG_NOTICE, "killed by signal type %i", n);
+  remove(file_PID);
+  exit(0);
+}
+
+/* --- @daemon__setTimer@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the interval timer up.
+ */
+
+static void daemon__wakeUp(struct timeval *tv, void *p);
+
+static void daemon__setTimer(void)
+{
+  struct timeval tv;
+
+  gettimeofday(&tv, 0);
+  tv.tv_sec += daemon__awakeEvery;
+  sel_addtimer(&daemon__sel, &daemon__timer, &tv, daemon__wakeUp, 0);
+}
+
+/* --- @daemon__rescan@ --- *
+ *
+ * Arguments:  @int n@ = signal number
+ *             @void *p@ = ignored
+ *
+ * Returns:    ---
+ *
+ * Use:                Forces a rescan of the daemon's configuration.
+ */
+
+static void daemon__rescan(int n, void *p)
+{
+  syslog(LOG_INFO, "rescanning configuration file");
+  name_end();
+  rule_end();
+  netg_end();
+  userdb_end();
+  dsa_privfree(&daemon__key);
+  userdb_init();
+  userdb_local();
+  userdb_yp();
+  netg_init();
+  rule_init();
+  name_init();
+  if (daemon__readConfig(daemon__config))
+    syslog(LOG_ERR, "error reading configuration file");
+  sel_rmtimer(&daemon__timer);
+  daemon__setTimer();
+  fwatch_update(&daemon__cwatch, daemon__config);
+  fwatch_update(&daemon__kwatch, daemon__keyfile);
+}
+
+/* --- @daemon__wakeUp@ --- *
+ *
+ * Arguments:  @struct timeval *tv@ = ignored
+ *             @void *p@ = ignored
+ *
+ * Returns:    ---
+ *
+ * Use:                Wakes up periodically to check the configuration file.
+ */
+
+static void daemon__wakeUp(struct timeval *tv, void *p)
+{
+  T( trace(TRACE_DAEMON, "daemon: interval timer"); )
+  rand_seed(RAND_GLOBAL, 160);
+  daemon__setTimer();
+  if (fwatch_update(&daemon__cwatch, daemon__config))
+    daemon__rescan(0, 0);
+  else if (fwatch_update(&daemon__kwatch, daemon__keyfile)) {
+    const char *kf = daemon__keyfile;
+    daemon__keyfile = 0;
+    daemon_readKey(kf);
+  }
 }
 
 /* --- @daemon_init@ --- *
  *
  * Arguments:  @const char *cf@ = pointer to name of configuration file
  *             @int port@ = port to listen to, or %$-1$% for default
+ *             @unsigned f@ = various flags
  *
  * Returns:    Never.
  *
  * Use:                Starts `become' up in daemon mode.
  */
 
-void daemon_init(const char *cf, int port)
+void daemon_init(const char *cf, int port, unsigned f)
 {
   int s;
+  int i;
+
+  static struct sigvec {
+    int sig;
+    void (*proc)(int n, void *p);
+    sig s;
+  } sigs[] = {
+    { SIGHUP, daemon__rescan },
+    { SIGINT, daemon__die },
+    { SIGTERM, daemon__die },
+    { SIGQUIT, daemon__die },
+    { 0, 0 }
+  };
 
   /* --- Remove my root privileges --- *
    *
@@ -286,22 +500,32 @@ void daemon_init(const char *cf, int port)
    * user wants me to start on a funny port.
    */
 
-  seteuid(getuid());
+  setuid(getuid());
+
+  /* --- Initialize the random number generator --- */
+
+  rand_noisesrc(RAND_GLOBAL, &noise_source);
+  rand_seed(RAND_GLOBAL, 160);
 
   /* --- Initialise bits of the program --- */
 
-  daemon__running = 1;
+  daemon__config = cf;
   daemon__port = port;
+  sel_init(&daemon__sel);
+  sig_init(&daemon__sel);
   userdb_init();
   userdb_local();
   userdb_yp();
+  netg_init();
   name_init();
   rule_init();
   openlog(quis(), 0, LOG_DAEMON);
   syslog(LOG_NOTICE, "starting up");
 
-  if (daemon__readConfig(cf))
-    die("couldn't read configuration file");
+  if (daemon__readConfig(daemon__config))
+    die(1, "couldn't read configuration file");
+  fwatch_init(&daemon__cwatch, daemon__config);
+  fwatch_init(&daemon__kwatch, daemon__keyfile);
 
   /* --- Decide on a port to use --- *
    *
@@ -309,11 +533,11 @@ void daemon_init(const char *cf, int port)
    * look it up in /etc/services under whatever name I was started as.
    */
 
-  if (daemon__port <= 0) {
+  if (daemon__port == 0) {
     struct servent *se = getservbyname(quis(), "udp");
     if (!se)
-      die("no idea which port to use");
-    daemon__port = ntohs(se->s_port);
+      die(1, "no idea which port to listen to");
+    daemon__port = se->s_port;
   }
 
   /* --- Now set up a socket --- */
@@ -322,25 +546,26 @@ void daemon_init(const char *cf, int port)
     struct sockaddr_in sin;
 
     if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
-      die("couldn't create socket: %s", strerror(errno));
+      die(1, "couldn't create socket: %s", strerror(errno));
     sin.sin_family = AF_INET;
-    sin.sin_port = htons(daemon__port);
+    sin.sin_port = daemon__port;
     sin.sin_addr.s_addr = htonl(INADDR_ANY);
-    if (bind(s, (struct sockaddr *)&sin, sizeof(sin)))
-      die("couldn't bind socket to port: %s", strerror(errno));
+    if (bind(s, (struct sockaddr *)&sin, sizeof(sin))) {
+      die(1, "couldn't bind socket to port %i: %s",
+         ntohs(daemon__port), strerror(errno));
+    }
   }
 
   /* --- Fork off into the sunset --- */
 
-#ifdef NDEBUG
-  {
+  if (!(f & df_nofork)) {
     int pid = fork();
     FILE *fp;
 
     /* --- Make a background process --- */
 
     if (pid == -1)
-      die("couldn't fork daemon: %s", strerror(errno));
+      die(1, "couldn't fork daemon: %s", strerror(errno));
     else if (pid != 0)
       return;
 
@@ -356,84 +581,30 @@ void daemon_init(const char *cf, int port)
     }
     T( trace(TRACE_DAEMON, "daemon: forked to pid %li", (long)getpid()); )
   }
-#endif
-
-  /* --- Program in daemon death mode --- */
-
-  if (setjmp(daemon__dieBuf)) {
-#ifdef TRACING
-    if (daemon__signum == SIGQUIT && tracing() & TRACE_RULE) {
-      T( rule_dump(); )
-      signal(SIGQUIT, daemon__die);
-    } else
-#endif
-    {
-      T( trace(TRACE_DAEMON, "daemon: killed by signal %i",
-              daemon__signum); )
-      syslog(LOG_NOTICE, "killed by signal type %i", daemon__signum);
-      remove(file_PID);
-      exit(0);
-    }
-  } else {
-
-    /* --- Set signal handlers --- */
-
-    signal(SIGHUP, daemon__restart);
-    signal(SIGQUIT, daemon__die);
-    signal(SIGINT, daemon__die);
-    signal(SIGTERM, daemon__die);
-    signal(SIGSEGV, daemon__die);
-    signal(SIGFPE, daemon__die);
-    signal(SIGBUS, daemon__die);
-  }
-
-  /* --- Now wait for something exciting to happen --- *
-   *
-   * Actually, every so often (5 minutes, perhaps) I need to wake up and
-   * rescan the users to see whether they've changed.  Time to play with
-   * @select@.
-   */
-
-  {
-    time_t when;
-
-    /* --- Find when I am, and thus when I need to be awoken again --- */
-
-    when = time(0) + daemon__awakeEvery;
-
-    for (;;) {
-      fd_set fds;
-      int i;
 
-      /* --- Set up the file descriptor tables --- */
+  /* --- Set signal handlers --- */
 
-      FD_ZERO(&fds);
-      FD_SET(s, &fds);
+  for (i = 0; sigs[i].proc; i++)
+    sig_add(&sigs[i].s, sigs[i].sig, sigs[i].proc, 0);
 
-      /* --- Now wait for something interesting --- */
+  /* --- Set the timer for rescanning the file --- */
 
-      T( trace(TRACE_DAEMON, "daemon: waiting for requests"); )
-      i = select(FD_SETSIZE, &fds, 0, 0, 0);
+  daemon__setTimer();
 
-      /* --- Now, see if I need to rescan the config --- */
+  /* --- Watch for input --- */
 
-      if (daemon__rescan || time(0) - when > 0) {
-       daemon__rescan = 0;
-       syslog(LOG_INFO, "rescanning configuration file");
-       userdb_reinit();
-       userdb_local();
-       userdb_yp();
-       rule_reinit();
-       name_reinit();
-       if (daemon__readConfig(cf))
-         syslog(LOG_ERR, "error reading configuration file");
-       when = time(0) + daemon__awakeEvery;
-      }
+  sel_initfile(&daemon__sel, &daemon__listen, s, SEL_READ,
+              daemon__read, 0);
+  sel_addfile(&daemon__listen);
 
-      /* --- Read the data from the request --- */
+  /* --- Now wait for something exciting to happen --- */
 
-      if (i > 0)
-       daemon__read(s);
+  for (;;) {
+    if (sel_select(&daemon__sel)) {
+      if (errno == EINTR || errno == EAGAIN)
+       continue;
+      syslog(LOG_ERR, "error from select: %s", strerror(errno));
+      exit(1);
     }
   }
 }