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:
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);
+}