commit 458e39242e04c74201aea3a56fec2d913a1bab94
parent 4f980a523dcc75ab77beadcc036ee45c95505c9f
Author: Gerrit Pape <pape@smarden.org>
Date: Sat, 23 Jul 2005 12:08:56 +0000
* sv.c: new sv program to control services, optionally can be linked to
/etc/init.d/ as lsb "init script" command line interface.
* Makefile, TARGETS: adapt.
Diffstat:
3 files changed, 326 insertions(+), 0 deletions(-)
diff --git a/src/Makefile b/src/Makefile
@@ -24,6 +24,9 @@ runsvstat: load runsvstat.o unix.a byte.a time.a
runsvctrl: load runsvctrl.o unix.a byte.a
./load runsvctrl unix.a byte.a
+sv: load sv.o unix.a byte.a time.a
+ ./load sv unix.a byte.a time.a
+
svwaitup: load svwaitup.o unix.a byte.a time.a
./load svwaitup unix.a byte.a time.a
@@ -61,6 +64,9 @@ runsvstat.o: compile sysdeps runsvstat.c
runsvctrl.o: compile runsvctrl.c
./compile runsvctrl.c
+sv.o: compile sysdeps sv.c
+ ./compile sv.c
+
svwaitup.o: compile sysdeps svwaitup.c
./compile svwaitup.c
diff --git a/src/TARGETS b/src/TARGETS
@@ -10,6 +10,8 @@ runsvstat
runsvstat.o
runsvctrl
runsvctrl.o
+sv
+sv.o
svwaitdown
svwaitdown.o
svwaitup
diff --git a/src/sv.c b/src/sv.c
@@ -0,0 +1,318 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "str.h"
+#include "strerr.h"
+#include "error.h"
+#include "sgetopt.h"
+#include "open.h"
+#include "env.h"
+#include "buffer.h"
+#include "fmt.h"
+#include "scan.h"
+#include "tai.h"
+#include "taia.h"
+
+#define USAGE " [-v] [-w sec] action service ..."
+#define USAGELSB " [-w sec] action"
+
+#define VERSION "$Id$"
+
+#define FATAL "fatal: "
+#define FAIL "fail: "
+#define WARN "warning: "
+#define OK "ok: "
+
+/* #define VARSERVICE "./" */
+#define VARSERVICE "/var/service/"
+/* #define ETCRUNITRUNSV "/etc/runit/runsv/" */
+#define USLEEPDELAY 250000
+
+char *progname;
+char *action;
+char actch =0;
+char **service;
+char **servicex;
+unsigned int services;
+unsigned int rc =0;
+unsigned int lsb;
+unsigned int verbose =0;
+unsigned long wait =7;
+struct taia tstart, tnow, tdiff;
+struct tai tstatus;
+
+int (*act)() =0;
+int (*cbk)() =0;
+
+int curdir, fd, r;
+char svstatus[20];
+char sulong[FMT_ULONG];
+
+void usage() {
+ if (!lsb) strerr_die4x(100, "usage: ", progname, USAGE, "\n");
+ strerr_die4x(2, "usage: ", progname, USAGELSB, "\n");
+}
+void done(unsigned int e) { if (curdir != -1) fchdir(curdir); _exit(e); }
+void fatal(char *m1) {
+ strerr_warn3(FATAL, m1, ": ", &strerr_sys);
+ done(lsb ? 150 : 100); }
+void fatal2(char *m1, char *m2) {
+ strerr_warn4(FATAL, m1, m2, ": ", &strerr_sys);
+ done(lsb ? 150 : 100);
+}
+void out(char *p, char *m1) {
+ buffer_puts(buffer_1, p);
+ buffer_puts(buffer_1, *service);
+ buffer_puts(buffer_1, ": ");
+ buffer_puts(buffer_1, m1);
+ if (errno) {
+ buffer_puts(buffer_1, ": ");
+ buffer_puts(buffer_1, error_str(errno));
+ }
+ buffer_puts(buffer_1, "\n");
+ buffer_flush(buffer_1);
+}
+void fail(char *m1) { ++rc; out(FAIL, m1); }
+void failx(char *m1) { errno =0; fail(m1); }
+void warn(char *m1) { ++rc; out(WARN, m1); }
+void warnx(char *m1) { errno =0; warn(m1); }
+void ok(char *m1) { errno =0; out(OK, m1); }
+
+int svstatus_get() {
+ if ((fd =open_write("supervise/ok")) == -1) {
+ if (errno == error_nodevice) {
+ actch == 'x' ? ok("runsv not running") : failx("runsv not running");
+ return(0);
+ }
+ warn("unable to open supervise/ok");
+ return(-1);
+ }
+ close(fd);
+ if ((fd =open_read("supervise/status")) == -1) {
+ warn("unable to open supervise/status");
+ return(-1);
+ }
+ r =read(fd, svstatus, 20);
+ close(fd);
+ switch(r) {
+ case 20: break;
+ case -1: warn("unable to read supervise/status"); return(-1);
+ default: warnx("unable to read supervise/status: bad format"); return(-1);
+ }
+ return(1);
+}
+unsigned int svstatus_print() {
+ int pid;
+ int normallyup =0;
+ struct stat s;
+
+ if (stat("down", &s) == -1) {
+ if (errno != error_noent) {
+ warn("unable to stat down");
+ return(0);
+ }
+ normallyup =1;
+ }
+ buffer_puts(buffer_1, *service);
+ // buffer_puts(buffer_1, ": status: ");
+ buffer_puts(buffer_1, ": ");
+
+ pid =(unsigned char) svstatus[15];
+ pid <<=8; pid +=(unsigned char)svstatus[14];
+ pid <<=8; pid +=(unsigned char)svstatus[13];
+ pid <<=8; pid +=(unsigned char)svstatus[12];
+ tai_unpack(svstatus, &tstatus);
+ if (pid) {
+ switch (svstatus[19]) {
+ case 1: buffer_puts(buffer_1, "run "); break;
+ case 2: buffer_puts(buffer_1, "finish "); break;
+ }
+ buffer_puts(buffer_1, "(pid ");
+ buffer_put(buffer_1, sulong, fmt_ulong(sulong, pid));
+ buffer_puts(buffer_1, ") ");
+ }
+ else
+ buffer_puts(buffer_1, "down ");
+ buffer_put(buffer_1, sulong,
+ fmt_ulong(sulong, tnow.sec.x < tstatus.x ? 0 : tnow.sec.x -tstatus.x));
+ buffer_puts(buffer_1, "s");
+ if (pid && !normallyup) buffer_puts(buffer_1,", normally down");
+ if (!pid && normallyup) buffer_puts(buffer_1,", normally up");
+ if (pid && svstatus[16]) buffer_puts(buffer_1,", paused");
+ if (!pid && (svstatus[17] == 'u')) buffer_puts(buffer_1,", want up");
+ if (pid && (svstatus[17] == 'd')) buffer_puts(buffer_1,", want down");
+ if (pid && svstatus[18]) buffer_puts(buffer_1, ", got TERM");
+ buffer_puts(buffer_1, "\n");
+ buffer_flush(buffer_1);
+ return(pid ? 1 : 2);
+}
+int status() {
+ r =svstatus_get();
+ switch(r) { case -1: if (lsb) done(4); case 0: return(0); }
+ buffer_puts(buffer_1, "status: ");
+ r =svstatus_print();
+ if (lsb) switch(r) { case 1: done(0); case 2: done(3); case 0: done(4); }
+ return(r);
+}
+
+int check() {
+ unsigned int pid;
+
+ /*
+ r =svstatus_get();
+ switch(r) { case 0: ++rc; case -1: return(-1); }
+ */
+ if (svstatus_get() <= 0) return(-1);
+ pid =(unsigned char) svstatus[15];
+ pid <<=8; pid +=(unsigned char)svstatus[14];
+ pid <<=8; pid +=(unsigned char)svstatus[13];
+ pid <<=8; pid +=(unsigned char)svstatus[12];
+ switch (actch) {
+ case 'u': if (!pid) return(0); break;
+ case 'd': if (pid) return(0); break;
+ case 't':
+ tai_unpack(svstatus, &tstatus);
+ if ((tstart.sec.x > tstatus.x) || ! pid || svstatus[18]) return(0);
+ break;
+ case 'o':
+ tai_unpack(svstatus, &tstatus);
+ if ((! pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
+ return(0);
+ }
+ buffer_puts(buffer_1, OK);
+ svstatus_print();
+ return(1);
+}
+int check_shtdown() {
+ if ((r =svstatus_get()) == -1) return(-1);
+ if (r == 0) return(1);
+ return(0);
+}
+
+int control() {
+ if (svstatus_get() <= 0) return(-1);
+ if (svstatus[17] == actch) return(0);
+ if ((fd =open_write("supervise/control")) == -1) {
+ if (errno != error_nodevice)
+ warn("unable to open supervise/control");
+ else
+ actch == 'x' ? ok("runsv not running") : failx("runsv not running");
+ return(-1);
+ }
+ r =write(fd, &actch, 1);
+ close(fd);
+ if (r != 1) {
+ warn("unable to write to supervise/control");
+ return(-1);
+ }
+ return(1);
+}
+
+int main(int argc, char **argv) {
+ unsigned int i, done;
+ char *x;
+
+ progname =*argv;
+ for (i =str_len(*argv); i; --i) if ((*argv)[i -1] == '/') break;
+ *argv +=i;
+ optprogname =progname =*argv;
+ service =argv;
+ services =1;
+ lsb =(str_diff(progname, "sv"));
+ if ((x =env_get("SVWAIT"))) scan_ulong(x, &wait);
+ while ((i =getopt(argc, (const char* const*)argv, "w:vV")) != opteof) {
+ switch(i) {
+ case 'w': scan_ulong(optarg, &wait);
+ case 'v': verbose =1; break;
+ case 'V': strerr_warn1("$Id$", 0);
+ case '?': usage();
+ }
+ }
+ argv +=optind; argc -=optind;
+ if (!(action =*argv++)) usage(); --argc;
+ if (!lsb) {
+ service =argv;
+ services =argc;
+ }
+ if (! *service) usage();
+
+ taia_now(&tnow); tstart =tnow;
+ if ((curdir =open_read(".")) == -1) fatal("unable to open current directory");
+
+ act =&control;
+ if (verbose) cbk =✓
+ switch (*action) {
+ case 'x': case 'e': action ="x";
+ case 'u': case 'd': case 'o': case 't': case 'p': case 'c': case 'h':
+ case 'a': case 'i': case 'k': case 'q': case '1': case '2':
+ actch =*action; break;
+ case 's':
+ if (!str_diff(action, "shutdown")) {
+ actch ='x'; cbk =&check_shtdown; break;
+ }
+ if (!str_diff(action, "start")) { actch ='u'; cbk =✓ break; }
+ if (!str_diff(action, "stop")) { actch ='d'; cbk =✓ break; }
+ if (lsb && str_diff(action, "status")) usage();
+ act =&status; cbk =0; break;
+ case 'r':
+ if (!str_diff(action, "restart")) { actch ='t'; cbk =✓ break; }
+ usage();
+ case 'f':
+ if (!str_diff(action, "force-reload")) { actch ='t'; cbk =✓ break; }
+ default:
+ usage();
+ }
+
+ servicex =service;
+ for (i =0; i < services; ++i) {
+ if ((**service != '/') && (**service != '.')) {
+ if ((chdir(VARSERVICE) == -1) || (chdir(*service) == -1)) {
+ fail("unable to change to service directory");
+ *service =0;
+ }
+ }
+ else
+ if (chdir(*service) == -1) {
+ fail("unable to change to service directory");
+ *service =0;
+ }
+ if (*service) if (act() == -1) *service =0;
+ if (fchdir(curdir) == -1) fatal("unable to change to original directory");
+ service++;
+ }
+
+ if (*cbk)
+ for (;;) {
+ taia_sub(&tdiff, &tnow, &tstart);
+ service =servicex; done =1;
+ for (i =0; i < services; ++i, ++service) {
+ if (! *service) continue;
+ if ((**service != '/') && (**service != '.')) {
+ if ((chdir(VARSERVICE) == -1) || (chdir(*service) == -1)) {
+ fail("unable to change to service directory");
+ *service =0;
+ }
+ }
+ else
+ if (chdir(*service) == -1) {
+ fail("unable to change to service directory");
+ *service =0;
+ }
+ if (taia_approx(&tdiff) > wait) {
+ buffer_puts(buffer_1, "timeout ");
+ sulong[fmt_ulong(sulong, wait)] =0;
+ buffer_puts(buffer_1, sulong);
+ buffer_puts(buffer_1, "s: ");
+ if (svstatus_get() > 0) { svstatus_print(); ++rc; }
+ *service =0;
+ }
+ if (*service) { if (cbk() != 0) *service =0; else done =0; }
+ if (fchdir(curdir) == -1)
+ fatal("unable to change to original directory");
+ }
+ if (done) break;
+ usleep(USLEEPDELAY);
+ taia_now(&tnow);
+ }
+ return(rc);
+}