math/mpreduce.h: Missing include files.
[u/mdw/catacomb] / progs / pixie.c
index 4a5e9ab..4380703 100644 (file)
@@ -83,10 +83,11 @@ static unsigned flags = 0;
 
 #define F_SYSLOG 1u
 #define F_FETCH 2u
+#define F_REPLACE 4u
 
 /*----- Event logging -----------------------------------------------------*/
 
-/* --- @log@ --- *
+/* --- @pxlog@ --- *
  *
  * Arguments:  @const char *p@ = @printf@-style format string
  *             @...@ = extra arguments to fill in
@@ -96,7 +97,7 @@ static unsigned flags = 0;
  * Use:                Writes out a timestamped log message.
  */
 
-static void log(const char *p, ...)
+static void PRINTF_LIKE(1, 2) pxlog(const char *p, ...)
 {
   dstr d = DSTR_INIT;
   va_list ap;
@@ -125,8 +126,7 @@ static void log(const char *p, ...)
 /* --- Data structures --- */
 
 typedef struct phrase {
-  struct phrase *next;
-  struct phrase *prev;
+  struct phrase *next, *prev;
   char *tag;
   char *p;
   unsigned long t;
@@ -136,8 +136,21 @@ typedef struct phrase {
 
 /* --- Variables --- */
 
-#define P_ROOT ((phrase *)&p_root)
-static struct { phrase *next; phrase *prev; } p_root = { P_ROOT, P_ROOT };
+static phrase *p_head = 0, *p_tail = 0;
+
+/* --- Utility macros --- */
+
+#define P_LINKTAIL(p) do {                                             \
+  (p)->next = 0;                                                       \
+  (p)->prev = p_tail;                                                  \
+  *(p_tail ? &p_tail->next : &p_head) = (p);                           \
+  p_tail = (p);                                                                \
+} while (0)
+
+#define P_UNLINK(p) do {                                               \
+  *((p)->next ? &(p)->next->prev : &p_tail) = (p)->prev;               \
+  *((p)->prev ? &(p)->prev->next : &p_head) = (p)->next;               \
+} while (0)
 
 /* --- @p_free@ --- *
  *
@@ -154,8 +167,7 @@ static void p_free(phrase *p)
     sel_rmtimer(&p->timer);
   xfree(p->tag);
   l_free(&lm, p->p);
-  p->next->prev = p->prev;
-  p->prev->next = p->next;
+  P_UNLINK(p);
   DESTROY(p);
 }
 
@@ -173,7 +185,7 @@ static void p_timer(struct timeval *tv, void *p)
 {
   phrase *pp = p;
   if (verbose)
-    log("expiring passphrase `%s'", pp->tag);
+    pxlog("expiring passphrase `%s'", pp->tag);
   p_free(pp);
 }
 
@@ -193,13 +205,13 @@ static void *p_alloc(size_t sz)
     char *p;
     if ((p = l_alloc(&lm, sz)) != 0)
       return (p);
-    if (P_ROOT->next == P_ROOT)
+    if (!p_head)
       return (0);
     if (verbose) {
-      log("flushing passphrase `%s' to free up needed space",
-         P_ROOT->next->tag);
+      pxlog("flushing passphrase `%s' to free up needed space",
+           p_head->tag);
     }
-    p_free(P_ROOT->next);
+    p_free(p_head);
   }
 }
 
@@ -216,7 +228,7 @@ static phrase *p_find(const char *tag)
 {
   phrase *p;
 
-  for (p = P_ROOT->next; p != P_ROOT; p = p->next) {
+  for (p = p_head; p; p = p->next) {
     if (strcmp(p->tag, tag) == 0) {
       if (p->t) {
        struct timeval tv;
@@ -225,12 +237,8 @@ static phrase *p_find(const char *tag)
        tv.tv_sec += p->t;
        sel_addtimer(&sel, &p->timer, &tv, p_timer, p);
       }
-      p->next->prev = p->prev;
-      p->prev->next = p->next;
-      p->next = P_ROOT;
-      p->prev = P_ROOT->prev;
-      P_ROOT->prev->next = p;
-      P_ROOT->prev = p;
+      P_UNLINK(p);
+      P_LINKTAIL(p);
       return (p);
     }
   }
@@ -279,10 +287,7 @@ static phrase *p_add(const char *tag, const char *p, unsigned long t)
 
   /* --- Link the block into the chain --- */
 
-  pp->next = P_ROOT;
-  pp->prev = P_ROOT->prev;
-  P_ROOT->prev->next = pp;
-  P_ROOT->prev = pp;
+  P_LINKTAIL(pp);
   return (pp);
 }
 
@@ -300,15 +305,15 @@ static void p_flush(const char *tag)
   phrase *p;
 
   if (!tag && verbose > 1)
-    log("flushing all passphrases");
-  p = P_ROOT->next;
-  while (p != P_ROOT) {
+    pxlog("flushing all passphrases");
+  p = p_head;
+  while (p) {
     phrase *pp = p->next;
     if (!tag)
       p_free(p);
     else if (strcmp(p->tag, tag) == 0) {
       if (verbose > 1)
-       log("flushing passphrase `%s'", tag);
+       pxlog("flushing passphrase `%s'", tag);
       p_free(p);
       break;
     }
@@ -477,7 +482,7 @@ static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
   /* --- Write a log message --- */
 
   if (verbose > 1)
-    log("passphrase `%s' requested", tag);
+    pxlog("passphrase `%s' requested", tag);
 
   /* --- If there is no fetcher, life is simpler --- */
 
@@ -514,7 +519,7 @@ static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
       goto fail;
     if (strcmp(pp, p->p) != 0) {
       if (verbose)
-       log("passphrases for `%s' don't match", tag);
+       pxlog("passphrases for `%s' don't match", tag);
       p_free(p);
       goto fail;
     }
@@ -583,21 +588,27 @@ static void pixserv_expire(struct timeval *tv, void *p)
  *             @const char *p@ = pointer to skeleton string
  *             @...@ = other arguments to fill in
  *
- * Returns:    ---
+ * Returns:    Zero on success, @-1@ on error.
  *
  * Use:                Formats a string and emits it to the output file.
  */
 
-static void pixserv_write(pixserv *px, const char *p, ...)
+static int PRINTF_LIKE(2, 3) pixserv_write(pixserv *px, const char *p, ...)
 {
   dstr d = DSTR_INIT;
   va_list ap;
+  int rc;
 
   va_start(ap, p);
   dstr_vputf(&d, p, &ap);
-  write(px->fd, d.buf, d.len);
+  rc = write(px->fd, d.buf, d.len);
   va_end(ap);
   dstr_destroy(&d);
+  if (rc < 0) {
+    pxlog("failed to write to client: %s (closing)", strerror(errno));
+    return (-1);
+  }
+  return (0);
 }
 
 /* --- @pixserv_timeout@ --- *
@@ -650,13 +661,7 @@ static void pixserv_line(char *s, size_t len, void *p)
 
   if (!(px->f & px_stdin))
     sel_rmtimer(&px->timer);
-  if (!s) {
-    if (px->fd != px->b.reader.fd)
-      close(px->fd);
-    selbuf_destroy(&px->b);
-    close(px->b.reader.fd);
-    return;
-  }
+  if (!s) goto close;
 
   /* --- Fiddle the timeout --- */
 
@@ -677,17 +682,18 @@ static void pixserv_line(char *s, size_t len, void *p)
   /* --- Handle a help request --- */
 
   if (strcmp(q, "help") == 0) {
-    pixserv_write(px, "\
+    if (pixserv_write(px, "\
 INFO Commands supported:\n\
-INFO HELP\n\
-INFO LIST\n\
-INFO PASS tag [expire]\n\
-INFO VERIFY tag [expire]\n\
-INFO FLUSH [tag]\n\
-INFO SET tag [expire] -- phrase\n\
-INFO QUIT\n\
+INFO flush [TAG]\n\
+INFO help\n\
+INFO list\n\
+INFO pass TAG [EXPIRE]\n\
+INFO quit\n\
+INFO set TAG [EXPIRE] -- PHRASE\n\
+INFO verify TAG [EXPIRE]\n\
 OK\n\
-");
+"))
+      goto close;
   }
 
   /* --- List the passphrases --- */
@@ -695,17 +701,19 @@ OK\n\
   else if (strcmp(q, "list") == 0) {
     phrase *p;
 
-    for (p = P_ROOT->next; p != P_ROOT; p = p->next) {
-      if (!p->t)
-       pixserv_write(px, "ITEM %s no-expire\n", p->tag);
-      else {
+    for (p = p_head; p; p = p->next) {
+      if (!p->t) {
+       if (pixserv_write(px, "ITEM %s no-expire\n", p->tag)) goto close;
+      else {
        struct timeval tv;
        gettimeofday(&tv, 0);
        TV_SUB(&tv, &p->timer.tv, &tv);
-       pixserv_write(px, "ITEM %s %i\n", p->tag, tv.tv_sec);
+       if (pixserv_write(px, "ITEM %s %lu\n",
+                         p->tag, (unsigned long)tv.tv_sec))
+         goto close;
       }
     }
-    pixserv_write(px, "OK\n");
+    if (pixserv_write(px, "OK\n")) goto close;
   }
 
   /* --- Request a passphrase --- */
@@ -724,13 +732,14 @@ OK\n\
       rc = p_get(&p, q, mode, t > timeout ? timeout : t);
       switch (rc) {
        case 0:
-         pixserv_write(px, "OK %s\n", p);
+         if (pixserv_write(px, "OK %s\n", p)) goto close;
          break;
        case -1:
-         pixserv_write(px, "FAIL error reading passphrase\n");
+         if (pixserv_write(px, "FAIL error reading passphrase\n"))
+           goto close;
          break;
        case +1:
-         pixserv_write(px, "MISSING\n");
+         if (pixserv_write(px, "MISSING\n")) goto close;
          break;
       }
     }
@@ -741,7 +750,7 @@ OK\n\
   else if (strcmp(q, "flush") == 0) {
     q = str_getword(&s);
     p_flush(q);
-    pixserv_write(px, "OK\n");
+    if (pixserv_write(px, "OK\n")) goto close;
   }
 
   /* --- Set a passphrase --- */
@@ -749,24 +758,25 @@ OK\n\
   else if (strcmp(q, "set") == 0) {
     char *tag;
     unsigned long t;
-    if ((tag = str_getword(&s)) == 0)
-      pixserv_write(px, "FAIL missing tag\n");
-    else if ((q = str_getword(&s)) == 0)
-      pixserv_write(px, "FAIL no passphrase\n");
-    else {
+    if ((tag = str_getword(&s)) == 0) {
+      if (pixserv_write(px, "FAIL missing tag\n")) goto close;
+    } else if ((q = str_getword(&s)) == 0) {
+      if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
+    else {
       if (strcmp(q, "--") != 0) {
        t = pixserv_timeout(q);
        q = str_getword(&s);
       } else
        t = pixserv_timeout(0);
-      if (!q)
-       pixserv_write(px, "FAIL no passphrase\n");
-      else if (strcmp(q, "--") != 0)
-       pixserv_write(px, "FAIL rubbish found before passphrase\n");
-      else {
+      if (!q) {
+       if (pixserv_write(px, "FAIL no passphrase\n")) goto close;
+      } else if (strcmp(q, "--") != 0) {
+       if (pixserv_write(px, "FAIL rubbish found before passphrase\n"))
+         goto close;
+      } else {
        p_flush(tag);
        p_add(tag, s, t);
-       pixserv_write(px, "OK\n");
+       if (pixserv_write(px, "OK\n")) goto close;
       }
     }
   }
@@ -774,17 +784,26 @@ OK\n\
   /* --- Shut the server down --- */
 
   else if (strcmp(q, "quit") == 0) {
-    if (verbose)
-      log("%s client requested shutdown",
-         px->f & px_stdin ? "local" : "remote");
-    pixserv_write(px, "OK\n");
+    if (verbose) {
+      pxlog("%s client requested shutdown",
+           px->f & px_stdin ? "local" : "remote");
+    }
+    if (pixserv_write(px, "OK\n")) goto close;
     exit(0);
   }
 
   /* --- Report an error for other commands --- */
 
-  else
-    pixserv_write(px, "FAIL unknown command `%s'\n", q);
+  else {
+    if (pixserv_write(px, "FAIL unknown command `%s'\n", q)) goto close;
+  }
+  return;
+
+close:
+  if (px->fd != px->b.reader.fd)
+    close(px->fd);
+  selbuf_destroy(&px->b);
+  close(px->b.reader.fd);
 }
 
 /* --- @pixserv_create@ --- *
@@ -830,14 +849,14 @@ static void pixserv_accept(int fd, unsigned mode, void *p)
 {
   int nfd;
   struct sockaddr_un sun;
-  size_t sunsz = sizeof(sun);
+  socklen_t sunsz = sizeof(sun);
 
   if (mode != SEL_READ)
     return;
   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sunsz)) < 0) {
     if (verbose && errno != EAGAIN && errno != EWOULDBLOCK &&
        errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
-      log("new connection failed: %s", strerror(errno));
+      pxlog("new connection failed: %s", strerror(errno));
     return;
   }
   pixserv_create(nfd, nfd);
@@ -886,7 +905,7 @@ static void pix_sigdie(int sig, void *p)
        p = buf;
        break;
     }
-    log("shutting down on %s", p);
+    pxlog("shutting down on %s", p);
   }
   exit(0);
 }
@@ -915,7 +934,7 @@ static void pix_sigflush(int sig, void *p)
        p = buf;
        break;
     }
-    log("received %s; flushing passphrases", p);
+    pxlog("received %s; flushing passphrases", p);
   }
   p_flush(0);
 }
@@ -984,16 +1003,20 @@ static void pix_setup(struct sockaddr_un *sun, size_t sz)
        if (!S_ISSOCK(st.st_mode))
          die(1, "object `%s' isn't a socket", sun->sun_path);
        if (verbose)
-         log("stale socket found; removing it");
+         pxlog("stale socket found; removing it");
        unlink(sun->sun_path);
        close(fd);
-      } else {
+      } else if (flags & F_REPLACE) {
        if (verbose)
-         log("server already running; shutting it down");
-       write(fd, "QUIT\n", 5);
+         pxlog("server already running; shutting it down");
+       if (write(fd, "QUIT\n", 5) < 0) {
+         die(EXIT_FAILURE, "failed to shut down old server: %s",
+             strerror(errno));
+       }
        sleep(1);
        close(fd);
-      }
+      } else
+       die(EXIT_FAILURE, "pixie already running; not starting");
       goto again;
     }
     chmod(sun->sun_path, 0600);
@@ -1034,7 +1057,8 @@ static void c_uline(char *s, size_t len, void *p)
     c_flags |= cf_uclose;
   } else {
     s[len++] = '\n';
-    write(c_server.reader.fd, s, len);
+    if (write(c_server.reader.fd, s, len) < 0)
+      die(EXIT_FAILURE, "failed to read from stdin: %s", strerror(errno));
   }
 }
 
@@ -1105,8 +1129,8 @@ static void pix_client(struct sockaddr_un *sun, size_t sz, char *argv[])
       DPUTS(&d, *argv++);
     }
     DPUTC(&d, '\n');
-    write(fd, d.buf, d.len);
-    shutdown(fd, 1);
+    if (write(fd, d.buf, d.len) < 0 || shutdown(fd, 1))
+      die(EXIT_FAILURE, "failed to write command: %s", strerror(errno));
     c_flags |= cf_uclose | cf_cooked;
     dstr_destroy(&d);
   }
@@ -1166,6 +1190,7 @@ protect important keys.  Options provided:\n\
 -q, --quiet            Emit fewer log messages.\n\
 -v, --version          Emit more log messages.\n\
 -s, --socket=FILE      Name the pixie's socket.\n\
+-r, --replace          Replace existing pixie, if one is running.\n\
 -c, --command=COMMAND  Shell command to read a passphrase.\n\
 -f, --fetch            Fetch passphrases from the terminal.\n\
 -t, --timeout=TIMEOUT  Length of time to retain a passphrase in memory.\n\
@@ -1240,6 +1265,7 @@ int main(int argc, char *argv[])
       { "passphrase",  0,              0,      'P' },
       { "verify-passphrase",   0,      0,      '+' },
       { "socket",      OPTF_ARGREQ,    0,      's' },
+      { "replace",     0,              0,      'r' },
       { "command",     OPTF_ARGREQ,    0,      'c' },
       { "fetch",       0,              0,      'f' },
       { "timeout",     OPTF_ARGREQ,    0,      't' },
@@ -1254,7 +1280,7 @@ int main(int argc, char *argv[])
       { 0,             0,              0,      0 }
     };
 
-    int i = mdwopt(argc, argv, "hVuqvCPs:c:ft:idl", opts, 0, 0, 0);
+    int i = mdwopt(argc, argv, "hVuqvCPs:rc:ft:idl", opts, 0, 0, 0);
     if (i < 0)
       break;
 
@@ -1298,6 +1324,9 @@ int main(int argc, char *argv[])
       case 's':
        path = optarg;
        break;
+      case 'r':
+       flags |= F_REPLACE;
+       break;
       case 't':
        if ((timeout = pixserv_timeout(optarg)) == 0)
          die(1, "bad timeout `%s'", optarg);
@@ -1374,10 +1403,10 @@ int main(int argc, char *argv[])
     dstr d = DSTR_INIT;
     int rc = l_report(&lm, &d);
     if (rc < 0)
-      die(EXIT_FAILURE, d.buf);
+      die(EXIT_FAILURE, "%s", d.buf);
     else if (rc && verbose) {
-      log(d.buf);
-      log("couldn't lock passphrase buffer");
+      pxlog("%s", d.buf);
+      pxlog("couldn't lock passphrase buffer");
     }
     dstr_destroy(&d);
     arena_setsecure(&lm.a);
@@ -1429,7 +1458,7 @@ int main(int argc, char *argv[])
       }
     }
 #endif
-    chdir("/");
+    DISCARD(chdir("/"));
     setsid();
 
     if (fork() != 0)
@@ -1437,7 +1466,7 @@ int main(int argc, char *argv[])
   }
 
   if (verbose)
-    log("initialized ok");
+    pxlog("initialized ok");
 
   {
     int selerr = 0;
@@ -1445,10 +1474,10 @@ int main(int argc, char *argv[])
       if (!sel_select(&sel))
        selerr = 0;
       else if (errno != EINTR && errno != EAGAIN) {
-       log("error from select: %s", strerror(errno));
+       pxlog("error from select: %s", strerror(errno));
        selerr++;
        if (selerr > 8) {
-         log("too many consecutive select errors: bailing out");
+         pxlog("too many consecutive select errors: bailing out");
          exit(EXIT_FAILURE);
        }
       }