chiark / gitweb /
Fix for new key-data interface.
[tripe] / client.c
index ce1acb6520ab9f294c23ab571119e88f5dded4e6..314bb87cb0241103760aa81803aa585bde680b3e 100644 (file)
--- a/client.c
+++ b/client.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: client.c,v 1.3 2001/02/04 17:10:15 mdw Exp $
+ * $Id$
  *
  * Client for TrIPE
  *
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-/*----- Revision history --------------------------------------------------* 
- *
- * $Log: client.c,v $
- * Revision 1.3  2001/02/04 17:10:15  mdw
- * Reopen logfiles on receipt of @SIGHUP@ (not done very well).  Don't
- * change directory -- just mangle pathnames instead.
- *
- * Revision 1.2  2001/02/04 01:17:54  mdw
- * Create a configuration header file to tidy up command lines.
- *
- * Revision 1.1  2001/02/03 20:26:37  mdw
- * Initial checkin.
- *
- */
-
 /*----- Header files ------------------------------------------------------*/
 
 #include "config.h"
 #include <mLib/alloc.h>
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
-#include <mLib/lbuf.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
 #include <mLib/sel.h>
 #include <mLib/selbuf.h>
+#include <mLib/sig.h>
 #include <mLib/str.h>
 
 #include "util.h"
 
 /*----- Static variables --------------------------------------------------*/
 
+static const char *pidfile = 0;
+static const char *logname = 0;
 static FILE *logfp = 0;
 static unsigned f = 0;
 static int fd;
-static volatile sig_atomic_t reopen = 0;
 
 #define f_bogus 1u
 #define f_spawn 2u
@@ -108,18 +94,12 @@ static volatile sig_atomic_t reopen = 0;
 
 static void reap(int sig)
 {
-  int s;
   int e = errno;
-  while (waitpid(-1, &s, WNOHANG) > 0)
+  while (waitpid(-1, 0, WNOHANG) > 0)
     ;
   errno = e;
 }
 
-static void sighup(int sig)
-{
-  reopen = 1;
-}
-
 static void writelog(const char *cat, const char *msg)
 {
   char buf[256];
@@ -129,7 +109,7 @@ static void writelog(const char *cat, const char *msg)
   fprintf(logfp, "%s %s: %s\n", buf, cat, msg);
 }
 
-static void cline(char *p, void *b)
+static void cline(char *p, size_t len, void *b)
 {
   char *q;
   if (!p) {
@@ -161,7 +141,7 @@ static void cline(char *p, void *b)
       writelog("error", d.buf);
       dstr_destroy(&d);
     }
-  } else if (strcmp(q, "ERR") == 0)
+  } else if (strcmp(q, "FAIL") == 0)
     die(EXIT_FAILURE, "%s", p);
   else if (strcmp(q, "INFO") == 0)
     puts(p);
@@ -171,7 +151,7 @@ static void cline(char *p, void *b)
     die(EXIT_FAILURE, "unexpected output `%s %s'", q, p); 
 }
 
-static void sline(char *p, void *b)
+static void sline(char *p, size_t len, void *b)
 {
   if (!p) {
     if (!(f & f_uclose))
@@ -181,29 +161,97 @@ static void sline(char *p, void *b)
   puts(p);
 }
 
-static void uline(char *p, void *b)
+static void uline(char *p, size_t len, void *b)
 {
-  size_t sz;
   if (!p) {
     selbuf_destroy(b);
     shutdown(fd, 1);
     f |= f_uclose;
   } else {
-    sz = strlen(p);
-    p[sz] = '\n';
-    write(fd, p, sz + 1);
+    p[len] = '\n';
+    errno = EIO;
+    if (write(fd, p, len + 1) != len + 1)
+      moan("write failed: %s", strerror(errno));
+  }
+}
+
+static void setup(const char *cmd)
+{
+  dstr d = DSTR_INIT;
+  char ch;
+  char *p, *q;
+  int n;
+
+  dstr_puts(&d, cmd);
+  dstr_putc(&d, '\n');
+  errno = EIO; /* Relax: be vague */
+  if (write(fd, d.buf, d.len) != d.len) {
+    die(EXIT_FAILURE, "error sending setup command `%s': %s",
+       cmd, strerror(errno));
+  }
+  dstr_reset(&d);
+  for (;;) {
+    n = read(fd, &ch, 1);
+    if (!n)
+      die(EXIT_FAILURE, "unexpected EOF during setup");
+    if (n < 0) {
+      die(EXIT_FAILURE, "error receiving reply to `%s': %s",
+         cmd, strerror(errno));
+    }
+    if (d.len < 256)
+      dstr_putc(&d, ch);
+    if (ch == '\n') {
+      p = d.buf;
+      q = str_getword(&p);
+      if (!q)
+       ;
+      else if (strcmp(q, "OK") == 0)
+       return;
+      else if (strcmp(q, "FAIL") == 0)
+       die(EXIT_FAILURE, "setup command `%s' failed: %s", cmd, p);
+      dstr_reset(&d);
+    }
   }
 }
 
 static void logfile(const char *name)
 {
-  if (logfp)
-    fclose(logfp);
-  if ((logfp = fopen(name, "a")) == 0) {
-    die(EXIT_FAILURE, "error opening logfile `%s': %s",
-       name, strerror(errno));
+  FILE *fp;
+
+  if ((fp = fopen(name, "a")) != 0) {
+    if (logfp)
+      fclose(logfp);
+    logfp =  fp;
+    setvbuf(logfp, 0, _IOLBF, BUFSIZ);
+  } else {
+    dstr d = DSTR_INIT;
+    dstr_putf(&d, "error opening logfile `%s': %s", name, strerror(errno));
+    if (logfp)
+      writelog("error", d.buf);
+    else if (logname)
+      die(EXIT_FAILURE, d.buf);
+    if (f & f_syslog)
+      syslog(LOG_ERR, d.buf);
+    dstr_destroy(&d);
   }
-  setvbuf(logfp, 0, _IOLBF, BUFSIZ);
+}
+
+static void sighup(int sig, void *v)
+{
+  logfile(logname);
+}
+
+static void cleanup(void)
+{
+  if (pidfile)
+    unlink(pidfile);
+}
+
+static void sigdie(int sig)
+{
+  cleanup();
+  signal(sig, SIG_DFL);
+  raise(sig);
 }
 
 static void version(FILE *fp)
@@ -215,10 +263,11 @@ static void usage(FILE *fp)
 {
   pquis(fp, "\
 Usage:\n\
-       $ [-w] [-options] [command [args]...]\n\
-       $ [-Dl] [-f file] [-options]\n\
+       $ [-w] [-OPTIONS] [COMMAND [ARGS]...]\n\
+       $ [-Dl] [-f FILE] [-OPTIONS]\n\
 Options:\n\
-       [-s] [-d directory] [-a socket] [-p program] [-S arg,arg,...]\n\
+       [-s] [-d DIRECTORY] [-a SOCKET] [-P PIDFILE]\n\
+       [-p PROGRAM] [-S ARG,ARG,...]\n\
 ");
 }
 
@@ -236,8 +285,10 @@ Options in full:\n\
 -u, --usage            Show brief usage message.\n\
 \n\
 -D, --daemon           Become a background task after connecting.\n\
--d, --directory=DIR    Select current directory [default /var/lib/tripe]\n\
--a, --admin-socket=FILE        Select socket to connect to.\n\
+-d, --directory=DIR    Select current directory [default " CONFIGDIR "].\n\
+-a, --admin-socket=FILE        Select socket to connect to\n\
+                          [default " SOCKETDIR "/tripesock].\n\
+-P, --pidfile=FILE     Write process-id to FILE.\n\
 \n\
 -s, --spawn            Start server rather than connecting.\n\
 -p, --spawn-path=PATH  Specify path to executable.\n\
@@ -251,12 +302,12 @@ Options in full:\n\
 
 int main(int argc, char *argv[])
 {
-  const char *dir = "/var/lib/tripe";
-  const char *sock = "tripesock";
+  const char *dir = CONFIGDIR;
+  const char *sock = SOCKETDIR "/tripesock";
   const char *spawnpath = "tripe";
   string_v spawnopts = DA_INIT;
-  const char *logname = 0;
   char *p;
+  FILE *pidfp = 0;
 
   ego(argv[0]);
 
@@ -279,10 +330,11 @@ int main(int argc, char *argv[])
       { "syslog",      0,              0,      'l' },
       { "logfile",     OPTF_ARGREQ,    0,      'f' },
       { "warnings",    0,              0,      'w' },
+      { "pidfile",     OPTF_ARGREQ,    0,      'P' },
       { 0,             0,              0,      0 }
     };
 
-    int i = mdwopt(argc, argv, "hvuDd:a:sp:S:lwf:n", opts, 0, 0, 0);
+    int i = mdwopt(argc, argv, "+hvuDd:a:sp:S:lwf:nP:", opts, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -324,9 +376,11 @@ int main(int argc, char *argv[])
        break;
       case 'f':
        logname = optarg;
-       logfile(logname);
        f |= f_noinput;
        break;
+      case 'P':
+       pidfile = optarg;
+       break;
       default:
        f |= f_bogus;
        break;
@@ -337,6 +391,25 @@ int main(int argc, char *argv[])
     exit(EXIT_FAILURE);
   }
 
+  /* --- Set various things up --- */
+
+  if (chdir(dir)) {
+    die(EXIT_FAILURE, "couldn't set `%s' as current directory: %s",
+       dir, strerror(errno));
+  }
+  if (logname)
+    logfile(logname);
+  if (!pidfile && (f & f_daemon) && ((f & f_syslog) || logname))
+    pidfile = "tripectl.pid";
+  if (pidfile && (pidfp = fopen(pidfile, "w")) == 0) {
+    die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
+       pidfile, strerror(errno));
+  }
+  signal(SIGINT, sigdie);
+  signal(SIGQUIT, sigdie);
+  signal(SIGTERM, sigdie);
+  atexit(cleanup);
+
   /* --- Connect to the server --- */
 
   if (f & f_spawn) {
@@ -353,13 +426,10 @@ int main(int argc, char *argv[])
 #endif
     sigaction(SIGCHLD, &sa, 0);
 
+    DA_UNSHIFT(&spawnopts, (char *)sock);
+    DA_UNSHIFT(&spawnopts, "-a");
+    DA_UNSHIFT(&spawnopts, "-d.");
     DA_UNSHIFT(&spawnopts, (char *)spawnpath);
-    if (!(f & f_spawnopts)) {
-      DA_PUSH(&spawnopts, "-d");
-      DA_PUSH(&spawnopts, (char *)dir);
-      DA_PUSH(&spawnopts, "-a");
-      DA_PUSH(&spawnopts, (char *)sock);
-    }
     DA_PUSH(&spawnopts, 0);
     if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd))
       die(EXIT_FAILURE, "error from socketpair: %s", strerror(errno));
@@ -375,7 +445,11 @@ int main(int argc, char *argv[])
       close(pfd[0]);
       if (logfp)
        fclose(logfp);
+      if (pidfp)
+       fclose(pidfp);
       closelog();
+      if (f & f_daemon)
+       u_detach();
       execvp(DA(&spawnopts)[0], DA(&spawnopts));
       die(127, "couldn't exec `%s': %s", spawnpath, strerror(errno));
     }
@@ -385,15 +459,12 @@ int main(int argc, char *argv[])
   } else {
     struct sockaddr_un sun;
     size_t sz = strlen(sock) + 1;
-    dstr d = DSTR_INIT;
-    dstr_putf(&d, "%s/%s", dir, sock);
-    if (d.sz + 1 > sizeof(sun.sun_path))
+    if (sz > sizeof(sun.sun_path))
       die(EXIT_FAILURE, "socket name `%s' too long", sock);
     memset(&sun, 0, sizeof(sun));
     sun.sun_family = AF_UNIX;
-    memcpy(sun.sun_path, d.buf, d.sz + 1);
-    sz = d.sz + offsetof(struct sockaddr_un, sun_path) + 1;
-    dstr_destroy(&d);
+    memcpy(sun.sun_path, sock, sz);
+    sz = sz + offsetof(struct sockaddr_un, sun_path);
     if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
       die(EXIT_FAILURE, "error making socket: %s", strerror(errno));
     if (connect(fd, (struct sockaddr *)&sun, sz)) {
@@ -406,9 +477,16 @@ int main(int argc, char *argv[])
     if (u_daemon())
       die(EXIT_FAILURE, "error becoming daemon: %s", strerror(errno));
   }
+  if (pidfp) {
+    fprintf(pidfp, "%li\n", (long)getpid());
+    fclose(pidfp);
+  }
+  signal(SIGPIPE, SIG_IGN);
 
   /* --- If we're meant to be interactive, do that --- */
 
+  if (optind == argc)
+    setup("WATCH -A+tw");
   if (!(f & f_noinput) && optind == argc) {
     sel_state sel;
     selbuf bu, bs;
@@ -417,7 +495,7 @@ int main(int argc, char *argv[])
     selbuf_init(&bu, &sel, STDIN_FILENO, uline, &bu);
     selbuf_init(&bs, &sel, fd, sline, &bs);
     for (;;) {
-      if (sel_select(&sel))
+      if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
        die(EXIT_FAILURE, "select failed: %s", strerror(errno));
     }
   }
@@ -426,14 +504,16 @@ int main(int argc, char *argv[])
 
   if (optind < argc) {
     dstr d = DSTR_INIT;
+    setup((f & f_warn) ? "WATCH -A+w" : "WATCH -A");
     dstr_puts(&d, argv[optind++]);
     while (optind < argc) {
       dstr_putc(&d, ' ');
       dstr_puts(&d, argv[optind++]);
     }
     dstr_putc(&d, '\n');
-    write(fd, d.buf, d.len);
-    shutdown(fd, 1);
+    errno = EIO;
+    if (write(fd, d.buf, d.len) != d.len || shutdown(fd, 1))
+      die(EXIT_FAILURE, "write failed: %s", strerror(errno));
     dstr_destroy(&d);
     f |= f_command;
   }
@@ -441,36 +521,23 @@ int main(int argc, char *argv[])
   /* --- Pull everything else out of the box --- */
 
   {
-    lbuf b;
-    lbuf_init(&b, cline, 0);
+    sel_state sel;
+    selbuf b;
+    sig hup;
+
+    sel_init(&sel);
+    selbuf_init(&b, &sel, fd, cline, 0);
+
     if (f & f_syslog)
       openlog(QUIS, 0, LOG_DAEMON);
     if (logfp) {
-      struct sigaction sa;
-      sa.sa_handler = sighup;
-      sa.sa_flags = 0;
-      sigemptyset(&sa.sa_mask);
-      sigaction(SIGHUP, &sa, 0);
+      sig_init(&sel);
+      sig_add(&hup, SIGHUP, sighup, 0);
     }
     for (;;) {
-      size_t sz;
-      ssize_t n;
-      if (reopen) {
-       logfile(logname);
-       reopen = 0;
-      }
-      sz = lbuf_free(&b, &p);
-      n = read(fd, p, sz);
-      if (n < 0) {
-       if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
-         continue;
-       die(EXIT_FAILURE, "read failed: %s", strerror(errno));
-      }
-      if (n == 0)
-       break;
-      lbuf_flush(&b, p, n);
+      if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
+       die(EXIT_FAILURE, "select failed: %s", strerror(errno));
     }
-    lbuf_close(&b);
   }
 
   return (0);