diff options
Diffstat (limited to 'runit/runsvdir.c')
-rw-r--r-- | runit/runsvdir.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/runit/runsvdir.c b/runit/runsvdir.c new file mode 100644 index 000000000..9238eec82 --- /dev/null +++ b/runit/runsvdir.c @@ -0,0 +1,306 @@ +/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ +/* TODO: depends on runit_lib.c - review and reduce/eliminate */ + +#include <sys/poll.h> +#include <sys/file.h> +#include "busybox.h" +#include "runit_lib.h" + +#define MAXSERVICES 1000 + +static char *svdir; +static unsigned long dev; +static unsigned long ino; +static struct service { + unsigned long dev; + unsigned long ino; + int pid; + int isgone; +} *sv; +static int svnum; +static int check = 1; +static char *rplog; +static int rploglen; +static int logpipe[2]; +static iopause_fd io[1]; +static struct taia stamplog; +static int exitsoon; +static int pgrp; + +#define usage() bb_show_usage() +static void fatal2_cannot(char *m1, char *m2) +{ + bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2); + /* was exiting 100 */ +} +static void warn3x(char *m1, char *m2, char *m3) +{ + bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3); +} +static void warn2_cannot(char *m1, char *m2) +{ + warn3x("cannot ", m1, m2); +} +static void warnx(char *m1) +{ + warn3x(m1, "", ""); +} + +static void s_term(int sig_no) +{ + exitsoon = 1; +} +static void s_hangup(int sig_no) +{ + exitsoon = 2; +} + +static void runsv(int no, char *name) +{ + int pid = fork(); + + if (pid == -1) { + warn2_cannot("fork for ", name); + return; + } + if (pid == 0) { + /* child */ + char *prog[3]; + + prog[0] = "runsv"; + prog[1] = name; + prog[2] = 0; + sig_uncatch(sig_hangup); + sig_uncatch(sig_term); + if (pgrp) setsid(); + execvp(prog[0], prog); + //pathexec_run(*prog, prog, (char* const*)environ); + fatal2_cannot("start runsv ", name); + } + sv[no].pid = pid; +} + +static void runsvdir(void) +{ + DIR *dir; + direntry *d; + int i; + struct stat s; + + dir = opendir("."); + if (!dir) { + warn2_cannot("open directory ", svdir); + return; + } + for (i = 0; i < svnum; i++) + sv[i].isgone = 1; + errno = 0; + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + if (stat(d->d_name, &s) == -1) { + warn2_cannot("stat ", d->d_name); + errno = 0; + continue; + } + if (!S_ISDIR(s.st_mode)) continue; + for (i = 0; i < svnum; i++) { + if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) { + sv[i].isgone = 0; + if (!sv[i].pid) + runsv(i, d->d_name); + break; + } + } + if (i == svnum) { + /* new service */ + struct service *svnew = realloc(sv, (i+1) * sizeof(*sv)); + if (!svnew) { + warn3x("cannot start runsv ", d->d_name, + " too many services"); + continue; + } + sv = svnew; + svnum++; + memset(&sv[i], 0, sizeof(sv[i])); + sv[i].ino = s.st_ino; + sv[i].dev = s.st_dev; + //sv[i].pid = 0; + //sv[i].isgone = 0; + runsv(i, d->d_name); + check = 1; + } + } + if (errno) { + warn2_cannot("read directory ", svdir); + closedir(dir); + check = 1; + return; + } + closedir(dir); + + /* SIGTERM removed runsv's */ + for (i = 0; i < svnum; i++) { + if (!sv[i].isgone) + continue; + if (sv[i].pid) + kill(sv[i].pid, SIGTERM); + sv[i] = sv[--svnum]; + check = 1; + } +} + +static int setup_log(void) +{ + rploglen = strlen(rplog); + if (rploglen < 7) { + warnx("log must have at least seven characters"); + return 0; + } + if (pipe(logpipe) == -1) { + warnx("cannot create pipe for log"); + return -1; + } + coe(logpipe[1]); + coe(logpipe[0]); + ndelay_on(logpipe[0]); + ndelay_on(logpipe[1]); + if (fd_copy(2, logpipe[1]) == -1) { + warnx("cannot set filedescriptor for log"); + return -1; + } + io[0].fd = logpipe[0]; + io[0].events = IOPAUSE_READ; + taia_now(&stamplog); + return 1; +} + +int runsvdir_main(int argc, char **argv) +{ + struct stat s; + time_t mtime = 0; + int wstat; + int curdir; + int pid; + struct taia deadline; + struct taia now; + struct taia stampcheck; + char ch; + int i; + + argv++; + if (!argv || !*argv) usage(); + if (**argv == '-') { + switch (*(*argv + 1)) { + case 'P': pgrp = 1; + case '-': ++argv; + } + if (!argv || !*argv) usage(); + } + + sig_catch(sig_term, s_term); + sig_catch(sig_hangup, s_hangup); + svdir = *argv++; + if (argv && *argv) { + rplog = *argv; + if (setup_log() != 1) { + rplog = 0; + warnx("log service disabled"); + } + } + curdir = open_read("."); + if (curdir == -1) + fatal2_cannot("open current directory", ""); + coe(curdir); + + taia_now(&stampcheck); + + for (;;) { + /* collect children */ + for (;;) { + pid = wait_nohang(&wstat); + if (pid <= 0) break; + for (i = 0; i < svnum; i++) { + if (pid == sv[i].pid) { + /* runsv has gone */ + sv[i].pid = 0; + check = 1; + break; + } + } + } + + taia_now(&now); + if (now.sec.x < (stampcheck.sec.x - 3)) { + /* time warp */ + warnx("time warp: resetting time stamp"); + taia_now(&stampcheck); + taia_now(&now); + if (rplog) taia_now(&stamplog); + } + if (taia_less(&now, &stampcheck) == 0) { + /* wait at least a second */ + taia_uint(&deadline, 1); + taia_add(&stampcheck, &now, &deadline); + + if (stat(svdir, &s) != -1) { + if (check || s.st_mtime != mtime + || s.st_ino != ino || s.st_dev != dev + ) { + /* svdir modified */ + if (chdir(svdir) != -1) { + mtime = s.st_mtime; + dev = s.st_dev; + ino = s.st_ino; + check = 0; + if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime)) + sleep(1); + runsvdir(); + while (fchdir(curdir) == -1) { + warn2_cannot("change directory, pausing", ""); + sleep(5); + } + } else + warn2_cannot("change directory to ", svdir); + } + } else + warn2_cannot("stat ", svdir); + } + + if (rplog) { + if (taia_less(&now, &stamplog) == 0) { + write(logpipe[1], ".", 1); + taia_uint(&deadline, 900); + taia_add(&stamplog, &now, &deadline); + } + } + taia_uint(&deadline, check ? 1 : 5); + taia_add(&deadline, &now, &deadline); + + sig_block(sig_child); + if (rplog) + iopause(io, 1, &deadline, &now); + else + iopause(0, 0, &deadline, &now); + sig_unblock(sig_child); + + if (rplog && (io[0].revents | IOPAUSE_READ)) + while (read(logpipe[0], &ch, 1) > 0) + if (ch) { + for (i = 6; i < rploglen; i++) + rplog[i-1] = rplog[i]; + rplog[rploglen-1] = ch; + } + + switch (exitsoon) { + case 1: + _exit(0); + case 2: + for (i = 0; i < svnum; i++) + if (sv[i].pid) + kill(sv[i].pid, SIGTERM); + _exit(111); + } + } + /* not reached */ + return 0; +} |