/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+//usage:#define sv_trivial_usage
+//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
+//usage:#define sv_full_usage "\n\n"
+//usage: "Control services monitored by runsv supervisor.\n"
+//usage: "Commands (only first character is enough):\n"
+//usage: "\n"
+//usage: "status: query service status\n"
+//usage: "up: if service isn't running, start it. If service stops, restart it\n"
+//usage: "once: like 'up', but if service stops, don't restart it\n"
+//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
+//usage: " if it exists. After it stops, don't restart service\n"
+//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
+//usage: " runsv exits too\n"
+//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
+//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
+
#include <sys/poll.h>
#include <sys/file.h>
#include "libbb.h"
/* "Bernstein" time format: unix + 0x400000000000000aULL */
uint64_t tstart, tnow;
svstatus_t svstatus;
-};
+} FIX_ALIASING;
#define G (*(struct globals*)&bb_common_bufsiz1)
#define acts (G.acts )
#define service (G.service )
#define INIT_G() do { } while (0)
+#define str_equal(s,t) (!strcmp((s), (t)))
+
+
static void fatal_cannot(const char *m1) NORETURN;
static void fatal_cannot(const char *m1)
{
{
int fd, r;
- fd = open_write("supervise/ok");
+ fd = open("supervise/ok", O_WRONLY|O_NDELAY);
if (fd == -1) {
if (errno == ENODEV) {
*acts == 'x' ? ok("runsv not running")
: failx("runsv not running");
return 0;
}
- warn("cannot open supervise/ok");
+ warn("can't open supervise/ok");
return -1;
}
close(fd);
- fd = open_read("supervise/status");
+ fd = open("supervise/status", O_RDONLY|O_NDELAY);
if (fd == -1) {
- warn("cannot open supervise/status");
+ warn("can't open supervise/status");
return -1;
}
r = read(fd, &svstatus, 20);
case 20:
break;
case -1:
- warn("cannot read supervise/status");
+ warn("can't read supervise/status");
return -1;
default:
errno = 0;
- warn("cannot read supervise/status: bad format");
+ warn("can't read supervise/status: bad format");
return -1;
}
return 1;
if (stat("down", &s) == -1) {
if (errno != ENOENT) {
- bb_perror_msg(WARN"cannot stat %s/down", *service);
+ bb_perror_msg(WARN"can't stat %s/down", *service);
return 0;
}
normallyup = 1;
{
int r;
- r = svstatus_get();
- switch (r) { case -1: case 0: return 0; }
+ if (svstatus_get() <= 0)
+ return 0;
r = svstatus_print(*service);
if (chdir("log") == -1) {
if (errno != ENOENT) {
- printf("; log: "WARN"cannot change to log service directory: %s",
+ printf("; log: "WARN"can't change to log service directory: %s",
strerror(errno));
}
} else if (svstatus_get()) {
if (stat("check", &s) == -1) {
if (errno == ENOENT) return 1;
- bb_perror_msg(WARN"cannot stat %s/check", *service);
+ bb_perror_msg(WARN"can't stat %s/check", *service);
return 0;
}
/* if (!(s.st_mode & S_IXUSR)) return 1; */
prog[1] = NULL;
pid = spawn(prog);
if (pid <= 0) {
- bb_perror_msg(WARN"cannot %s child %s/check", "run", *service);
+ bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
return 0;
}
while (safe_waitpid(pid, &w, 0) == -1) {
- bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service);
+ bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
return 0;
}
- return !wait_exitcode(w);
+ return WEXITSTATUS(w) == 0;
}
static int check(const char *a)
{
int r;
- unsigned pid;
+ unsigned pid_le32;
uint64_t timestamp;
r = svstatus_get();
return 1;
return -1;
}
- pid = SWAP_LE32(svstatus.pid_le32);
+ pid_le32 = svstatus.pid_le32;
switch (*a) {
case 'x':
return 0;
case 'u':
- if (!pid || svstatus.run_or_finish != 1) return 0;
+ if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
if (!checkscript()) return 0;
break;
case 'd':
- if (pid) return 0;
+ if (pid_le32) return 0;
break;
case 'c':
- if (pid && !checkscript()) return 0;
+ if (pid_le32 && !checkscript()) return 0;
break;
case 't':
- if (!pid && svstatus.want == 'd') break;
+ if (!pid_le32 && svstatus.want == 'd') break;
timestamp = SWAP_BE64(svstatus.time_be64);
- if ((tstart > timestamp) || !pid || svstatus.got_term || !checkscript())
+ if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
return 0;
break;
case 'o':
timestamp = SWAP_BE64(svstatus.time_be64);
- if ((!pid && tstart > timestamp) || (pid && svstatus.want != 'd'))
+ if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
return 0;
}
printf(OK);
static int control(const char *a)
{
- int fd, r;
+ int fd, r, l;
+/* Is it an optimization?
+ It causes problems with "sv o SRV; ...; sv d SRV"
+ ('d' is not passed to SRV because its .want == 'd'):
if (svstatus_get() <= 0)
return -1;
if (svstatus.want == *a)
return 0;
- fd = open_write("supervise/control");
+*/
+ fd = open("supervise/control", O_WRONLY|O_NDELAY);
if (fd == -1) {
if (errno != ENODEV)
- warn("cannot open supervise/control");
+ warn("can't open supervise/control");
else
*a == 'x' ? ok("runsv not running") : failx("runsv not running");
return -1;
}
- r = write(fd, a, strlen(a));
+ l = strlen(a);
+ r = write(fd, a, l);
close(fd);
- if (r != strlen(a)) {
- warn("cannot write to supervise/control");
+ if (r != l) {
+ warn("can't write to supervise/control");
return -1;
}
return 1;
}
int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int sv_main(int argc, char **argv)
+int sv_main(int argc UNUSED_PARAM, char **argv)
{
- unsigned opt;
- unsigned i, want_exit;
char *x;
char *action;
- const char *varservice = "/var/service/";
- unsigned services;
- char **servicex;
+ const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
unsigned waitsec = 7;
smallint kll = 0;
int verbose = 0;
if (x) waitsec = xatou(x);
opt_complementary = "w+:vv"; /* -w N, -v is a counter */
- opt = getopt32(argv, "w:v", &waitsec, &verbose);
- argc -= optind;
+ getopt32(argv, "w:v", &waitsec, &verbose);
argv += optind;
action = *argv++;
if (!action || !*argv) bb_show_usage();
- service = argv;
- services = argc - 1;
- tnow = time(0) + 0x400000000000000aULL;
+ tnow = time(NULL) + 0x400000000000000aULL;
tstart = tnow;
- curdir = open_read(".");
+ curdir = open(".", O_RDONLY|O_NDELAY);
if (curdir == -1)
fatal_cannot("open current directory");
bb_show_usage();
}
- servicex = service;
- for (i = 0; i < services; ++i) {
- if ((**service != '/') && (**service != '.')) {
+ service = argv;
+ while ((x = *service) != NULL) {
+ if (x[0] != '/' && x[0] != '.') {
if (chdir(varservice) == -1)
goto chdir_failed_0;
}
- if (chdir(*service) == -1) {
+ if (chdir(x) == -1) {
chdir_failed_0:
- fail("cannot change to service directory");
+ fail("can't change to service directory");
goto nullify_service_0;
}
if (act && (act(acts) == -1)) {
nullify_service_0:
- *service = NULL;
+ *service = (char*) -1L; /* "dead" */
}
if (fchdir(curdir) == -1)
fatal_cannot("change to original directory");
}
if (cbk) while (1) {
+ int want_exit;
int diff;
diff = tnow - tstart;
- service = servicex;
+ service = argv;
want_exit = 1;
- for (i = 0; i < services; ++i, ++service) {
- if (!*service)
- continue;
- if ((**service != '/') && (**service != '.')) {
+ while ((x = *service) != NULL) {
+ if (x == (char*) -1L) /* "dead" */
+ goto next;
+ if (x[0] != '/' && x[0] != '.') {
if (chdir(varservice) == -1)
goto chdir_failed;
}
- if (chdir(*service) == -1) {
+ if (chdir(x) == -1) {
chdir_failed:
- fail("cannot change to service directory");
+ fail("can't change to service directory");
goto nullify_service;
}
if (cbk(acts) != 0)
if (diff >= waitsec) {
printf(kll ? "kill: " : "timeout: ");
if (svstatus_get() > 0) {
- svstatus_print(*service);
+ svstatus_print(x);
++rc;
}
bb_putchar('\n'); /* will also flush the output */
if (kll)
control("k");
nullify_service:
- *service = NULL;
+ *service = (char*) -1L; /* "dead" */
}
if (fchdir(curdir) == -1)
fatal_cannot("change to original directory");
+ next:
+ service++;
}
if (want_exit) break;
usleep(420000);
- tnow = time(0) + 0x400000000000000aULL;
+ tnow = time(NULL) + 0x400000000000000aULL;
}
return rc > 99 ? 99 : rc;
}