chiark / gitweb /
Use new mdup(3mLib) function.
[tripe] / client / tripectl.c
index 7386b304b3a7dd808b4cd46a978fd5073e543498..514506162429860fd6047c85db2b49159c08b2ca 100644 (file)
@@ -1,13 +1,11 @@
 /* -*-c-*-
- *
- * $Id$
  *
  * Client for TrIPE
  *
  * (c) 2001 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of Trivial IP Encryption (TrIPE).
  *
  * 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.
- * 
+ *
  * TrIPE 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 TrIPE; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #include <netdb.h>
 
 #include <mLib/alloc.h>
+#include <mLib/daemonize.h>
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/mdup.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -60,6 +60,7 @@
 #include <mLib/selbuf.h>
 #include <mLib/sig.h>
 #include <mLib/str.h>
+#include <mLib/versioncmp.h>
 
 #include "util.h"
 
@@ -74,6 +75,7 @@
 
 /*----- Static variables --------------------------------------------------*/
 
+static sel_state sel;
 static const char *pidfile = 0;
 static const char *logname = 0;
 static FILE *logfp = 0;
@@ -119,12 +121,33 @@ static void checkbg(char **p)
     die(EXIT_FAILURE, "unexpected background tag `%s'", q);
 }
 
-static void checkfg(void)
+static void dolog(int prio, const char *msg, ...)
 {
-  if (bgtag)
-    die(EXIT_FAILURE, "unexpected foreground response");
+  va_list ap;
+  dstr d = DSTR_INIT;
+  const char *cat;
+
+  va_start(ap, msg);
+  dstr_vputf(&d, msg, &ap);
+  va_end(ap);
+  if (f & f_syslog) syslog(prio, "%s", d.buf);
+  if (logfp) {
+    switch (prio) {
+      case LOG_WARNING: cat = "warning"; break;
+      case LOG_DEBUG: cat = "debug"; break;
+      case LOG_ERR: cat = "error"; break;
+      default: cat = "message"; break;
+    }
+    writelog(cat, d.buf);
+  }
+  if (prio == LOG_WARNING && (f & f_warn))
+    fprintf(stderr, "Warning: %s\n", d.buf);
+  dstr_destroy(&d);
 }
 
+static void checkfg(void)
+  { if (bgtag) die(EXIT_FAILURE, "unexpected foreground response"); }
+
 static void cline(char *p, size_t len, void *b)
 {
   char *q;
@@ -136,33 +159,19 @@ static void cline(char *p, size_t len, void *b)
   q = str_getword(&p);
   if (!q)
     return;
-  if (strcmp(q, "WARN") == 0) {
-    if (f & f_syslog)
-      syslog(LOG_WARNING, "%s", p);
-    if (logfp)
-      writelog("warning", p);
-    if (f & f_warn)
-      fprintf(stderr, "Warning: %s\n", p);
-  } else if (strcmp(q, "TRACE") == 0) {
-    if (f & f_syslog)
-      syslog(LOG_DEBUG, "%s", p);
-    if (logfp)
-      writelog("debug", p);
-  } else if (!(f & f_command)) {
-    if (f & f_syslog)
-      syslog(LOG_ERR, "unexpected output `%s %s'", q, p);
-    if (logfp) {
-      dstr d = DSTR_INIT;
-      dstr_putf(&d, "unexpected output `%s %s'", q, p);
-      writelog("error", d.buf);
-      dstr_destroy(&d);
-    }
-  } else if (strcmp(q, "FAIL") == 0) {
+  if (strcmp(q, "WARN") == 0)
+    dolog(LOG_WARNING, p);
+  else if (strcmp(q, "TRACE") == 0)
+    dolog(LOG_DEBUG, p);
+  else if (!(f & f_command))
+    dolog(LOG_ERR, "unexpected output `%s %s'", q, p);
+  else if (strcmp(q, "FAIL") == 0) {
     checkfg();
     die(EXIT_FAILURE, "%s", p);
   } else if (strcmp(q, "INFO") == 0) {
     checkfg();
     puts(p);
+    fflush(stdout);
   } else if (strcmp(q, "OK") == 0) {
     checkfg();
     exit(0);
@@ -176,11 +185,12 @@ static void cline(char *p, size_t len, void *b)
   } else if (strcmp(q, "BGINFO") == 0) {
     checkbg(&p);
     puts(p);
+    fflush(stdout);
   } else if (strcmp(q, "BGFAIL") == 0) {
     checkbg(&p);
     die(EXIT_FAILURE, "%s", p);
   } else
-    die(EXIT_FAILURE, "unexpected output `%s %s'", q, p); 
+    die(EXIT_FAILURE, "unexpected output `%s %s'", q, p);
 }
 
 static void sline(char *p, size_t len, void *b)
@@ -191,6 +201,7 @@ static void sline(char *p, size_t len, void *b)
     exit(0);
   }
   puts(p);
+  fflush(stdout);
 }
 
 static void uline(char *p, size_t len, void *b)
@@ -207,6 +218,16 @@ static void uline(char *p, size_t len, void *b)
   }
 }
 
+static void eline(char *p, size_t len, void *b)
+{
+  if (p)
+    dolog(LOG_WARNING, "(stderr): %s", p);
+  else {
+    selbuf_destroy(b);
+    close(fd);
+  }
+}
+
 static void setup(const char *cmd)
 {
   dstr d = DSTR_INIT;
@@ -263,24 +284,29 @@ static void logfile(const char *name)
     else if (logname)
       die(EXIT_FAILURE, d.buf);
     if (f & f_syslog)
-      syslog(LOG_ERR, d.buf);
+      syslog(LOG_ERR, "%s", d.buf);
     dstr_destroy(&d);
   }
 }
 
 static void sighup(int sig, void *v) { logfile(logname); }
 
-static void cleanup(void)
-{
-  if (pidfile)
-    unlink(pidfile);
-}
+static void cleanup(void) { if (pidfile) unlink(pidfile); }
 
 static void sigdie(int sig)
+  { cleanup(); signal(sig, SIG_DFL); raise(sig); }
+
+static void putarg(string_v *av, const char *fmt, ...)
 {
-  cleanup();
-  signal(sig, SIG_DFL);
-  raise(sig);
+  va_list ap;
+  dstr d = DSTR_INIT;
+
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, &ap);
+  dstr_putz(&d);
+  va_end(ap);
+  DA_UNSHIFT(av, xstrdup(d.buf));
+  dstr_destroy(&d);
 }
 
 static void version(FILE *fp)
@@ -313,8 +339,10 @@ Options in full:\n\
 \n\
 -D, --daemon           Become a background task after connecting.\n\
 -d, --directory=DIR    Select current directory [default " CONFIGDIR "].\n\
+-U, --setuid=USER      Set uid to USER after initialization.\n\
+-G, --setgid=GROUP     Set gid to GROUP after initialization.\n\
 -a, --admin-socket=FILE        Select socket to connect to\n\
-                          [default " SOCKETDIR "/tripesock].\n\
+                         [default " SOCKETDIR "/tripesock].\n\
 -P, --pidfile=FILE     Write process-id to FILE.\n\
 \n\
 -s, --spawn            Start server rather than connecting.\n\
@@ -335,11 +363,26 @@ int main(int argc, char *argv[])
   string_v spawnopts = DA_INIT;
   char *p;
   FILE *pidfp = 0;
+  int i;
+  size_t sz;
+  uid_t u = -1;
+  gid_t g = -1;
+  int pfd[2], efd[2];
+  mdup_fd md[3];
+  pid_t kid;
+  struct sigaction sa;
+  sigset_t newmask, oldmask;
+  struct sockaddr_un sun;
+  selbuf bu, bs, be;
+  dstr d = DSTR_INIT;
+  sig hup;
 
   ego(argv[0]);
 
   if ((p = getenv("TRIPEDIR")) != 0)
     dir = p;
+  if ((p = getenv("TRIPESOCK")) != 0)
+    sock = p;
 
   /* --- Parse the arguments --- */
 
@@ -349,6 +392,10 @@ int main(int argc, char *argv[])
       { "version",     0,              0,      'v' },
       { "usage",       0,              0,      'u' },
       { "daemon",      0,              0,      'D' },
+      { "uid",         OPTF_ARGREQ,    0,      'U' },
+      { "setuid",      OPTF_ARGREQ,    0,      'U' },
+      { "gid",         OPTF_ARGREQ,    0,      'G' },
+      { "setgid",      OPTF_ARGREQ,    0,      'G' },
       { "directory",   OPTF_ARGREQ,    0,      'd' },
       { "admin-socket",        OPTF_ARGREQ,    0,      'a' },
       { "spawn",       0,              0,      's' },
@@ -361,7 +408,7 @@ int main(int argc, char *argv[])
       { 0,             0,              0,      0 }
     };
 
-    int i = mdwopt(argc, argv, "+hvuDd:a:sp:S:lwf:nP:", opts, 0, 0, 0);
+    i = mdwopt(argc, argv, "+hvuDU:G:d:a:sp:S:lwf:nP:", opts, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -377,6 +424,12 @@ int main(int argc, char *argv[])
       case 'D':
        f |= f_daemon | f_noinput;
        break;
+      case 'U':
+       u = u_getuser(optarg, &g);
+       break;
+      case 'G':
+       g = u_getgroup(optarg);
+       break;
       case 'd':
        dir = optarg;
        break;
@@ -432,6 +485,8 @@ int main(int argc, char *argv[])
     die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
        pidfile, strerror(errno));
   }
+  sel_init(&sel);
+  sig_init(&sel);
   signal(SIGINT, sigdie);
   signal(SIGQUIT, sigdie);
   signal(SIGTERM, sigdie);
@@ -440,11 +495,6 @@ int main(int argc, char *argv[])
   /* --- Connect to the server --- */
 
   if (f & f_spawn) {
-    int pfd[2];
-    pid_t kid;
-    struct sigaction sa;
-    sigset_t newmask, oldmask;
-
     sa.sa_handler = reap;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_NOCLDSTOP;
@@ -453,12 +503,14 @@ 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);
     DA_PUSH(&spawnopts, 0);
-    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd))
+    if (g != (gid_t)-1) putarg(&spawnopts, "-G%lu", (unsigned long)g);
+    if (u != (uid_t)-1) putarg(&spawnopts, "-U%lu", (unsigned long)u);
+    putarg(&spawnopts, "-a%s", sock);
+    putarg(&spawnopts, "-d.");
+    putarg(&spawnopts, "-F");
+    putarg(&spawnopts, "%s", spawnpath);
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd) || pipe(efd))
       die(EXIT_FAILURE, "error from socketpair: %s", strerror(errno));
     sigemptyset(&newmask);
     sigaddset(&newmask, SIGCHLD);
@@ -466,26 +518,24 @@ int main(int argc, char *argv[])
     if ((kid = fork()) < 0)
       die(EXIT_FAILURE, "fork failed: %s", strerror(errno));
     if (!kid) {
-      dup2(pfd[1], STDIN_FILENO);
-      dup2(pfd[1], STDOUT_FILENO);
-      close(pfd[1]);
-      close(pfd[0]);
-      if (logfp)
-       fclose(logfp);
-      if (pidfp)
-       fclose(pidfp);
+      close(pfd[0]); close(efd[0]);
+      md[0].cur = pfd[1]; md[0].want = STDIN_FILENO;
+      md[1].cur = pfd[1]; md[1].want = STDOUT_FILENO;
+      md[2].cur = efd[1]; md[2].want = STDERR_FILENO;
+      mdup(md, 3);
+      if (logfp) fclose(logfp);
+      if (pidfp) fclose(pidfp);
       closelog();
-      if (f & f_daemon)
-       u_detach();
+      if (f & f_daemon) detachtty();
       execvp(DA(&spawnopts)[0], DA(&spawnopts));
       die(127, "couldn't exec `%s': %s", spawnpath, strerror(errno));
     }
     sigprocmask(SIG_SETMASK, &oldmask, 0);
     fd = pfd[0];
-    close(pfd[1]);
+    close(pfd[1]); close(efd[1]);
+    selbuf_init(&be, &sel, efd[0], eline, &be);
   } else {
-    struct sockaddr_un sun;
-    size_t sz = strlen(sock) + 1;
+    sz = strlen(sock) + 1;
     if (sz > sizeof(sun.sun_path))
       die(EXIT_FAILURE, "socket name `%s' too long", sock);
     memset(&sun, 0, sizeof(sun));
@@ -500,8 +550,9 @@ int main(int argc, char *argv[])
     }
   }
 
+  u_setugid(u, g);
   if (f & f_daemon) {
-    if (u_daemon())
+    if (daemonize())
       die(EXIT_FAILURE, "error becoming daemon: %s", strerror(errno));
   }
   if (pidfp) {
@@ -515,10 +566,6 @@ int main(int argc, char *argv[])
   if (optind == argc)
     setup("WATCH -A+tw");
   if (!(f & f_noinput) && optind == argc) {
-    sel_state sel;
-    selbuf bu, bs;
-
-    sel_init(&sel);
     selbuf_init(&bu, &sel, STDIN_FILENO, uline, &bu);
     selbuf_init(&bs, &sel, fd, sline, &bs);
     for (;;) {
@@ -530,13 +577,9 @@ int main(int argc, char *argv[])
   /* --- If there's a command, submit it --- */
 
   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++]);
-    }
+    while (optind < argc)
+      u_quotify(&d, argv[optind++]);
     dstr_putc(&d, '\n');
     errno = EIO;
     if (write(fd, d.buf, d.len) != d.len || shutdown(fd, 1))
@@ -547,24 +590,15 @@ int main(int argc, char *argv[])
 
   /* --- Pull everything else out of the box --- */
 
-  {
-    sel_state sel;
-    selbuf b;
-    sig hup;
-
-    sel_init(&sel);
-    selbuf_init(&b, &sel, fd, cline, 0);
+  selbuf_init(&bs, &sel, fd, cline, 0);
 
-    if (f & f_syslog)
-      openlog(QUIS, 0, LOG_DAEMON);
-    if (logfp) {
-      sig_init(&sel);
-      sig_add(&hup, SIGHUP, sighup, 0);
-    }
-    for (;;) {
-      if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
-       die(EXIT_FAILURE, "select failed: %s", strerror(errno));
-    }
+  if (f & f_syslog)
+    openlog(QUIS, 0, LOG_DAEMON);
+  if (logfp)
+    sig_add(&hup, SIGHUP, sighup, 0);
+  for (;;) {
+    if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
+      die(EXIT_FAILURE, "select failed: %s", strerror(errno));
   }
 
   return (0);