runit

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 4eca94e0fb382af9cfa7e9bcb504cd6989e8f885
parent ff28e6be59f57663c9c4dbd3ae2e2ef79cb02e05
Author: Gerrit Pape <pape@smarden.org>
Date:   Mon, 23 Sep 2002 10:12:50 +0000

  * runsv, runsvdir: new; svscan/supervise replacement.
0.5.2.

Diffstat:
MMakefile | 2+-
Mdebian/changelog | 6++++++
Mdebian/postinst | 2+-
Mdebian/rules | 48++++++++++++++++++++++++------------------------
Mdoc/install.html | 10+++++-----
Mdoc/replaceinit.html | 4++--
Mpackage/CHANGES | 4++++
Mpackage/commands | 2++
Mpackage/upgrade | 4++--
Msrc/Makefile | 16++++++++++++++--
Msrc/TARGETS | 4++++
Asrc/runsv.c | 470+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/runsvdir.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 777 insertions(+), 37 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ DESTDIR= -PACKAGE=runit-0.5.0 +PACKAGE=runit-0.5.2 DIRS=doc man etc package src MANPAGES=man/runit.8 man/runit-init.8 man/svwaitdown.8 man/svwaitup.8 \ man/utmpset.8 diff --git a/debian/changelog b/debian/changelog @@ -1,3 +1,9 @@ +runit (0.5.2-1) sarge; urgency=low + + * See /package/admin/runit/package/CHANGES. + + -- Gerrit Pape <pape@smarden.org> Mon, 23 Sep 2002 12:06:09 +0200 + runit (0.5.0-1) sarge; urgency=low * See /package/admin/runit/package/CHANGES. diff --git a/debian/postinst b/debian/postinst @@ -25,7 +25,7 @@ set -e case "$1" in configure) ( \ - cd /package/admin/runit-0.5.0 + cd /package/admin/runit-0.5.2 package/upgrade ) ;; diff --git a/debian/rules b/debian/rules @@ -23,15 +23,15 @@ build-stamp: # $(MAKE) #/usr/bin/docbook-to-man debian/runit.sgml > runit.1 - tar xfzvp runit-0.5.0.tar.gz + tar xfzvp runit-0.5.2.tar.gz ( \ - cd admin/runit-0.5.0 ; \ + cd admin/runit-0.5.2 ; \ echo 'diet gcc -O2 -Wall' > src/conf-cc ; \ echo 'diet gcc -s -Os -pipe' > src/conf-ld ; \ package/compile ; \ - echo "/package/admin/runit-0.5.0" > compile/home ; \ + echo "/package/admin/runit-0.5.2" > compile/home ; \ rm -f compile/src ; \ - ln -s /package/admin/runit-0.5.0/src compile/src ; \ + ln -s /package/admin/runit-0.5.2/src compile/src ; \ ) touch build-stamp @@ -58,44 +58,44 @@ install: build chmod 1755 debian/runit/package cp -a admin debian/runit/package/ - rm -rf debian/runit/package/admin/runit-0.5.0/compile - cp -a debian/runit/package/admin/runit-0.5.0/man . - rm -rf debian/runit/package/admin/runit-0.5.0/man - cp -a debian/runit/package/admin/runit-0.5.0/doc . - rm -rf debian/runit/package/admin/runit-0.5.0/doc - cp -a debian/runit/package/admin/runit-0.5.0/etc . - rm -rf debian/runit/package/admin/runit-0.5.0/etc - cp -a debian/runit/package/admin/runit-0.5.0/package . + rm -rf debian/runit/package/admin/runit-0.5.2/compile + cp -a debian/runit/package/admin/runit-0.5.2/man . + rm -rf debian/runit/package/admin/runit-0.5.2/man + cp -a debian/runit/package/admin/runit-0.5.2/doc . + rm -rf debian/runit/package/admin/runit-0.5.2/doc + cp -a debian/runit/package/admin/runit-0.5.2/etc . + rm -rf debian/runit/package/admin/runit-0.5.2/etc + cp -a debian/runit/package/admin/runit-0.5.2/package . # /etc/runit - cp -p admin/runit-0.5.0/etc/debian/[123] \ + cp -p admin/runit-0.5.2/etc/debian/[123] \ debian/runit/etc/runit/ - cp -p admin/runit-0.5.0/etc/debian/ctrlaltdel \ + cp -p admin/runit-0.5.2/etc/debian/ctrlaltdel \ debian/runit/etc/runit/ - cp -p admin/runit-0.5.0/etc/debian/getty-tty5/run \ + cp -p admin/runit-0.5.2/etc/debian/getty-tty5/run \ debian/runit/etc/runit/getty-5/run # runit programs - cp -p debian/runit/package/admin/runit-0.5.0/command/runit* \ + cp -p debian/runit/package/admin/runit-0.5.2/command/runit* \ debian/runit/sbin/ # cleanup - rm -rf debian/runit/package/admin/runit-0.5.0/compile + rm -rf debian/runit/package/admin/runit-0.5.2/compile # create debian/links rm -f debian/links for i in \ - `cat debian/runit/package/admin/runit-0.5.0/package/commands` ; \ + `cat debian/runit/package/admin/runit-0.5.2/package/commands` ; \ do \ echo "/package/admin/runit/command/$$i /command/$$i" \ >> debian/links ; \ done -# for i in \ -# `cat debian/runit/package/admin/runit-0.5.0/package/commands` ; \ -# do \ -# echo "/command/$$i /usr/local/bin/$$i" \ -# >> debian/links ; \ -# done + for i in \ + `cat debian/runit/package/admin/runit-0.5.2/package/commands` ; \ + do \ + echo "/command/$$i /usr/local/bin/$$i" \ + >> debian/links ; \ + done # Build architecture-independent files here. diff --git a/doc/install.html b/doc/install.html @@ -13,14 +13,14 @@ Check that you have the recent version of <a href="http://cr.yp.to/daemontools.html">daemontools</a> installed. <p> Download -<a href="runit-0.5.0.tar.gz">runit-0.5.0.tar.gz</a> into <tt>/package</tt> +<a href="runit-0.5.2.tar.gz">runit-0.5.2.tar.gz</a> into <tt>/package</tt> and unpack the archive <pre> # cd /package - # gunzip runit-0.5.0.tar - # tar -xpf runit-0.5.0.tar - # rm runit-0.5.0.tar - # cd admin/runit-0.5.0 + # gunzip runit-0.5.2.tar + # tar -xpf runit-0.5.2.tar + # rm runit-0.5.2.tar + # cd admin/runit-0.5.2 </pre> Compile and install the <i>runit</i> programs <pre> diff --git a/doc/replaceinit.html b/doc/replaceinit.html @@ -107,7 +107,7 @@ default Unix process no 1 <i>runit</i>. </pre> To report success: <pre> - # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.5.0@smarden.org + # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.5.2@smarden.org </pre> <hr> @@ -187,7 +187,7 @@ Use <b>init 6</b> to reboot and <b>init 0</b> to halt a system that runs <p> To report success: <pre> - # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.5.0@smarden.org + # ( uname -a ; cat /etc/runit/[123] ) | mail pape-runit-0.5.2@smarden.org </pre> <h3>Step 5: Service migration</h3> The goal is to migrate all services from <i>/etc/rc.*</i> scheme to the diff --git a/package/CHANGES b/package/CHANGES @@ -1,3 +1,7 @@ +runit 0.5.2 +Mon, 23 Sep 2002 12:07:42 +0200 + * runsv, runsvdir: new; svscan/supervise replacement. + runit 0.5.0 Wed, 28 Aug 2002 11:18:28 +0200 * utmpset: avoids libutil; compiles with deitlibc; built by default. diff --git a/package/commands b/package/commands @@ -1,5 +1,7 @@ runit runit-init +runsv +runsvdir svwaitdown svwaitup utmpset diff --git a/package/upgrade b/package/upgrade @@ -7,9 +7,9 @@ test -d src || ( echo 'Wrong working directory.'; exit 1 ) here=`env - PATH=$PATH pwd` parent=`dirname $here` -echo 'Creating symlink runit -> runit-0.5.0...' +echo 'Creating symlink runit -> runit-0.5.2...' rm -f runit -ln -s runit-0.5.0 runit +ln -s runit-0.5.2 runit mv -f runit .. echo 'Making command links in /command...' diff --git a/src/Makefile b/src/Makefile @@ -1,4 +1,4 @@ -IT=runit runit-init svwaitup svwaitdown utmpset sysdeps +IT=sysdeps runit runit-init runsv runsvdir svwaitup svwaitdown utmpset default: $(IT) @@ -8,18 +8,30 @@ runit: load runit.o unix.a byte.a runit-init: load runit-init.o unix.a byte.a ./load runit-init unix.a byte.a -static +runsv: load runsv.o unix.a byte.a time.a + ./load runsv unix.a byte.a time.a + +runsvdir: load runsvdir.o unix.a byte.a time.a + ./load runsvdir unix.a byte.a time.a + svwaitup: load svwaitup.o unix.a byte.a time.a ./load svwaitup unix.a byte.a time.a svwaitdown: load svwaitdown.o unix.a byte.a time.a ./load svwaitdown unix.a byte.a time.a -runit.o: compile runit.c iopause.h uint64.h +runit.o: compile sysdeps runit.c ./compile runit.c runit-init.o: compile runit-init.c ./compile runit-init.c +runsv.o: compile sysdeps runsv.c + ./compile runsv.c + +runsvdir.o: compile runsvdir.c direntry.h iopause.h uint64.h + ./compile runsvdir.c + svwaitup.o: compile svwaitup.c uint64.h ./compile svwaitup.c diff --git a/src/TARGETS b/src/TARGETS @@ -2,6 +2,10 @@ runit runit.o runit-init runit-init.o +runsv +runsv.o +runsvdir +runsvdir.o svwaitdown svwaitdown.o svwaitup diff --git a/src/runsv.c b/src/runsv.c @@ -0,0 +1,470 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include "strerr.h" +#include "error.h" +#include "taia.h" +#include "sig.h" +#include "env.h" +#include "coe.h" +#include "ndelay.h" +#include "fifo.h" +#include "open.h" +#include "lock.h" +#include "iopause.h" +#include "wait.h" +#include "fd.h" +#include "buffer.h" +#include "fmt.h" + +#define USAGE " dir" + +#define VERSION "$Id$" + +char *progname; +int selfpipe[2]; + +/* state */ +#define S_DOWN 0 +#define S_RUN 1 +#define S_FINISH 2 +/* ctrl */ +#define C_NOOP 0 +#define C_TERM 1 +#define C_PAUSE 2 +/* want */ +#define W_UP 0 +#define W_DOWN 1 +#define W_EXIT 2 + +struct svdir { + int pid; + int state; + int ctrl; + int want; + struct taia start; + int fdlock; + int fdcontrol; + int fdcontrolwrite; + // int fdok; + int islog; +}; +struct svdir svd[2]; + +int haslog =0; +int logpipe[2]; +char *dir; + +void usage () { + strerr_die4x(1, "usage: ", progname, USAGE, "\n"); +} +void fatal(char *m) { + strerr_die5sys(111, "runsv ", dir, ": fatal: ", m, ": "); +} +void fatal2(char *m1, char *m2) { + strerr_die6sys(111, "runsv ", dir, ": fatal: ", m1, m2, ": "); +} +void warn(char *m) { + strerr_warn5("runsv ", dir, ": warning: ", m, ": ", &strerr_sys); +} +void warnx(char *m) { + strerr_warn4("runsv ", dir, ": warning: ", m, 0); +} + +void stopservice(struct svdir *); + +void s_child() { + write(selfpipe[1], "", 1); +} +void s_term() { + svd[0].want =W_EXIT; + stopservice(&svd[0]); + write(selfpipe[1], "", 1); +} + +void update_status(struct svdir *s) { + unsigned long l; + int fd; + char status[19]; + char bspace[64]; + buffer b; + char spid[FMT_ULONG]; + + if (s->islog) { + if ((fd =open_trunc("log/supervise/state")) == -1) + fatal("unable to open log/supervise/state"); + } + else { + if ((fd =open_trunc("supervise/state")) == -1) + fatal("unable to open supervise/state"); + } + buffer_init(&b, buffer_unixwrite, fd, bspace, sizeof bspace); + spid[fmt_ulong(spid, (unsigned long)s->pid)] =0; + switch (s->state) { + case S_DOWN: + buffer_puts(&b, "down"); + break; + case S_RUN: + buffer_puts(&b, "run"); + break; + case S_FINISH: + buffer_puts(&b, "finish"); + break; + } + if (s->pid) { + buffer_puts(&b, " pid ("); + buffer_puts(&b, spid); + buffer_puts(&b, ")"); + } + if (s->ctrl & C_PAUSE) + buffer_puts(&b, ", paused"); + if (s->ctrl & C_TERM) + buffer_puts(&b, ", got TERM"); + switch(s->want) { + case W_DOWN: + if (s->state != S_DOWN) buffer_puts(&b, ", want down"); + break; + case W_EXIT: + buffer_puts(&b, ", want exit"); + break; + } + buffer_putsflush(&b, "\n"); + close(fd); + + /* supervise compatibility */ + taia_pack(status, &s->start); + l =(unsigned long)s->pid; + status[12] =l; l >>=8; + status[13] =l; l >>=8; + status[14] =l; l >>=8; + status[15] =l; + if (s->ctrl & C_PAUSE) + status[16] =1; + else + status[16] =0; + if (s->want == W_UP) + status[17] ='u'; + else + status[17] ='d'; + if (s->ctrl & C_TERM) + status[18] =1; + else + status[18] =0; + if ((fd =open_trunc("supervise/status.new")) == -1) { + warn("unable to open supervise/status.new"); + return; + } + if ((l =write(fd, status, sizeof status)) == -1) { + warn("unable to write supervise/status.new"); + close(fd); + unlink("supervise/status.new"); + return; + } + close(fd); + if (l < sizeof status) { + warnx("unable to write supervise/status.new: partial write."); + return; + } + if (s->islog) { + if (rename("supervise/status.new", "log/supervise/status") == -1) + warn("unable to rename supervise/status.new to log/supervise/status"); + } + else { + if (rename("supervise/status.new", "supervise/status") == -1) + warn("unable to rename supervise/status.new to supervise/status"); + } +} + +void stopservice(struct svdir *s) { + if (s->pid) kill(s->pid, SIGTERM); + s->ctrl |=C_TERM; + update_status(s); +} + +void startservice(struct svdir *s) { + int p; + char *run[2]; + + if (s->state == S_FINISH) + run[0] ="./finish"; + else + run[0] ="./run"; + run[1] =0; + + if (s->pid != 0) stopservice(s); /* should never happen */ + while ((p =fork()) == -1) { + warn("unable to fork, sleeping"); + sleep(5); + } + if (p == 0) { + /* child */ + if (haslog) { + if (s->islog) { + if (fd_copy(0, logpipe[0]) == -1) + fatal("unable to setup filedescriptor for ./log/run"); + if (chdir("./log") == -1) + fatal("unable to change directory ./log"); + } + else { + if (fd_copy(1, logpipe[1]) == -1) + fatal("unable to setup filedescriptor for ./run"); + } + } + sig_uncatch(sig_child); + sig_unblock(sig_child); + sig_uncatch(sig_term); + sig_unblock(sig_term); + execve(*run, run, environ); + if (s->islog) + fatal2("unable to start log/", *run); + else + fatal2("unable to start ", *run); + } + if (s->state != S_FINISH) + taia_now(&s->start); + s->pid =p; + s->ctrl =C_NOOP; + if (s->state != S_FINISH) s->state =S_RUN; + update_status(s); + sleep(1); +} + +int ctrl(struct svdir *s, char c) { + switch(c) { + case 'd': /* down */ + s->want =W_DOWN; + if (s->pid && s->state != S_FINISH) stopservice(s); + else update_status(s); + break; + case 'u': /* up */ + s->want =W_UP; + if (s->pid == 0) startservice(s); + else update_status(s); + break; + case 'x': /* exit */ + if (s->islog) break; + s->want =W_EXIT; + if (s->pid && s->state != S_FINISH) + stopservice(s); + break; + case 't': /* sig term */ + if (s->pid && s->state != S_FINISH) stopservice(s); + break; + case 'k': /* sig kill */ + if (s->pid) kill(s->pid, SIGKILL); + s->state =S_DOWN; + break; + case 'p': /* sig pause */ + kill(s->pid, SIGSTOP); + s->ctrl |=C_PAUSE; + update_status(s); + break; + case 'c': /* sig cont */ + kill(s->pid, SIGCONT); + if (s->ctrl & C_PAUSE) + s->ctrl &=~C_PAUSE; + update_status(s); + break; + case 'o': /* once */ + s->want =W_DOWN; + if (! s->pid) startservice(s); + else update_status(s); + break; + case 'a': /* sig alarm */ + if (s->pid) kill(s->pid, SIGALRM); + break; + case 'h': /* sig hup */ + if (s->pid) kill(s->pid, SIGHUP); + break; + case 'i': /* sig int */ + if (s->pid) kill(s->pid, SIGINT); + break; + } + return(1); +} + +int main(int argc, char **argv) { + struct stat s; + int fd; + + progname =argv[0]; + if (! argv[1] || argv[2]) usage(); + dir =argv[1]; + + if (pipe(selfpipe) == -1) + fatal("unable to create selfpipe"); + coe(selfpipe[0]); + coe(selfpipe[1]); + ndelay_on(selfpipe[0]); + ndelay_on(selfpipe[1]); + + sig_block(sig_child); + sig_catch(sig_child, s_child); + sig_block(sig_term); + sig_catch(sig_term, s_term); + + if (chdir(dir) == -1) + fatal("unable to change to directory"); + svd[0].pid =0; + svd[0].state =S_DOWN; + svd[0].ctrl =C_NOOP; + svd[0].want =W_UP; + svd[0].islog =0; + taia_now(&svd[0].start); + if (stat("down", &s) != -1) + svd[0].want =W_DOWN; + + if (stat("log", &s) == -1) { + if (errno != error_noent) + warn("unable to stat() ./log: "); + } + else { + if (! S_ISDIR(s.st_mode)) + warnx("./log: not a directory."); + else { + haslog =1; + svd[1].pid =0; + svd[1].state =S_DOWN; + svd[1].ctrl =C_NOOP; + svd[1].want =W_UP; + svd[1].islog =1; + taia_now(&svd[1].start); + if (stat("log/down", &s) != -1) + svd[1].want =W_DOWN; + } + } + + if (haslog) { + if (pipe(logpipe) == -1) + fatal("unable to create log pipe"); + coe(logpipe[0]); + coe(logpipe[1]); + } + + mkdir("supervise", 0700); + if ((svd[0].fdlock =open_append("supervise/lock")) == -1) + fatal("unable to open lock."); + if (lock_exnb(svd[0].fdlock) == -1) + fatal("unable to lock."); + if (haslog) { + mkdir("log/supervise", 0700); + if ((svd[1].fdlock =open_append("log/supervise/lock")) == -1) + fatal("unable to open log/lock."); + if (lock_ex(svd[1].fdlock) == -1) + fatal("unable to log/lock."); + } + + fifo_make("supervise/control", 0600); + if ((svd[0].fdcontrol =open_read("supervise/control")) == -1) + fatal("unable to open supervise/control"); + coe(svd[0].fdcontrol); + if ((svd[0].fdcontrolwrite =open_write("supervise/control")) == -1) + fatal("unable to open supervise/control"); + coe(svd[0].fdcontrolwrite); + update_status(&svd[0]); + if (haslog) { + fifo_make("log/supervise/control", 0600); + if ((svd[1].fdcontrol =open_read("log/supervise/control")) == -1) + fatal("unable to open log/supervise/control"); + coe(svd[1].fdcontrol); + if ((svd[1].fdcontrolwrite =open_write("log/supervise/control")) == -1) + fatal("unable to open log/supervise/control"); + coe(svd[1].fdcontrolwrite); + update_status(&svd[1]); + } + fifo_make("supervise/ok",0600); + if ((fd =open_read("supervise/ok")) == -1) + fatal("unable to read supervise/ok"); + coe(fd); + if (haslog) { + fifo_make("log/supervise/ok",0600); + if ((fd =open_read("log/supervise/ok")) == -1) + fatal("unable to read log/supervise/ok"); + coe(fd); + } + for (;;) { + iopause_fd x[3]; + struct taia deadline; + struct taia now; + char ch; + + if (haslog) + if (! svd[1].pid && svd[1].want == W_UP) startservice(&svd[1]); + if (! svd[0].pid && svd[0].want == W_UP) startservice(&svd[0]); + + x[0].fd =selfpipe[0]; + x[0].events =IOPAUSE_READ; + x[1].fd =svd[0].fdcontrol; + x[1].events =IOPAUSE_READ; + if (haslog) { + x[2].fd =svd[1].fdcontrol; + x[2].events =IOPAUSE_READ; + } + taia_now(&now); + taia_uint(&deadline, 3600); + taia_add(&deadline, &now, &deadline); + + sig_unblock(sig_term); + sig_unblock(sig_child); + iopause(x, 2 +haslog, &deadline, &now); + sig_block(sig_term); + sig_block(sig_child); + + while (read(selfpipe[0], &ch, 1) == 1) + ; + for (;;) { + int child; + int wstat; + + child = wait_nohang(&wstat); + if (!child) break; + if ((child == -1) && (errno != error_intr)) break; + if (child == svd[0].pid) { + svd[0].pid =0; + if (open_read("finish") != -1) { + svd[0].state =S_FINISH; + startservice(&svd[0]); + update_status(&svd[0]); + break; + } + svd[0].state =S_DOWN; + taia_now(&svd[0].start); + update_status(&svd[0]); + if (svd[0].want == W_UP) { + startservice(&svd[0]); + break; + } + } + if (haslog) { + if (child == svd[1].pid) { + svd[1].pid =0; + svd[1].state =S_DOWN; + taia_now(&svd[1].start); + update_status(&svd[1]); + if (svd[1].want == W_UP) { + startservice(&svd[1]); + break; + } + } + } + } + if (read(svd[0].fdcontrol, &ch, 1) == 1) + ctrl(&svd[0], ch); + if (haslog) { + if (read(svd[1].fdcontrol, &ch, 1) == 1) + ctrl(&svd[1], ch); + } + + if (svd[0].want == W_EXIT && svd[0].pid == 0) { + if (svd[1].pid == 0) + exit(0); + if (svd[1].want != W_EXIT) { + svd[1].want =W_EXIT; + stopservice(&svd[1]); + } + } + } + exit(0); +} diff --git a/src/runsvdir.c b/src/runsvdir.c @@ -0,0 +1,242 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include "direntry.h" +#include "strerr.h" +#include "error.h" +#include "wait.h" +#include "env.h" +#include "pathexec.h" +#include "fd.h" +#include "str.h" +#include "coe.h" +#include "iopause.h" +#include "sig.h" +#include "ndelay.h" + +#define USAGE " dir" +#define VERSION "$Id$" + +#define MAXSERVICES 1000 + +char *progname; +char *svdir; +struct { + unsigned long dev; + unsigned long ino; + int pid; + int isgone; +} sv[MAXSERVICES]; +int svnum =0; +int check =1; +char *svlog =0; +int svloglen; +int logpipe[2]; +iopause_fd io[1]; +struct taia stamplog; + +void usage () { + strerr_die4x(1, "usage: ", progname, USAGE, "\n"); +} +void fatal(char *m1, char *m2) { + strerr_die6sys(100, "runsvdir ", svdir, ": fatal: ", m1, m2, ": "); +} +void warn(char *m1, char *m2) { + strerr_warn6("runsvdir ", svdir, ": warning: ", m1, m2, ": ", &strerr_sys); +} +void warn3x(char *m1, char *m2, char *m3) { + strerr_warn6("runsvdir ", svdir, ": warning: ", m1, m2, m3, 0); +} +void runsv(int no, char *name) { + struct stat s; + int pid; + + if (stat(name, &s) == -1) { + warn("unable to stat ", name); + return; + } + if (! S_ISDIR(s.st_mode)) + return; + if ((pid =fork()) == -1) { + warn("unable to fork for ", name); + return; + } + if (pid == 0) { + /* child */ + const char *prog[3]; + + prog[0] ="runsv"; + prog[1] =name; + prog[2] =0; + if (svlog) + if (fd_move(2, logpipe[1]) == -1) + warn("unable to set filedescriptor for log service", 0); + pathexec_run(*prog, prog, (const char* const*)environ); + fatal("unable to start runsv ", name); + } + sv[no].pid =pid; +} + +void runsvdir() { + DIR *dir; + direntry *d; + int i; + struct stat s; + + if (! (dir =opendir("."))) { + warn("unable to 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) { + warn("unable to stat ", d->d_name); + return; + } + 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 */ + if (svnum >= MAXSERVICES) { + warn3x("unable to start runsv ", d->d_name, ": too many services."); + continue; + } + sv[i].ino =s.st_ino; + sv[i].dev =s.st_dev; + sv[i].pid =0; + sv[i].isgone =0; + svnum++; + runsv(i, d->d_name); + } + } + if (errno) { + warn("unable to read directory ", svdir); + closedir(dir); + 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; + } +} + +int setup_svlog() { + if ((svloglen =str_len(svlog)) < 7) { + warn3x("log must have at least seven characters.", 0, 0); + return(0); + } + if (pipe(logpipe) == -1) { + warn3x("unable to create pipe for log.", 0, 0); + return(-1); + } + coe(logpipe[1]); + coe(logpipe[0]); + ndelay_on(logpipe[0]); + ndelay_on(logpipe[1]); + if (fd_copy(2, logpipe[1]) == -1) { + warn3x("unable to set filedescriptor for log.", 0, 0); + return(-1); + } + io[0].fd =logpipe[0]; + io[0].events =IOPAUSE_READ; + taia_now(&stamplog); + return(1); +} + +int main(int argc, char **argv) { + struct stat s; + time_t mtime =0; + int wstat; + int pid; + struct taia deadline; + struct taia now; + char ch; + int i; + + progname =*argv++; + if (! argv || ! *argv) usage(); + + svdir =*argv++; + if (argv && *argv) { + svlog =*argv; + if (setup_svlog() != 1) { + svlog =0; + warn3x("log service disabled.", 0, 0); + } + } + + if (chdir(svdir) == -1) + fatal("unable to change directory to ", svdir); + + for (;;) { + /* collect children */ + for (;;) { + if ((pid =wait_nohang(&wstat)) <= 0) break; + for (i =0; i < svnum; i++) { + if (pid == sv[i].pid) { + /* runsv has gone */ + sv[i].pid =0; + check =1; + break; + } + } + } + if (stat(".", &s) != -1) { + if (check || s.st_mtime > mtime) { + /* svdir modified */ + mtime =s.st_mtime; + check =0; + runsvdir(); + } + } + else + warn("unable to stat ", svdir); + + taia_now(&now); + if (svlog) + if (taia_less(&now, &stamplog) == 0) { + write(logpipe[1], ".", 1); + taia_uint(&deadline, 900); + taia_add(&stamplog, &stamplog, &deadline); + } + taia_uint(&deadline, 5); + taia_add(&deadline, &now, &deadline); + + sig_block(sig_child); + if (svlog) + iopause(io, 1, &deadline, &now); + else + iopause(0, 0, &deadline, &now); + sig_unblock(sig_child); + + if (svlog) + if (io[0].revents | IOPAUSE_READ) { + while (read(logpipe[0], &ch, 1) > 0) { + if (ch) { + for (i =6; i < svloglen; i++) + svlog[i -1] =svlog[i]; + svlog[svloglen -1] =ch; + } + } + } + } + /* not reached */ + exit(0); +}