X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/73189848e83a15269a9c3af541ec32537db647b9..d59d4fe9e094b3ce28c47c89197a0c8ad79fc35f:/client.c diff --git a/client.c b/client.c index e78f86e7..314bb87c 100644 --- a/client.c +++ b/client.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: client.c,v 1.2 2001/02/04 01:17:54 mdw Exp $ + * $Id$ * * Client for TrIPE * @@ -26,17 +26,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: client.c,v $ - * 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" @@ -64,12 +53,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include "util.h" @@ -85,6 +74,8 @@ /*----- Static variables --------------------------------------------------*/ +static const char *pidfile = 0; +static const char *logname = 0; static FILE *logfp = 0; static unsigned f = 0; static int fd; @@ -103,9 +94,8 @@ static int fd; static void reap(int sig) { - int s; int e = errno; - while (waitpid(-1, &s, WNOHANG) > 0) + while (waitpid(-1, 0, WNOHANG) > 0) ; errno = e; } @@ -119,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) { @@ -151,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); @@ -161,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)) @@ -171,20 +161,99 @@ 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) +{ + 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); + } +} + +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) { pquis(fp, "$, TrIPE version " VERSION "\n"); @@ -194,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\ "); } @@ -215,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\ @@ -230,11 +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; char *p; + FILE *pidfp = 0; ego(argv[0]); @@ -257,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) { @@ -301,14 +375,12 @@ int main(int argc, char *argv[]) f |= f_warn; break; case 'f': - if (logfp) - fclose(logfp); - if ((logfp = fopen(optarg, "a")) == 0) { - die(EXIT_FAILURE, "error opening logfile `%s': %s", - optarg, strerror(errno)); - } + logname = optarg; f |= f_noinput; break; + case 'P': + pidfile = optarg; + break; default: f |= f_bogus; break; @@ -319,12 +391,24 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - /* --- Set the world up --- */ + /* --- Set various things up --- */ if (chdir(dir)) { - die(EXIT_FAILURE, "couldn't set current directory to `%s': %s", + 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 --- */ @@ -342,12 +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, "-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)); @@ -361,6 +443,13 @@ int main(int argc, char *argv[]) dup2(pfd[1], STDOUT_FILENO); close(pfd[1]); 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)); } @@ -375,12 +464,12 @@ int main(int argc, char *argv[]) memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; memcpy(sun.sun_path, sock, sz); - sz += offsetof(struct sockaddr_un, sun_path); + 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)) { die(EXIT_FAILURE, "error connecting to `%s': %s", - sock, strerror(errno)); + sun.sun_path, strerror(errno)); } } @@ -388,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; @@ -399,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)); } } @@ -408,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; } @@ -423,20 +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) { + sig_init(&sel); + sig_add(&hup, SIGHUP, sighup, 0); + } for (;;) { - size_t sz = lbuf_free(&b, &p); - ssize_t n = read(fd, p, sz); - if (n < 0) - 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);