progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / progs / pixie.c
index 1198fcb..f827c20 100644 (file)
@@ -57,6 +57,7 @@
 #include <mLib/alloc.h>
 #include <mLib/dstr.h>
 #include <mLib/fdflags.h>
+#include <mLib/macros.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -83,6 +84,7 @@ static unsigned flags = 0;
 
 #define F_SYSLOG 1u
 #define F_FETCH 2u
+#define F_REPLACE 4u
 
 /*----- Event logging -----------------------------------------------------*/
 
@@ -96,7 +98,7 @@ static unsigned flags = 0;
  * Use:                Writes out a timestamped log message.
  */
 
-static void pxlog(const char *p, ...)
+static void PRINTF_LIKE(1, 2) pxlog(const char *p, ...)
 {
   dstr d = DSTR_INIT;
   va_list ap;
@@ -125,8 +127,7 @@ static void pxlog(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 +137,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 +168,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);
 }
 
@@ -193,13 +206,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) {
       pxlog("flushing passphrase `%s' to free up needed space",
            p_head->tag);
     }
-    p_free(P_ROOT->next);
+    p_free(p_head);
   }
 }
 
@@ -216,8 +229,8 @@ static phrase *p_find(const char *tag)
 {
   phrase *p;
 
-  for (p = P_ROOT->next; p != P_ROOT; p = p->next) {
-    if (strcmp(p->tag, tag) == 0) {
+  for (p = p_head; p; p = p->next) {
+    if (STRCMP(p->tag, ==, tag)) {
       if (p->t) {
        struct timeval tv;
        sel_rmtimer(&p->timer);
@@ -225,12 +238,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 +288,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);
 }
 
@@ -301,12 +307,12 @@ static void p_flush(const char *tag)
 
   if (!tag && verbose > 1)
     pxlog("flushing all passphrases");
-  p = P_ROOT->next;
-  while (p != P_ROOT) {
+  p = p_head;
+  while (p) {
     phrase *pp = p->next;
     if (!tag)
       p_free(p);
-    else if (strcmp(p->tag, tag) == 0) {
+    else if (STRCMP(p->tag, ==, tag)) {
       if (verbose > 1)
        pxlog("flushing passphrase `%s'", tag);
       p_free(p);
@@ -440,7 +446,7 @@ static int p_request(const char *msg, const char *tag, char *buf, size_t sz)
 ok: {
     char *p = buf;
     size_t len;
-    while (isspace((unsigned char)*p))
+    while (ISSPACE(*p))
       p++;
     len = strlen(p);
     memmove(buf, p, len);
@@ -512,7 +518,7 @@ static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
       goto fail;
     if (p_request("Verify passphrase", tag, pp, LBUFSZ) < 0)
       goto fail;
-    if (strcmp(pp, p->p) != 0) {
+    if (STRCMP(pp, !=, p->p)) {
       if (verbose)
        pxlog("passphrases for `%s' don't match", tag);
       p_free(p);
@@ -583,21 +589,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 +662,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 --- */
 
@@ -672,46 +678,49 @@ static void pixserv_line(char *s, size_t len, void *p)
   if ((q = str_getword(&s)) == 0)
     return;
   for (qq = q; *qq; qq++)
-    *qq = tolower((unsigned char)*qq);
+    *qq = TOLOWER(*qq);
 
   /* --- Handle a help request --- */
 
-  if (strcmp(q, "help") == 0) {
-    pixserv_write(px, "\
+  if (STRCMP(q, ==, "help")) {
+    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 --- */
 
-  else if (strcmp(q, "list") == 0) {
+  else if (STRCMP(q, ==, "list")) {
     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 --- */
 
-  else if ((mode = PMODE_READ, strcmp(q, "pass") == 0) ||
-          (mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) {
+  else if ((mode = PMODE_READ, STRCMP(q, ==, "pass")) ||
+          (mode = PMODE_VERIFY, STRCMP(q, ==, "verify"))) {
     unsigned long t;
     const char *p;
     int rc;
@@ -724,13 +733,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;
       }
     }
@@ -738,53 +748,63 @@ OK\n\
 
   /* --- Flush existing passphrases --- */
 
-  else if (strcmp(q, "flush") == 0) {
+  else if (STRCMP(q, ==, "flush")) {
     q = str_getword(&s);
     p_flush(q);
-    pixserv_write(px, "OK\n");
+    if (pixserv_write(px, "OK\n")) goto close;
   }
 
   /* --- Set a passphrase --- */
 
-  else if (strcmp(q, "set") == 0) {
+  else if (STRCMP(q, ==, "set")) {
     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 (strcmp(q, "--") != 0) {
+    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, !=, "--")) {
        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, !=, "--")) {
+       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");
+       p_add(tag, s ? s : "", t);
+       if (pixserv_write(px, "OK\n")) goto close;
       }
     }
   }
 
   /* --- Shut the server down --- */
 
-  else if (strcmp(q, "quit") == 0) {
-    if (verbose)
+  else if (STRCMP(q, ==, "quit")) {
+    if (verbose) {
       pxlog("%s client requested shutdown",
            px->f & px_stdin ? "local" : "remote");
-    pixserv_write(px, "OK\n");
+    }
+    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,7 +850,7 @@ 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;
@@ -987,13 +1007,17 @@ static void pix_setup(struct sockaddr_un *sun, size_t sz)
          pxlog("stale socket found; removing it");
        unlink(sun->sun_path);
        close(fd);
-      } else {
+      } else if (flags & F_REPLACE) {
        if (verbose)
          pxlog("server already running; shutting it down");
-       write(fd, "QUIT\n", 5);
+       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 +1058,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));
   }
 }
 
@@ -1052,14 +1077,14 @@ static void c_sline(char *s, size_t len, void *p)
     puts(s);
   else {
     char *q = str_getword(&s);
-    if (strcmp(q, "FAIL") == 0)
+    if (STRCMP(q, ==, "FAIL"))
       die(1, "%s", s);
-    else if (strcmp(q, "INFO") == 0 ||
-            strcmp(q, "ITEM") == 0)
+    else if (STRCMP(q, ==, "INFO") ||
+            STRCMP(q, ==, "ITEM"))
       puts(s);
-    else if (strcmp(q, "OK") == 0) {
+    else if (STRCMP(q, ==, "OK")) {
       if (s && *s) puts(s);
-    } else if (strcmp(q, "MISSING") == 0)
+    } else if (STRCMP(q, ==, "MISSING"))
       ;
     else
       moan("unexpected output: %s %s", q, s);
@@ -1105,8 +1130,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 +1191,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\
@@ -1219,7 +1245,7 @@ int main(int argc, char *argv[])
   /* --- Set up the locked memory area --- */
 
   l_init(&lm, 16384);
-  setuid(getuid());
+  if (setuid(getuid())) _exit(125);
 
   /* --- Parse command line arguments --- */
 
@@ -1240,6 +1266,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 +1281,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 +1325,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,9 +1404,9 @@ 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) {
-      pxlog(d.buf);
+      pxlog("%s", d.buf);
       pxlog("couldn't lock passphrase buffer");
     }
     dstr_destroy(&d);
@@ -1429,7 +1459,7 @@ int main(int argc, char *argv[])
       }
     }
 #endif
-    chdir("/");
+    DISCARD(chdir("/"));
     setsid();
 
     if (fork() != 0)